Micronaut CRaC

Adds support for CRaC (Coordinated Restore at Checkpoint) to the Micronaut Framework.


1 Introduction

This Micronaut module adds support for CRaC (Coordinated Restore at Checkpoint) within the framework.

2 Release History

For this project, you can find a list of releases (with release notes) here:

3 Installation

Add the following the dependency to your build:


4 CRaC Resources

4.1 DataSources

We currently support Hikari DataSources that are configured to allow suspension with:

    allow-pool-suspension: true
datasources {
  'default' {
    allowPoolSuspension = true
  datasources {
    default {
      allow-pool-suspension = true
  "datasources": {
    "default": {
      "allow-pool-suspension": true

When a checkpoint is taken, the pool is suspended and the connections are closed. When the checkpoint is restored, the pool is resumed and the connections are re-established.

4.2 Redis

Since v1.2.1 of this library, we also support Redis connections.

When the checkpoint is taken, we destroy RedisClient, RedisCache and StatefulRedisConnection beans that exist in the application.

These will be re-created once the checkpoint is restored, however you will need to add your beans to the refresh scope so that these new beans are used.

The resources for each of the above can be disabled via configuration:

        enabled: false            # disable all redis support
        client-enabled: false     # disable RedisClient support
        cache-enabled: false      # disable RedisCache support
        connection-enabled: false # disable StatefulRedisConnection support
crac {
  redis {
    enabled = false
    clientEnabled = false
    cacheEnabled = false
    connectionEnabled = false
  crac {
    redis {
      enabled = false
      client-enabled = false
      cache-enabled = false
      connection-enabled = false
  "crac": {
    "redis": {
      "enabled": false,
      "client-enabled": false,
      "cache-enabled": false,
      "connection-enabled": false

4.3 Custom CRaC Resources

To provide custom CRaC resources, create beans of type OrderedResource.

Micronaut CRaC registers resources for you into the CRaC Context. You just focus on providing implementations for OrderedResource::beforeCheckpoint and OrderedResource::afterRestore in your resources.

Micronaut CRaC registers resources in order. You can control the order by overriding OrderedResource::getOrder.

5 Refresh scope

Prior to a checkpoint being taken, a RefreshEvent will be published to invalidate all beans in the @Refreshable scope.

This behaviour can be disabled by setting the crac.refresh-beans property to false in the application config.

6 Events

To notify external components when the default Resource handlers execute, there are two events; BeforeCheckpointEvent and AfterRestoreEvent. These events contain the java.time.Instant that the action completed, the length of time it took to execute the action in nanoseconds, and the Resource that was acted upon.

Please see the Micronaut Framework guide for information on how to listen for these events.

7 Context Provider

GlobalCracContextProvider, Micronaut CRaC’s default implementation of CracContextProvider, returns the global context. You can provide a replacement for CracContextProvider and provide your custom Context.

8 Docker Support

Support for building CRaC-enabled Docker images is provided by the Micronaut Gradle or Maven plugin. Either one is capable of generating a docker image containing a CRaC enabled JDK and a pre-warmed, checkpointed application.

9 Testing

When you create a Micronaut Framework application (either via https://launch.micronaut.io or the command line application), it creates a ContextConfigurer with enables eager singleton initialization.

As tests annotated with @MicronautTest are implicitly in the Singleton scope, this can cause problems injecting some beans (for example an HttpClient) into your test class.

To avoid this, you can either disable eager singleton initialization for your tests, or you will need to manually get an instance of the bean you would normally inject. As an example, to get an HttpClient you could do:

Lazily get an HttpClient in a test
EmbeddedServer server; // (1)

Supplier<HttpClient> clientSupplier = SupplierUtil.memoizedNonEmpty(() -> // (2)
    server.getApplicationContext().createBean(HttpClient.class, server.getURL())

void testClient() {
    assertEquals("ok", clientSupplier.get().toBlocking().retrieve("/eager")); // (3)
Lazily get an HttpClient in a test
EmbeddedServer server // (1)

@Memoized // (2)
HttpClient clientSupplier() {
    server.applicationContext.createBean(HttpClient, server.URL)

void 'test client'() {
    clientSupplier().toBlocking().retrieve("/eager") == "ok" // (3)
Lazily get an HttpClient in a test
lateinit var server: EmbeddedServer // (1)

val client by lazy {
    server.applicationContext.createBean(HttpClient::class.java, server.url) // (2)

fun testClient() {
    assertEquals("ok", client.toBlocking().retrieve("/eager")) // (3)
1 Inject the EmbeddedServer as normal
2 Lazily create a HttpClient when it is first called
3 Get the HttpClient and make the request

10 Repository

You can find the source code of this project in this repository: