VoltDB provides a client interface for programs written in Python. The Python client interface is packaged with VoltDB
and is also available from PyPI via pip install voltdbclient. The distribution package includes two
modules: the older voltdbclient module and the new voltclient module, which this section
describes. To use the client from the package included in the VoltDB distribution, make sure the distribution's
lib/python folder is on the Python module search path (for example, by adding it to the
PYTHONPATH environment variable). The client requires Python 3.9 or later and has no third-party
dependencies.
The new VoltDB Python client interface offers similar functionality to the Java client interface. The
voltclient module provides a topology-aware client that maintains a connection to every node in the
cluster, intelligently sends single-partitioned calls directly to the node responsible, supports asynchronous calls, and
adjusts automatically to changes in the cluster — nodes failing, rejoining, or being added — without having to
restart the application.
The following sections explain the four key steps when using the Python interface:
Creating a client connection
Invoking stored procedures (both synchronously and asynchronously)
Interpreting the results
Closing a client connection
Before you can call VoltDB stored procedures, you must create a client instance and connect to the database cluster.
In the Python interface this is done through the class VoltClient. The following example imports
VoltClient and creates a connection to the cluster containing the servers voltsvr1 and
voltsvr2:
from voltclient import VoltClient client = VoltClient(hosts=["voltsvr1", "voltsvr2"])
You do not need to list every server in the cluster. If the client is given at least one reachable node, it finds the rest automatically.
The Python client provides both a synchronous and asynchronous interface. In either case, to make stored procedure
calls, you must first import VoltType from the voltclient module so you can declare
the datatypes of the procedure arguments. To execute a stored procedure synchronously, use the call()
method. The following example imports VoltType and calls the Vote procedure, which has three parameters
— phone number, contestant number, and maximum votes allowed per phone number:
from voltclient import VoltType
response = client.call("Vote",
[VoltType.BIGINT, VoltType.INTEGER, VoltType.BIGINT],
[5551234567, 3, 2])To make asynchronous procedure calls, use call_async(), which returns a standard Python
concurrent.futures.Future (a placeholder for an eventual result). This allows your application to start
many calls at once and collect the responses as the calls complete. The following example creates one
Future per vote, then iterates through the results in the order they arrive:
import concurrent.futures
# Each vote is (phone number, contestant number)
votes = [
(6175550101, 1),
(6175550102, 3),
(2125550199, 1),
(2125550143, 6),
]
futures = [client.call_async("Vote",
[VoltType.BIGINT, VoltType.INTEGER, VoltType.BIGINT],
[num, contestant, 1])
for num, contestant in votes]
for f in concurrent.futures.as_completed(futures):
response = f.result()
# ... check each response ...Both the synchronous and asynchronous invocations return a VoltResponse object that contains both
the status of the call and the return values. A status of 1 indicates a success, while any other value indicates a problem.
You can use the statusString attribute to display further information on problems encountered while
running the stored procedure. For example:
if response.status != 1:
print(f"Stored procedure failed: {response.statusString}")An error that prevents a call from completing at all, like the connection being lost, raises a Python exception. The outcome of the call is uncertain, and the client will not resend it. The application must decide whether it is safe to make the same call a second time.
If the stored procedure is successful, you can use the client response to retrieve the results. The results are
returned as a list of VoltTable objects via the tables attribute. Each table has a
columns list describing the column names and datatypes, and a tuples list containing
the rows. The values in each row are already converted to the corresponding Python datatypes (for instance, VoltDB TIMESTAMP
values become Python datetime objects and DECIMAL values become Python Decimal
objects). The following example calls the Results procedure, which returns a single table containing each contestant's name,
number, and their total votes received:
response = client.call("Results")
if response.status != 1:
print("Results failed:", response.statusString)
else:
table = response.tables[0]
for row in table.tuples:
name, number, total = row
print(f"Contestant {number}, {name}: {total} votes."The client runs background threads for its connections, so it is important to close it when your application is done.
This is done manually by calling close():
client = VoltClient(hosts=["voltsvr1", "voltsvr2"]) try: ... # make procedure calls finally: client.close()
Alternatively, you can use the client as a context manager with the Python with ... as syntax,
which closes the connection automatically:
with VoltClient(hosts=["voltsvr1", "voltsvr2"]) as client ... # make procedure calls
When you make asynchronous calls, it is important to ensure all calls complete before closing the connection. The
following example uses concurrent.futures.wait, which only returns when every future has a result or
error, to ensure the connection does not close prematurely:
try: futures = [client.call_async(...) for ... in ...] finally: concurrent.futures.wait(futures) # returns when every call has completed client.close() # safely closes the connection
Documentation