// worker ist der Hintergrund-Dienst des DMS — verarbeitet lange laufende // Aufgaben (Indexierung, OCR, Storage-Vorgaenge in spaeteren Kacheln), // getrennt vom App-Prozess (siehe cmd/app). DOC-16: verarbeitet zusaetzlich // Requeue-Jobs fuer fehlgeschlagene RET-05-Registrierungen. package main import ( "context" "errors" "log" "os" "os/signal" "syscall" "time" "github.com/jackc/pgx/v5/pgxpool" "gitea.perlbach24.de/scripte/nexarch/dms/internal/jobqueue" "gitea.perlbach24.de/scripte/nexarch/dms/internal/retentionclient" "gitea.perlbach24.de/scripte/nexarch/dms/internal/retentiondestroy" "gitea.perlbach24.de/scripte/nexarch/dms/internal/shared" ) func main() { log.Printf("dms-worker gestartet (version %s)", shared.Version) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() var ( queue *jobqueue.Queue client *retentionclient.Client ) if dsn := os.Getenv("NEXARCH_DMS_WORKER_TENANT_DSN"); dsn != "" { pool, err := pgxpool.New(ctx, dsn) if err != nil { log.Fatalf("dms-worker: datenbankverbindung: %v", err) } queue = jobqueue.NewQueue(pool, 5*time.Minute) client = retentionclient.New(os.Getenv("NEXARCH_DMS_WORKER_RETENTION_BASE_URL")) } else { log.Print("dms-worker: NEXARCH_DMS_WORKER_TENANT_DSN nicht gesetzt, DOC-16-Requeue-Verarbeitung deaktiviert") } ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() for { select { case <-ctx.Done(): log.Println("dms-worker beendet") return case <-ticker.C: if queue != nil { processRetentionRegisterJobs(ctx, queue, client) } } } } // processRetentionRegisterJobs holt und verarbeitet ALLE aktuell // abholbaren doc16_retention_register-Jobs (Akzeptanzkriterium 3: Requeue // statt Absturz/Verwerfen — Fail() haengt selbst nie ab, sondern setzt // pending mit Backoff oder dead_letter, siehe FDN-04). func processRetentionRegisterJobs(ctx context.Context, queue *jobqueue.Queue, client *retentionclient.Client) { for { job, err := queue.Dequeue(ctx, "dms-worker", []string{retentiondestroy.JobTypeRegister}) if err != nil { if !errors.Is(err, jobqueue.ErrNoJobAvailable) { log.Printf("dms-worker: dequeue fehlgeschlagen: %v", err) } return } if err := retentiondestroy.ProcessRegisterJob(ctx, client, job.Payload); err != nil { log.Printf("dms-worker: retention-registrierung erneut fehlgeschlagen (job %s): %v", job.ID, err) if failErr := queue.Fail(ctx, job.ID, err); failErr != nil { log.Printf("dms-worker: job als fehlgeschlagen markieren: %v", failErr) } continue } if err := queue.Complete(ctx, job.ID); err != nil { log.Printf("dms-worker: job abschliessen: %v", err) } else { log.Printf("dms-worker: retention-registrierung erfolgreich nachgeholt (job %s)", job.ID) } } }