perf: DT detaylari 10 paralel + adaptif fren (0.36 -> 3.2 kayit/sn) & feat: EKAP'tan inen dokumani ihaleye yukleme
This commit is contained in:
+41
-11
@@ -36,7 +36,10 @@ const argVal = (name, def) => {
|
||||
return i >= 0 && args[i + 1] ? Number(args[i + 1]) : def;
|
||||
};
|
||||
const MAX_PAGES = argVal('--pages', 10);
|
||||
const MAX_DETAILS = argVal('--details', 250);
|
||||
const MAX_DETAILS = argVal('--details', 1500);
|
||||
// Detaylar PARALEL çekilir; asıl darboğaz istek başına bekleme idi.
|
||||
const DETAY_PARALEL = Number(process.env.DT_PARALEL || argVal('--paralel', 10));
|
||||
const DETAY_GECIKME_MS = Number(process.env.DT_GECIKME_MS || 400);
|
||||
const DELAY_BASE_MS = 2000;
|
||||
const DELAY_JITTER_MS = 1500;
|
||||
|
||||
@@ -262,7 +265,10 @@ async function run() {
|
||||
await humanDelay();
|
||||
}
|
||||
|
||||
// 2) Eksik detaylar
|
||||
// 2) Eksik detaylar — paralel + adaptif fren
|
||||
// EKAP'ı zorlamamak için: her turda DETAY_PARALEL istek eş zamanlı,
|
||||
// aralarda kısa duraklama. Sunucu direnç gösterirse (challenge/hata)
|
||||
// gecikme kendiliğinden büyür, temiz turlarda tekrar daralır.
|
||||
const pending = db
|
||||
.prepare(
|
||||
// EN YENİ kayıtlar önce: arayüz en yeni 5000'i gösteriyor; varsayılan
|
||||
@@ -273,18 +279,42 @@ async function run() {
|
||||
ORDER BY rowid DESC LIMIT ?`,
|
||||
)
|
||||
.all(MAX_DETAILS);
|
||||
console.log(`[dt-worker] ${pending.length} detay çekilecek`);
|
||||
console.log(`[dt-worker] ${pending.length} detay çekilecek (${DETAY_PARALEL} paralel)`);
|
||||
let ok = 0;
|
||||
for (const row of pending) {
|
||||
try {
|
||||
const detail = await fetchDetail(sess, row.dogrudan_temin_id, row.idare_id);
|
||||
setDetail.run(JSON.stringify(detail), row.dogrudan_temin_id);
|
||||
ok++;
|
||||
} catch (err) {
|
||||
console.warn(`[dt-worker] detay hatası (${row.dogrudan_temin_id}): ${err.message}`);
|
||||
let hata = 0;
|
||||
let gecikme = DETAY_GECIKME_MS;
|
||||
const basladi = Date.now();
|
||||
|
||||
for (let i = 0; i < pending.length; i += DETAY_PARALEL) {
|
||||
const grup = pending.slice(i, i + DETAY_PARALEL);
|
||||
const sonuc = await Promise.all(
|
||||
grup.map(async (row) => {
|
||||
try {
|
||||
const detail = await fetchDetail(sess, row.dogrudan_temin_id, row.idare_id);
|
||||
setDetail.run(JSON.stringify(detail), row.dogrudan_temin_id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.warn(`[dt-worker] detay hatası (${row.dogrudan_temin_id}): ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
);
|
||||
const grupHata = sonuc.filter((x) => !x).length;
|
||||
ok += sonuc.length - grupHata;
|
||||
hata += grupHata;
|
||||
|
||||
// Adaptif fren: hata varsa yavaşla, temizse hızlan (2.5 sn ile 250 ms arası)
|
||||
gecikme = grupHata
|
||||
? Math.min(2500, Math.round(gecikme * 2))
|
||||
: Math.max(250, Math.round(gecikme * 0.9));
|
||||
|
||||
if (i && i % (DETAY_PARALEL * 20) === 0) {
|
||||
const hiz = (ok / ((Date.now() - basladi) / 1000)).toFixed(1);
|
||||
console.log(`[dt-worker] ${ok}/${pending.length} detay (${hiz} kayıt/sn, gecikme ${gecikme}ms, hata ${hata})`);
|
||||
}
|
||||
await humanDelay();
|
||||
if (i + DETAY_PARALEL < pending.length) await sleep(gecikme + Math.floor(Math.random() * 200));
|
||||
}
|
||||
console.log(`[dt-worker] detay turu: ${ok} başarılı, ${hata} hata, ${Math.round((Date.now() - basladi) / 1000)} sn`);
|
||||
|
||||
const total = db.prepare('SELECT COUNT(*) AS c FROM dt_tenders').get().c;
|
||||
console.log(
|
||||
|
||||
+4
-1
@@ -709,6 +709,7 @@ const server = http.createServer(async (req, res) => {
|
||||
mime: body.mime || null,
|
||||
dosya: dosyaAdi,
|
||||
sonKullanma: body.sonKullanma || null,
|
||||
ikn: body.ikn ? String(body.ikn).slice(0, 40) : null, // ihaleye bağlı belge
|
||||
});
|
||||
return json(res, 200, { success: true, id, belgeler: store.listDocuments(user.email), kullanim: store.documentUsage(user.email) });
|
||||
} catch (e) {
|
||||
@@ -719,9 +720,11 @@ const server = http.createServer(async (req, res) => {
|
||||
const ok = store.deleteDocument(user.email, url.searchParams.get('id'));
|
||||
return json(res, ok ? 200 : 404, { success: ok, belgeler: store.listDocuments(user.email), kullanim: store.documentUsage(user.email) });
|
||||
}
|
||||
// ?ikn=... verilirse yalnızca o ihaleye yüklenen belgeler döner
|
||||
const iknFiltre = url.searchParams.get('ikn') || undefined;
|
||||
return json(res, 200, {
|
||||
success: true,
|
||||
belgeler: store.listDocuments(user.email),
|
||||
belgeler: store.listDocuments(user.email, { ikn: iknFiltre }),
|
||||
kullanim: store.documentUsage(user.email),
|
||||
haklar: { adet: haklar(user.email, opts).belgeAdet, bayt: haklar(user.email, opts).belgeBayt },
|
||||
});
|
||||
|
||||
+21
-7
@@ -69,9 +69,11 @@ CREATE TABLE IF NOT EXISTS belgeler (
|
||||
mime TEXT,
|
||||
dosya TEXT NOT NULL, -- diskteki dosya adı
|
||||
son_kullanma TEXT,
|
||||
ikn TEXT, -- belge bir ihaleye aitse İKN/DTN
|
||||
olusturma TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_belge_email ON belgeler(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_belge_ikn ON belgeler(email, ikn);
|
||||
`;
|
||||
|
||||
export function available() { return Boolean(DatabaseSync); }
|
||||
@@ -82,6 +84,16 @@ export function openDb() {
|
||||
fs.mkdirSync(BELGE_DIR, { recursive: true });
|
||||
const db = new DatabaseSync(DB_PATH);
|
||||
db.exec(SCHEMA);
|
||||
// Geçiş: belgeler ihaleye bağlanabilsin diye ikn kolonu eklendi
|
||||
try {
|
||||
const k = db.prepare('PRAGMA table_info(belgeler)').all().map((c) => c.name);
|
||||
if (!k.includes('ikn')) {
|
||||
db.exec('ALTER TABLE belgeler ADD COLUMN ikn TEXT');
|
||||
console.log('[user-store] belgeler.ikn kolonu eklendi');
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[user-store] belge şema geçişi atlandı:', e.message);
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
@@ -212,18 +224,20 @@ export function verifyApiKey(ham) {
|
||||
|
||||
// --- Belgeler (gerçek dosya, gerçek boyut) ---------------------------------
|
||||
|
||||
export function addDocument(email, { ad, kategori, boyut, mime, dosya, sonKullanma }) {
|
||||
export function addDocument(email, { ad, kategori, boyut, mime, dosya, sonKullanma, ikn }) {
|
||||
const id = crypto.randomUUID();
|
||||
withDb((db) => db.prepare(
|
||||
'INSERT INTO belgeler (id,email,ad,kategori,boyut,mime,dosya,son_kullanma,olusturma) VALUES (?,?,?,?,?,?,?,?,?)'
|
||||
).run(id, email, ad, kategori || null, boyut, mime || null, dosya, sonKullanma || null, now()));
|
||||
'INSERT INTO belgeler (id,email,ad,kategori,boyut,mime,dosya,son_kullanma,ikn,olusturma) VALUES (?,?,?,?,?,?,?,?,?,?)'
|
||||
).run(id, email, ad, kategori || null, boyut, mime || null, dosya, sonKullanma || null, ikn || null, now()));
|
||||
return id;
|
||||
}
|
||||
|
||||
export function listDocuments(email) {
|
||||
return withDb((db) => db.prepare(
|
||||
'SELECT id, ad, kategori, boyut, mime, son_kullanma, olusturma FROM belgeler WHERE email=? ORDER BY olusturma DESC'
|
||||
).all(email), []);
|
||||
export function listDocuments(email, { ikn } = {}) {
|
||||
const alanlar = 'id, ad, kategori, boyut, mime, son_kullanma, ikn, olusturma';
|
||||
return withDb((db) => (ikn
|
||||
? db.prepare(`SELECT ${alanlar} FROM belgeler WHERE email=? AND ikn=? ORDER BY olusturma DESC`).all(email, ikn)
|
||||
: db.prepare(`SELECT ${alanlar} FROM belgeler WHERE email=? ORDER BY olusturma DESC`).all(email)
|
||||
), []);
|
||||
}
|
||||
|
||||
export function getDocument(email, id) {
|
||||
|
||||
Reference in New Issue
Block a user