skip to content

Explain the difference between the step-scoped and job-scoped ExecutionContext, and when each is persisted.

level: middleimportance: must knowfreq 60%

answer

  1. step ctx per StepExecution, job ctx per JobExecution
  2. step ctx persisted at chunk commit (transactional)
  3. job ctx persisted on job-execution update / between steps
  4. not auto-merged -> promote to share
  5. reloaded on restart of same JobInstance

basics

~20 s

Each StepExecution has its own step context; each JobExecution has a job context. They are separate. The step context is saved to the database at every chunk commit; the job context is saved as the job execution is updated.

solid answer

~40 s

There are two independent contexts. The step-scoped ExecutionContext belongs to one StepExecution — readers and writers store their progress here (read counts, cursor position). It is persisted transactionally at every chunk commit, so after each successful chunk the progress is durable and a restart can resume mid-step. The job-scoped ExecutionContext belongs to the JobExecution and is meant for values that should outlive a single step or be shared across steps; it is persisted when the JobRepository updates the job execution (e.g. between steps and at completion/failure). Crucially, they don't sync automatically: putting a value in a step context does not make it visible to the job context or to later steps — you must explicitly promote it. Both are reloaded intact when you restart the same JobInstance after a failure.

go deeper

for a junior

May only know 'there's a context that persists'.

for a middle

Should clearly separate the two scopes and their persistence points.

for a senior

Should explain the transactional coupling of step context with chunk commit.

for a principal

Should connect persistence timing to exactly-once semantics and restart correctness.

## Two contexts, two lifecycles Spring Batch keeps **one ExecutionContext per StepExecution** and **one per JobExecution**. They map to separate metadata tables: - `BATCH_STEP_EXECUTION_CONTEXT` — keyed by `STEP_EXECUTION_ID`. - `BATCH_JOB_EXECUTION_CONTEXT` — keyed by `JOB_EXECUTION_ID`. ### Step-scoped context Lives for the duration of a single step run. This is the **workhorse for restartability**: chunk-oriented readers (subclasses of `AbstractItemCountingItemStreamItemReader`, e.g. `FlatFileItemReader`, `JdbcCursorItemReader`) write their read count / position here through the `ItemStream.update(ExecutionContext)` callback. **Persistence timing:** the step context is saved **as part of the chunk transaction commit**. Concretely, for each chunk the framework calls `ItemStream.update(...)` to refresh the context, then persists the `StepExecution` (including its context) in the same transaction that commits the chunk. That coupling is what guarantees the saved progress matches the committed data — no gap, no double-processing. ### Job-scoped context Lives for the whole job run. Use it to carry a value **across steps** (for example, a total computed in step 1 that step 2 needs). It is persisted when the `JobRepository` updates the `JobExecution` — notably between steps and on completion/failure — not on every chunk. ## They are NOT automatically merged A common trap: writing to `stepExecution.getExecutionContext()` and expecting the next step to see it. The step context is private to that step. To share, you **promote** selected keys into the job context (typically via `ExecutionContextPromotionListener`, an `afterStep` hook). Once in the job context, later steps read it from `jobExecution.getExecutionContext()` (Spring copies the job context into each new step's context on start, so `@Value("#{jobExecutionContext['key']}")` and late-binding work). ## Restart behavior When you relaunch the **same JobInstance** (same identifying job parameters) after a failure/stop: - The job context is reloaded from the last JobExecution. - For steps that had not COMPLETED, the step context is reloaded so readers resume. - Steps already marked COMPLETED are skipped (unless `allowStartIfComplete(true)`). ## Gotchas - Don't rely on the job context being updated mid-step — it isn't flushed per chunk. - Contexts are size-limited; keep values tiny. - Because step context is committed with the chunk, a rollback also rolls back the context update — keeping data and progress consistent.

  • Why is it important that the step context is committed in the same transaction as the chunk?
    It guarantees the saved read-count matches the committed data. On restart there's no window where progress and data disagree, so items are neither skipped nor double-processed.
  • How does a value written to the job context become visible inside a later step?
    When a step starts, Spring copies the job ExecutionContext entries in, and late-binding expressions like #{jobExecutionContext['key']} resolve against it.

saying these in an interview costs you the question

  • Assuming step and job contexts are the same map
  • Expecting the job context to be flushed on every chunk
  • Thinking a value written in step 1's step context is visible in step 2

context