Add spring-batch: jobs, steps, chunk processing and restartability on Boot 4.1
Companion code for "Spring Batch on Boot 4.1: Jobs, Steps, Chunk Processing and Restartability". A productImportJob configured three ways by profile against a poisoned CSV row, run as real java -jar processes (not just JUnit) so the restart story is genuine: a chunk fails and rolls back, the process exits, a brand-new JVM against the same file-based H2 database resumes at the exact next unread row (READ_COUNT 20, not 60) and completes. Findings the build pins: - StepBuilder.chunk(int, PlatformTransactionManager) still compiles in Batch 6.0.5 but returns the legacy SimpleStepBuilder; chunk(int) returns the new ChunkOrientedStepBuilder, and only the latter is used here. - Two different ExecutionContext classes now exist in two different packages (infrastructure.item vs core.repository.persistence) with different shapes. - spring-boot-starter-batch alone gives a resourceless JobRepository that forgets every JobInstance the moment the JVM exits; spring-boot-starter- batch-jdbc is what makes the restart demo possible at all, demonstrated by excluding BatchJdbcAutoConfiguration and watching a "restart" collide with the previous run's own data instead of resuming it. - A migration-guide summary claiming CommandLineJobRunner was removed in 6.0 is wrong -- javap against the real jar shows @Deprecated(forRemoval=true), not removed. - RepeatStatus moved from core.repeat to infrastructure.repeat, caught by the compiler rather than by reading docs. 11 documentation chapters, 10 captured transcripts (unit tests, javap output, and real two-JVM scenario runs), all regenerated by scripts/run-all.sh. Fixed after push: three dead docs.spring.io links in the doc chapters (readersAndWriters/* and chunk-oriented-processing/*.html paths moved when Spring Batch 6 reorganized its reference docs; corrected to the current readers-and-writers/*, processor.html and chunk-oriented-processing.html paths, verified 200 via curl before committing). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019DXsJ1zpikbA1MQJN6RqFA
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# 11. Production checklist
|
||||
|
||||
[← Previous](10-resourceless-vs-jdbc.md) | [README](../README.md)
|
||||
|
||||
A short list, aimed at the gap between "this demo works" and "this is safe to point at a real
|
||||
feed." Each item links back to the chapter that explains the why.
|
||||
|
||||
- **Confirm the job repository is actually persistent** — query for `BATCH_JOB_INSTANCE`
|
||||
after startup, don't assume from the dependency list. [Chapter 10](10-resourceless-vs-jdbc.md).
|
||||
- **Pick chunk size from measurement, not habit.** Smaller costs more round trips; larger costs
|
||||
more rework per failure. [Chapter 3](03-chunk-oriented-processing.md).
|
||||
- **Decide skip vs. fail-and-restart per failure type, not per job.** A step can have a
|
||||
`skipLimit` for genuinely expected bad rows and still fail hard past that limit — the two
|
||||
are not mutually exclusive. [Chapter 8](08-skip-vs-restart.md).
|
||||
- **Use `chunk(int).transactionManager(tx)`, not `chunk(int, tx)`,** unless you specifically need
|
||||
the legacy `SimpleStepBuilder` for something the new model does not yet cover.
|
||||
[Chapter 2](02-anatomy-of-a-job.md).
|
||||
- **Do not use `beanMapped()` against a Java record** without checking your exact Spring
|
||||
Framework version's record support; prefer `itemPreparedStatementSetter` when in doubt.
|
||||
[Chapter 6](06-jdbc-writer-and-records.md).
|
||||
- **Give every step you want to always re-run on restart `allowStartIfComplete(true)`
|
||||
explicitly** — a reporting or notification step that silently gets skipped after a
|
||||
restart is a surprising, hard-to-notice gap. [Chapter 8](08-skip-vs-restart.md).
|
||||
- **A custom `ItemReader` that does not implement `ItemStream` restarts from scratch every time**,
|
||||
which is fine for an idempotent reader and a bug for anything else — check this
|
||||
deliberately rather than by finding out during an incident. [Chapter 7](07-restartability.md).
|
||||
- **`spring.batch.job.enabled=false` if you launch jobs yourself** through `JobOperator`, so the
|
||||
auto-configured runner does not also try. [Chapter 5](05-launching-and-jobparameters.md).
|
||||
- **Delete or secure any diagnostic endpoint you add while building this out.** This module has
|
||||
none, but the pattern (an endpoint dumping live `JobExplorer` state) is common enough to call
|
||||
out: it is invaluable while developing and a liability left in production.
|
||||
|
||||
## Should you even build a custom batch job for this?
|
||||
|
||||
Sometimes the honest answer is no. If the volume is small enough to fit in a single request-response
|
||||
cycle and does not need to survive a crash mid-way, a scheduled `@Component` method with its own
|
||||
try/catch is less machinery than a full Job/Step/JobRepository setup, and easier for the next
|
||||
person to read. Spring Batch earns its complexity when you actually need the things this article
|
||||
demonstrates — durable progress tracking, transactional chunking, a real restart story
|
||||
— not by default just because the word "batch" is in the requirements.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Spring Batch reference documentation](https://docs.spring.io/spring-batch/reference/) (`rel="nofollow"`)
|
||||
- [What's new in Spring Batch 6](https://docs.spring.io/spring-batch/reference/whatsnew.html) (`rel="nofollow"`)
|
||||
- [Spring Batch 6.0 Migration Guide](https://github.com/spring-projects/spring-batch/wiki/Spring-Batch-6.0-Migration-Guide) (`rel="nofollow"`, and see [chapter 9](09-corrections.md) for where this specific document was wrong)
|
||||
- [Spring Boot 4.1.0 release announcement](https://spring.io/blog/2026/06/10/spring-boot-4/) (`rel="nofollow"`)
|
||||
Reference in New Issue
Block a user