Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring batch allows job without parameters to restart even if the job already completed successfully #4755

Open
fmbenhassine opened this issue Jan 31, 2025 · 1 comment
Labels
has: minimal-example Bug reports that provide a minimal complete reproducible example in: core type: bug
Milestone

Comments

@fmbenhassine
Copy link
Contributor

Discussed in #4694

Originally posted by ELMORABITYounes October 28, 2024
Right now even if a job was completed successfuly, spring batch allow It to be restarted if It contains no identifying params as shown here:

if (!identifyingJobParameters.isEmpty()                                                        
		&& (status == BatchStatus.COMPLETED || status == BatchStatus.ABANDONED)) {            
	throw new JobInstanceAlreadyCompleteException(                                             
			"A job instance already exists and is complete for identifying parameters="       
					+ identifyingJobParameters + ".  If you want to run this job again, "    
					+ "change the parameters.");                                             
}                                                                                              

I am wondering why is that done like this? I mean if the job already completed why It does not throw JobInstanceAlreadyCompleteException? Shouldn't second job instance without param considered the same and hence not allow It to be restarted if already succeeded?

@fmbenhassine fmbenhassine added has: minimal-example Bug reports that provide a minimal complete reproducible example in: core type: bug labels Jan 31, 2025
@fmbenhassine
Copy link
Contributor Author

Thank you for reporting this issue! I was able to isolate the case in this example:

package org.springframework.batch.samples.helloworld;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.support.JdbcTransactionManager;

import javax.sql.DataSource;

@Configuration
@EnableBatchProcessing
public class HelloWorldJobConfiguration {

	public static void main(String[] args) throws Exception {
		ApplicationContext context = new AnnotationConfigApplicationContext(HelloWorldJobConfiguration.class);
		JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
		Job job = (Job) context.getBean("job");
		JobParameters jobParameters1 = new JobParametersBuilder().addString("name", "foo", false).toJobParameters();
		JobParameters jobParameters2 = new JobParametersBuilder().addString("name", "bar", false).toJobParameters();
		jobLauncher.run(job, jobParameters1);
		jobLauncher.run(job, jobParameters2); // expected: JobInstanceAlreadyCompleteException
	}

	@Bean
	public Job job(JobRepository jobRepository, Step step) {
		return new JobBuilder("job", jobRepository).start(step).build();
	}

	@Bean
	public Step step(JobRepository jobRepository, JdbcTransactionManager transactionManager) {
		return new StepBuilder("step", jobRepository).tasklet((contribution, chunkContext) -> {
			System.out.println("Hello world!");
			return RepeatStatus.FINISHED;
		}, transactionManager).build();
	}

	// infra beans

	@Bean
	public DataSource dataSource() {
		return new EmbeddedDatabaseBuilder()
				.addScript("/org/springframework/batch/core/schema-hsqldb.sql")
				.build();
	}

	@Bean
	public JdbcTransactionManager transactionManager(DataSource dataSource) {
		return new JdbcTransactionManager(dataSource);
	}

}

while the default job key generator works as expected (it gives the same hash for the same input, ie an empty identifying job parameters set), spring batch still considers this as a different job instance and runs it, which should not be the case.


Just FTR, the default job key generator is working as expected (the following tests pass with 5.2.1):

// to add in org.springframework.batch.core.DefaultJobKeyGeneratorTests

	@Test
	public void testCreateJobKeyForEmptyParameters() {
		JobParameters jobParameters1 = new JobParameters();
		JobParameters jobParameters2 = new JobParameters();
		String key1 = jobKeyGenerator.generateKey(jobParameters1);
		String key2 = jobKeyGenerator.generateKey(jobParameters2);
		assertEquals(key1, key2);
	}

	@Test
	public void testCreateJobKeyForEmptyParametersAndNonIdentifying() {
		JobParameters jobParameters1 = new JobParameters();
		JobParameters jobParameters2 = new JobParametersBuilder()
				.addString("name", "foo", false)
				.toJobParameters();
		String key1 = jobKeyGenerator.generateKey(jobParameters1);
		String key2 = jobKeyGenerator.generateKey(jobParameters2);
		assertEquals(key1, key2);
	}

@fmbenhassine fmbenhassine added this to the 6.0.0 milestone Jan 31, 2025
fmbenhassine added a commit to fmbenhassine/spring-batch that referenced this issue Jan 31, 2025
FBibonne pushed a commit to FBibonne/spring-batch that referenced this issue Feb 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: minimal-example Bug reports that provide a minimal complete reproducible example in: core type: bug
Projects
None yet
Development

No branches or pull requests

1 participant