diff --git a/docs/copyright.rst b/docs/copyright.rst index bed320efbc..6bf7df6143 100644 --- a/docs/copyright.rst +++ b/docs/copyright.rst @@ -1,7 +1,7 @@ Copyright ========= -Copyright (c) 2008-2023, Hazelcast, Inc. All Rights Reserved. +Copyright (c) 2008-2026, Hazelcast, Inc. All Rights Reserved. Visit `hazelcast.com `__ for more information. \ No newline at end of file diff --git a/docs/features.rst b/docs/features.rst index 4a2122c850..4b6c1b7a31 100644 --- a/docs/features.rst +++ b/docs/features.rst @@ -51,3 +51,4 @@ features: - Global Serialization - Connection Strategy - Connection Retry +- Vector Collection (BETA) diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 173486fd06..b58def4f3f 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -6,13 +6,16 @@ Hazelcast Python client. It outlines the requirements, installation and configuration of the client, setting up a cluster, and provides a simple application that uses a distributed map in Python client. +.. Note:: See :ref:`getting_started_asyncio:getting started - asyncio api (beta)` for getting started with the the asyncio API. + + Requirements ------------ - Windows, Linux/UNIX or Mac OS X -- Python 3.7 or newer -- Hazelcast 4.0 or newer -- `Supported Java virtual machine ` +- Python 3.11 or newer +- Hazelcast 5.0 or newer +- `Supported Java virtual machine `__ - Latest Hazelcast Python client Working with Hazelcast Clusters @@ -48,7 +51,7 @@ There are following options to start a Hazelcast cluster easily: .. code:: bash - docker run -p 5701:5701 hazelcast/hazelcast:5.5.0 + docker run -p 5701:5701 hazelcast/hazelcast:latest - You can use `Hazelcast CLI `__. @@ -393,9 +396,9 @@ on a cluster using the client. client = hazelcast.HazelcastClient() personnel_map = client.get_map("personnel-map") - personnel_map.put("Alice", "IT") - personnel_map.put("Bob", "IT") - personnel_map.put("Clark", "IT") + personnel_map.put("Alice", "IT").result() + personnel_map.put("Bob", "IT").result() + personnel_map.put("Clark", "IT").result() print("Added IT personnel. Printing all known personnel") @@ -425,9 +428,9 @@ Now, run the following code. client = hazelcast.HazelcastClient() personnel_map = client.get_map("personnel-map") - personnel_map.put("Denise", "Sales") - personnel_map.put("Erwing", "Sales") - personnel_map.put("Faith", "Sales") + personnel_map.put("Denise", "Sales").result() + personnel_map.put("Erwing", "Sales").result() + personnel_map.put("Faith", "Sales").result() print("Added Sales personnel. Printing all known personnel") @@ -461,7 +464,7 @@ our map lives in the cluster and no matter which client we use, we can access the whole map. You may wonder why we have used ``result()`` method over the -``entry_set()`` method of the ``personnel_map``. This is because the +``put()`` and ``entry_set()`` methods of the ``personnel_map``. This is because the Hazelcast Python client is designed to be fully asynchronous. Every method call over distributed objects such as ``put()``, ``get()``, ``entry_set()``, etc. will return a ``Future`` object that is similar to diff --git a/docs/getting_started_asyncio.rst b/docs/getting_started_asyncio.rst new file mode 100644 index 0000000000..cdff656375 --- /dev/null +++ b/docs/getting_started_asyncio.rst @@ -0,0 +1,262 @@ +Getting Started - Asyncio API (BETA) +==================================== + +.. warning:: + + Hazelcast's asyncio API is BETA. Do not use it in production. + +This chapter provides information on how to get started with the Hazelcast Python client using its asyncio API. +It provides the requirements, installation instructions, and a simple application that uses a distributed map in a Python client. + +Requirements +------------ + +- Windows, Linux, macOS +- Python 3.11 or newer +- Hazelcast 5.6 or newer +- `Supported Java virtual machine `__ +- Latest Hazelcast Python client + +Working with Hazelcast Clusters +------------------------------- + +The Hazelcast Python client requires a working Hazelcast cluster to run. +The cluster handles storage and manipulation of user data. + +There are several options to run a Hazelcast cluster: + +- `Docker `__ +- `Hazelcast CLI (hz) `__ +- `Binary `__ +- `Hazelcast Enterprise `__ + +Once the Hazelcast cluster is running, you can carry on with creating and starting the Hazelcast client. + +Working with the Hazelcast Client +--------------------------------- + +Creating and Starting Client +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The asyncio client-specific public API is exposed in the ``hazelcast.asyncio`` package. +Call the ``create_and_start`` method of the ``HazelcastClient`` class to create an instance and connect it to the cluster. + +.. code:: python + + import asyncio + + from hazelcast.asyncio import HazelcastClient + + async def amain(): + client = await HazelcastClient.create_and_start() + # ... use the client ... + + asyncio.run(amain()) + +The Hazelcast client requires an event loop to be running. +Additionally, any code that uses the ``await`` keyword must be put in an ``async`` function. + +Configuring the Client +~~~~~~~~~~~~~~~~~~~~~~ + +You must pass configuration options as keyword arguments to your client at startup. + +This section describes some network options to cover common use cases for connecting the client to a cluster. +See the :ref:`configuration_overview:configuration overview` section for information. + +You can omit the keyword arguments to use the default settings: + +.. code:: python + + client = await HazelcastClient.create_and_start() + +Cluster Name Setting +^^^^^^^^^^^^^^^^^^^^ + +If the cluster was configured with a name other than the default ``dev``, the same name must be used in the client configuration: + +.. code:: python + + client = await hazelcast.HazelcastClient( + cluster_name="name-of-your-cluster", + ) + +Network Settings +^^^^^^^^^^^^^^^^ + +You need to provide the IP address and port of at least one member in +your cluster so the client can find it. + +.. code:: python + + client = await hazelcast.HazelcastClient.create_and_start( + cluster_members=["some-ip-address:port"] + ) + +Basic Usage +----------- + +Run a simple program to use a distributed map in the Python client: + +.. code:: python + + import asyncio + import logging + + from hazelcast.asyncio import HazelcastClient + + # Enable logging to see the logs + logging.basicConfig(level=logging.INFO) + + async def amain(): + # Connect to Hazelcast cluster + client = await HazelcastClient.create_and_start() + await client.shutdown() + + asyncio.run(amain()) + +This should print logs about the cluster members such as address, port +and UUID to the ``stderr``. + +.. code:: text + + INFO:hazelcast.lifecycle:HazelcastClient 5.6.0 is STARTING + INFO:hazelcast.lifecycle:HazelcastClient 5.6.0 is STARTED + INFO:hazelcast.internal.asyncio_connection:Trying to connect to Address(host=127.0.0.1, port=5701) + INFO:hazelcast.lifecycle:HazelcastClient 5.6.0 is CONNECTED + INFO:hazelcast.internal.asyncio_connection:Authenticated with server Address(host=127.0.0.1, port=5701):49c5407d-78f4-4611-a025-95f7afd0ab68, server version: 5.5.2, local address: Address(host=127.0.0.1, port=56134) + INFO:hazelcast.internal.asyncio_cluster: + + Members [1] { + Member [127.0.0.1]:5701 - 49c5407d-78f4-4611-a025-95f7afd0ab68 + } + + INFO:hazelcast.internal.asyncio_client:Client started + INFO:hazelcast.lifecycle:HazelcastClient 5.6.0 is SHUTTING_DOWN + INFO:hazelcast.internal.asyncio_connection:Removed connection to Address(host=127.0.0.1, port=5701):49c5407d-78f4-4611-a025-95f7afd0ab68, connection: + INFO:hazelcast.lifecycle:HazelcastClient 5.6.0 is DISCONNECTED + INFO:hazelcast.lifecycle:HazelcastClient 5.6.0 is SHUTDOWN + +Congratulations! +You just started a Hazelcast Python client instance. + +**Using a Map** + +Let's manipulate a distributed map on a cluster using the client. + +.. code:: python + + import asyncio + + from hazelcast.asyncio import HazelcastClient + + async def amain(): + client = await HazelcastClient.create_and_start() + + personnel_map = await client.get_map("personnel-map") + await personnel_map.put("Alice", "IT") + await personnel_map.put("Bob", "IT") + await personnel_map.put("Clark", "IT") + + print("Added IT personnel. Printing all known personnel") + + entries = await personnel_map.entry_set() + for person, department in entries: + print("%s is in %s department" % (person, department)) + + await client.shutdown() + + asyncio.run(amain()) + +**Output** + +.. code:: text + + Added IT personnel. Printing all known personnel + Alice is in IT department + Clark is in IT department + Bob is in IT department + +You see this example puts all the IT personnel into a cluster-wide +``personnel-map`` and then prints all the known personnel. + +Now, run the following code. + +.. code:: python + + import asyncio + + from hazelcast.asyncio import HazelcastClient + + async def amain(): + client = await HazelcastClient.create_and_start() + personnel_map = await client.get_map("personnel-map") + await personnel_map.put("Denise", "Sales") + await personnel_map.put("Erwing", "Sales") + await personnel_map.put("Faith", "Sales") + + print("Added Sales personnel. Printing all known personnel") + + entries = await personnel_map.entry_set() + for person, department in entries: + print("%s is in %s department" % (person, department)) + + await client.shutdown() + + asyncio.run(amain()) + +**Output** + +.. code:: text + + Added Sales personnel. Printing all known personnel + Denise is in Sales department + Erwing is in Sales department + Faith is in Sales department + Alice is in IT department + Clark is in IT department + Bob is in IT department + +This time you added only the sales employees but you got the list of all known employees including the ones in IT. +That is because the map lives in the cluster and no matter which client you use, you can access the whole map. + +Note that the ``await`` keyword causes the ``put`` methods to run serially. +To run them concurrently, you can wrap them in tasks. +Running the tasks in a ``TaskGroup`` makes sure the errors are handled, and all tasks are done: + +.. code:: python + + import asyncio + + from hazelcast.asyncio import HazelcastClient + + async def amain(): + client = await HazelcastClient.create_and_start() + personnel_map = await client.get_map("personnel-map") + + # update the map concurrently + async with asyncio.TaskGroup() as tg: + tg.create_task(personnel_map.put("Denise", "Sales")) + tg.create_task(personnel_map.put("Erwing", "Sales")) + tg.create_task(personnel_map.put("Faith", "Sales")) + + # all items are added to the map at this stage + + print("Added Sales personnel. Printing all known personnel") + + entries = await personnel_map.entry_set() + for person, department in entries: + print("%s is in %s department" % (person, department)) + + await client.shutdown() + + asyncio.run(amain()) + +Using tasks, method calls over the distributed objects are executed asynchronously without blocking the execution order of your program. + +Code Samples +------------ + +See the Hazelcast Python +`examples `__ +for more code samples. diff --git a/docs/index.rst b/docs/index.rst index f06234a7a5..3f6f779276 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,8 +21,8 @@ in-memory data store and computation platform that provides a wide variety of distributed data structures and concurrency primitives. Hazelcast Python client is a way to communicate to Hazelcast clusters -and access the cluster data. The client provides a Future-based -asynchronous API suitable for wide ranges of use cases. +and access the cluster data. The client provides both a Future-based +asynchronous API, and an asyncio API suitable for wide ranges of use cases. Overview @@ -33,10 +33,10 @@ Usage .. code:: python - import hazelcast + from hazelcast import HazelcastClient # Connect to Hazelcast cluster. - client = hazelcast.HazelcastClient() + client = HazelcastClient() # Get or create the "distributed-map" on the cluster. distributed_map = client.get_map("distributed-map") @@ -61,6 +61,44 @@ Usage # Shutdown the client. client.shutdown() +Usage (Asyncio) +~~~~~~~~~~~~~~~ + +.. code:: python + + import asyncio + + from hazelcast.asyncio import HazelcastClient + + async def amain(): + # Connect to Hazelcast cluster. + client = await HazelcastClient.create_and_start() + + # Get or create the "distributed-map" on the cluster. + distributed_map = await client.get_map("distributed-map") + + # Put "key", "value" pair into the "distributed-map". + # And wait for the request to complete. + await distributed_map.set("key", "value") + + # Try to get the value associated with the given key from the cluster + # and attach a callback to be executed once the response for the + # get request is received. Note that, the set request above was + # synchronous, since the result is awaited. + # Whereas the get request below is asynchronous. + get_future = asyncio.create_task(distributed_map.get("key")) + get_future.add_done_callback(lambda future: print("Future's value:", future.result())) + + # Do other operations. The operations below won't wait for + # the get request above to complete. + map_size = await distributed_map.size() + print("Map size:", map_size) + + # Shutdown the client. + await client.shutdown() + + + asyncio.run(amain()) If you are using Hazelcast and the Python client on the same machine, the default configuration should work out-of-the-box. However, @@ -72,9 +110,7 @@ Configuration .. code:: python - import hazelcast - - client = hazelcast.HazelcastClient( + client = HazelcastClient( cluster_name="cluster-name", cluster_members=[ "10.90.0.2:5701", @@ -85,8 +121,21 @@ Configuration ] ) - print("Connected to cluster") - client.shutdown() +Configuration (Asyncio) +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + client = await HazelcastClient.create_and_start( + cluster_name="cluster-name", + cluster_members=[ + "10.90.0.2:5701", + "10.90.0.3:5701", + ], + lifecycle_listeners=[ + lambda state: print("Lifecycle event >>>", state), + ] + ) See the API documentation of :class:`hazelcast.client.HazelcastClient` @@ -121,6 +170,7 @@ Features config api/modules getting_started + getting_started_asyncio features configuration_overview serialization