diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 2fbed6a9d..832f1f1f6 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -188,6 +188,8 @@ include::tiered-storage:partial$nav.adoc[] ** xref:integrate:feast-config.adoc[] ** xref:integrate:feature-engineering-with-feast.adoc[Get started with Feast batch features] ** xref:integrate:streaming-features-with-feast.adoc[Get started with Feast streaming features] +* xref:integrate:integrate-with-vertx.adoc[] +** xref:integrate:get-started-with-vertx.adoc[] // Connectors * Messaging System Connectors ** xref:integrate:messaging-system-connectors.adoc[Overview] diff --git a/docs/modules/ROOT/pages/whats-new.adoc b/docs/modules/ROOT/pages/whats-new.adoc index 5d2779885..76ca5819a 100644 --- a/docs/modules/ROOT/pages/whats-new.adoc +++ b/docs/modules/ROOT/pages/whats-new.adoc @@ -32,6 +32,13 @@ image:ROOT:client-routing.png[Hazelcast Cluster Routing diagram] For more information, see xref:clients:java.adoc#client-cluster-routing-modes[Client cluster routing modes]. +== New Hazelcast Cluster Manager for Vert.x +[.enterprise]*Enterprise* + +Vert.x is a reactive application toolkit for creating resource-efficient, concurrent, asynchronous and flexible applications on the JVM. The new `vertx-hazelcast-enterprise` module available for Hazelcast Platform 5.5 offers advanced features like strong consistency and enhanced fault tolerance. This integration reduces complexity, abstracts low-level management tasks, and ensures optimal resource efficiency, making it ideal for dynamic environments requiring high availability and minimal downtime. The open-source `vertx-hazelcast` module is still available directly from https://vertx.io/docs/vertx-hazelcast/java/[Vert.x] but does not support strong consistency. + +For more information, see xref:integrate:integrate-with-vertx.adoc[]. + == Introduction of Long-term Support (LTS) releases Hazelcast Platform 5.5 introduces Long-term Support (LTS) releases to simplify your upgrade experience and give you greater control over when and how you upgrade your software. You can perform upgrades with more precision and consistency, and with reduced effort and risk. You can upgrade directly from a supported previous release to the LTS release or directly between LTS releases using a rolling upgrade. Between LTS releases, Hazelcast will provide short-term support (STS) releases, which will focus on innovation, and provide the latest features and bug fixes. diff --git a/docs/modules/architecture/pages/data-partitioning.adoc b/docs/modules/architecture/pages/data-partitioning.adoc index e4c23a70d..7ded99527 100644 --- a/docs/modules/architecture/pages/data-partitioning.adoc +++ b/docs/modules/architecture/pages/data-partitioning.adoc @@ -201,6 +201,7 @@ Please note that `IndeterminateOperationStateException` does not apply to read-only operations, such as `map.get()`. If a partition primary replica member crashes before replying to a read-only operation, the operation is retried on the new owner of the primary replica. +[[best-effort-consistency]] === Best-Effort Consistency The replication algorithm for AP data structures enables Hazelcast clusters to offer high throughput. diff --git a/docs/modules/integrate/pages/get-started-with-vertx.adoc b/docs/modules/integrate/pages/get-started-with-vertx.adoc new file mode 100644 index 000000000..dea60ddee --- /dev/null +++ b/docs/modules/integrate/pages/get-started-with-vertx.adoc @@ -0,0 +1,461 @@ += Get started with Vert.x + +This tutorial helps you integrate Vert.x with Hazelcast and use Hazelcast for distributed session management and other distributed data structures. + +In this tutorial, you will + +- start with a simple Vert.x Hello World application +- add vertx-hazelcast module and enable distributed session management +- use `io.vertx.core.shareddata.Counter` data structure to implement a unique id generator + +== Prerequisites + +- Java 17 or newer +- Maven 3.9+ +- link:https://httpie.io[httpie] client + +== Create a new project + +1. Go to link:https://start.vertx.io[start.vertx.io], change the artifact id to `messages`, the version to 5.0.0, and generate a new project. + +2. Extract the project and build it using: + +[source,bash] +---- +$ mvn clean package +---- + +and start the application using: + +[source,bash] +---- +java -jar target/messages-1.0.0-SNAPSHOT-fat.jar +---- + +You should see output similar to the following: + +[source] +---- +HTTP server started on port 8888 +Aug 29, 2024 2:22:38 PM io.vertx.launcher.application.VertxApplication +INFO: Succeeded in deploying verticle +---- + +== Storing Data in Session + +Go to the `MainVerticle.java` file and replace the contents of the start method with the following: + +NOTE: This tutorial uses 2-space indentation, which is customary for Vertx projects due to the high number of nested callbacks. + +[source,java] +---- + public void start() { + // Create a Router + Router router = router(vertx); + + // Create local SessionStore + SessionStore store = LocalSessionStore.create(vertx); + + // Use the SessionStore to handle all requests + router.route() + .handler(SessionHandler.create(store)); + + router.route(HttpMethod.PUT, "/").handler(context -> { + context.request().bodyHandler(body -> { + List messages = getMessagesFromSession(context); + + JsonObject json = body.toJsonObject(); + String message = json.getString("message"); + messages.add(message); + + putMessagesToSession(context, messages); + + context.json( + new JsonObject() + .put("messages", messages) + ); + }); + }); + + // Create the HTTP server + vertx.createHttpServer() + // Handle every request using the router + .requestHandler(router) + // Start listening + .listen(8888) + // Print the port + .onSuccess(server -> + System.out.println( + "HTTP server started on port " + server.actualPort() + ) + ); + } + + private static List getMessagesFromSession(RoutingContext context) { + String messages = context.session().get("messages"); + if (messages == null) { + return new ArrayList<>(); + } else { + return new ArrayList<>(Arrays.asList(messages.split(","))); + } + } + + private void putMessagesToSession(RoutingContext context, List messages) { + context.session().put("messages", String.join(",", messages)); + } +---- + +[source,bash] +---- +$ http put localhost:8888 message=Hello\ World! +HTTP/1.1 200 OK +content-length: 29 +content-type: application/json +set-cookie: vertx-web.session=ed22f77473a7f613c9305431a62832a6; Path=/ + +{ + "messages": [ + "Hello World!" + ] +} + +---- + +Execute another request with the cookie: + +[source,bash] +---- +$ http put localhost:8888 'Cookie:vertx-web.session=ed22f77473a7f613c9305431a62832a6' message=Hello\ World\ 2! +HTTP/1.1 200 OK +content-length: 46 +content-type: application/json + +{ + "messages": [ + "Hello World!", + "Hello World 2!" + ] +} + +---- + +== Distributed Sessions + +Let's modify the code, so we can start multiple instances easily - the application will start on the defined port, and when the port is not available it will search for another port: + +Add the following method to the `MainVerticle.java` class: + +[source,java] +---- + private int findFreePort(int from) { + for (int port = from; port < from + 100; port++) { + try { + new ServerSocket(port).close(); + return port; + } catch (IOException e) { + // port not available, try next + } + } + throw new RuntimeException("Could not find an available port"); + } +---- + +and use it in the `start` method: + +[source,java] +---- + ... + int port = findFreePort(8888); + + // Create the HTTP server + vertx.createHttpServer() + // Handle every request using the router + .requestHandler(router) + // Start listening + .listen(port) + ... +---- + +Now, we can start two instances: + +[source,bash] +---- +$ java -jar target/vertx-hz-1.0.0-SNAPSHOT-fat.jar +HTTP server started on port 8888 +Aug 30, 2024 9:09:44 AM io.vertx.launcher.application.VertxApplication +INFO: Succeeded in deploying verticle + +... + +$ java -jar target/vertx-hz-1.0.0-SNAPSHOT-fat.jar +HTTP server started on port 8889 +Aug 30, 2024 9:09:47 AM io.vertx.launcher.application.VertxApplication +INFO: Succeeded in deploying verticle +---- + +and we can see the session is not shared between the instances. Here is the request to the first instance: + +[source, bash] +---- +$ http PUT localhost:8888 message="Hello world" +HTTP/1.1 200 OK +content-length: 28 +content-type: application/json +set-cookie: vertx-web.session=00f219c166ca50727d23eaaf9fe54229; Path=/ + +{ + "messages": [ + "Hello world" + ] +} +---- + +and here is the request to the 2nd instance. Notice the different port and that we use the cookie we received, but the data does not contain the previous message. + +[source, bash] +---- +$ http PUT localhost:8889 message="Hello world 2" 'Cookie: vertx-web.session=00f219c166ca50727d23eaaf9fe54229' +HTTP/1.1 200 OK +content-length: 30 +content-type: application/json +set-cookie: vertx-web.session=a1486c5ed6416972fdc356e4d91d2397; Path=/ + +{ + "messages": [ + "Hello world 2" + ] +} +---- + +We will fix that by using a Hazelcast Cluster Manager. There are two modules that provide Hazelcast Cluster Manager: + +- `io.vertx:vertx-hazelcast` - this module is maintained by the Vert.x team, with contributions from Hazelcast, and is built on top of open-source Hazelcast +- `com.hazelcast:vertx-hazelcast-enterprise` - this module is maintained by the Hazelcast team and is built on top of the `vertx-hazelcast` but uses Hazelcast Enterprise instead. You need an enterprise license to use Hazelcast Enterprise. + +You can use either module for most of this tutorial. At the end of this tutorial you will need the `vertx-hazelcast-enterprise` module. + +NOTE: You can get your trial key at https://hazelcast.com/get-started/?utm_source=docs-website or you can use `vertx-hazelcast` and a community edition of Hazelcast. + +Add the following dependency to the `pom.xml`: + +[source,xml] +---- + + com.hazelcast + vertx-hazelcast-enterprise + {vertx.version} + +---- + +Change the following part of the `start` method: + +[source,java] +---- +// Create local SessionStore +SessionStore store = LocalSessionStore.create(vertx); +---- + +to the following: + +[source,java] +---- +// Create clustered SessionStore +SessionStore store = ClusteredSessionStore.create(vertx); +---- + +and from now on we will start the application with `-server` parameter, which tells Vert.x to look for a cluster manager implementation. + +We also need to provide a Hazelcast configuration file, and create a file cluster.xml in the `src/main/resources` directory: + +[source,xml] +---- + + + + + replace/with/your/key + + + + + + + + + 1 + SET + + + + 1 + + + + 1 + + + + 0 + + + __vertx.* + false + 1 + + + + +---- + +Now rebuild the project and start the application. You will see more verbose output as Hazelcast prints its own startup logs: + +[source,bash] +---- +$ java -jar target/vertx-hz-1.0.0-SNAPSHOT-fat.jar -cluster +... +HTTP server started on port 8888 +... +Members {size:2, ver:2} [ + Member [192.168.0.10]:5701 - e29f0362-f9a9-4708-b6e5-1a6067b5aa39 this + Member [192.168.0.10]:5702 - 74014573-a18a-44f2-9ca7-fd90b70dcb43 +] +... +---- + +and + +[source,bash] +---- +$ java -jar target/vertx-hz-1.0.0-SNAPSHOT-fat.jar -cluster +... +HTTP server started on port 8889 +... +Members {size:2, ver:2} [ + Member [192.168.0.10]:5701 - e29f0362-f9a9-4708-b6e5-1a6067b5aa39 + Member [192.168.0.10]:5702 - 74014573-a18a-44f2-9ca7-fd90b70dcb43 this +] +... +---- + +Putting two messages into different instances while using the same cookie, we see that the session is shared between the instances. + +[source,bash] +---- +$ http PUT localhost:8888 message="Hello world" +HTTP/1.1 200 OK +content-length: 31 +content-type: application/json +set-cookie: vertx-web.session=1ab47cb96731123135f25ec7b67efd64; Path=/ + +{ + "messages": [ + "", + "Hello world" + ] +} +---- + +[source,bash] +---- +$ http PUT localhost:8889 message="Hello world 2" 'Cookie: vertx-web.session=674806546c690674962f279670abefcf' +HTTP/1.1 200 OK +content-length: 44 +content-type: application/json + +{ + "messages": [ + "Hello world", + "Hello world 2" + ] +} +---- + +== Using Counter + +Replace this part of the code at the end of the `start()` method: + +[source,java] +---- +context.json( + new JsonObject() + .put("messages", messages) +); +---- + +with the following: + +[source,java] +---- +context.vertx() + .sharedData() + .getCounter("requestId") + .onSuccess(counter -> { + counter.incrementAndGet() + .onSuccess(requestId -> { + context.json( + new JsonObject() + .put("requestId", requestId) + .put("messages", messages) + ); + }); + }); +---- + +When you now try the application, you can see the response contains an additional field named `requestId` and its value increments for every request. + +[source,bash] +---- +$ http PUT localhost:8888 message="Hello world" +HTTP/1.1 200 OK +content-length: 42 +content-type: application/json +set-cookie: vertx-web.session=d9fb4cada5c0fc625089a38f3de13e3c; Path=/ + +{ + "messages": [ + "Hello world" + ], + "requestId": 1 +} +---- + +== CP Subsystem backed Lock and Counter + +The module `vertx-hazelcast-enterprise` provides a different implementation of the `io.vertx.core.shareddata.Counter` and `io.vertx.core.shareddata.Lock` data structures. The implementation in `vertx-hazelcast` is based on the IMap data structure and provides guarantees defined in the xref:architecture:data-partitioning.adoc#best-effort-consistency[Best-effort consistency] section. This means that under certain network partition conditions the counter doesn't provide strong consistency guarantees and can generate duplicate values. + +The module `vertx-hazelcast-enterprise` uses the CP Subsystem from {enterprise-product-name} to implement the Lock and Counter. + +NOTE: For the rest of this tutorial you need to have an {enterprise-product-name} license. + +Make sure you have the following dependency: + +[source,xml] +---- + + com.hazelcast + vertx-hazelcast-enterprise + {vertx.version} + +---- + +and your XML config contains a valid license key: + +[source,xml] +---- +... + replace/with/your/key +... +---- + +Enable the CP subsystem, and in cluster.xml change the value of the `` property to `3`: + +[source,xml] +---- + 3 +---- + +You need to start at least 3 instances for the cluster to form successfully. For complete documentation, see the xref:cp-subsystem:cp-subsystem.adoc[CP Subsystem] section. + diff --git a/docs/modules/integrate/pages/integrate-with-vertx.adoc b/docs/modules/integrate/pages/integrate-with-vertx.adoc new file mode 100644 index 000000000..3355f8b9e --- /dev/null +++ b/docs/modules/integrate/pages/integrate-with-vertx.adoc @@ -0,0 +1,132 @@ += Integrate with Vert.x + +Vert.x is a reactive application toolkit for creating resource-efficient, concurrent, asynchronous and flexible applications on the JVM. + +Hazelcast integrates with Vert.x in a form of a cluster manager - the link:https://vertx.io/docs/vertx-hazelcast/java/[Hazelcast Cluster Manager]. + + +In Vert.x a cluster manager is used for various functions including: + +- Discovery and group membership of Vert.x nodes in a cluster +- Maintaining cluster-wide topic subscriber lists (so we know which nodes are interested in which event bus addresses) +- Distributed Map support +- Distributed Locks +- Distributed Counters + +There are 2 modules to choose from: +- `io.vertx:vertx-hazelcast` - this module is part of Vert.x and is maintained by the Vert.x team with contributions from Hazelcast developers. This module is licensed under the Apache 2 license. + +- `com.hazelcast:vertx-hazelcast-enterprise` - this module is built on top of `vertx-hazelcast` and leverages functionality of {enterprise-product-name} to implement some of the cluster manager functionality (e.g. it uses the CP Subsystem to implement strongly consistent `io.vertx.core.shareddata.Lock` and `io.vertx.core.shareddata.Counter`). + +== Using Vert.x Hazelcast Enterprise Cluster Manager +[.enterprise]*Enterprise* + +To use the Vert.x Hazelcast Enterprise Cluster Manager, add the following dependency to your Vert.x application: + +[source,xml] +---- + + com.hazelcast + vertx-hazelcast-enterprise + 5.0.0-SNAPSHOT + +---- + +The dependency is located in the Hazelcast private repository, and you need to add that as well: + +[source,xml] +---- + + + hazelcast-private-repository + Hazelcast Private Repository + https://repository.hazelcast.com/release/ + + +---- + +Alternatively, if you need to use a snapshot version: + +[source,xml] +---- + + + hazelcast-private-snapshot-repository + Hazelcast Private Snapshot Repository + https://repository.hazelcast.com/snapshot/ + + +---- + +To enable clustering, start your Vert.x application with the `-cluster` parameter. + +=== Configuration + +Provide a file named `cluster.xml` on your classpath to configure a Hazelcast instance used by Vert.x. + +To take advantage of the Lock and Counter data structures backed by the CP subsystem, you need to enable the xref:cp-subsystem:cp-subsystem.adoc[CP Subsytem]. + +For other configuration methods see the link:https://vertx.io/docs/vertx-hazelcast/java/#configcluster[Vert.x documentation]. + +=== Versioning and Hazelcast compatibility + +The Vert.x Hazelcast Enterprise module follows the versioning of Vertx. If you use an `x.y.z` version of Vert.x, you should use an `x.y.z` version of `vertx-hazelcast-enteprise`. + +The `vertx-hazelcast-enteprise` module is compatible with all Hazelcast versions supported at the time of the release of the `vertx-hazelcast-enteprise` module unless stated otherwise. + +While older versions may work, such configurations are not supported. + +== Using Vert.x Hazelcast Cluster Manager + +See the Vert.x Hazelcast Cluster Manager site for reference documentation for the `vertx-hazelcast` module. + +You can also follow our xref:get-started-with-vertx.adoc[Get started with Vert.x guide]. + +== Using a different Hazelcast version + +Due to Java compatibility reasons the Vert.x Hazelcast module doesn't depend on the latest version of Hazelcast. +You can change the Hazelcast dependency to any version of Hazelcast you need, e.g. you can change it to Hazelcast 5.2.x for Java 8 compatibilty, or to the latest. + +NOTE: The old versions may not be supported by Hazelcast anymore and don't receive any patches. + +There are multiple ways to replace the transitive dependency. The most reliable way is to exclude the `com.hazelcast:hazelcast` transitive dependency of the `vert-hazelcast` module and add a direct dependency on `com.hazelcast:hazelcast` to the pom.xml of your project. + +[source,xml] +---- + + io.vertx + vertx-hazelcast + + + com.hazelcast + hazelcast + + + + + com.hazelcast + hazelcast + 5.5.0 + +---- + +Similarly, for `vertx-hazelcast-enterprise`: + +[source,xml] +---- + + com.hazelcast + vertx-hazelcast-enterprise + + + com.hazelcast + hazelcast + + + + + com.hazelcast + hazelcast + 5.5.0 + +---- diff --git a/docs/modules/release-notes/pages/5-5-2.adoc b/docs/modules/release-notes/pages/5-5-2.adoc index 3cd753b69..fca6a41cf 100644 --- a/docs/modules/release-notes/pages/5-5-2.adoc +++ b/docs/modules/release-notes/pages/5-5-2.adoc @@ -6,6 +6,7 @@ == New Features * xref:clients:java.adoc#java-client-standalone-beta[{java-client-new} available (BETA)] * Debezium 2.x support added in {enterprise-product-name} +* Hazelcast Cluster Manager available in Vert.x version 5 For more details on new features, see xref:ROOT:whats-new.adoc[What's new in 5.5].