dflockd python client
Project description
dflockd-client
A Python client library for dflockd — a lightweight distributed lock server with FIFO ordering, automatic lease expiry, and background renewal.
Installation
pip install dflockd-client
Or with uv:
uv add dflockd-client
Quick start
Async client
import asyncio
from dflockd_client.client import DistributedLock
async def main():
async with DistributedLock("my-key", acquire_timeout_s=10) as lock:
print(lock.token, lock.lease)
# critical section — lease auto-renews in background
asyncio.run(main())
Sync client
from dflockd_client.sync_client import DistributedLock
with DistributedLock("my-key", acquire_timeout_s=10) as lock:
print(lock.token, lock.lease)
# critical section — lease auto-renews in background thread
Tip: You can also use the top-level import alias:
from dflockd_client import SyncDistributedLock(orAsyncDistributedLockfor async).
Manual acquire/release
Both clients support explicit acquire() / release() outside of a context manager:
from dflockd_client.sync_client import DistributedLock
lock = DistributedLock("my-key")
if lock.acquire():
try:
pass # critical section
finally:
lock.release()
Two-phase lock acquisition
The enqueue() / wait() methods split lock acquisition into two steps, allowing you to notify an external system after joining the queue but before blocking:
from dflockd_client.sync_client import DistributedLock
lock = DistributedLock("my-key")
status = lock.enqueue() # join queue, returns "acquired" or "queued"
notify_external_system() # your application logic here
if lock.wait(timeout_s=10): # block until granted (no-op if already acquired)
try:
pass # critical section
finally:
lock.release()
Async equivalent:
lock = DistributedLock("my-key")
status = await lock.enqueue()
await notify_external_system()
if await lock.wait(timeout_s=10):
try:
pass # critical section
finally:
await lock.release()
Parameters
| Parameter | Default | Description |
|---|---|---|
key |
(required) | Lock name |
acquire_timeout_s |
10 |
Seconds to wait for lock acquisition |
lease_ttl_s |
None (server default) |
Lease duration in seconds |
servers |
[("127.0.0.1", 6388)] |
List of (host, port) tuples |
sharding_strategy |
stable_hash_shard |
Callable[[str, int], int] — maps (key, num_servers) to server index |
renew_ratio |
0.5 |
Renew at lease * ratio seconds |
ssl_context |
None |
ssl.SSLContext for TLS connections. None uses plain TCP |
auth_token |
None |
Auth token for servers started with --auth-token. None skips auth |
connect_timeout_s |
10 |
Seconds to wait for the TCP connection to the server |
Authentication
When the dflockd server is started with --auth-token, pass the token to authenticate:
from dflockd_client.sync_client import DistributedLock
with DistributedLock("my-key", auth_token="mysecret") as lock:
print(lock.token, lock.lease)
Async equivalent:
from dflockd_client.client import DistributedLock
async with DistributedLock("my-key", auth_token="mysecret") as lock:
print(lock.token, lock.lease)
Both DistributedLock and DistributedSemaphore accept auth_token in the async and sync clients. A PermissionError is raised if the token is invalid.
TLS
To connect to a TLS-enabled dflockd server, pass an ssl.SSLContext:
import ssl
from dflockd_client.sync_client import DistributedLock
ctx = ssl.create_default_context() # uses system CA bundle
# or: ctx = ssl.create_default_context(cafile="/path/to/ca.pem")
with DistributedLock("my-key", ssl_context=ctx) as lock:
print(lock.token, lock.lease)
Async equivalent:
import ssl
from dflockd_client.client import DistributedLock
ctx = ssl.create_default_context()
async with DistributedLock("my-key", ssl_context=ctx) as lock:
print(lock.token, lock.lease)
Both DistributedLock and DistributedSemaphore accept ssl_context in the async and sync clients.
Semaphores
DistributedSemaphore allows up to N concurrent holders per key, using the same API patterns as DistributedLock:
from dflockd_client.sync_client import DistributedSemaphore
# Allow up to 3 concurrent workers on this key
with DistributedSemaphore("my-key", limit=3, acquire_timeout_s=10) as sem:
print(sem.token, sem.lease)
# critical section — up to 3 holders at once
Async equivalent:
from dflockd_client.client import DistributedSemaphore
async with DistributedSemaphore("my-key", limit=3, acquire_timeout_s=10) as sem:
print(sem.token, sem.lease)
Manual acquire/release and two-phase (enqueue() / wait()) work the same as locks.
Parameters
| Parameter | Default | Description |
|---|---|---|
key |
(required) | Semaphore name |
limit |
(required) | Maximum concurrent holders |
acquire_timeout_s |
10 |
Seconds to wait for acquisition |
lease_ttl_s |
None (server default) |
Lease duration in seconds |
servers |
[("127.0.0.1", 6388)] |
List of (host, port) tuples |
sharding_strategy |
stable_hash_shard |
Callable[[str, int], int] — maps (key, num_servers) to server index |
renew_ratio |
0.5 |
Renew at lease * ratio seconds |
ssl_context |
None |
ssl.SSLContext for TLS connections. None uses plain TCP |
auth_token |
None |
Auth token for servers started with --auth-token. None skips auth |
connect_timeout_s |
10 |
Seconds to wait for the TCP connection to the server |
Stats
Query server state (connections, held locks, active semaphores) using the low-level stats() function:
import asyncio
from dflockd_client.client import stats
async def main():
reader, writer = await asyncio.open_connection("127.0.0.1", 6388)
result = await stats(reader, writer)
print(result)
# {'connections': 1, 'locks': [], 'semaphores': [], 'idle_locks': [], 'idle_semaphores': []}
writer.close()
await writer.wait_closed()
asyncio.run(main())
Sync equivalent:
import socket
from dflockd_client.sync_client import stats
sock = socket.create_connection(("127.0.0.1", 6388))
rfile = sock.makefile("r", encoding="utf-8")
result = stats(sock, rfile)
print(result)
rfile.close()
sock.close()
Returns a dict with connections, locks, semaphores, idle_locks, and idle_semaphores.
Multi-server sharding
When running multiple dflockd instances, the client can distribute keys across servers using consistent hashing. Each key always routes to the same server.
from dflockd_client.sync_client import DistributedLock
servers = [("server1", 6388), ("server2", 6388), ("server3", 6388)]
with DistributedLock("my-key", servers=servers) as lock:
print(lock.token, lock.lease)
The default strategy uses zlib.crc32 for stable, deterministic hashing. You can provide a custom strategy:
from dflockd_client.sync_client import DistributedLock
def my_strategy(key: str, num_servers: int) -> int:
"""Route all keys to the first server."""
return 0
with DistributedLock("my-key", servers=servers, sharding_strategy=my_strategy) as lock:
pass
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dflockd_client-1.8.1.tar.gz.
File metadata
- Download URL: dflockd_client-1.8.1.tar.gz
- Upload date:
- Size: 11.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3709fb7ca8fc6cec3f17503732f1635ec8b6517a4db62262408e903f4085ae9
|
|
| MD5 |
c5af83855acd66c5d398f22bba6da6a9
|
|
| BLAKE2b-256 |
2f3347dabe07528a8e3b4a470063233592e0697692b6fe3bba4ee009596a0fc8
|
Provenance
The following attestation bundles were made for dflockd_client-1.8.1.tar.gz:
Publisher:
publish.yml on mtingers/dflockd-client-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dflockd_client-1.8.1.tar.gz -
Subject digest:
a3709fb7ca8fc6cec3f17503732f1635ec8b6517a4db62262408e903f4085ae9 - Sigstore transparency entry: 1050364401
- Sigstore integration time:
-
Permalink:
mtingers/dflockd-client-py@9719d8438b029d1259360096587be245580718b8 -
Branch / Tag:
refs/tags/v1.8.1 - Owner: https://github.com/mtingers
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9719d8438b029d1259360096587be245580718b8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file dflockd_client-1.8.1-py3-none-any.whl.
File metadata
- Download URL: dflockd_client-1.8.1-py3-none-any.whl
- Upload date:
- Size: 16.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90433214babd1e293878543f6b19fd26cd217a9307d813b9f9c6ee9eaab5dc63
|
|
| MD5 |
1dc64d8a8c601bfd51ec00ea6079e63a
|
|
| BLAKE2b-256 |
a91d18cbbfce16e623a28673ec41f3779207602ef02f9c344acf06217e5ef8da
|
Provenance
The following attestation bundles were made for dflockd_client-1.8.1-py3-none-any.whl:
Publisher:
publish.yml on mtingers/dflockd-client-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dflockd_client-1.8.1-py3-none-any.whl -
Subject digest:
90433214babd1e293878543f6b19fd26cd217a9307d813b9f9c6ee9eaab5dc63 - Sigstore transparency entry: 1050364403
- Sigstore integration time:
-
Permalink:
mtingers/dflockd-client-py@9719d8438b029d1259360096587be245580718b8 -
Branch / Tag:
refs/tags/v1.8.1 - Owner: https://github.com/mtingers
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9719d8438b029d1259360096587be245580718b8 -
Trigger Event:
release
-
Statement type: