Skip to main content
Archived

This project has been archived by its maintainers, and is no longer receiving any updates.

dflockd

A lightweight distributed lock server using a simple line-based TCP protocol with FIFO ordering, automatic lease expiry, and background renewal.

Read the docs here

Quick start

# Install
uv sync

# Run the server
uv run dflockd

The server listens on 0.0.0.0:6388 by default.

Server configuration

All tuning is via environment variables:

Variable Default Description
DFLOCKD_HOST 0.0.0.0 Bind address
DFLOCKD_PORT 6388 Bind port
DFLOCKD_DEFAULT_LEASE_TTL_S 33 Default lock lease duration (seconds)
DFLOCKD_LEASE_SWEEP_INTERVAL_S 1 How often to check for expired leases
DFLOCKD_GC_LOOP_SLEEP 5 How often to prune idle lock state
DFLOCKD_GC_MAX_UNUSED_TIME 60 Seconds before idle lock state is pruned
DFLOCKD_MAX_LOCKS 1024 Maximum number of unique lock keys
DFLOCKD_READ_TIMEOUT_S 23 Client read timeout (seconds)
DFLOCKD_AUTO_RELEASE_ON_DISCONNECT 1 Release locks when a client disconnects (1, yes, true to enable)

CLI arguments

Settings can also be passed as command-line flags. Environment variables take precedence over CLI arguments.

uv run dflockd --port 7000 --max-locks 512
Flag Default Env var override
--host 0.0.0.0 DFLOCKD_HOST
--port 6388 DFLOCKD_PORT
--default-lease-ttl 33 DFLOCKD_DEFAULT_LEASE_TTL_S
--lease-sweep-interval 1 DFLOCKD_LEASE_SWEEP_INTERVAL_S
--gc-interval 5 DFLOCKD_GC_LOOP_SLEEP
--gc-max-idle 60 DFLOCKD_GC_MAX_UNUSED_TIME
--max-locks 1024 DFLOCKD_MAX_LOCKS
--read-timeout 23 DFLOCKD_READ_TIMEOUT_S
--auto-release-on-disconnect / --no-auto-release-on-disconnect true DFLOCKD_AUTO_RELEASE_ON_DISCONNECT

Protocol

The wire protocol is line-based UTF-8 over TCP. Each request is exactly 3 lines: command\nkey\narg\n.

Commands

Lock (acquire)

l
<key>
<timeout_s> [<lease_ttl_s>]

Response: ok <token> <lease_ttl>\n or timeout\n

Renew

n
<key>
<token> [<lease_ttl_s>]

Response: ok <seconds_remaining>\n or error\n

Enqueue (two-phase step 1)

e
<key>
[<lease_ttl_s>]

Response: acquired <token> <lease_ttl>\n or queued\n

Wait (two-phase step 2)

w
<key>
<timeout_s>

Response: ok <token> <lease_ttl>\n or timeout\n

Release

r
<key>
<token>

Response: ok\n or error\n

Behavior

  • Locks are granted in strict FIFO order per key.
  • Leases expire automatically if not renewed. On expiry, the lock passes to the next waiter.
  • Connections that disconnect have their held locks released automatically.

Client usage

Async client

import asyncio
from dflockd.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.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

Manual acquire/release

Both clients support explicit acquire() / release() outside of a context manager:

from dflockd.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.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

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.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.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

Metadata

Release files for dflockd 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dflockd 0.5.0
File Size Uploaded
dflockd-0.5.0.tar.gz 13.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dflockd 0.5.0
File Interpreter ABI Platform
dflockd-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 29.0 kB

Release files / dflockd-0.5.0.tar.gz

Download URL dflockd-0.5.0.tar.gz
Size 13.2 kB
Tags Source
SHA-256 checksum
How to use checksums
1d57b5dadac18e01e3cb5febd5f1fdffb63e3451a983a1d9ef5ec26a8098b80f
BLAKE2b-256 checksum
How to use checksums
5729e2fba0799dbc7977ccc05e7d19edb8768d4436db0d280b1b7bbcc695ba15
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / dflockd-0.5.0-py3-none-any.whl

Download URL dflockd-0.5.0-py3-none-any.whl
Size 15.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7ab984d653855ba4680cae13fa7255c19d8d92edd4fa0f271c8da42af21444aa
BLAKE2b-256 checksum
How to use checksums
9c2477cf3010f18bb61f369440f15592082bfc68178648fe67bade1a1810184b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page