Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Micronaut CRaC JDBC Guide #1379

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions guides/distribution-base/hellocontroller.adoc
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
== Controller

Add a controller which responds with the JSON payload in the root route.

[source,json]
----
{"message":"Hello World"}
----
common:helloworld-controller-intro.adoc[]

source:HelloController[]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package example.micronaut;

import io.micronaut.context.env.Environment;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.context.ApplicationContextBuilder;
import io.micronaut.context.ApplicationContextConfigurer;
import io.micronaut.context.annotation.ContextConfigurer;
import io.micronaut.runtime.Micronaut;

public class Application {

@ContextConfigurer
public static class Configurer implements ApplicationContextConfigurer {
@Override
public void configure(@NonNull ApplicationContextBuilder builder) {
builder.eagerInitSingletons(true).defaultEnvironments(Environment.DEVELOPMENT);
}
}
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package example.micronaut;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;

import java.util.Collections;
import java.util.Map;

@Controller // <1>
class HelloWorldController {

private final MessageRepository messageRepository;

HelloWorldController(MessageRepository messageRepository) { // <2>
this.messageRepository = messageRepository;
}

@Get // <3>
Map<String, String> index() {
return messageRepository.findAll()
.stream().map(entity ->
Collections.singletonMap("message", entity.message()))
.findFirst()
.orElseGet(Collections::emptyMap);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package example.micronaut;

import io.micronaut.core.annotation.Nullable;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;

@MappedEntity // <1>
public record Message(@Nullable @Id @GeneratedValue Long id, // <2>
String message) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package example.micronaut;

import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;

@JdbcRepository(dialect = Dialect.POSTGRES) // <1>
public interface MessageRepository extends CrudRepository<Message, Long> { // <2>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package example.micronaut;

import io.micronaut.context.event.ApplicationEventListener;
import io.micronaut.runtime.server.event.ServerStartupEvent;
import jakarta.inject.Singleton;

@Singleton // <1>
public class ServerStartupEventListener implements ApplicationEventListener<ServerStartupEvent> { // <2>

private final MessageRepository messageRepository;

public ServerStartupEventListener(MessageRepository messageRepository) { // <3>
this.messageRepository = messageRepository;
}

@Override
public void onApplicationEvent(ServerStartupEvent event) { // <4>
if (messageRepository.count() == 0) {
messageRepository.save(new Message(null, "Hello World"));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package example.micronaut;

import io.micronaut.context.ApplicationContext;
import io.micronaut.crac.test.CheckpointSimulator;
import io.micronaut.http.client.BlockingHttpClient;
import io.micronaut.http.client.HttpClient;
import io.micronaut.runtime.server.EmbeddedServer;
import java.util.function.Function;

public class CheckpointTestUtils {

public static void test(Function<BlockingHttpClient, Object> testScenario) {
try (EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class)) {
CheckpointSimulator checkpointSimulator =
server.getApplicationContext().getBean(CheckpointSimulator.class);
testApp(server, testScenario);

checkpointSimulator.runBeforeCheckpoint();
server.stop();
checkpointSimulator.runAfterRestore();
server.start();
testApp(server, testScenario);
}
}

public static Object testApp(EmbeddedServer embeddedServer, Function<BlockingHttpClient, Object> clientConsumer) {
try (HttpClient httpClient = embeddedServer.getApplicationContext().createBean(HttpClient.class, embeddedServer.getURL())) {
BlockingHttpClient client = httpClient.toBlocking();
return clientConsumer.apply(client);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package example.micronaut;

import io.micronaut.core.type.Argument;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.client.BlockingHttpClient;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.Map;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class HelloWorldControllerTest {

@Test
void emulateCheckpoint() {
CheckpointTestUtils.test(this::testHelloWorld);
}

private Void testHelloWorld(BlockingHttpClient client) {
HttpResponse<Map<String, String>> response = client.exchange(HttpRequest.GET("/"), Argument.mapOf(String.class, String.class));
assertEquals(HttpStatus.OK, response.getStatus());
Optional<Map<String, String>> bodyOptional = response.getBody();
assertTrue(bodyOptional.isPresent());
assertEquals(Collections.singletonMap("message", "Hello World"), bodyOptional.get());
return null;
}
}
15 changes: 15 additions & 0 deletions guides/micronaut-crac-data-jdbc/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"title": "Micronaut CRaC and Micronaut Data JDBC",
"intro": "Learn how to use CRaC with Micronaut Data JDBC and Hikari",
"authors": ["Sergio del Amo"],
"tags": ["endpoint-info"],
"categories": ["CRaC"],
"publicationDate": "2023-11-22",
"languages": ["java"],
"apps": [
{
"name": "default",
"features": ["crac", "data-jdbc", "postgres"]
}
]
}
133 changes: 133 additions & 0 deletions guides/micronaut-crac-data-jdbc/micronaut-crac-data-jdbc.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
common:header-top.adoc[]

common:crac.adoc[]

common:requirements-testcontainers.adoc[]

common:gettingStarted.adoc[]

common:completesolution.adoc[]

common:create-app-features.adoc[]

common:data-jdbc-postgresql-configuration.adoc[]

:leveloffset: +1

== Micronaut CRaC Dependency

Add the Micronaut CRaC dependency:

dependency:micronaut-crac[groupId=io.micronaut.crac]

It has a transitive dependency to https://github.com/CRaC/org.crac[org.crac:crac].

== Configuration Allow Pool Suspension

To use Hikari Datasources with Micronaut CRaC you need to allow suspension.

resource:application.properties[tag=allow-pool-suspension]

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.

== Entity

common:mapped-entity-intro.adoc[]

source:Message[]

callout:mapped-entity[1]
callout:mapped-entity-id[2]

== Repository

common:jdbc-repository-intro.adoc[]

source:MessageRepository[]

callout:jdbcrepository[1]
callout:crudrepository[2]

== ServerStartupEvent Event Listener

Save a message on startup:

source:ServerStartupEventListener[]
callout:singleton[1]
callout:application-event-listener-startup-event[2]
callout:constructor-di[number=3,arg0=MessageRepository]
callout:server-startup-event.adoc[4]

== Controller

common:helloworld-controller-intro.adoc[]

Create a controller that uses the repository:

source:HelloWorldController[]

callout:controller[number=1,arg0=/]
callout:constructor-di[number=2,arg0=MessageRepository]
callout:get-generic[3]

== Test

To simplify testing, we create a utility class that allows you to run a test scenario before and after the checkpoint.

test:CheckpointTestUtils[]

You could write a test for the previous controller:

test:HelloWorldControllerTest[]

:leveloffset: -1

== Testing

:leveloffset: +1

common:test-resources-postgres.adoc[]

common:testApp.adoc[]

:leveloffset: -1

== Running the Application

:leveloffset: +1

== Run PostgreSQL with Docker

common:run-postgres-with-docker.adoc[]

common:dev-env.adoc[]

common:crac-eagerly-initialize-singletons-intro.adoc[]

common:default-dev-environment-application-dev-properties.adoc[]

The above configuration allows the application, which we will run as a Docker image, to connect to the PostgreSQL instance, which we run as a Docker image as well.

common:docker-crac.adoc[]

== Run the Application with Docker

The Docker Image entry point is the CRaC checkpoint, you can run it with:

common:docker-run.adoc[]

common:helloworld-curl.adoc[]

:leveloffset: -1

== Conclusion

As you have seen in this guide, you can use Micronaut CRaC today with Netty runtime, Micronaut Data JDBC, and Hikari connection pool.
You don't need to write any custom code.

common:next.adoc[]

Learn more about:

* https://micronaut-projects.github.io/micronaut-crac/latest/guide/[Micronaut CRaC (Coordinated Restore at checkpoint)]
* https://docs.azul.com/core/crac/crac-introduction[CRaC Introduction]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
datasources.default.username=dbuser
datasources.default.password=theSecretPassword
datasources.default.url=jdbc:postgresql://host.docker.internal:5432/postgres
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
micronaut.application.name=micronautguide
#tag::allow-pool-suspension[]
datasources.default.allow-pool-suspension=true
#end::allow-pool-suspension[]
#tag::datasource[]
# <1>
datasources.default.db-type=postgres
datasources.default.schema-generate=CREATE_DROP
# <2>
datasources.default.dialect=POSTGRES
datasources.default.driver-class-name=org.postgresql.Driver
#end::datasource[]
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,9 @@ The following test verifies the `TimeController` is offloaded prior to a checkpo

test:TimeControllerTest[]

== Eagerly initialize Singletons
common-crac-eagerly-initialize-singletons.adoc[]

When you use CRaC, you should use https://docs.micronaut.io/latest/guide/#eagerInit[eager initialization] to ensure the application is fully loaded before the checkpoint is taken.

Modify your application class:

source:Application[]
== Test Eager Initializion of Singletons

You can test eager initialization:

Expand Down
6 changes: 1 addition & 5 deletions guides/micronaut-dynamodb/micronaut-dynamodb.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,7 @@ resource:application.yml[tag=testresources]

This will start DynamoDB in a container via TestContainers, and inject the properties into your application.

==== Dev default environment

Modify `Application` to use `dev` as a https://docs.micronaut.io/latest/guide/index.html#_default_environment[default environment].

source:Application[]
common:dev-env.adoc[]

==== Dev Bootstrap

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When you use CRaC, you should use https://docs.micronaut.io/latest/guide/#eagerInit[eager initialization] to ensure the application is fully loaded before the checkpoint is taken.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
== Eagerly initialize Singletons

common-crac-eagerly-initialize-singletons-intro.adoc[]

Modify your application class:

source:Application[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
== Development Environment Configuration

Create `src/main/resources/application-dev.properties`.
The Micronaut framework applies this configuration file only for the `dev` environment.

resource:application-dev.properties[]
2 changes: 1 addition & 1 deletion src/docs/common/snippets/common-dev-env.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
=== Dev default environment
== `Dev` Default environment

Modify `Application` to use `dev` as a https://docs.micronaut.io/latest/guide/index.html#_default_environment[default environment].

Expand Down
Loading
Loading