nano-api / lock
Two instances woke up
and only one should run
One call says whether you got the lock. Nobody else gets it until you let go, or until
it expires on its own. A job runs once even when two instances try.
(flock, for things that talk over HTTP.)
Free. Signup is one curl and the key comes back in the response. No form, no
card, nobody to ask.
POST /v1/locks/nightly-import ttl 60s - worker-1 holds it · fence 7 released
- worker-2 409 · waiting 409 · waiting
- worker-3 409 · waiting holds it · fence 8
One writer at a time. The other two are told who has it, and for how long.
The whole thing, in a shell script
# Capture the response first: piping curl into jq would hide curl's exit
# status, and a 409 would silently leave TOKEN empty and run the job anyway.
RESPONSE=$(curl -sf -X POST "https://lock.nano-api.com/v1/locks/nightly-import?ttl=60&owner=$HOSTNAME" \
-H "Authorization: Bearer $NANO_API_KEY") || { echo "someone else has it"; exit 0; }
TOKEN=$(jq -r .token <<< "$RESPONSE")
trap 'curl -sf -X DELETE "https://lock.nano-api.com/v1/locks/nightly-import?token=$TOKEN" \
-H "Authorization: Bearer $NANO_API_KEY" > /dev/null' EXIT
./import.sh curl -sf exits non-zero on 409, so the second instance stops. The
trap releases the lock however the script ends, crash included. Capture the
response before jq sees it: in a pipeline, || would test jq's
exit status, not curl's.
What goes wrong without one
Doing it by hand
- Two pods wake at 03:00, both run the import
- A slow run overlaps the next one
- A retry fires while the original is still working
- You add a flag column and get the race wrong (I did)
With NanoLock
- One atomic call decides who proceeds
- The lease expires by itself if the holder dies
- Only the holder can release it
- You can see who holds what, and until when
Guarantees, and the limits
Acquiring is a single atomic write that only overwrites an expired lease. Two callers in the same millisecond cannot both win; twenty parallel requests produce exactly one, which is a test in the repo rather than a claim on a page.
A lease is not consensus. Stall past your TTL (long GC, suspended VM) and someone else legitimately acquires it while you still think you hold it.
That is what fence is for: a number that only ever increases. Give it to whatever you are protecting, have that reject anything older, and a late holder is turned away on arrival.
In order of importance: renew while you work, keep the TTL above your worst-case runtime, use the fence where being wrong would cost something.
Using it with the others
Lock the job Relay calls, ping Pulse when it finishes. Runs, runs once, and you hear about it if it stops.