Skip to content

Commit 5c07115

Browse files
committed
Add test for job restart after recovering from abrupt shutdown
Related to #4943
1 parent 175fa1f commit 5c07115

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/MongoDBJobRestartIntegrationTests.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.io.IOException;
1919
import java.nio.file.Files;
20+
import java.util.Collections;
2021

2122
import com.mongodb.client.MongoCollection;
2223
import org.bson.Document;
@@ -25,6 +26,7 @@
2526
import org.junit.jupiter.api.Test;
2627
import org.testcontainers.junit.jupiter.Testcontainers;
2728

29+
import org.springframework.batch.core.BatchStatus;
2830
import org.springframework.batch.core.ExitStatus;
2931
import org.springframework.batch.core.job.Job;
3032
import org.springframework.batch.core.job.JobExecution;
@@ -42,7 +44,11 @@
4244
import org.springframework.core.io.Resource;
4345
import org.springframework.data.mongodb.MongoTransactionManager;
4446
import org.springframework.data.mongodb.core.MongoTemplate;
47+
import org.springframework.data.mongodb.core.query.Criteria;
48+
import org.springframework.data.mongodb.core.query.Query;
49+
import org.springframework.data.mongodb.core.query.Update;
4550
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
51+
import org.springframework.util.Assert;
4652

4753
/**
4854
* @author Mahmoud Ben Hassine
@@ -56,6 +62,11 @@ public class MongoDBJobRestartIntegrationTests {
5662

5763
@BeforeEach
5864
public void setUp() throws IOException {
65+
// TODO put drop statements in schema-drop-mongodb.jsonl
66+
mongoTemplate.dropCollection("BATCH_JOB_INSTANCE");
67+
mongoTemplate.dropCollection("BATCH_JOB_EXECUTION");
68+
mongoTemplate.dropCollection("BATCH_STEP_EXECUTION");
69+
mongoTemplate.dropCollection("BATCH_SEQUENCES");
5970
Resource resource = new FileSystemResource(
6071
"src/main/resources/org/springframework/batch/core/schema-mongodb.jsonl");
6172
Files.lines(resource.getFilePath()).forEach(line -> mongoTemplate.executeCommand(line));
@@ -109,4 +120,54 @@ void testJobExecutionRestart(@Autowired JobOperator jobOperator, @Autowired JobR
109120
Assertions.assertEquals(3, lastStepExecution.getId());
110121
}
111122

123+
/*
124+
* Test for https://github.com/spring-projects/spring-batch/issues/4943: after abrupt
125+
* shutdown, the embedded job.execution.stepExecutions array is not synchronized, but
126+
* BATCH_STEP_EXECUTION collection still contains the data.
127+
*/
128+
@Test
129+
void testRestartAfterRecoverFromAbruptShutdown(@Autowired JobOperator jobOperator,
130+
@Autowired JobRepository jobRepository, @Autowired Job job) throws Exception {
131+
// Step 1: Run job normally
132+
JobParameters jobParameters = new JobParametersBuilder().addString("name", "foo").toJobParameters();
133+
134+
JobExecution jobExecution = jobOperator.start(job, jobParameters);
135+
136+
// Verify job completed successfully
137+
Assertions.assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
138+
139+
// Step 2: Simulate the core issue:
140+
// - set job execution status to STARTED
141+
// - clear embedded stepExecutions array while keeping BATCH_STEP_EXECUTION
142+
// collection intact
143+
144+
jobExecution.setStatus(BatchStatus.STARTED);
145+
jobRepository.update(jobExecution);
146+
147+
Query jobQuery = new Query(Criteria.where("jobExecutionId").is(jobExecution.getId()));
148+
Update jobUpdateStepExecutions = new Update().set("stepExecutions", Collections.emptyList());
149+
mongoTemplate.updateFirst(jobQuery, jobUpdateStepExecutions, "BATCH_JOB_EXECUTION");
150+
151+
// Step 3: Verify that job's status = STARTED and embedded array is empty but
152+
// collection still has data
153+
MongoCollection<Document> jobExecutionsCollection = mongoTemplate.getCollection("BATCH_JOB_EXECUTION");
154+
MongoCollection<Document> stepExecutionsCollection = mongoTemplate.getCollection("BATCH_STEP_EXECUTION");
155+
156+
Document document = jobExecutionsCollection.find(new Document("jobExecutionId", jobExecution.getId())).first();
157+
Assertions.assertTrue(document.getString("status").equals("STARTED"), "job must be in STARTED status");
158+
Assertions.assertTrue(document.getList("stepExecutions", Document.class).isEmpty(),
159+
"Embedded stepExecutions array should be empty");
160+
Assertions.assertEquals(2, stepExecutionsCollection.countDocuments(),
161+
"BATCH_STEP_EXECUTION collection should still contain data");
162+
163+
// Step 4: recover the job execution
164+
JobExecution recoveredJobExecution = jobOperator.recover(jobExecution);
165+
Assert.notNull(recoveredJobExecution.getExecutionContext().get("recovered"),
166+
"Job execution should be marked as recovered");
167+
168+
// Step 5: restart the job
169+
JobExecution restartedJobExecution = jobOperator.restart(recoveredJobExecution);
170+
Assertions.assertEquals(BatchStatus.COMPLETED, restartedJobExecution.getStatus());
171+
}
172+
112173
}

0 commit comments

Comments
 (0)