A lightweight distributed lock server with FIFO ordering, automatic lease expiry, semaphores, and pub/sub signals. Single Go binary, zero dependencies.
go build -o dflockd ./cmd/dflockd
./dflockd # listens on 127.0.0.1:6388Or install directly:
go install github.com/mtingers/dflockd/cmd/dflockd@latest$ nc localhost 6388
l
my-key
10
ok abc123... 33
r
my-key
abc123...
okEach request is 3 newline-terminated UTF-8 lines (command\nkey\narg\n). The connection must stay open — locks are auto-released on disconnect. See the protocol reference for all commands.
Each operation is one lock acquire + release over a persistent TCP connection. Measured on an Apple M1 (MacBook Air, 8 GB RAM) with server and clients on localhost.
| Workers | Rounds | Ops | Throughput | Mean | p50 | p99 |
|---|---|---|---|---|---|---|
| 1 | 1,000 | 1,000 | 14,030 ops/s | 0.071 ms | 0.055 ms | 0.223 ms |
| 10 | 1,000 | 10,000 | 49,974 ops/s | 0.199 ms | 0.191 ms | 0.430 ms |
| 50 | 1,000 | 50,000 | 95,741 ops/s | 0.504 ms | 0.386 ms | 2.977 ms |
| 100 | 1,000 | 100,000 | 92,948 ops/s | 1.042 ms | 0.766 ms | 6.606 ms |
| 200 | 1,000 | 200,000 | 92,895 ops/s | 2.079 ms | 1.503 ms | 13.354 ms |
| 500 | 1,000 | 500,000 | 93,460 ops/s | 5.172 ms | 4.295 ms | 29.387 ms |
All workers use unique keys (no contention). Run your own benchmarks with go run ./cmd/bench --help.
CLI flags take precedence over environment variables.
| Flag | Env var | Default | Description |
|---|---|---|---|
--host |
DFLOCKD_HOST |
127.0.0.1 |
Bind address |
--port |
DFLOCKD_PORT |
6388 |
Bind port |
--default-lease-ttl |
DFLOCKD_DEFAULT_LEASE_TTL_S |
33 |
Default lease duration (seconds) |
--max-locks |
DFLOCKD_MAX_LOCKS |
1024 |
Max unique lock+semaphore keys |
--max-connections |
DFLOCKD_MAX_CONNECTIONS |
0 |
Max concurrent connections (0 = unlimited) |
--max-waiters |
DFLOCKD_MAX_WAITERS |
0 |
Max waiters per key (0 = unlimited) |
--max-subscriptions |
DFLOCKD_MAX_SUBSCRIPTIONS |
0 |
Max signal subscriptions per connection (0 = unlimited) |
--read-timeout |
DFLOCKD_READ_TIMEOUT_S |
23 |
Client read timeout (seconds) |
--write-timeout |
DFLOCKD_WRITE_TIMEOUT_S |
5 |
Client write timeout (seconds) |
--shutdown-timeout |
DFLOCKD_SHUTDOWN_TIMEOUT_S |
30 |
Graceful shutdown timeout (seconds, 0 = wait forever) |
--tls-cert |
DFLOCKD_TLS_CERT |
(unset) | TLS certificate PEM file |
--tls-key |
DFLOCKD_TLS_KEY |
(unset) | TLS private key PEM file |
--auth-token |
DFLOCKD_AUTH_TOKEN |
(unset) | Shared secret for authentication |
--auth-token-file |
DFLOCKD_AUTH_TOKEN_FILE |
(unset) | File containing the auth token |
--auto-release-on-disconnect |
DFLOCKD_AUTO_RELEASE_ON_DISCONNECT |
true |
Release locks on disconnect |
--debug |
DFLOCKD_DEBUG |
false |
Enable debug logging |
See the server docs for GC tuning, TLS setup, authentication, and other advanced options.
- Go (in-repo) —
go get github.com/mtingers/dflockd/client(docs) - Python — dflockd-client-py (docs)
- TypeScript — dflockd-client-ts (docs)
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/mtingers/dflockd/client"
)
func main() {
l := &client.Lock{
Key: "my-resource",
AcquireTimeout: 10 * time.Second,
Servers: []string{"127.0.0.1:6388"},
}
ok, err := l.Acquire(context.Background())
if err != nil {
log.Fatal(err)
}
if !ok {
log.Fatal("timed out waiting for lock")
}
defer l.Release(context.Background())
fmt.Println("lock acquired, doing work...")
}go test ./... -vgo run ./cmd/bench --workers 100 --rounds 500See go run ./cmd/bench --help for all options.