Files
spring-boot-demo/spring-batch-partitioning/docs/08-restart-reruns-only-the-failed-partition.md
T

75 lines
5.5 KiB
Markdown

# 8. Restart reruns only the failed partition — proved, not assumed
[← Previous](07-the-rejectedexecutionexception.md) | [README](../README.md) | [Next: JobExecutionAlreadyRunningException, and recover →](09-jobexecutionalreadyrunning-and-recover.md)
The reference documentation states that a restarted partitioned step only re-executes partitions
that did not complete. This chapter is that claim, checked against a real `BATCH_STEP_EXECUTION`
table rather than taken on faith — using exactly the stuck job from
[chapter 7](07-the-rejectedexecutionexception.md), after
[chapter 9](09-jobexecutionalreadyrunning-and-recover.md)'s recovery step unsticks it.
## The evidence
```console
$ java -cp h2-2.4.240.jar org.h2.tools.Shell -url jdbc:h2:file:./data/rejecttest -user sa -password "" \
-sql "SELECT JOB_EXECUTION_ID, STEP_EXECUTION_ID, STEP_NAME, STATUS, READ_COUNT
FROM BATCH_STEP_EXECUTION WHERE JOB_EXECUTION_ID IN (1,33) ORDER BY JOB_EXECUTION_ID, STEP_EXECUTION_ID;"
JOB_EXECUTION_ID | STEP_EXECUTION_ID | STEP_NAME | STATUS | READ_COUNT
1 | 1 | ordersManagerStep | FAILED | 5000
1 | 2 | ordersWorkerStep:partition3 | FAILED | 0
1 | 3 | ordersWorkerStep:partition2 | FAILED | 0
1 | 4 | ordersWorkerStep:partition1 | COMPLETED | 5000
1 | 5 | ordersWorkerStep:partition0 | FAILED | 0
33 | 33 | ordersManagerStep | COMPLETED | 15000
33 | 34 | ordersWorkerStep:partition3 | COMPLETED | 5000
33 | 35 | ordersWorkerStep:partition2 | COMPLETED | 5000
33 | 36 | ordersWorkerStep:partition0 | COMPLETED | 5000
33 | 37 | reportStep | COMPLETED | 0
```
Full transcript: [`docs/output/08-recover-then-restart.txt`](output/08-recover-then-restart.txt).
`JobExecution` 33 — the restart — has exactly three new worker `StepExecution` rows:
`partition3`, `partition2`, `partition0`, the three [chapter 9](09-jobexecutionalreadyrunning-and-recover.md)'s
recovery step marked `FAILED`. There is no new row for `partition1`. It stayed `COMPLETED` from
`JobExecution` 1, at `READ_COUNT` 5000, untouched. `ordersManagerStep`'s own `READ_COUNT` for
execution 33 is 15000 — the sum of the three re-run partitions, not all four.
<figure>
<svg viewBox="0 0 740 200" role="img" aria-label="JobExecution 1: partition1 completes, the other three fail. JobExecution 33, the restart: only partition0, partition2 and partition3 run again; partition1 is skipped entirely, its 5000-row result untouched.">
<style>.h{font:600 12px sans-serif;fill:#1a1a1a}.c{font:11px sans-serif;fill:#4b5563}.m{font:11px monospace;fill:#1a1a1a}</style>
<text x="20" y="24" class="h">JobExecution 1</text>
<rect x="20" y="36" width="160" height="40" rx="4" fill="#e7f4ea" stroke="#4a9d63"/><text x="35" y="60" class="m">partition1: COMPLETED</text>
<rect x="200" y="36" width="160" height="40" rx="4" fill="#f7d9d3" stroke="#c56a54"/><text x="215" y="60" class="m">partition0: FAILED</text>
<rect x="380" y="36" width="160" height="40" rx="4" fill="#f7d9d3" stroke="#c56a54"/><text x="395" y="60" class="m">partition2: FAILED</text>
<rect x="560" y="36" width="160" height="40" rx="4" fill="#f7d9d3" stroke="#c56a54"/><text x="575" y="60" class="m">partition3: FAILED</text>
<text x="20" y="112" class="h">JobExecution 33 (restart, same shardsDir)</text>
<rect x="20" y="124" width="160" height="40" rx="4" fill="#f4f5f7" stroke="#b7bec9" stroke-dasharray="3 3"/><text x="34" y="148" class="c">partition1: SKIPPED</text>
<rect x="200" y="124" width="160" height="40" rx="4" fill="#e7f4ea" stroke="#4a9d63"/><text x="220" y="148" class="m">partition0: reran</text>
<rect x="380" y="124" width="160" height="40" rx="4" fill="#e7f4ea" stroke="#4a9d63"/><text x="400" y="148" class="m">partition2: reran</text>
<rect x="560" y="124" width="160" height="40" rx="4" fill="#e7f4ea" stroke="#4a9d63"/><text x="580" y="148" class="m">partition3: reran</text>
<text x="20" y="185" class="c">manager READ_COUNT for execution 33 = 15000 (3 x 5000), not 20000</text>
</svg>
</figure>
## Why this works: partition names are stable across attempts
`MultiResourcePartitioner` names partitions `partition0`, `partition1`, ... in the order its
`resources` array iterates ([chapter 3](03-what-gridsize-actually-controls.md)), and that order is
deterministic once the shard files and their sort order are fixed &mdash; `BatchConfig.partitioner()`
explicitly sorts `resources` by filename before handing them to `MultiResourcePartitioner` for
exactly this reason. Restart resolves each partition's `StepExecution` by that stable name against
the same `JobInstance`, the same mechanism [the earlier `spring-batch` module](https://ankurm.com/)
demonstrated for a single, unpartitioned step: same identifying job parameters, same instance,
already-`COMPLETED` steps skipped. Partitioning does not add a different restart mechanism; it
applies the same one once per partition.
## Going deeper
- Ordinary (unpartitioned) restart semantics, which this chapter's mechanism is not different
from: [Spring Batch on Boot 4.1, chapter 7](https://ankurm.com/).
- Why `JobExecution` 1 needed a recovery step before this restart was even possible:
[chapter 9](09-jobexecutionalreadyrunning-and-recover.md).
[Next: JobExecutionAlreadyRunningException, and recover &rarr;](09-jobexecutionalreadyrunning-and-recover.md)