← notlar

2026-03-15

Uzun süren scraping job'larını arka planda yönetmek

Bir fiyat scraping job'ı 30 saniye ile 3 dakika arası sürebiliyor. Bunu senkron API endpoint'inde yapamazsın, timeout yersin. Arka planda çalıştırıp sonucu sonradan almak lazım.

Job lifecycle

Her scraping isteği bir "search job". Durumlar basit:

PENDING → RUNNING → COMPLETED veya FAILED

Client job'ı oluşturuyor, bir ID alıyor, sonra o ID ile sonucu sorguluyor.

Event-driven akış

Spring'in ApplicationEventPublisher'ını kullanıyorum:

1. Job oluştur → SearchJobCreatedEvent
2. Listener scraping başlatır → RawResultReceivedEvent
3. Listener parse eder → RawResultParsedEvent
4. Listener özetler → job COMPLETED

Her adım @Async method'da çalışıyor. Biri patlarsa diğerlerini etkilemiyor.

Önemli detay: @TransactionalEventListener(phase = AFTER_COMMIT) kullanıyorum. Event ancak DB transaction commit olduktan sonra tetikleniyor. Yoksa listener henüz commit olmamış veriyi okumaya çalışıyor.

Thread pool

var executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(6);
executor.setMaxPoolSize(16);
executor.setQueueCapacity(100);

4 core sunucuda 16 max thread makul. Queue 100 job tutuyor.

Takılan job'ları temizle

Bazen job takılıyor, site cevap vermiyor veya captcha döngüsüne giriyor. Scheduled task ile temizliyorum:

@Scheduled(fixedRate = 60000) // her dakika kontrol
public void failStaleJobs() {
    // 30 dakikadır PENDING veya RUNNING olanları FAILED yap
}

JSONB ile parametre saklama

Her job'ın arama parametreleri PostgreSQL JSONB olarak duruyor:

{
  "STATEINC": "1",
  "CHECKIN": "2026-04-15",
  "NIGHTS": "7",
  "CURRENCY": "EUR"
}

Her operatörün farklı parametreleri var, hepsini ayrı kolon yapmak yerine JSONB kullanmak daha esnek. Debug için de güzel: aynı parametrelerle job'ı replay edebiliyorsun.

Parametre validasyonu

Her operatörün desteklediği değerler farklı. Birisi EUR kabul eder, diğeri sadece USD. Yanlış parametre boş sonuç döndürüyor.

operator_search_param tablosunda her operatörün geçerli değerleri kayıtlı. Job oluşturulmadan önce kontrol ediliyor, geçersizse 400.

Queue'ya geçiş hazırlığı

Şu an Spring event'leri kullanıyorum ama mimari queue-ready. ApplicationEventPublisher yerine @KafkaListener veya @RabbitListener koysan aynı şekilde çalışır. Event class'ları değişmez, sadece transport değişir. Baştan event-driven yazmak scale gelince büyük refactor'ı engelliyor.