Getting Started with the Hazelcast Go Client

What You’ll Learn

This tutorial will get you started with the Hazelcast Go client and manipulate a map.

Before you Begin

Start a Hazelcast Viridian Cloud Cluster

  1. Sign up for a Hazelcast Viridian Cloud account (free trial is available).

  2. Log in to your Hazelcast Viridian Cloud account and start your trial by filling in the welcome questionnaire.

  3. A Viridian cluster will be created automatically when you start your trial.

  4. Press the Connect Cluster dialog and switch over to the Advanced setup tab for connection information needed below.

  5. From the Advanced setup tab, download the keystore files and take note of your Cluster ID, Discovery Token and Password as you will need them later.

Setup a Hazelcast Client

Create a new folder and navigate to it:

mkdir hazelcast-go-example
cd hazelcast-go-example

Initialize a new go module:

go mod init example

Install Hazelcast Go client’s latest version as a dependency:

go get github.com/hazelcast/hazelcast-go-client@latest

Extract the keystore files you downloaded from Viridian into this directory. The files you need for this tutorial are:

ca.pem
cert.pem
key.pem

Understanding the Go Client

The following section creates and starts a Hazelcast client with default configuration, connects to your Viridian cluster before shutting the client down at the end.

Create a Go file named “example.go” and put the following code inside it:

package main

import (
	"context"
	"fmt"

	"github.com/hazelcast/hazelcast-go-client"
)

func main() {

	// Connection details for cluster
	config := hazelcast.Config{}
	config.Cluster.Name = "<YOUR_CLUSTER_ID>"

	config.Cluster.Cloud.Enabled = true
	config.Cluster.Cloud.Token = "<YOUR_DISCOVERY_TOKEN>"

	config.Cluster.Network.SSL.SetCAPath("ca.pem")
	config.Cluster.Network.SSL.AddClientCertAndEncryptedKeyPath("cert.pem", "key.pem", "<YOUR_CERTIFICATE_PASSWORD>")

	// create the client and connect to the cluster
	client, err := hazelcast.StartNewClientWithConfig(context.TODO(), config)
	// error checking is omitted for brevity

	fmt.Println("Welcome to your Hazelcast Viridian Cluster!")

	defer client.Shutdown(context.TODO())

	if err != nil {
		panic(err)
	}
}

To run this Go script, use the following command:

go run example.go

Understanding the Hazelcast SQL API

Hazelcast SQL API is a Calcite SQL based interface to allow you to interact with Hazelcast much like any other datastore.

In the following example, we will create a map and insert into it, entries where the keys are ids and the values are defined as an object representing a city.

package main

import (
	"context"
	"fmt"
	"reflect"

	"github.com/hazelcast/hazelcast-go-client"
	"github.com/hazelcast/hazelcast-go-client/logger"
	"github.com/hazelcast/hazelcast-go-client/serialization"
)

type CityDTO struct {
	city       string
	country    string
	population int32
}

type CitySerializer struct{}

func (s CitySerializer) Type() reflect.Type {
	return reflect.TypeOf(CityDTO{})
}

func (s CitySerializer) TypeName() string {
	return "CityDTO"
}

func (s CitySerializer) Write(writer serialization.CompactWriter, value interface{}) {
	city := value.(CityDTO)

	writer.WriteString("City", &city.city)
	writer.WriteString("Country", &city.country)
	writer.WriteInt32("Population", city.population)
}

func (s CitySerializer) Read(reader serialization.CompactReader) interface{} {
	return CityDTO{
		city:       *reader.ReadString("city"),
		country:    *reader.ReadString("country"),
		population: reader.ReadInt32("population"),
	}
}

func createMapping(ctx context.Context, client hazelcast.Client) error {
	fmt.Println("Creating the mapping...")

	// Mapping is required for your distributed map to be queried over SQL.
	// See: https://docs.hazelcast.com/hazelcast/latest/sql/mapping-to-maps
	mappingQuery := `
        CREATE OR REPLACE MAPPING
        cities (
            __key INT,
            country VARCHAR,
            city VARCHAR,
            population INT) TYPE IMAP
        OPTIONS (
            'keyFormat' = 'int',
            'valueFormat' = 'compact',
            'valueCompactTypeName' = 'CityDTO')
    `

	_, err := client.SQL().Execute(ctx, mappingQuery)
	if err != nil {
		return err
	}

	fmt.Println("OK.\n")
	return nil
}

func populateCities(ctx context.Context, client hazelcast.Client) error {
	fmt.Println("Inserting data...")

	// Mapping is required for your distributed map to be queried over SQL.
	// See: https://docs.hazelcast.com/hazelcast/latest/sql/mapping-to-maps
	insertQuery := `
		INSERT INTO cities
		(__key, city, country, population) VALUES
		(1, 'London', 'United Kingdom', 9540576),
		(2, 'Manchester', 'United Kingdom', 2770434),
		(3, 'New York', 'United States', 19223191),
		(4, 'Los Angeles', 'United States', 3985520),
		(5, 'Istanbul', 'Türkiye', 15636243),
		(6, 'Ankara', 'Türkiye', 5309690),
		(7, 'Sao Paulo ', 'Brazil', 22429800)
    `

	_, err := client.SQL().Execute(ctx, "DELETE from cities")
	if err != nil {
		return err
	}
	_, err = client.SQL().Execute(ctx, insertQuery)
	if err != nil {
		return err
	}

	fmt.Println("OK.\n")
	return nil
}

func fetchCities(ctx context.Context, client hazelcast.Client) error {
	fmt.Println("Fetching cities...")

	result, err := client.SQL().Execute(ctx, "SELECT __key, this FROM cities")
	if err != nil {
		return err
	}
	defer result.Close()

	fmt.Println("OK.")
	fmt.Println("--Results of SELECT __key, this FROM cities")
	fmt.Printf("| %4s | %20s | %20s | %15s |\n", "id", "country", "city", "population")

	iter, err := result.Iterator()
	for iter.HasNext() {
		row, err := iter.Next()

		key, err := row.Get(0)
		cityDTO, err := row.Get(1)

		fmt.Printf("| %4d | %20s | %20s | %15d |\n", key.(int32), cityDTO.(CityDTO).country, cityDTO.(CityDTO).city, cityDTO.(CityDTO).population)

		if err != nil {
			return err
		}
	}

	fmt.Println("\n!! Hint !! You can execute your SQL queries on your Viridian cluster over the management center. \n 1. Go to 'Management Center' of your Hazelcast Viridian cluster. \n 2. Open the 'SQL Browser'. \n 3. Try to execute 'SELECT * FROM cities'.")
	return nil
}

///////////////////////////////////////////////////////

func main() {

	// Connection details for cluster
	config := hazelcast.Config{}
	config.Cluster.Name = "<YOUR_CLUSTER_ID>"

	config.Cluster.Cloud.Enabled = true
	config.Cluster.Cloud.Token = "<YOUR_DISCOVERY_TOKEN>"

	config.Cluster.Network.SSL.SetCAPath("ca.pem")
	config.Cluster.Network.SSL.AddClientCertAndEncryptedKeyPath("cert.pem", "key.pem", "<YOUR_CERTIFICATE_PASSWORD>")

	// Register Compact Serializers
	config.Serialization.Compact.SetSerializers(CitySerializer{})

	// Other environment propreties
	config.Logger.Level = logger.FatalLevel

	ctx := context.TODO()
	// create the client and connect to the cluster
	client, err := hazelcast.StartNewClientWithConfig(ctx, config)
	if err != nil {
		panic(err)
	}

	//
	if err := createMapping(ctx, *client); err != nil {
		panic(fmt.Errorf("creating mapping: %w", err))
	}
	if err := populateCities(ctx, *client); err != nil {
		panic(fmt.Errorf("populating cities: %w", err))
	}
	if err := fetchCities(ctx, *client); err != nil {
		panic(fmt.Errorf("fetching cities: %w", err))
	}

	if err := client.Shutdown(ctx); err != nil {
		panic(err)
	}
}

The output of this code is given below:

Creating the mapping...OK.
Inserting data...OK.
Fetching cities...OK.
--Results of 'SELECT __key, this FROM cities'
|   id | country              | city                 | population      |
|    2 | United Kingdom       | Manchester           | 2770434         |
|    6 | Türkiye              | Ankara               | 5309690         |
|    1 | United Kingdom       | London               | 9540576         |
|    7 | Brazil               | Sao Paulo            | 22429800        |
|    4 | United States        | Los Angeles          | 3985520         |
|    5 | Türkiye              | Istanbul             | 15636243        |
|    3 | United States        | New York             | 19223191        |

Summary

In this tutorial, you learned how to get started with the Hazelcast Go Client, connect to a Viridian instance and put data into a distributed map.

See Also

There are a lot of things that you can do with the Go Client. For more, such as how you can query a map with predicates and SQL, check out our Go Client repository and our Go API documentation to better understand what is possible.

If you have any questions, suggestions, or feedback please do not hesitate to reach out to us via Hazelcast Community Slack. Also, please take a look at the issue list if you would like to contribute to the client.