= Coherence Spring Boot Kubernetes Example
This repository is an example of running a simple Spring Boot Coherence application in Kubernetes.
There are a number of parts to this example, each of the modules is a separate Maven projects. The reason for this is that in the real world each of those modules could be built by a separate development team. Each module may have a different development and deployment lifecycle.
Domain Classes domain-model/
directory. This is a very simple module that just contains the domain classes
(Student
and Address
) used by the example.
These classes have been annotated with Coherence POF annotations. During the build the POF Maven plugin will instrument
the classes to make the properly implement evolvable portable objects.
The classes have also been annotated with JPA annotations so that they can be stored in a database.
This module will be used by the other modules that require these domain classes.
Storage Service storage-service
directory. This is the Coherence storage enabled cluster service.
This module is a Spring Boot application that uses Spring JPA to implament a simple Coherence CacheStore
.
Student REST Service rest-service
directory. This is a Spring Boot REST application that provides REST endpoints
to perform CRUD operations on Students. This application is a Coherence Extend client application that connects
to the storage service.
== Spring Boot CacheStore Implementation
This example demonstrates how to write a Spring Boot CacheStore
implementation. The SpringJpaCacheStore
class is generic
and takes any Spring CrudRepository
as its constructor parameter. It then uses this to perform the required cache store
operations.
There is a Spring JPA repository class for Student
named StudentRepository
. This is just an interface that extends the
Spring CrudRepository
. There is no need to write any more code than this interface.
NOTE: The StudentRepository
class has been annotated with @Repository("students")
to make it a Spring bean with the name students
.
This name is important as it will be used by the CacheStore
factory class to look up the bean in the Spring context.
This example used the convention that the Coherence cache name will match the corresponding repository bean name.
=== CacheStore Factory
There is a factory class which will be used by Coherence to create CacheStore
instances.
The CacheStoreFactory
class is generic, so no additional code will be needed if more entity classes get added
to the project.
The factory will look-up in the Spring context a CrudRepository
bean with the same name as the cache which is being created.
The Coherence cache configuration file configures the cache scheme to use the CacheStoreFactory
to create CacheStore
instances, as shown below.
=== Adding More Domain Classes & CacheStores
To add more domain classes to the project all that needs to be implemented is the domain classes themselves, annotated with JPA and Coherence POF annotations and a corresponding repository class.
For example, suppose we wanted to add a Course
entity to our Student database, we create the CourseId
and Course
classes:
@Embeddable @PortableType(id = 1003) public class CourseId implements Serializable {
@Portable
private String classId;
public CourseId() {
}
public CourseId(String classId) {
this.classId = classId;
}
public String getClassId() {
return classId;
}
public void setClassId(String classId) {
this.classId = classId;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CourseId courseId = (CourseId) o;
return Objects.equals(classId, courseId.classId);
}
@Override
public int hashCode() {
return Objects.hash(classId);
}
@Entity @PortableType(id = POF_TYPE_COURSE) public class Course {
@EmbeddedId
@Portable
private CourseId id;
@Portable
private String name;
public Course() {
}
public Course(CourseId id, String name) {
this.id = id;
this.name = name;
}
public CourseId getId() {
return id;
}
public void setId(CourseId id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
CacheStore
to work all we need is a repository class for the Course
entity like this:
[source,java]Now when a cache named courses
is created the CacheStoreFactory
will look up the repository bean named courses
and use
it to create the cache store.
== Size Limiting the Caches
The cache configuration does not contain any size limits.
In the scheme above in its <internal-cache-scheme>
the <local-scheme>
refers to a local-scheme named storage-local-scheme
which is defined further down in the cache configuration file and looks like this:
The storage-local-scheme
is a plain local-scheme
with no other configuration, so it has no size limit or eviction policy.
high-units
like this:
[source,xml]This means that the caches will be limited to 1000 entries each. When a cache reaches 1000 entries a number of entries will
be evicted down to the low-units
value. In the example above we have not set low-units
, so the default is 80% of the high units.
So in this case when the cache size reaches 1000 it will be reduced down to 800.
low-units
to a different value like this:
[source,xml]Now when the cache reaches 1000 entries, 500 will be evicted.
=== Size Limiting Using Size
The high-units
can be set as a bytes value by setting the unit-calculator
value of the local-scheme
to BINARY
:
Now the high-units
is set to 10 mega-bytes, and the low-units
is 5 mega-bytes. When the amount of data in the cache
reaches 10MB, regardless of how many entries, then entries will be evicted until the size gets down to 5MB or less.
There is lots of information on this in the Coherence documentation: https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/develop-applications/cache-configurations-example.html#GUID-3A02103F-0DBF-47B2-A940-9EDD928E45C5
== Build the Examples
The examples are Maven projects and should be build with the following commands in this order:
spring-boot:build-image
to get the Spring Boot plugin to
build the image.
[source,bash]spring-boot:build-image
to get the Spring Boot plugin to
build the image.== Running the Example
This example requires an Oracle database to connect to. One way to do this for testing is use the Oracle DB Docker image on Oracle Container Registry, which is simple to start and throw away afterward.
The following database details will be required:
The user must have tables in schema like the ones below:
CREATE USER College IDENTIFIED BY Coherence2020;
GRANT CONNECT, RESOURCE, DBA TO College;
DROP TABLE COLLEGE.ADDRESS; DROP TABLE COLLEGE.STUDENT;
CREATE TABLE COLLEGE.ADDRESS ( ROLL VARCHAR2(255 char) NOT NULL PRIMARY KEY, LINE_ONE VARCHAR2(255 char), LINE_TWO VARCHAR2(255 char), CITY VARCHAR2(255 char), POSTAL_CODE VARCHAR2(255 char), COUNTRY VARCHAR2(255 char) );
The different modules can be run in Docker or in Kubernetes.
=== Running in Docker
First start the Coherence storage application. Choose whether you want to run the plain JDBC storage application or the Spring Boot storage application, ONLY RUN ONE OF THEM.
==== Run the Coherence Storage Application
Run the command below after changing the environment variables to the values required for your database.
=== Run the REST Microservice
Now run the Spring Boot REST microservice. This is a Coherence Extend client application that will connect to the storage application. When we started the storage application we exposed the Coherence Extend port 20000 to port 20000 on the host. We can configure the REST service to connect to Extend on the local machines IP address.
Run the command below after changing the extend.host
system property value to the IP address of the local machine.
=== Try the REST Service
We can use curl to execute REST commands. The REST service exposed the container port 8080 to port 8080 on the local machine so we can connect to that port.
==== First Get a Non-Existent Student
HTTP/1.1 404 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 10 Sep 2020 14:00:30 GMT
==== Add a New Student
We can do a POST command to add a Student. The API requires the request body to be a json representation of the Student.
HTTP/1.1 201 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 10 Sep 2020 14:08:45 GMT
The json returned will show the roll number that has been created as the Student identifier, in this case 3161f377-e98c-4a19-8992-05329699088f
.
==== Get an Existing Student
Now we have added a Student we can execute a GET for that Student using the roll number.
HTTP/1.1 200 Content-Type: application/json Transfer-Encoding: chunked Date: Thu, 10 Sep 2020 14:10:51 GMT
=== Running in Kubernetes
To run in Kubernetes you still require an Oracle Database as with the Docker example. Again, a simple solution is to run the Oracle DB image in k8s.
As before there are two choices for storage, the plain JDBC storage or the Spring Boot JPA storage, CHOOSE ONLY ONE.
First make sure the Coherence Operator has been installed, as this will be required to run the storage cluster.
NOTE: The operator must be at least version 3.1.0 to support Spring Boot Buildpacks images.
==== Start the Coherence Storage Cluster
apiVersion: coherence.oracle.com/v1 kind: Coherence metadata: name: student-storage spec: annotations: openshift.io/scc: anyuid image: storage-service:1.0.0-SNAPSHOT # <1> application: type: spring # <2> env: