Hazelcast Python Client 0.2.1: A Getting Started Guide

We are happy to announce the release of Hazelcast Python client 0.2.1. This new client is a full-blown Hazelcast Client Protocol implementation targeting Hazelcast 3.6+ clusters and Python 2.7.

In this post, I’ll introduce the general usage of Hazelcast Python client.

Installation

You can install the Hazelcast Python client simply by executing either of the following command:

$ python setup.py install

An even simpler way is to install the client from the official Python Package Index:

$ pip install hazelcast-python-client

After installation we’re all set to jump right in.

Getting started

As a first step, you need a simple configuration. This configuration provides information on how to connect to an already existing Hazelcast cluster and looks as simple as the following example:

import hazelcast, logging
    
    config = hazelcast.ClientConfig()
    # Hazelcast.Address is the hostname or IP address, e.g. 'localhost:5701'
    config.network_config.addresses.append('Hazelcast.Address')
    
    # basic logging setup to see client logs
    logging.basicConfig()
    logging.getLogger().setLevel(logging.INFO)

After the configuration, you can initialize the client as follows:

client = hazelcast.HazelcastClient(config)

The client object should be created once unless you connect to multiple clusters. And, it should be eventually shut down after using the method client.shutdown().

With our newly created access to the Hazelcast cluster, we now want to read and write data. Same as before, Hazelcast is as simple as possible and access to the distributed map is granted by a single command again:

my_async_map = client.get_map("map-name")

The Python client is designed to be fully asynchronous. Every distributed object function such as map.put, map.get, etc. will return a future to you which works the same way as the Python 3 future class.

You can simply retrieve the operations result from the future:

future = my_map.put("key", "async_val")
    old_value = future.result()

Or use it to register callback methods which will be executed asynchronously:

def get_async_callback(f):
        print("map.get_async:", f.result())
    
    future = my_map.get("key")
    future.add_done_callback(get_async_callback)

Although async operations are more efficient in a single threaded Python interpreter, we sometimes need a simpler code. Python client provides a convenience method to support blocking methods.

Every distributed object provides a blocking helper function as shown below.

my_map = client.get_map("map-name").blocking()

Our map implementation is still completely asynchronous internally, but the blocking helper function will call the method result() and return the result instead.

my_map.put("key_1", "value_1")
    value = my_map.get("key_1")

Please note that this time the result is returned instead of the future object, compared to the previous example.

Listeners

Like every Hazelcast client, our Python client also supports adding listeners to distributed objects to retrieve events of different types like entry events, cluster events and more. Adding listeners to our previously created map is also easy:

Python client supports adding listeners to distributed objects.

def item_added(event):
        print("item_added", event)
    
    def item_removed(event):
        print("item_removed", event)
    
    my_map.add_entry_listener(include_value=True, added=item_added, removed=item_removed)

After adding the listeners, the above functions will be called accordingly.

Serialization

Python client has full support of all Hazelcast internal serialization techniques, including IdentifiedDataSerializable and Portable. Those serialization systems are optimized for different use cases, to learn more about the advantages and disadvantages please see the Hazelcast documentation or our blogpost.

One feature of the Portable serialization is to support server side queries without the need to re-ensemble the same class on the Java side. A simple Portable implementation class is shown below.

from hazelcast.serialization.api import Portable

FACTORY_ID=1
class Customer(Portable):
    CLASS_ID=1
    def __init__(self, id=None, name=None, surname=None, mobile=None):
        self.id = id
        self.name = name
        self.surname = surname
        self.mobile = mobile

    def write_portable(self, writer):
        writer.write_int("id", self.id)
        writer.write_utf("name", self.name)
        writer.write_utf("surname", self.surname)
        writer.write_utf("mobile", self.mobile)

    def read_portable(self, reader):
        self.id = reader.read_int("id")
        self.name = reader.read_utf("name")
        self.surname = reader.read_utf("surname")
        self.mobile = reader.read_utf("mobile")

    def get_factory_id(self):
        return FACTORY_ID

    def get_class_id(self):
        return CLASS_ID

Now we need to register the Portable class with our Hazelcast client configuration before we create the client instance.

config = hazelcast.ClientConfig()
config.serialization_config.portable_factories[FACTORY_ID] = 
    {Customer.CLASS_ID: Customer}
client = hazelcast.HazelcastClient(config)
my_map = client.get_map("map").blocking()

After inserting data into the map, you’re now able to query information on server side without having an actual Java class.

# my_map is populated with (id, Customer)
sequence = my_map.values(sql("name ILIKE 'Jon%'"))
for customer in sequence:
    print(customer.name)

Summary

While the Hazelcast Python client already supports quite a lot of features, the development continues with bug fixes and performance improvements. Please feel free to send your contributions and/or bug reports using the following means: