Wiederanlauf
Fertige Steps werden übersprungen statt wiederholt.
Ein Shell/JCL-ähnlicher Nachtjob verarbeitet Dateien, Datenbankupdates und Reports in einem Lauf. Nach dem Refactoring gibt es Steps, Kontrolltabelle, Checkpoints und klare Wiederanlaufpunkte.
| Aspekt | Beschreibung |
|---|---|
| Symptom | Bei Fehler muss der gesamte Nachtlauf manuell neu gestartet oder per SQL repariert werden. |
| Risiko | Doppelte Dateiimporte, falsche Reports oder verpasster Tagesabschluss. |
| Refactoring-Ziel | Job in stabile Steps schneiden, jeden Step idempotent machen und Laufstatus dokumentieren. |
#!/bin/bash
set -e
RUN_DATE=$1
FILE=/transfer/in/orders_${RUN_DATE}.csv
LOG=/logs/nightly_${RUN_DATE}.log
echo "start nightly" >> $LOG
sqlplus billing/billing@ORCL <<SQL
TRUNCATE TABLE STG_ORDER;
SQL
while IFS=';' read -r customer amount article; do
sqlplus billing/billing@ORCL <<SQL
INSERT INTO STG_ORDER(customer_no, amount, article_no, run_date)
VALUES('$customer', $amount, '$article', TO_DATE('$RUN_DATE','YYYYMMDD'));
SQL
done < $FILE
sqlplus billing/billing@ORCL @/jobs/apply_orders.sql $RUN_DATE
sqlplus billing/billing@ORCL @/jobs/create_reports.sql $RUN_DATE
mv $FILE /transfer/archive/
echo "done" >> $LOG
CREATE TABLE BATCH_STEP_RUN (
RUN_ID VARCHAR2(40) NOT NULL,
STEP_NAME VARCHAR2(80) NOT NULL,
STATUS VARCHAR2(20) NOT NULL,
STARTED_AT TIMESTAMP,
FINISHED_AT TIMESTAMP,
ROWS_READ NUMBER,
ROWS_WRITTEN NUMBER,
ERROR_TEXT VARCHAR2(1000),
CONSTRAINT PK_BATCH_STEP_RUN PRIMARY KEY (RUN_ID, STEP_NAME)
);
public final class NightlyOrderJob extends RestartableJobTemplate { // Pattern: Template Method
private final List<JobStep> steps;
public NightlyOrderJob(JobRepository repository, OrderImportService importService,
OrderBookingService bookingService, ReportService reportService) {
super(repository);
this.steps = List.of(
new ValidateInputFileStep(importService),
new ImportStagingStep(importService), // Pattern: Idempotent Writer
new ApplyOrdersStep(bookingService),
new CreateReportStep(reportService),
new ArchiveInputFileStep(importService)
);
}
@Override
protected List<JobStep> steps() {
return steps;
}
}
public abstract class RestartableJobTemplate {
private final JobRepository repository;
protected RestartableJobTemplate(JobRepository repository) {
this.repository = repository;
}
public final JobReport run(JobRunId runId) {
JobReport report = new JobReport(runId);
for (JobStep step : steps()) {
if (repository.isCompleted(runId, step.name())) {
report.skipped(step.name());
continue; // Pattern: Checkpoint
}
repository.markStarted(runId, step.name());
StepResult result = step.execute(runId);
repository.markCompleted(runId, step.name(), result.metrics());
report.completed(step.name(), result.metrics());
}
return report;
}
protected abstract List<JobStep> steps();
}
Fertige Steps werden übersprungen statt wiederholt.
Pro Step sind Mengen und Fehler sichtbar.
Pipeline kann später in Spring Batch oder Kubernetes CronJob wandern.
Dateiimport und Buchung sind voneinander getrennt.