Feature: Scheduler-Tick-Zaehlung korrigieren und Sitzungstotale einfuehren
Der Scheduler-Tab meldete nach erfolgreicher Verarbeitung faelschlich "keine neuen Dokumente". Ursache war ein hartkodiertes RunSummary.noOp() im BatchRunTrigger der Bootstrap; der echte Lauf-Summary wurde nie gelesen. - Bootstrap: BatchRunProgressObserver erfasst RunSummary aus onRunEnded und uebersetzt ihn in den ausgehenden RunSummary fuer das Tick-Ergebnis - Neuer Wert-Typ SchedulerSessionTotals (success/failed) plus Optional-Feld in SchedulerStatus - DefaultSchedulerControlUseCase setzt die Totale beim start() auf null zurueck, summiert pro Started-Tick auf, friert sie beim stop() ein - GuiSchedulerTab zeigt pro Tick "X verarbeitet, Y Fehler" oder "keine neuen Dokumente" sowie ein zusaetzliches Label "Seit Scheduler-Start: X verarbeitet, Y Fehler" Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+58
@@ -0,0 +1,58 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.port.in;
|
||||
|
||||
/**
|
||||
* Aggregierte Zähler über alle abgeschlossenen Ticks der laufenden bzw. zuletzt
|
||||
* gelaufenen Scheduler-Sitzung.
|
||||
* <p>
|
||||
* Eine Sitzung beginnt mit dem nächsten erfolgreichen
|
||||
* {@link SchedulerControlUseCase#start()} und endet mit dem zugehörigen
|
||||
* {@link SchedulerControlUseCase#stop()}. Beim Start einer neuen Sitzung
|
||||
* werden die Zähler auf null zurückgesetzt; nach dem Stopp bleiben sie
|
||||
* eingefroren sichtbar, bis der Scheduler erneut gestartet wird.
|
||||
* <p>
|
||||
* Übersprungene Dokumente werden in dieser Sitzungsstatistik bewusst nicht
|
||||
* gezählt, da sie für den Bediener keine neue Verarbeitungsleistung darstellen.
|
||||
*
|
||||
* @param successCount Summe aller erfolgreich verarbeiteten Dokumente seit
|
||||
* Sitzungsstart; nie negativ
|
||||
* @param failedCount Summe aller fehlgeschlagenen Dokumente seit Sitzungsstart
|
||||
* (retryable und final zusammengefasst); nie negativ
|
||||
*/
|
||||
public record SchedulerSessionTotals(int successCount, int failedCount) {
|
||||
|
||||
/**
|
||||
* Validiert, dass beide Zähler nicht negativ sind.
|
||||
*
|
||||
* @throws IllegalArgumentException wenn einer der Zähler kleiner als null ist
|
||||
*/
|
||||
public SchedulerSessionTotals {
|
||||
if (successCount < 0 || failedCount < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"SchedulerSessionTotals counts must not be negative; was: "
|
||||
+ successCount + "/" + failedCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert ein neutrales Ausgangsobjekt mit beiden Zählern auf null.
|
||||
*
|
||||
* @return Sitzungstotal mit allen Zählern auf null; nie {@code null}
|
||||
*/
|
||||
public static SchedulerSessionTotals zero() {
|
||||
return new SchedulerSessionTotals(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert ein neues Sitzungstotal, in dem die übergebenen Werte additiv
|
||||
* aufgenommen wurden. Das aktuelle Objekt bleibt unverändert.
|
||||
*
|
||||
* @param additionalSuccess hinzuzurechnende Erfolgreich-Zahl; muss ≥ 0 sein
|
||||
* @param additionalFailed hinzuzurechnende Fehler-Zahl; muss ≥ 0 sein
|
||||
* @return aufaddiertes Sitzungstotal; nie {@code null}
|
||||
*/
|
||||
public SchedulerSessionTotals plus(int additionalSuccess, int additionalFailed) {
|
||||
return new SchedulerSessionTotals(
|
||||
successCount + additionalSuccess,
|
||||
failedCount + additionalFailed);
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -26,13 +26,19 @@ import de.gecheckt.pdf.umbenenner.application.port.out.RunSummary;
|
||||
* @param lastError letzte aufgetretene deutsche Fehlermeldung;
|
||||
* wird bei erfolgreichem Lauf gelöscht,
|
||||
* bei {@code SkippedBusy} unverändert gelassen
|
||||
* @param sessionTotals aggregierte Zähler seit dem letzten Sitzungsstart;
|
||||
* leer vor dem allerersten {@code start()}, ab dem
|
||||
* ersten erfolgreichen Start gefüllt und bei jedem
|
||||
* weiteren Start auf null zurückgesetzt; nach dem
|
||||
* Stopp bleibt der eingefrorene Endwert sichtbar
|
||||
*/
|
||||
public record SchedulerStatus(
|
||||
SchedulerState state,
|
||||
Optional<Instant> lastRunEndedAt,
|
||||
Optional<RunSummary> lastRunSummary,
|
||||
Optional<Instant> nextTickAt,
|
||||
Optional<String> lastError
|
||||
Optional<String> lastError,
|
||||
Optional<SchedulerSessionTotals> sessionTotals
|
||||
) {
|
||||
|
||||
/**
|
||||
@@ -54,6 +60,9 @@ public record SchedulerStatus(
|
||||
if (lastError == null) {
|
||||
throw new IllegalArgumentException("lastError darf nicht null sein");
|
||||
}
|
||||
if (sessionTotals == null) {
|
||||
throw new IllegalArgumentException("sessionTotals darf nicht null sein");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +79,7 @@ public record SchedulerStatus(
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
Optional.empty()
|
||||
);
|
||||
}
|
||||
|
||||
+18
-4
@@ -1,6 +1,7 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.usecase;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerControlUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerSessionTotals;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerStartException;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerState;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerStatus;
|
||||
@@ -132,9 +133,15 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
"Scheduler-Adapter konnte nicht gestartet werden.", e);
|
||||
}
|
||||
|
||||
// Schritt 4: Zustand auf RUNNING_IDLE setzen
|
||||
// Schritt 4: Zustand auf RUNNING_IDLE setzen, Sitzungstotal auf null zurücksetzen
|
||||
Instant nextTick = Instant.now().plusSeconds(intervalSeconds);
|
||||
statusRef.updateAndGet(s -> withState(s, SchedulerState.RUNNING_IDLE, Optional.of(nextTick)));
|
||||
statusRef.updateAndGet(s -> new SchedulerStatus(
|
||||
SchedulerState.RUNNING_IDLE,
|
||||
s.lastRunEndedAt(),
|
||||
s.lastRunSummary(),
|
||||
Optional.of(nextTick),
|
||||
s.lastError(),
|
||||
Optional.of(SchedulerSessionTotals.zero())));
|
||||
logger.info("Scheduler gestartet. Intervall: {} Sekunden.", intervalSeconds);
|
||||
}
|
||||
|
||||
@@ -247,12 +254,17 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
Optional<Instant> lastRunEndedAt = afterBatch.lastRunEndedAt();
|
||||
Optional<RunSummary> lastRunSummary = afterBatch.lastRunSummary();
|
||||
Optional<String> lastError = afterBatch.lastError();
|
||||
Optional<SchedulerSessionTotals> sessionTotals = afterBatch.sessionTotals();
|
||||
|
||||
switch (result) {
|
||||
case BatchRunTriggerResult.Started started -> {
|
||||
lastRunEndedAt = Optional.of(started.endedAt());
|
||||
lastRunSummary = Optional.of(started.summary());
|
||||
lastError = Optional.empty();
|
||||
sessionTotals = Optional.of(sessionTotals
|
||||
.orElse(SchedulerSessionTotals.zero())
|
||||
.plus(started.summary().successCount(),
|
||||
started.summary().failedCount()));
|
||||
logger.info("Scheduler-Tick abgeschlossen: {} erfolgreich, {} fehlgeschlagen, {} übersprungen.",
|
||||
started.summary().successCount(),
|
||||
started.summary().failedCount(),
|
||||
@@ -271,7 +283,8 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
lastRunEndedAt,
|
||||
lastRunSummary,
|
||||
nextTickAt,
|
||||
lastError));
|
||||
lastError,
|
||||
sessionTotals));
|
||||
|
||||
if (stopping) {
|
||||
lockPort.releaseLock();
|
||||
@@ -292,6 +305,7 @@ public class DefaultSchedulerControlUseCase implements SchedulerControlUseCase {
|
||||
base.lastRunEndedAt(),
|
||||
base.lastRunSummary(),
|
||||
nextTickAt,
|
||||
base.lastError());
|
||||
base.lastError(),
|
||||
base.sessionTotals());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user