Kafayı yiyecektim! Her gece düzenli çalışan, Pandas ve SMTP ile süslenmiş güzelim rapor botum, bir sabah uyandığımda mail kutumun 50 aynı raporla dolduğunu görünce kalakaldım. Meğerse o "akıllı" dostum, işi bitirince kapanmak yerine, kendini yeniden başlatıp aynı işi tekrar yapmış. 50 kez. Veritabanı sorgularından sunucu kaynaklarına her şey allak bullak olmuştu.
Botun Gece Macerası
Olay şuydu: Bot, ana işlevini bir while döngüsü içinde değil de, main() fonksiyonunda yapıyordu. İşlem bittiğinde, script'i çalıştıran cron job veya Task Scheduler durmasını bekliyordu. Ama benim kodumda, ana işlem bittikten sonra arka planda çalışan bir thread veya bir timer event'i, sanki bot hiç başlamamış gibi yeni bir proses tetikliyordu.
Debug Kabusu ve Çözüm
StackOverflow'da bile böyle saçma bir senaryo bulamadım. Logları incelerken, botun her çalışmada aynı process ID ile değil, yepyeni ID'lerle başladığını fark ettim. Meğerse sorun, bir file lock mekanizmasının yanlış implemente edilmesiydi. Bot çalışmaya başlarken bir kilit dosyası oluşturuyor, bitince siliyordu. Ama işlem bitmeden, bir hata veya beklenmedik bir durumda, bu kilit dosyası silinmiyor ve bir sonraki çalışmada "Aa kilit yok, demek ki çalışmıyor" diyerek yeniden başlıyordu. Aslında çalışıyordu!
Çözüm, botun aynı anda sadece bir kopyasının çalışabildiğinden kesinlikle emin olmaktı. Bunun için psutil kütüphanesiyle kendi process ismimi tarayıp, birden fazla varsa çıkış yaptırdım.
Alınan Acı Ders
Şaka gibi ama, otomasyon script'lerinde, özellikle de zamanlanmış görevlerde, idempotency (aynı işlemi tekrar tekrar yapmanın zararsız olması) ve singleton pattern (aynı anda tek bir örneğin çalışması) hayati önemde. Artık her botuma ve script'ime, başlangıçta mutlaka bir "Ben zaten çalışıyor muyum?" kontrolü ekliyorum.
Siz de böyle bir "kendini klonlayan bot" veya "sonsuz döngüye giren cron job" faciası yaşadınız mı? Bu işin daha temiz, daha garantili bir yolu var mı sizce? Yorumlara yazın, birlikte ağlayalım.
Olay şuydu: Bot, ana işlevini bir while döngüsü içinde değil de, main() fonksiyonunda yapıyordu. İşlem bittiğinde, script'i çalıştıran cron job veya Task Scheduler durmasını bekliyordu. Ama benim kodumda, ana işlem bittikten sonra arka planda çalışan bir thread veya bir timer event'i, sanki bot hiç başlamamış gibi yeni bir proses tetikliyordu.
Python:
# Sorunun özü şuna benziyordu:
def gorevi_yap():
# Raporu oluştur, mail at...
print("İşlem tamam!")
def arka_plan_kontrolcusu():
# İşlem bitti sanıp, yenisini başlatan hain kod!
if not is_running: # Bu kontrol TAM çalışmıyordu!
gorevi_yap()
# Ve bu ikisi aynı anda ateşleniyordu.
StackOverflow'da bile böyle saçma bir senaryo bulamadım. Logları incelerken, botun her çalışmada aynı process ID ile değil, yepyeni ID'lerle başladığını fark ettim. Meğerse sorun, bir file lock mekanizmasının yanlış implemente edilmesiydi. Bot çalışmaya başlarken bir kilit dosyası oluşturuyor, bitince siliyordu. Ama işlem bitmeden, bir hata veya beklenmedik bir durumda, bu kilit dosyası silinmiyor ve bir sonraki çalışmada "Aa kilit yok, demek ki çalışmıyor" diyerek yeniden başlıyordu. Aslında çalışıyordu!
Çözüm, botun aynı anda sadece bir kopyasının çalışabildiğinden kesinlikle emin olmaktı. Bunun için psutil kütüphanesiyle kendi process ismimi tarayıp, birden fazla varsa çıkış yaptırdım.
Python:
import psutil, os, sys
current_pid = os.getpid()
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
if proc.info['pid'] != current_pid:
if 'my_report_bot.py' in str(proc.info['cmdline']):
print("Zaten çalışan bir bot var, çıkıyorum!")
sys.exit()
Şaka gibi ama, otomasyon script'lerinde, özellikle de zamanlanmış görevlerde, idempotency (aynı işlemi tekrar tekrar yapmanın zararsız olması) ve singleton pattern (aynı anda tek bir örneğin çalışması) hayati önemde. Artık her botuma ve script'ime, başlangıçta mutlaka bir "Ben zaten çalışıyor muyum?" kontrolü ekliyorum.
Siz de böyle bir "kendini klonlayan bot" veya "sonsuz döngüye giren cron job" faciası yaşadınız mı? Bu işin daha temiz, daha garantili bir yolu var mı sizce? Yorumlara yazın, birlikte ağlayalım.