skip to content

What determines whether launching a job creates a new JobInstance or reuses an existing one?

level: middleimportance: must knowfreq 66%

answer

  1. Identity = name + identifying params
  2. Repository lookup decides new vs reuse
  3. Completed -> AlreadyComplete; running -> AlreadyRunning
  4. Change an identifying param for a new run
  5. RunIdIncrementer bumps run.id

basics

~20 s

The job name plus its identifying JobParameters. If that combination has been seen before, Spring Batch reuses the existing JobInstance; if it is new, it creates a new one. Same job + same identifying params = same instance.

solid answer

~40 s

A JobInstance's identity is the job's name plus its identifying JobParameters. When you call JobLauncher.run(job, params), the JobRepository looks up whether a JobInstance already exists for that name-plus-identifying-parameters combination. If none exists, it creates a new JobInstance and a first JobExecution. If one already exists and it is not yet completed (e.g. a prior FAILED attempt), it reuses that JobInstance and creates a new JobExecution to restart it. If it already completed successfully, it throws JobInstanceAlreadyCompleteException. So to force a genuinely new run you must change at least one identifying parameter — commonly via a RunIdIncrementer or a timestamp parameter — otherwise you are targeting the same logical instance.

code

java · 14 lines
java
// Same identifying param -> same JobInstance on every launch.
JobParameters daily = new JobParametersBuilder()
        .addLocalDate("runDate", LocalDate.of(2026, 7, 22))  // identifying
        .toJobParameters();

jobLauncher.run(job, daily);          // creates JobInstance + JobExecution #1
// jobLauncher.run(job, daily);       // if #1 FAILED -> restart (new execution, same instance)
//                                    // if #1 COMPLETED -> JobInstanceAlreadyCompleteException

// Force a brand-new instance each launch via an incrementer:
Job restartableEachTime = new JobBuilder("reprocess", jobRepository)
        .incrementer(new RunIdIncrementer())   // adds/bumps run.id (identifying)
        .start(step)
        .build();

go deeper

for a junior

Should at least say 'same job name + same parameters = same run'.

for a middle

Expected to explain the repository lookup outcomes and how to force a new instance.

for a senior

Should discuss restart implications and the trade-off of timestamp-as-identity.

for a principal

Should frame identity as the idempotency contract and design schedulers/incrementers accordingly.

## Identity is the deciding factor When you launch a job through the **`JobLauncher`** (`run(Job job, JobParameters parameters)`), Spring Batch must decide: *is this a brand-new logical run, or another attempt at an existing one?* That decision is made purely by **`JobInstance` identity**: > **Identity = job name + identifying `JobParameters`.** ### The lookup flow 1. `JobLauncher` hands the job name and parameters to the **`JobRepository`**. 2. The repository queries `BATCH_JOB_INSTANCE` / `BATCH_JOB_EXECUTION_PARAMS` for a matching instance. 3. **No match** → create a new `JobInstance`, then a new `JobExecution`. 4. **Match, previous attempt not completed** (e.g. `FAILED` or `STOPPED`) → reuse the `JobInstance`, create a **new** `JobExecution` (a restart). 5. **Match, already `COMPLETED`** → throw **`JobInstanceAlreadyCompleteException`** — Spring Batch refuses to re-run a logically-finished instance. 6. **Match, an execution is currently running** → throw **`JobExecutionAlreadyRunningException`**. ### Identifying vs non-identifying parameters Only **identifying** `JobParameters` contribute to identity. A parameter added as non-identifying is recorded on the execution but does **not** change which `JobInstance` you target. (The precise rules and API for marking parameters are owned by the launch-metadata topic; the key takeaway here is that identity is composed of *identifying* parameters only.) ### Forcing a new run Because re-launching with the same identifying parameters targets the same instance, teams that need a fresh run every time (e.g. an ad-hoc reprocessing job) add a changing identifying parameter. The idiomatic approach is a **`JobParametersIncrementer`** — Spring Batch ships **`RunIdIncrementer`**, which adds/bumps a `run.id` parameter — invoked when you launch via `JobOperator.start`/`startNextInstance` or wire an incrementer on the `Job`. A timestamp parameter achieves the same effect manually. ### Why this design - **Idempotency / safety**: you cannot accidentally run today's settlement twice — identity + the completed-guard prevent it. - **Restart semantics**: because identity is stable across attempts, the framework can find the failed instance and resume it with its saved `ExecutionContext`. ### Gotchas - Adding a timestamp as an *identifying* parameter means **every launch is a new `JobInstance`** — which also means such jobs can never be restarted (each run is unique). That is sometimes desired, sometimes a bug. - Parameter **type and value** both matter — `LocalDate 2026-07-22` and the string `"2026-07-22"` may or may not compare equal depending on how they were added; be consistent. - Reusing identifying parameters after a successful completion is a frequent cause of the `JobInstanceAlreadyCompleteException` surprise in retries/cron reruns.

  • You scheduled a job with a fixed identifying parameter and it completed. The next cron trigger fails with JobInstanceAlreadyCompleteException. Why, and how do you fix it?
    The identifying parameters were identical, so it targeted the already-completed JobInstance. Fix it by varying an identifying parameter each run — e.g. a RunIdIncrementer or a run date/timestamp parameter — so each trigger creates a new instance.
  • What is the downside of adding a timestamp as an identifying parameter to guarantee uniqueness?
    Every launch becomes a distinct JobInstance, so the job is effectively never restartable — a subsequent run can't resume a failed prior run because the identity no longer matches.

saying these in an interview costs you the question

  • Thinking any launch always creates a new JobInstance
  • Believing non-identifying parameters change instance identity
  • Not knowing re-launching a completed instance throws JobInstanceAlreadyCompleteException

context