Replicate Data between Two Hazelcast Clusters with Hazelcast Platform Operator

Learn how to keep data in sync across two Hazelcast clusters.

Context

In this tutorial, you’ll do the following:

  • Deploy two Hazelcast clusters.

  • Create two Hazelcast map configurations on one of the clusters.

  • Synchronize map data between the two Hazelcast clusters.

Before you Begin

Before starting this tutorial, make sure that you have the following:

Step 1. Start the Hazelcast Cluster

  1. Create a secret with your Hazelcast Enterprise License.

    kubectl create secret generic hazelcast-license-key --from-literal=license-key=<hz-license-key>
  2. Create the Hazelcast clusters.

    1. Run the following command to create the first cluster.

      cat <<EOF | kubectl apply -f -
      apiVersion: hazelcast.com/v1alpha1
      kind: Hazelcast
      metadata:
        name: hazelcast-first
      spec:
        clusterSize: 1
        repository: 'docker.io/hazelcast/hazelcast-enterprise'
        version: '5.1.3'
        licenseKeySecret: hazelcast-license-key
        exposeExternally:
          type: Unisocket
          discoveryServiceType: LoadBalancer
      EOF
    2. Run the following command to create the second cluster.

      cat <<EOF | kubectl apply -f -
      apiVersion: hazelcast.com/v1alpha1
      kind: Hazelcast
      metadata:
        name: hazelcast-second
      spec:
        clusterSize: 1
        repository: 'docker.io/hazelcast/hazelcast-enterprise'
        version: '5.1.3'
        licenseKeySecret: hazelcast-license-key
        exposeExternally:
          type: Unisocket
          discoveryServiceType: LoadBalancer
      EOF
  3. Check the status of the clusters to make sure that both clusters are running.

    kubectl get hazelcast
    NAME               STATUS    MEMBERS   EXTERNAL-ADDRESSES
    hazelcast-first    Running   1/1       172.18.0.222:5701
    hazelcast-second   Running   1/1       172.18.0.207:5701
  4. Find the addresses of the clusters.

    kubectl get service hazelcast-first hazelcast-second
    NAME               TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)          AGE
    hazelcast-first    LoadBalancer   10.96.229.87   172.18.0.222   5701:32092/TCP   1m
    hazelcast-second   LoadBalancer   10.96.34.62    172.18.0.207   5701:32691/TCP   1m

    The field EXTERNAL-IP is the address of your Hazelcast cluster.

Step 2. Create a WAN Replication Configuration

  1. Create two maps on the first cluster. In this example, the following maps are created:

    • example-map-1

    • example-map-2.

      cat <<EOF | kubectl apply -f -
      apiVersion: hazelcast.com/v1alpha1
      kind: Map
      metadata:
        name: example-map-1
      spec:
        hazelcastResourceName: hazelcast-first
      ---
      apiVersion: hazelcast.com/v1alpha1
      kind: Map
      metadata:
        name: example-map-2
      spec:
        hazelcastResourceName: hazelcast-first
      EOF
  2. Create the configuration for WAN replication:

    • Use the first cluster as the source cluster by adding its name as a resource in the WAN Replication configuration. Adding the cluster name as a resource starts WAN replication for both the maps that you created earlier.

    • Add the second cluster as the target cluster to receive the WAN Replication events.

      Run the following command to apply the configuration.

    cat <<EOF | kubectl apply -f -
    apiVersion: hazelcast.com/v1alpha1
    kind: WanReplication
    metadata:
      name: example-wan-replication
    spec:
      resources:
        - name: hazelcast-first
          kind: Hazelcast
      targetClusterName: dev
      endpoints: "<SECOND-CLUSTER-EXTERNAL-IP>"
    EOF
  3. Configure the Hazelcast client to connect to the first cluster, using its address.

    • Java

    • NodeJS

    • Go

    • Python

    ClientConfig config = new ClientConfig();
    config.getNetworkConfig().addAddress("<FIRST-CLUSTER-EXTERNAL-IP>");
    const { Client } = require('hazelcast-client');
    
    const clientConfig = {
        network: {
            clusterMembers: [
                '<FIRST-CLUSTER-EXTERNAL-IP>'
            ]
        }
    };
    const client = await Client.newHazelcastClient(clientConfig);
    import (
    	"log"
    
    	"github.com/hazelcast/hazelcast-go-client"
    )
    
    func main() {
    	config := hazelcast.Config{}
    	cc := &config.Cluster
    	cc.Network.SetAddresses("<FIRST-CLUSTER-EXTERNAL-IP>")
    	ctx := context.TODO()
    	client, err := hazelcast.StartNewClientWithConfig(ctx, config)
    	if err != nil {
    		panic(err)
    	}
    }
    import logging
    import hazelcast
    
    logging.basicConfig(level=logging.INFO)
    
    client = hazelcast.HazelcastClient(
        cluster_members=["<FIRST-CLUSTER-EXTERNAL-IP>"],
        use_public_ip=True,
    )
  4. Now start the application for each map, using the map name as an argument to fill each map with random entries. If you’re reusing the sample code from this tutorial, use the map names example-map-1 and example-map-2.

    • Java

    • NodeJS

    • Go

    • Python

    cd clients/java
    mvn package
    java -jar target/*jar-with-dependencies*.jar fill <MAP-NAME>
    cd clients/nodejs
    npm install
    npm start fill <MAP-NAME>
    cd clients/go
    go run main.go fill <MAP-NAME>
    cd clients/python
    pip install -r requirements.txt
    python main.py fill <MAP-NAME>

    You should see the following output.

    Successful connection!
    Starting to fill the map (<MAP-NAME>) with random entries.
    Current map size: 2
    Current map size: 3
    Current map size: 4
    Current map size: 5
    Current map size: 6
    Current map size: 7
    Current map size: 8
    Current map size: 9
    Current map size: 10

Step 3. Verify the Replication of Map Entries

In this step, you’ll check the sizes of the maps on the second, target cluster to make sure that WAN replication events have been received.

  1. Configure the Hazelcast client to connect to the second cluster, as you did in Configure the Hazelcast Client.

    • Java

    • NodeJS

    • Go

    • Python

    ClientConfig config = new ClientConfig();
    config.getNetworkConfig().addAddress("<SECOND-CLUSTER-EXTERNAL-IP>");
    const { Client } = require('hazelcast-client');
    
    const clientConfig = {
        network: {
            clusterMembers: [
                '<SECOND-CLUSTER-EXTERNAL-IP>'
            ]
        }
    };
    const client = await Client.newHazelcastClient(clientConfig);
    import (
    	"log"
    
    	"github.com/hazelcast/hazelcast-go-client"
    )
    
    func main() {
    	config := hazelcast.Config{}
    	cc := &config.Cluster
    	cc.Network.SetAddresses("<SECOND-CLUSTER-EXTERNAL-IP>")
    	ctx := context.TODO()
    	client, err := hazelcast.StartNewClientWithConfig(ctx, config)
    	if err != nil {
    		panic(err)
    	}
    }
    import logging
    import hazelcast
    
    logging.basicConfig(level=logging.INFO)
    
    client = hazelcast.HazelcastClient(
        cluster_members=["<SECOND-CLUSTER-EXTERNAL-IP>"],
        use_public_ip=True,
    )
  2. Start the application for each map, using the map name as an argument to check the map size, and to check that WAN replication was successful. If you’re reusing the sample code from this tutorial, use the map names example-map-1 and example-map-2.

    • Java

    • NodeJS

    • Go

    • Python

    cd clients/java
    mvn package
    java -jar target/*jar-with-dependencies*.jar size <MAP-NAME>
    cd clients/nodejs
    npm install
    npm start size <MAP-NAME>
    cd clients/go
    go run main.go size <MAP-NAME>
    cd clients/python
    pip install -r requirements.txt
    python main.py size <MAP-NAME>

    You should see the following output:

    Successful connection!
    Current map (<MAP-NAME>) size: 12

Clean Up

To remove all custom resources and PVCs, run the following commands:

kubectl delete secret hazelcast-license-key
kubectl delete $(kubectl get wanreplications,map,hazelcast -o name)
kubectl delete pvc -l "app.kubernetes.io/managed-by=hazelcast-platform-operator"