feat: Webhook-Warteschlange in Datenbank persistieren
Build and Push Multi-Platform Images / build-and-push (push) Successful in 31s
Build and Push Multi-Platform Images / build-and-push (push) Successful in 31s
Die Warteschlange liegt nicht mehr im Speicher, sondern in der neuen Tabelle webhook_queue (Entity WebhookQueueItem, documentId als Primärschlüssel). So überstehen ausstehende IDs einen Neustart und werden nach dem Boot weiterverarbeitet. - Neue Entity + Migration (CreateWebhookQueue); in data-source.ts/Barrel registriert. Dev: synchronize legt die Tabelle an; Prod: migrationsRun. - enqueue nutzt INSERT IGNORE (atomar, race-sicher) -> Dedup über den PK. - processQueue holt FIFO (createdAt ASC), entfernt die Zeile vor der Verarbeitung, arbeitet sequenziell (isProcessing-Guard). - getStatus/queueSize lesen die DB-Anzahl. Controller awaitet enqueue. - Unit-Test mit simuliertem FIFO-Repo: Dedup, Remove-on-start, Re-Enqueue, No-Parallel (deterministisch). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
66a2cccd20
commit
9718d6888a
8 files changed
+182
-61
No files matched your search
@@ -26,6 +26,7 @@ import {
|
||||
CorrespondentEmailMapping,
|
||||
UserSettings,
|
||||
LabelPrintJob,
|
||||
WebhookQueueItem,
|
||||
} from './entities';
|
||||
|
||||
// CLI-Kontext: .env laden (Laufzeit im Container liefert die Variablen via Docker,
|
||||
@@ -57,6 +58,7 @@ export const entities = [
|
||||
CorrespondentEmailMapping,
|
||||
UserSettings,
|
||||
LabelPrintJob,
|
||||
WebhookQueueItem,
|
||||
];
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
@@ -21,3 +21,4 @@ export { InboxPostprocessingAction } from './inbox-postprocessing-action.entity'
|
||||
export { CorrespondentEmailMapping } from './correspondent-email-mapping.entity';
|
||||
export { UserSettings } from './user-settings.entity';
|
||||
export { LabelPrintJob } from './label-print-job.entity';
|
||||
export { WebhookQueueItem } from './webhook-queue-item.entity';
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Index,
|
||||
} from 'typeorm';
|
||||
|
||||
/**
|
||||
* Persistente Warteschlange der vom Paperless-Webhook gemeldeten Dokument-IDs.
|
||||
* `documentId` ist Primärschlüssel und erzwingt damit, dass jede ID nur einmal
|
||||
* in der Warteschlange steht (Dedup auf DB-Ebene). Die Tabelle übersteht
|
||||
* Neustarts; ausstehende IDs werden nach dem Boot weiterverarbeitet.
|
||||
*/
|
||||
@Entity('webhook_queue')
|
||||
export class WebhookQueueItem {
|
||||
@PrimaryColumn({ type: 'int' })
|
||||
documentId!: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 100, nullable: true })
|
||||
action!: string | null;
|
||||
|
||||
@Index()
|
||||
@CreateDateColumn()
|
||||
createdAt!: Date;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
/**
|
||||
* Legt die persistente Webhook-Warteschlange an (`webhook_queue`).
|
||||
* `documentId` ist Primärschlüssel → jede Dokument-ID kommt nur einmal vor.
|
||||
*/
|
||||
export class CreateWebhookQueue1782700000000 implements MigrationInterface {
|
||||
name = 'CreateWebhookQueue1782700000000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE IF NOT EXISTS \`webhook_queue\` (
|
||||
\`documentId\` int NOT NULL,
|
||||
\`action\` varchar(100) NULL,
|
||||
\`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (\`documentId\`),
|
||||
INDEX \`IDX_webhook_queue_createdAt\` (\`createdAt\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE \`webhook_queue\``);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user