fix: Warteschlange blieb stehen – Altersvergleich in die DB verlagert
Build and Push Multi-Platform Images / build-and-push (push) Successful in 32s
Build and Push Multi-Platform Images / build-and-push (push) Successful in 32s
Der Mindest-Wartezeit-Check verglich Date.now() (Node-Uhr) mit dem aus der DB gelesenen createdAt. Ohne gesetzte timezone (mysql2-Default 'local') wird createdAt bei abweichender DB-Zeitzone als zukünftig interpretiert → Alter negativ → jeder Eintrag galt als "zu jung" und wurde nie verarbeitet. Der Vergleich läuft jetzt in der DB (createdAt <= NOW(6) - INTERVAL 5s via TypeORM Raw), unabhängig von Zeitzone/Uhr des Node-Prozesses. Bereits wartende Einträge werden dadurch nach dem Deploy sofort abgearbeitet. Test-Mock bildet den DB-Alters-Filter nach. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -41,16 +41,17 @@ function createQueueRepoMock() {
|
||||
}),
|
||||
})),
|
||||
find: jest.fn(() => {
|
||||
const sorted = [...store].sort(
|
||||
(a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
|
||||
);
|
||||
// Simuliert den DB-seitigen Alters-Filter (createdAt <= NOW() - 5000 ms).
|
||||
const eligible = [...store]
|
||||
.filter((s) => Date.now() - s.createdAt.getTime() >= 5000)
|
||||
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
||||
return Promise.resolve(
|
||||
sorted.length
|
||||
eligible.length
|
||||
? [
|
||||
{
|
||||
documentId: sorted[0].documentId,
|
||||
documentId: eligible[0].documentId,
|
||||
action: null,
|
||||
createdAt: sorted[0].createdAt,
|
||||
createdAt: eligible[0].createdAt,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Interval } from '@nestjs/schedule';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Raw, Repository } from 'typeorm';
|
||||
import { PaperlessProcessorService } from '../paperless/paperless-processor.service';
|
||||
import { Setting } from '../database/entities/setting.entity';
|
||||
import { WebhookQueueItem } from '../database/entities/webhook-queue-item.entity';
|
||||
@@ -99,18 +99,25 @@ export class WebhookQueueService {
|
||||
if (this.isProcessing) return;
|
||||
this.isProcessing = true;
|
||||
try {
|
||||
// Solange Einträge vorhanden sind, einzeln und sequenziell abarbeiten.
|
||||
// Solange (alte genug) Einträge vorhanden sind, sequenziell abarbeiten.
|
||||
for (;;) {
|
||||
// Ältesten Eintrag (FIFO) holen ...
|
||||
// Ältesten Eintrag (FIFO) holen, der die Mindest-Wartezeit erfüllt.
|
||||
// Der Altersvergleich läuft bewusst in der DB (NOW() vs. createdAt) und
|
||||
// ist damit unabhängig von Zeitzone/Uhr des Node-Prozesses – ein Node/DB-
|
||||
// Zeitversatz würde sonst den Vergleich verfälschen (Einträge nie "alt
|
||||
// genug"). Nur ein numerisches Konstantenliteral wird interpoliert.
|
||||
const [next] = await this.queueRepo.find({
|
||||
where: {
|
||||
createdAt: Raw(
|
||||
(alias) =>
|
||||
`${alias} <= (NOW(6) - INTERVAL ${MIN_QUEUE_AGE_MS * 1000} MICROSECOND)`,
|
||||
),
|
||||
},
|
||||
order: { createdAt: 'ASC' },
|
||||
take: 1,
|
||||
});
|
||||
if (!next) break;
|
||||
// Mindest-Wartezeit einhalten: ist der älteste Eintrag noch zu jung,
|
||||
// sind es (FIFO) alle → diesen Tick beenden, beim nächsten erneut prüfen.
|
||||
if (Date.now() - next.createdAt.getTime() < MIN_QUEUE_AGE_MS) break;
|
||||
// ... und SOFORT (vor Verarbeitungsstart) aus der Tabelle entfernen.
|
||||
if (!next) break; // Warteschlange leer oder noch nichts alt genug
|
||||
// SOFORT (vor Verarbeitungsstart) aus der Tabelle entfernen.
|
||||
await this.queueRepo.delete({ documentId: next.documentId });
|
||||
await this.handleDocument(next.documentId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user