We are in middle of a migration project where we want to migrate millions of documents from one mongo collection to another (or in certain cases add a new field to existing documents). During testing we are seeing that when the operation takes longer than 10 minutes (which is expected), K8s kills the pod since the health check did not pass for the pod.
Am I missing something? Are there any best practises to follow while doing such large migrations. We expect the migration to go as long as 3-4 hours in a single stretch.
Regarding your question about how to run Mongock asynchronously to achieve this.
Regardless of if you are using springboot or not, you need the following
Using the builder
Run Mongock in a thread with something like this new Thread(mongockRunner::execute).start()
Create an event listener for the Mongock events: success and failure. Ideally the start event as well, to indicate Mongock process started, although it's not mandatory. Below I will add the code for both cases, standalone and springboot
Have two different endpoints for your liveness and readiness probes
The above is the foundation, with this you can have multiple approaches. I would go with the approach of having an shared object(MongockStateTracker
) containing the Mongock state, which will be updated from the listeners.
MongockStateTracker
class would look like something like this:public class MongockStateTracker {
public enum State {
NOT_STARTED, RUNNING, FINISHED_OK, FINISHED_FAILED
}
private State state = State.NOT_STARTED;
public synchronized void setFinishedOk() {
state = State.FINISHED_OK;
}
public synchronized void setFinishedFailed() {
state = State.FINISHED_FAILED;
}
public synchronized void setRunning() {
state = State.RUNNING;
}
public State getState() {
return state;
}
public Boolean isNotFinished() {
return getState() == State.NOT_STARTED || getState() == State.RUNNING;
}
}
MongockStateTracker
)if(stateTracker.isNotFinished() || stateTracker.getState() == MongockStateTracker.State.FINISHED_OK) {
return "ALIVE";
} else {
return "NOT ALIVE";
}
MongockStateTracker
)if(stateTracker.getState() == MongockStateTracker.State.FINISHED_OK) {
return "READY";
} else {
return "NOT READY";
}
@Bean
public ApplicationRunner mongockApplicationRunner(ApplicationContext springContext,
MongoTemplate mongoTemplate) {
MongockRunner mongockRunner = MongockSpringboot.builder()
.addMigrationScanPackage("YOUR_MIGRATION_PACKAGE_PATH")
.setEventPublisher(springContext)
.setSpringContext(springContext)
//more setters
.buildRunner();
return args -> new Thread(mongockRunner::execute).start();
}
...And finally you need to update the MongockStateTracker's state. I will show the code for both, standalone and springboot
MongockStandalone.builder()
//...more setters
.setMigrationStartedListener(startedEvent -> stateTracker.setRunning())
.setMigrationSuccessListener(successEvent -> stateTracker.setFinishedOk())
.setMigrationFailureListener(failEvent -> stateTracker.setFinishedFailed());
@Bean
public ApplicationListener<SpringMigrationFailureEvent> failureEventListener() {
return event -> stateTracker.setFinishedFailed();
}
@Bean
public ApplicationListener<SpringMigrationSuccessEvent> successEventEventListener() {
return event -> stateTracker.setFinishedOk();
}
@Bean
public ApplicationListener<SpringMigrationStartedEvent> startedEventListener() {
return event -> stateTracker.setRunning();
}
That should do the work ;)