Skip to main content

Python client for DS service.

Project description

ds-service: Yet Another Data Structure Server

ds-service is a small, in-memory data structure server that is accessible via gRPC.

ds-service runs a single server process (ds-service) that holds shared state in memory and lets many distributed clients and workers coordinate using it.

Presently, it provides two things:

  • A key-value store -- a shared string -> bytes store for passing data between processes.
  • A task queue -- a priority-based work queue that distributes tasks to workers, tracks their state, and requeues tasks whose workers have died or stalled.

It is designed as a lightweight coordination system for distributed batch jobs (for example, when running calibration and projection workflows across many nodes of an HPC cluster), where you need some shared data structures and a way to hand work out to a pool of workers.

Architecture

  • Server (cpp/ds-service.cpp) -- a C++23 gRPC service. All state lives in memory and is guarded by a single global lock, so operations are serialized and consistent. State is not persisted; that is, when the server stops all data is lost.
  • Client (python/ds_service_client/) -- a Python 3.12+ client library that wraps the generated gRPC stubs and translates gRPC status codes into ordinary Python exceptions (KeyError, ValueError, TimeoutError).
  • Interface (misc/ds-service.proto) -- the protobuf/gRPC contract shared by both sides.

By default the server listens on 127.0.0.1:5051.

The key-value store

A flat string -> bytes key-value store.

RPC Description
MapSet(key, value) Store value under key, overwriting any existing value.
MapGet(key) Return the value for key, or NOT_FOUND if it is missing.

Values are opaque bytes, so callers are free to store whatever serialization they like (JSON, pickle, protobuf, raw binary).

The task queue

Tasks are units of work identified by a unique task_id. Each task carries an opaque function and input payload, a floating-point priority, and one or more named queues it should be dispatched from. A task moves through three states: ReadyRunningComplete.

RPC Description
TaskAdd(task_id, queue, priority, function, input) Register a new task and enqueue it on each named queue. Returns ALREADY_EXISTS if the id is already known.
TaskGet(worker_id, queue) Claim the highest-priority Ready task from the first non-empty queue, mark it Running, and return its payload. Returns UNAVAILABLE when no work is ready.
TaskDone(task_id, output) Mark a Running task Complete and store its output.
TaskStatus(task_id) Return a task's current state and, once complete, its output.
Requeue(timeout_s) Reset any task that has been Running longer than timeout_s back to Ready and re-enqueue it.

Within a queue, higher priority values are dispatched first. A worker polls TaskGet across the queues it cares about, runs the work, and reports back with TaskDone. Requeue provides fault tolerance: if a worker crashes without completing its task, a periodic Requeue call makes that task available to another worker.

Building the server

Dependencies are managed with Conan and the build is driven by CMake.

conan install .
. build/Release/generators/conanbuild.sh
cmake -S . -B build/Release \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake
cmake --build build/Release --parallel

A reproducible container build is defined in scripts/apptainer/ds-service.def.

Running

ds-service --address 0.0.0.0:5051

Run ds-service --help for the full argument list.

Python client

pip install .  # installs the ds-service-client package
from ds_service_client import Client, TaskState

client = Client("127.0.0.1:5051")  # or set DS_SERVER_ADDRESS and call Client()

# Key-value map
client.map_set("greeting", b"hello")
assert client.map_get("greeting") == b"hello"

# Task queue
client.task_add("job-1", queue="work", priority=1.0, function=b"...", input=b"...")

task = client.task_get(worker_id="worker-a", queue="work")
# ... do the work ...
client.task_done(task.task_id, output=b"result")

status = client.task_status("job-1")
assert status.state == TaskState.Complete

If Client() is constructed without an address, it reads the server address from the DS_SERVER_ADDRESS environment variable.

License

MIT -- see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ds_service_client-0.2.1.tar.gz (20.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ds_service_client-0.2.1-py3-none-any.whl (11.1 kB view details)

Uploaded Python 3

File details

Details for the file ds_service_client-0.2.1.tar.gz.

File metadata

  • Download URL: ds_service_client-0.2.1.tar.gz
  • Upload date:
  • Size: 20.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for ds_service_client-0.2.1.tar.gz
Algorithm Hash digest
SHA256 4472b460b8f3f7cd14e7fc2454af16a81f077582f16cfafeb020b538c89ad2a5
MD5 ddb694c5afaef4a1b2b5f3578bc97717
BLAKE2b-256 a52597e63dc1b7af8c786e14ed2a823c97b3b05675bc3fa4170ce85981ea6f60

See more details on using hashes here.

File details

Details for the file ds_service_client-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for ds_service_client-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ebcc9ff9a5fbbc700c99d1134665c3277e466beb91952bff11327c3973e6fefa
MD5 1e6495b2f0454e8dcfa8897773fd5e0e
BLAKE2b-256 26944703f4edc9de391bba7ec62cc96220f4bab214d79556926c16dd12afe11e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page