AcquireLock
Attempts to acquire a lock under the caller’s lease. Locks do not need to be created upfront — the first successful acquire creates the lock record.
Lock names are paths; an acquire can be blocked by an existing holder at an ancestor or descendant path — see Locks for the hierarchical compatibility rules.
Re-acquiring under the same lease_id is always allowed and just refreshes the holder’s
locked_at.
Request
exclusive: truerequests a write (exclusive) lock — only one holder.exclusive: falserequests a read (shared) lock — many holders allowed concurrently.timeout_secondstells the server how long to wait for an incompatible holder to release before giving up.- The call blocks server-side, so set client/RPC timeouts comfortably above
timeout_seconds. metadatais an optional, opaque map of string key/value pairs attached to this lock holder — see Metadata.
{
"namespace_name": "UserObjects",
"lock_name": "users/123/profile",
"exclusive": true,
"lease_id": "ls_NfKKeiPbP18NFeU3lLGrRWWgDJRB",
"timeout_seconds": 60,
"metadata": {
"host": "node-1"
}
}
Response
The outcome enum reports the result: ACQUIRE_OUTCOME_ACQUIRED when the lease now holds the
lock, ACQUIRE_OUTCOME_UNAVAILABLE when a non-blocking attempt (timeout_seconds: 0) found it
held by someone else and returned without waiting, or ACQUIRE_OUTCOME_TIMED_OUT when the call
blocked until timeout_seconds elapsed without ever acquiring. The non-acquired outcomes return
(without an error) the current lock state so the caller can see who is holding it.
When the outcome is not ACQUIRE_OUTCOME_ACQUIRED, the reason enum hints at what the acquire
was blocked on: CONTENTION_REASON_PEER (the lock itself is held in an incompatible mode),
CONTENTION_REASON_ANCESTOR (a lock on an ancestor path blocks it), or
CONTENTION_REASON_DESCENDANT (one or more locks on descendant paths block it). It is
CONTENTION_REASON_UNSPECIFIED when the lock was acquired.
blocking_locks carries the locks standing in the way — the blocking ancestor lock(s) or
blocking descendant locks, matching reason — so the caller can see who holds them. It is empty
for CONTENTION_REASON_PEER: there the conflicting lock is the one you are acquiring, already
returned in the top-level lock field, so it is not duplicated here. Both reason and
blocking_locks are best-effort, point-in-time diagnostics: they reflect the last acquire
attempt and may already be stale, so branch on outcome, not on these. blocking_locks is
always capped at 50 (a contended descendant subtree may hold more), which keeps it cheap and
safe to read. It is empty when the lock was acquired.
- Returns
NotFoundif the namespace does not exist. - Returns
NotFoundif the lease does not exist or has already expired. - Returns
ResourceExhaustedif creating a new lock would exceed the namespace’s lock quota.
Success:
{
"lock": {
"name": "users/123/profile",
"state": "LOCK_STATE_EXCLUSIVE_LOCKED",
"locked_at": 1695826239671432000,
"last_activity_at": 1695826239671432000,
"lock_holders": [
{
"lease_id": "ls_NfKKeiPbP18NFeU3lLGrRWWgDJRB",
"locked_at": 1695826239671432000,
"metadata": {
"host": "node-1"
}
}
]
},
"outcome": "ACQUIRE_OUTCOME_ACQUIRED"
}
Held by someone else:
The lock itself is held in an incompatible mode, so reason is CONTENTION_REASON_PEER. The
conflicting lock and its holder are already in the top-level lock field, so blocking_locks is
empty (omitted below).
{
"lock": {
"name": "users/123/profile",
"state": "LOCK_STATE_EXCLUSIVE_LOCKED",
"locked_at": 1695826200000000000,
"last_activity_at": 1695826200000000000,
"lock_holders": [
{
"lease_id": "ls_qB7XwYzAaaaaaaaaaaaaaaaaaaaa",
"locked_at": 1695826200000000000,
"metadata": {
"host": "node-2"
}
}
]
},
"outcome": "ACQUIRE_OUTCOME_TIMED_OUT",
"reason": "CONTENTION_REASON_PEER"
}
Blocked by an ancestor:
A request for users/123/profile while an exclusive lock is held on the ancestor users/123
returns reason CONTENTION_REASON_ANCESTOR, with the blocking ancestor in blocking_locks. A
descendant conflict looks the same with CONTENTION_REASON_DESCENDANT and the blocking
descendant lock(s) instead.
{
"lock": {
"name": "users/123/profile",
"state": "LOCK_STATE_UNLOCKED",
"locked_at": 0,
"last_activity_at": 1695826200000000000,
"lock_holders": []
},
"outcome": "ACQUIRE_OUTCOME_TIMED_OUT",
"reason": "CONTENTION_REASON_ANCESTOR",
"blocking_locks": [
{
"name": "users/123",
"state": "LOCK_STATE_EXCLUSIVE_LOCKED",
"locked_at": 1695826200000000000,
"last_activity_at": 1695826200000000000,
"lock_holders": [
{
"lease_id": "ls_qB7XwYzAaaaaaaaaaaaaaaaaaaaa",
"locked_at": 1695826200000000000
}
]
}
]
}