constrow=this._db.prepare('SELECT status, backoff_until FROM adapter_queue WHERE key=?').get(key)as{status?: string;backoff_until?: string|null}|undefined;
this._db.prepare('INSERT OR REPLACE INTO adapter_queue (key,status,last_attempt,retry_count,backoff_until,error,scheduled_for) VALUES (?,?,?,?,?,?,?)').run(key,'pending',null,0,null,null,scheduledFor??null);
"SELECT key, retry_count, backoff_until, scheduled_for FROM adapter_queue WHERE status IN ('pending','backoff') ORDER BY (last_attempt IS NULL) DESC, last_attempt ASC",
this._db.prepare("INSERT INTO queue_errors (queue_key, attempt, error_message, error_stack, ts) VALUES (?,?,?,?,?)").run(job.key,attempt,msg,stack,newDate().toISOString());
if(attempt>=MAX_ATTEMPTS){
this._db.prepare("UPDATE adapter_queue SET status='failed', last_attempt=?, retry_count=?, backoff_until=NULL, error=? WHERE key=?").run(newDate().toISOString(),attempt,msg,job.key);
this._db.prepare("UPDATE adapter_queue SET status='backoff', last_attempt=?, retry_count=?, backoff_until=?, error=? WHERE key=?").run(newDate().toISOString(),attempt,newDate(Date.now()+bo).toISOString(),msg,job.key);
this._db.prepare("UPDATE adapter_queue SET status='pending', backoff_until=NULL, retry_count=0, error=NULL WHERE key=?").run(key);
}
retrySource(kind: string):number{
constres=this._db.prepare(
"UPDATE adapter_queue SET status='pending', backoff_until=NULL, retry_count=0, error=NULL WHERE status IN ('failed','backoff') AND substr(key,1,instr(key,':')-1)=?"
this._db.prepare("INSERT OR REPLACE INTO queue_schedules (source_kind, interval_ms, last_enqueued, next_enqueue) VALUES (?,?,?,?)").run(sourceKind,intervalMs,null,newDate(Date.now()+intervalMs).toISOString());
}
deleteSchedule(sourceKind: string):void{
this._db.prepare("DELETE FROM queue_schedules WHERE source_kind=?").run(sourceKind);
}
listSchedules():ScheduleEntry[]{
constrows=this._db.prepare("SELECT source_kind, interval_ms, last_enqueued, next_enqueue FROM queue_schedules ORDER BY source_kind").all()asArray<{source_kind: string;interval_ms: number;last_enqueued: string|null;next_enqueue: string|null}>;
returnrows.map((r)=>({
sourceKind: r.source_kind,
intervalMs: r.interval_ms,
lastEnqueued: r.last_enqueued,
nextEnqueue: r.next_enqueue,
}));
}
asyncenqueueDueSchedules():Promise<void>{
constnow=newDate().toISOString();
constdue=this._db.prepare("SELECT * FROM queue_schedules WHERE next_enqueue IS NOT NULL AND next_enqueue <= ?").all(now)asArray<{source_kind: string;interval_ms: number;last_enqueued: string|null;next_enqueue: string|null}>;
for(constsofdue){
constsymbols=this._db.prepare("SELECT symbol FROM symbol_demand WHERE in_demand=1").all()asArray<{symbol:string}>;
constrow=this._db.prepare("SELECT SUM(status='pending') AS q, SUM(status='in_flight') AS i, SUM(status='failed') AS f, SUM(status='backoff') AS b, SUM(status='done') AS d, MAX(backoff_until) AS bu FROM adapter_queue").get()as{q: number|null;i: number|null;f: number|null;b: number|null;d: number|null;bu: string|null};
constfailedJobs=this._db.prepare("SELECT key, error FROM adapter_queue WHERE status='failed' AND error IS NOT NULL ORDER BY last_attempt DESC LIMIT 5").all()asArray<{key: string;error: string|null}>;
constrows=this._db.prepare("SELECT attempt, error_message, error_stack, ts FROM queue_errors WHERE queue_key=? ORDER BY attempt DESC LIMIT ?").all(key,limit)asArray<{attempt: number;error_message: string|null;error_stack: string|null;ts: string}>;