skip to content

What is a JobParametersIncrementer (e.g. RunIdIncrementer) and when do you need one?

level: middleimportance: should knowfreq 55%

answer

  1. getNext(prev) -> next params
  2. RunIdIncrementer bumps identifying run.id
  3. .incrementer(new RunIdIncrementer())
  4. only fires via startNextInstance / Boot runner
  5. avoids JobInstanceAlreadyComplete for repeatable jobs

basics

~20 s

A JobParametersIncrementer generates the next set of JobParameters from the previous run, typically by bumping an identifying run.id. RunIdIncrementer is the built-in one. It lets you re-run the same job repeatedly without a JobInstanceAlreadyComplete error.

solid answer

~40 s

JobParametersIncrementer is a strategy interface with getNext(JobParameters) that derives the parameters for the next run from the last one. The stock implementation, RunIdIncrementer, adds or increments an identifying Long parameter named run.id (1, 2, 3…). Because run.id is identifying, each incremented run resolves to a new JobInstance, sidestepping JobInstanceAlreadyCompleteException for jobs you want to run repeatedly with otherwise-identical inputs. You wire it on the job builder via .incrementer(new RunIdIncrementer()), and it only takes effect when you launch through JobOperator.startNextInstance(jobName) (or Spring Boot's runner), which reads the last instance's parameters and applies getNext. Calling JobLauncher.run directly with static parameters does not invoke the incrementer. Use it for scheduled/repeatable jobs; skip it when a natural identifying key (like businessDate) already makes each run unique.

code

java · 14 lines
java
@Bean
Job reportJob(JobRepository jobRepository, Step reportStep) {
    return new JobBuilder("reportJob", jobRepository)
        .incrementer(new RunIdIncrementer())   // adds/bumps identifying run.id
        .start(reportStep)
        .build();
}

// Fires the incrementer: loads last instance's params, applies getNext, launches.
jobOperator.startNextInstance("reportJob");

// Does NOT fire the incrementer -> 2nd call with same params throws
// JobInstanceAlreadyCompleteException:
// jobLauncher.run(reportJob, sameParams);

go deeper

for a junior

Know it bumps run.id so a job can run again without the already-complete error.

for a middle

Explain the getNext contract and that it only fires via startNextInstance / Boot runner.

for a senior

Distinguish it from restart and design custom incrementers (e.g., roll businessDate).

for a principal

Reason about scheduling architecture: incrementer vs natural identifying key vs external orchestration for idempotency.

## The problem it solves Spring Batch refuses to re-run an already-completed `JobInstance` (`JobInstanceAlreadyCompleteException`). But many jobs legitimately need to run again with the *same* logical inputs — e.g., an hourly cleanup job with no meaningful business parameter. You need a way to make each launch a **new** `JobInstance` without hand-crafting a unique parameter each time. ## The interface ```java public interface JobParametersIncrementer { JobParameters getNext(JobParameters parameters); } ``` `getNext` takes the **previous** run's parameters (or empty on first run) and returns the parameters for the **next** run. The returned set must differ in an **identifying** parameter, or you'd still collide. ## RunIdIncrementer `org.springframework.batch.core.launch.support.RunIdIncrementer` is the built-in implementation. It maintains an **identifying** `Long` parameter named **`run.id`**, starting at 1 and incrementing by 1 on each `getNext`. So successive runs get `run.id=1`, `run.id=2`, … each defining a distinct `JobInstance`. (The key name is configurable via `setKey`.) ## Wiring it ```java @Bean Job reportJob(JobRepository repo, Step step) { return new JobBuilder("reportJob", repo) .incrementer(new RunIdIncrementer()) .start(step) .build(); } ``` ## How it actually fires — the crucial gotcha The incrementer is **only** consulted when you launch via the "next instance" path: - `JobOperator.startNextInstance("reportJob")` — loads the last instance's parameters, calls `getNext`, and launches. - Spring Boot's `JobLauncherApplicationRunner` applies the job's incrementer on startup. If you instead call `JobLauncher.run(job, fixedParams)` yourself with static parameters, the incrementer is **ignored** — you'll hit the already-complete exception on the second run. This trips up many candidates. ## When to use vs. not - **Use** for repeatable/scheduled jobs with no natural unique key. - **Don't need it** when an identifying `businessDate`/`batchId` already makes each run unique — adding `run.id` on top is redundant. - **Custom incrementer** for domain sequencing: e.g., roll the `businessDate` forward one day each run. ## Gotchas - `run.id` is identifying by design; if you accidentally make your incremented key non-identifying, every run collides again. - The incrementer does not help with *restart* — restart reuses the same identifying params; the incrementer is about starting the *next* instance. - Merging: `startNextInstance` merges the incremented value with the previous instance's other parameters, so previously supplied inputs carry forward unless overridden.

  • Why doesn't calling JobLauncher.run directly trigger the incrementer?
    JobLauncher.run launches exactly the JobParameters you hand it. The incrementer's getNext is only invoked by the startNextInstance path (JobOperator or Spring Boot's runner), which loads the last instance's parameters and derives the next set.
  • What parameter does RunIdIncrementer manage and is it identifying?
    An identifying Long named run.id, incremented by one each time (key configurable via setKey). Being identifying is what makes each incremented run a new JobInstance.

saying these in an interview costs you the question

  • Claiming RunIdIncrementer fires on any JobLauncher.run call
  • Saying run.id is non-identifying
  • Confusing the incrementer with restart (it starts the next instance, not resumes a failed one)

context