mongock

How to avoid K8s killing pod in a large migration


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.


Solution

  • 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

    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.

    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;
        }
    }
    
    
    if(stateTracker.isNotFinished() || stateTracker.getState() == MongockStateTracker.State.FINISHED_OK) {
               return "ALIVE";
           } else {
               return "NOT ALIVE";
           }
    
    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 ;)