skip to content

StepExecution & Counts

StepExecution accumulates read, write, commit, rollback, skip and filter counts, contributed as each chunk completes. Those counters are how you answer 'how do you know the batch actually processed everything'.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

5

What is a StepExecution in Spring Batch, and which metrics/counts does it record?

level: juniorimportance: must knowfreq 55%

answer

  1. one attempt at a Step, persisted
  2. read/write/filter/commit/rollback/skip
  3. read = write + filter (no skips)
  4. afterStep listener reads them
  5. BATCH_STEP_EXECUTION table

basics

~10 s

A StepExecution represents one attempt to run a Step. It records runtime metrics: readCount, writeCount, filterCount, commitCount, rollbackCount, and skipCount, plus status, timestamps, and its own ExecutionContext.

solid answer

~30 s

A StepExecution is the domain object Spring Batch creates for each attempt to execute a Step within a JobExecution. It is persisted to the metadata tables (BATCH_STEP_EXECUTION) and carries the step's runtime statistics: readCount (items read by the ItemReader), writeCount (items written by the ItemWriter), filterCount (items dropped by an ItemProcessor returning null), commitCount (chunk transactions committed), rollbackCount (chunk transactions rolled back), and skipCount (items skipped across read/process/write). It also holds BatchStatus, ExitStatus, start/end times, and a per-step ExecutionContext used for restart. These counts let you audit throughput and drive restart/monitoring logic, typically read in a StepExecutionListener's afterStep.

code

java · 14 lines
java
public class MetricsListener implements StepExecutionListener {
    @Override
    public ExitStatus afterStep(StepExecution se) {
        long read     = se.getReadCount();
        long written  = se.getWriteCount();
        long filtered = se.getFilterCount();
        long skipped  = se.getSkipCount();      // read+process+write skips
        long commits  = se.getCommitCount();
        long rollbacks= se.getRollbackCount();
        log.info("read={} written={} filtered={} skipped={} commits={} rollbacks={}",
                 read, written, filtered, skipped, commits, rollbacks);
        return se.getExitStatus();  // return null to keep the default
    }
}

go deeper

for a junior

Name the object and list the main counts; know they're read in afterStep.

for a middle

Explain read=write+filter, that commitCount is per chunk, and where counts are persisted.

for a senior

Connect counts to chunk transaction boundaries and restart semantics (new StepExecution, ExecutionContext for resume).

for a principal

Discuss using these metrics for SLA/throughput monitoring and audit, plus how skip/retry perturbs commit/rollback counts.

### What a StepExecution is Spring Batch persists job metadata. A `JobExecution` is one run of a `Job`; within it, each `Step` that runs produces a `StepExecution` — the record of a **single attempt** to execute that step. If a step is restarted after failure, a **new** StepExecution is created for the new attempt (the old one is retained). StepExecutions are stored in the `BATCH_STEP_EXECUTION` table by the `JobRepository`. ### The counts it tracks For a **chunk-oriented step** (read → process → write in chunks), the StepExecution accumulates: - **readCount** — number of items successfully returned by the `ItemReader`. - **writeCount** — number of items successfully handed to the `ItemWriter`. - **filterCount** — items that were read but **filtered out** by the `ItemProcessor` returning `null` (not an error, not written). Roughly: `readCount = writeCount + filterCount` when there are no skips. - **commitCount** — number of chunk **transactions committed** (one per successful chunk, so roughly `ceil(items / chunkSize)`). - **rollbackCount** — number of chunk transactions **rolled back**, e.g. on a write exception or during skip/retry re-processing. - **skipCount** — total skipped items = `readSkipCount + processSkipCount + writeSkipCount` (skip policy caught an exception and the item was skipped instead of failing the step). ### Other fields Beyond counts it holds `BatchStatus` (STARTED/COMPLETED/FAILED…), `ExitStatus`, `startTime`/`endTime`, a `failureExceptions` list, and a step-scoped `ExecutionContext` (persistent state used to resume a restart, e.g. a reader's last-read position). ### How you read them Counts are typically inspected in a `StepExecutionListener.afterStep(StepExecution)` (or via `@AfterStep`), or queried later from `JobExplorer`/the metadata tables for monitoring dashboards. ### Gotchas - readCount/writeCount are **not** the same when a processor filters items — that gap is `filterCount`, not a skip. - commitCount is per-chunk-transaction, not per-item. - A tasklet-style step (not chunk-oriented) generally leaves read/write/filter counts at 0 — those metrics are meaningful for chunk steps. - These are cumulative totals for that one attempt; on restart the fresh StepExecution starts them back at 0.

  • Where are these counts persisted, and how would you query them after the job finishes?
    In the BATCH_STEP_EXECUTION metadata table written by the JobRepository. After the fact you read them via JobExplorer.getJobExecution(...).getStepExecutions() (or the JobOperator), or by querying the table directly for a monitoring dashboard.
  • If a step is restarted, do the counts continue from where they left off?
    No. A restart creates a brand-new StepExecution with counts starting at 0. Continuity of work (e.g. where the reader resumes) comes from the persisted ExecutionContext, not from the counters.

saying these in an interview costs you the question

  • Saying readCount always equals writeCount (ignores filterCount and skips)
  • Claiming commitCount is the number of items (it's the number of chunk transactions)
  • Thinking a StepExecution is reused across restarts instead of created fresh

context

open as a page

What is StepContribution and how do its counts get aggregated into the StepExecution?

level: seniorimportance: must knowfreq 40%

basics

~10 s

StepContribution is a per-chunk buffer that accumulates read/write/filter/skip counts while a chunk is processed. When the chunk commits, StepExecution.apply(contribution) merges those deltas into the step's running totals.

open as a page

What is the difference between filterCount and skipCount on a StepExecution?

level: middleimportance: should knowfreq 45%

basics

~10 s

filterCount counts items an ItemProcessor intentionally dropped by returning null — normal, no error. skipCount counts items that threw an exception and were skipped by a skip policy instead of failing the step.

open as a page

How do commitCount and rollbackCount relate to chunks and transactions, and what makes rollbackCount rise beyond a single failure?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Each chunk runs in one transaction. commitCount increments per successful chunk commit (roughly items/chunkSize). rollbackCount increments each time a chunk transaction rolls back — including the extra rollbacks caused by retry and by single-item re-scanning during skips.

open as a page

How are StepExecution counts aggregated safely across concurrency models — multi-threaded steps versus partitioned steps?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

In a multi-threaded step, many chunks share one StepExecution and merge counts via the synchronized apply(StepContribution). In partitioning, each partition is its own child StepExecution with independent counts; the manager step sums them for reporting.

open as a page