Building Microservices with Spring and Cloud Spanner
Develop and test your Spring applications with Google Cloud Spanner
Google Cloud Spanner is a globally distributed relational database service. Using this solution, you can execute ACID transactions and use SQL semantics without compromising horizontal scaling and high availability.
In this article, we will go through how to develop and test a Spring Boot Kotlin application using Spanner (for free).
Why choosing Spanner?
If you are familiar with CAP theorem, you may know that any distributed data store can provide only two of the following three guarantees:
- Consistency: Every read must return the most recent write .
- Availability: Every read/write must have zero-error response.
- Partition Tolerance: The system continues to operate despite delays or communication errors between nodes (network partitions).
ACID databases, such as Traditional Relational Databases, are Consistent and Available, but not Partition Tolerant.
BASE databases, like many NoSQL Databases, are Available and Partition Tolerant, but not Consistent.
Cloud Spanner’s private network makes it possible to provide Consistency and Availability, thus minimizing the chances of network partitioning (Partition Tolerance).
However, because partitions are possible, Spanner usually prioritises Consistency over Availability.
Spanner Emulator is an in-memory database that you can use to develop and test your applications for free.
Install and run the emulator following these instructions:
- Make sure Docker is installed.
- Get the latest emulator image.
$ docker pull gcr.io/cloud-spanner-emulator/emulator
3. Start the emulator.
$ docker run -d --name spanner-emulator -p 9010:9010 -p 9020:9020 gcr.io/cloud-spanner-emulator/emulator
Once the emulator is running, you will have two endpoints available:
localhost:9010 for gRPC communication, and
localhost:9020 for REST.
Instances and Databases in Spanner
To use Spanner, you must create a Spanner instance. Instances provide resources to Spanner databases within these instances.
Create an instance and the database following these instructions:
- Make sure to have gcloud CLI installed.
- Create and activate the emulator configuration.
$ gcloud config configurations create emulator
$ gcloud config set auth/disable_credentials true
3. Set the configuration for your project.
$ gcloud config set project your-project-id
4. Override the endpoint from Spanner to
$ gcloud config set api_endpoint_overrides/spanner http://localhost:9020/
5. Create an instance with the emulator configuration and one node.
$ gcloud spanner instances create test-instance \
--description="Test Instance" --config=emulator-config --nodes=1
6. Create a database within your instance.
$ gcloud spanner databases create test-database \
7. Check if the database has status READY.
$ gcloud spanner databases list --instance test-instance
Setting up the application
First, add the required dependencies for the Spring Boot application. Make sure you have the dependencies listed below in your
The Migration Tool
In the path
src/main/resources create a directory called
db and within it, another directory called
Then, create a file called
db.changelog-master.yaml. In this Changelog file, we will define Liquibase’s “changeSets”.
Spring will read the Database Changelog file from the location
classpath:/db/changelog/db.changelog-master.yaml when the application is launched.
In this example, we are using YAML to design the schema following the Schema Design Best Practices for Spanner. In the
db.changelog-master.yaml file, we have the table
singer with the columns
Although Cloud Spanner has its integration with Spring Framework, we are using JDBC to connect the application with the Database. Set up the Datasource with the following properties:
# spanner datasource
In the domain model, there is simply an Entity for the table
singer and its JPA Repository:
@Table(name = "singer")
@Id @GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
@Column(name = "singer_id") val id: String?,
@Column(name = "singer_name") val name: String = ""
interface SingerRepo : JpaRepository<Singer, String>
Testing the Application
For the integration tests, we are using Testcontainers for Spanner. This library allows to easily create the Spanner instance and database in a Docker container.
First, make sure you have the following test dependencies in your
In this example, we use a Companion Object that sets up the container with the Spanner Emulator, and creates the Spanner instance and the database for your tests:
@Container annotation to create a Docker container with the container image version. We recommend you to stick with a specific image version for your tests, instead of
Also, use the
@DynamicPropertySource annotation to override the
spring.datasource.url property with the URL pointing to the container.
Finally, we are creating a simple JUnit5 test, which injects the JPA Repository
SingerRepo to store and fetch the data from the database.
In this article, we learned how to build and test your microservices using a cloud database solution that manages distributed data, provides data consistency and is also highly available, like Google Cloud Spanner.
Thanks for reading. I hope this was helpful!
The example code is available on GitHub.