A restartable file reader records position at committed checkpoints; that position is valid only while the source bytes and reader mapping stay compatible.
Spring Batch FlatFileItemReader: save state against an immutable source
Know what the cursor proves
A settlement CSV reader may have consumed 47 records when a later write fails. The last committed chunk, not the last attempted read, defines safe restart progress. A named FlatFileItemReader can save its current item count in the StepExecution context and restore it on a new execution. That cursor does not certify the file contents. The source digest guard must reject replacement bytes under the same input identity.
Keep checkpoint and output aligned
Let the chunk transaction commit destination writes and repository state together where the infrastructure supports that boundary. A remote file or external API cannot join the database transaction; its writer needs idempotent keys so replay is safe. Turning off saveState can be reasonable for a one-shot disposable import, but it removes position-based restart. Idempotent writes remain useful even with a saved cursor because failures can occur between systems.
Exercise a real restart
Process a small immutable fixture, inject a failure during the second chunk, then terminate the process. Relaunch the same job instance with the same source version and inspect persisted StepExecution state. Compare the complete destination key set with the source: no gap, duplicate or silent skip. Test a changed file separately and require rejection before the reader resumes.
Implementation contract
@Bean
@StepScope
FlatFileItemReader<ReceiptCsvRow> settlementReader(
@Value("#{jobParameters['inputPath']}") String inputPath,
LineMapper<ReceiptCsvRow> receiptLineMapper) {
return new FlatFileItemReaderBuilder<ReceiptCsvRow>()
.name("settlementCsvReader")
.resource(new FileSystemResource(inputPath))
.lineMapper(receiptLineMapper)
.saveState(true)
.build();
}Cost and verification
Persisting a small cursor adds a repository update at checkpoints. A larger chunk reduces update frequency but increases work replayed after a failure and memory held before commit.
Common Mistakes
- Do not restart from a line number in a replaced file.
- Do not set saveState(false) while promising position-based restart.
- Do not assume a saved cursor makes a non-idempotent external writer safe.
Read next
Spring Batch step scope: bind a reader to one execution, Spring Batch source digest: reject changed rows before a restart, Spring Batch writers: use a stable source key when a chunk is replayed, Spring Batch chunk restart: know which receipts committed, Spring Batch ExecutionContext keys: give each stream its own checkpoint.
