Spring Boot Recipe: Embedding local instances of datastores

A technique we found very useful in our Spring Boot development process is to embed a local version of Redis, Cassandra or Elastic Search that starts when we call bootRun.

Embedding these datastores also simplifies the setup needed for Continous Delivery and testing, since all the bits needed to run all our tests is available within our source code.

In this post, I will give an example configuration of how we launch a local elastic search instance and how this can then be used.

Code Sample

import org.elasticsearch.client.Client;
import org.elasticsearch.node.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;
import static org.elasticsearch.node.NodeBuilder.nodeBuilder;

@Configuration
public class EsConfig {

  private static final Logger log = LoggerFactory.getLogger(EsConfig.class);

  Node node;

  @Bean
  public Client es(@Value("${elasticsearch.cluster:none}") String cluster
  ) {
    if (cluster.equals("none")) {
      node = nodeBuilder().local(true).node();
    } else {
      node = nodeBuilder().clusterName(cluster).node(); // loads from a local elasticsearch.yml definition
    }
    return node.client();
  }

  @PreDestroy
  void destroy() {
    if (node != null) {
      log.info("stopping es server");
      try {
        node.close();
      } catch (Exception e) {
        log.error("could not stop es server", e);
      }
    }
  }

}

This is a pretty standard Spring Configuration that adds a bean for an Elastic Search node client.

In line 24 and 25, we ask for a local elastic search cluster if the property ‘elasticsearch.cluster’ in application.properties cannot be found.

If the property is found, we then just use the cluster defined by the name, initiated on lines 27 and 28.

The method started in line 32 allows us to simply shut down our service when the application is shut down.

And that’s really it, to use this in our application, we would simply add the call to the bean and use the client normally:

@Autowired
Client elasticSearchClient

Benefits

While the implementation is quite simple, having the ability to start up a local cluster is quite useful.

For our tests, we don’t need to add any additional infrastructure to our jenkins jobs, as the embedded instance starts and shuts down by itself.

For smaller deployments and integration testing, we can simply just run off bootRun and not have to worry about having elastic search configured remotely.

To use a real cluster, we simply provide the cluster name and the local instance is not touched at all.

Other Implementations

I chose to showcase elastic search because it was the simplest one to embed due to it’s local instance implementation, but nothing stops other datastores from being used. We found the following useful:

Moreover, nothing really stops you from limiting yourself to just datastores. Conceptually, you could use this technique to start a message broker like RabbitMQ or ActiveMQ.

One thought on “Spring Boot Recipe: Embedding local instances of datastores

  1. Ian Yang

    I has couldn’t complete bootRun task by graceful shutdown. So I appended `System.gc();` in @PreDestroy method, and now the task completing is works.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s