How does BatchStatus severity ordering work, and what do max(), isRunning(), and isUnsuccessful() do?
answer
- Ordinal order = severity, COMPLETED lowest, UNKNOWN highest
- max() aggregates job status from steps — worst wins
- isRunning = STARTING/STARTED
- isUnsuccessful = FAILED or worse
- upgradeTo prevents downgrade; ExitStatus severity is separate
basics
~20 sBatchStatus values are ordered by severity. BatchStatus.max(a, b) returns the more severe of two statuses — used to aggregate a job's status from its steps. isRunning() is true for STARTING/STARTED; isUnsuccessful() is true for FAILED (and more severe).
solid answer
~40 sBatchStatus is an enum whose declaration order encodes severity, roughly COMPLETED (least severe) < STARTING < STARTED < STOPPING < STOPPED < FAILED < ABANDONED < UNKNOWN (most severe). The static BatchStatus.max(status1, status2) returns whichever is more severe; Spring uses this to roll a JobExecution's overall status up from its StepExecutions, so a single FAILED step drives the job to FAILED even if others completed. Instance helpers include isRunning() (STARTING or STARTED), isUnsuccessful() (FAILED or higher severity), and isGreaterThan/isLessThan for comparisons. There is also upgradeTo(...) used during lifecycle transitions to move a status forward without regressing. This ordering is what makes aggregation deterministic: the worst outcome wins.
code
java · 12 lines// Aggregating: the most severe step status wins for the job
BatchStatus jobStatus = BatchStatus.COMPLETED;
for (StepExecution se : jobExecution.getStepExecutions()) {
jobStatus = BatchStatus.max(jobStatus, se.getStatus());
}
// one FAILED step => jobStatus == BatchStatus.FAILED
BatchStatus.COMPLETED.isRunning(); // false
BatchStatus.STARTED.isRunning(); // true
BatchStatus.FAILED.isUnsuccessful(); // true
BatchStatus.STOPPED.isUnsuccessful(); // false (STOPPED is below FAILED)
BatchStatus.FAILED.isGreaterThan(BatchStatus.COMPLETED); // truego deeper
Aware the worst step status wins for the job.
Knows max/isRunning/isUnsuccessful exist and the severity ordering direction.
Explains aggregation, upgradeTo, STOPPED vs FAILED vs UNKNOWN semantics and restart impact.
Reasons about ABANDONED/UNKNOWN operational handling and why ordinal-based severity should not be hardcoded.
## Severity ordering `BatchStatus` is an enum, and its **ordinal order is the severity order**. From least to most severe: `COMPLETED`, `STARTING`, `STARTED`, `STOPPING`, `STOPPED`, `FAILED`, `ABANDONED`, `UNKNOWN`. The intuition: `COMPLETED` is the happy end state (least 'alarming'), running states sit in the middle, and terminal problems (`FAILED`, `ABANDONED`, `UNKNOWN`) are most severe. `UNKNOWN` is deliberately the highest because 'we don't know' is the worst thing to report. ## max(...) `BatchStatus.max(BatchStatus a, BatchStatus b)` is a **static** method returning the more severe of two values (the one with the higher ordinal). Spring Batch uses it to **aggregate**: when a `JobExecution` finishes, its overall `BatchStatus` is effectively the max across its `StepExecution` statuses. Consequence: **one FAILED step makes the whole job FAILED**, regardless of other steps completing. ## Instance helpers - `isRunning()` — true when the status is `STARTING` or `STARTED` (execution in progress). - `isUnsuccessful()` — true when the status is `FAILED` or more severe. Handy for guard logic. - `isGreaterThan(other)` / `isLessThan(other)` — severity comparisons. - `upgradeTo(BatchStatus other)` — returns the status you should transition to, preventing an illegal 'downgrade' during lifecycle changes (e.g. it will not move a FAILED back to STARTED). It combines lifecycle position and severity to pick a sensible next status. ## Where severity matters in practice - **Restartability**: the framework checks BatchStatus (e.g. a `COMPLETED` JobInstance normally cannot be rerun; a `FAILED`/`STOPPED` one can restart). - **STOPPED vs FAILED**: STOPPED is less severe than FAILED — a graceful stop is not an error. - **ABANDONED**: used for executions that should be skipped on restart (you manually mark them abandoned so restart ignores them). - **UNKNOWN**: typically set when a crash left an execution in an indeterminate state; it must be resolved manually because the framework cannot safely restart an UNKNOWN. ## Relationship to ExitStatus ExitStatus has its **own** severity mechanism via `ExitStatus.and(...)` and `Comparable`, comparing by exit-code String precedence. These are **parallel but separate** systems — do not conflate `BatchStatus.max` (enum) with `ExitStatus.and` (String codes). ## Gotchas - Because ordering is ordinal-based, do **not** reorder or rely on hardcoded ordinals in your own code — use the provided helpers. - `isUnsuccessful()` being true for ABANDONED/UNKNOWN too (not only FAILED) surprises people. - STOPPED does not satisfy `isUnsuccessful()`? It is below FAILED, so a stopped job is not 'unsuccessful' by that predicate — verify semantics before using it as an error gate.
- If a job has three steps and the second FAILs, what is the JobExecution's BatchStatus?FAILED. The job's status aggregates via max() across steps, so the most severe step status wins — one FAILED step makes the whole job FAILED, and typically the third step won't run.
- Why is UNKNOWN the most severe value?Because an indeterminate outcome is the least safe to act on — the framework can't decide whether to restart, so UNKNOWN outranks even FAILED and generally requires manual intervention.
saying these in an interview costs you the question
- Saying the job takes the status of the last step rather than the most severe.
- Confusing BatchStatus.max (enum) with ExitStatus.and (String codes).
- Assuming STOPPED counts as isUnsuccessful().