spring-boottransactionalthread-synchronizationshutdown-hook

Spring boot graceful shutdown mid-transaction


I'm working on a spring-boot service that performs sensitive payment processing, and would like to ensure that any shutdown to the app will be done without interrupting these transactions. Curious on how to best approach this in spring-boot.

I read about adding shutdown hooks to spring-boot, and I was thinking maybe to use a CountDownLatch on the class to check if the thread has completed processing - something like this:

@Service
public class PaymentService {

    private CountDownLatch countDownLatch;

    private void resetLatch() {
        this.countDownLatch = new CountDownLatch(1);
    }

    public void processPayment() {
        this.resetLatch();

        // do multi-step processing

        this.CountDownLatch.countDown();
    }

    public void shutdown() {
        // blocks until latch is available 
        this.countDownLatch.await();
    }
}

// ---

@SpringBootApplication
public class Application {
    public static void main(String[] args) {

        // init app and get context
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

        // retrieve bean needing special shutdown care
        PaymentService paymentService = context.getBean(PaymentService.class);

        Runtime.getRuntime().addShutdownHook(new Thread(paymentService::shutdown));
    }
}

Constructive feedback is greatly appreciated - thanks.


Solution

  • I ended up using @PreDestroy annotation on the shutdown method:

    @Service
    public class PaymentService {
    
        private CountDownLatch countDownLatch;
    
        private synchronized void beginTransaction() {
            this.countDownLatch = new CountDownLatch(1);
        }
    
        private synchronized void endTransaction() {
            this.countDownLatch.countDown();
        }
    
        public void processPayment() {
            try {
                this.beginTransaction();
    
                // - - - - 
                // do multi-step processing
                // - - - -
    
            } finally {
                this.endTransaction();
            }
        }
    
        @PreDestroy
        public void shutdown() {
            // blocks until latch is available 
            this.countDownLatch.await();
        }
    }