Quick start
-
Start the server.
Terminal window docker run -p 7070:7070 ghcr.io/monolock-dev/monolockThis is a complete server in its production shape: no config file, no database, no dependencies. Each setting is a flag, and each flag has an equivalent environment variable. See Configuration.
-
Acquire a lock from your code.
Terminal window go get github.com/monolock-dev/monolock-goimport ("context""time"monolock "github.com/monolock-dev/monolock-go")func main() {c := monolock.New(monolock.Config{Address: "127.0.0.1:7070"})// Blocks until this process owns "nightly-import", runs the// function, and releases the lock when it returns.err := c.Do(context.Background(), "nightly-import", 2*time.Second,func(ctx context.Context, token uint64) error {// Only one process across your fleet runs this at a time.// ctx is cancelled the moment ownership stops being// confirmed; token fences out stale holders (see below).return doTheWork(ctx, token)})if err != nil {// ...}} -
See the handover.
Run the program two times at the same time. The second process goes into the first-in, first-out (FIFO) queue and prints nothing. When you stop the first process, the server promotes the second process immediately. There is no timeout to wait for. If you stop the first process with
kill -9, the handover takes a maximum of the lease (2 seconds above). This is the failure-detection window that you selected in the call.
What occurred
Section titled “What occurred”sequenceDiagram
participant A as worker A
participant S as monolock
participant B as worker B
A->>S: ACQUIRE "nightly-import" lease=2s
S-->>A: ACQUIRED token=41
B->>S: ACQUIRE "nightly-import" lease=2s
S-->>B: WAITING (queued, FIFO)
par holder works
loop every lease/4
A->>S: HEARTBEAT
S-->>A: ACQUIRED token=41
end
and waiter keeps its place
loop every lease/4
B->>S: HEARTBEAT
S-->>B: WAITING
end
end
A--xS: connection closed
S-->>B: ACQUIRED token=42
Each connection makes a claim on one named lock. The owner holds the lock while its heartbeats continue to arrive. The waiter also sends heartbeats, and thus it keeps its position in the queue. When the connection of the owner closes in a controlled stop, the server promotes the subsequent waiter immediately. When the owner stops silently, the server waits for the lease that the client selected. Then the server moves the lock.
Examine the tokens: 41, then 42. Each grant contains a fencing token that
is strictly larger than each earlier token. Give the token to the resource
that your lock guards. The resource then rejects writes from a stale holder —
a holder that lost the lock and does not know it yet. This is the difference
between hope that mutual exclusion holds and enforcement by the resource.
Read Fencing tokens.

