fix: 10 dakikada bir havuz 120 kayda düşüyordu

Sorun: önbellek süresi dolunca getTenders cache.tenders'ı ilk partiyle (120 ilan)
DEĞİŞTİRİYOR, kalanı arka planda dolduruyordu. Yani her 10 dakikada bir kullanıcı
birkaç dakika boyunca eksik liste görüyordu (canlıda 8.258 havuza karşı 5.319
yüklü olarak yakalandı).

Çözüm: yenilemede mevcut dolu liste sunulmaya devam ediyor; yeni liste ayrı
kurulup TAMAMLANINCA atomik olarak devreye alınıyor. Yalnızca ilk açılışta
(elde hiç veri yokken) ilk parti anında gösteriliyor.

Doğrulama: zorla yenileme sırasında liste 3.795'te sabit kaldı (öncesinde 120).
This commit is contained in:
2026-08-29 03:03:03 +03:00
parent c701083642
commit 667b335ea7
+27 -17
View File
@@ -292,11 +292,14 @@ function ilanlariBirlestir(mevcut, yeniAds) {
return eklenen; return eklenen;
} }
// Kalan sayfaları arka planda çeker; kullanıcı beklemez, havuz zamanla dolar. // Kalan sayfaları arka planda çeker. Yeni liste ayrı kurulur ve TAMAMLANINCA
async function arkaPlandaTamamla(numFound) { // atomik olarak devreye alınır; böylece yenileme sırasında kullanıcı hiçbir
// zaman eksik listeye düşmez (önceden her yenilemede 120 kayda iniyordu).
async function arkaPlandaTamamla(numFound, ilkListe) {
if (arkaPlanDoluyor) return; if (arkaPlanDoluyor) return;
arkaPlanDoluyor = true; arkaPlanDoluyor = true;
try { try {
const yeni = [...ilkListe];
const gerekliSayfa = Math.min(MAX_PAGES, Math.ceil(numFound / LIST_SIZE)); const gerekliSayfa = Math.min(MAX_PAGES, Math.ceil(numFound / LIST_SIZE));
let page = ILK_PARTI; let page = ILK_PARTI;
while (page < gerekliSayfa) { while (page < gerekliSayfa) {
@@ -304,15 +307,17 @@ async function arkaPlandaTamamla(numFound) {
const sonuclar = await ilanSayfalari(page, grup); const sonuclar = await ilanSayfalari(page, grup);
const ads = sonuclar.flatMap((r) => r.ads || []); const ads = sonuclar.flatMap((r) => r.ads || []);
if (!ads.length) break; if (!ads.length) break;
const eklenen = ilanlariBirlestir(cache.tenders, ads); const eklenen = ilanlariBirlestir(yeni, ads);
if (eklenen.length) cache.tenders = [...cache.tenders, ...eklenen]; if (eklenen.length) yeni.push(...eklenen);
page += grup; page += grup;
if (page % 60 === 0) {
console.log(`[ihaleist-api] arka plan: ${cache.tenders.length}/${numFound} ilan yüklendi`);
}
await new Promise((r) => setTimeout(r, 120)); // kaynağı yormamak için await new Promise((r) => setTimeout(r, 120)); // kaynağı yormamak için
} }
console.log(`[ihaleist-api] havuz dolduruldu: ${cache.tenders.length}/${numFound} ilan`); // Yalnızca daha zengin bir liste elde ettiysek devreye al
if (yeni.length >= cache.tenders.length) {
cache.tenders = yeni;
cache.todayCount = yeni.filter((t) => t.isYeni).length;
}
console.log(`[ihaleist-api] havuz hazır: ${cache.tenders.length}/${numFound} ilan`);
} catch (e) { } catch (e) {
console.warn('[ihaleist-api] arka plan doldurma hatası:', e.message); console.warn('[ihaleist-api] arka plan doldurma hatası:', e.message);
} finally { } finally {
@@ -324,24 +329,29 @@ async function getTenders(force = false) {
const fresh = cache.tenders && Date.now() - cache.fetchedAt < CACHE_TTL_MS; const fresh = cache.tenders && Date.now() - cache.fetchedAt < CACHE_TTL_MS;
if (fresh && !force) return cache; if (fresh && !force) return cache;
// İlk parti: paralel, hızlı — kullanıcı bunu bekler
const ilk = await ilanSayfalari(0, ILK_PARTI); const ilk = await ilanSayfalari(0, ILK_PARTI);
const numFound = ilk.find((r) => r.numFound)?.numFound || 0; const numFound = ilk.find((r) => r.numFound)?.numFound || 0;
const ads = ilk.flatMap((r) => r.ads || []); const ads = ilk.flatMap((r) => r.ads || []);
const seen = new Set(); const seen = new Set();
cache.tenders = ads const ilkListe = ads
.filter((ad) => (seen.has(ad.id) ? false : seen.add(ad.id))) .filter((ad) => (seen.has(ad.id) ? false : seen.add(ad.id)))
.map(mapAd); .map(mapAd);
cache.totalCount = numFound || cache.tenders.length;
cache.todayCount = cache.tenders.filter((t) => t.isYeni).length;
cache.fetchedAt = Date.now();
console.log(`[ihaleist-api] ${cache.tenders.length} ihale yüklendi (havuz: ${cache.totalCount}) — kalanı arka planda`);
// Kalanı arka planda doldur (yanıtı bloklamaz) cache.totalCount = numFound || ilkListe.length;
if (cache.totalCount > cache.tenders.length) { cache.fetchedAt = Date.now();
arkaPlandaTamamla(cache.totalCount);
// İlk yükleme: elde hiçbir şey yoksa ilk partiyi hemen göster.
// Yenileme: eldeki dolu listeyi KORU, yenisi arka planda hazırlansın.
if (!cache.tenders || cache.tenders.length === 0) {
cache.tenders = ilkListe;
cache.todayCount = ilkListe.filter((t) => t.isYeni).length;
console.log(`[ihaleist-api] ilk yükleme: ${ilkListe.length} ilan (havuz ${cache.totalCount}) — kalanı arka planda`);
} else {
console.log(`[ihaleist-api] yenileme başladı (mevcut ${cache.tenders.length} ilan sunulmaya devam ediyor)`);
} }
if (cache.totalCount > ilkListe.length) arkaPlandaTamamla(cache.totalCount, ilkListe);
return cache; return cache;
} }