Locks (native.lock)¶
Mutual exclusion for scripts that must not run the same critical section twice at once.
local ran, result = native.lock.with("import-42", { scope = "db", lease_ms = 30000, wait_ms = 5000 }, function()
return import_everything()
end)
if not ran then
return { skipped = "another import is running" }
end
native.lock.with(name, opts?, fn) -> (acquired, ...) takes the lock, runs fn(), and releases the
lock again however fn ends (return, error, or interrupt). It returns true followed by whatever
fn returned, or just false if the lock could not be had in time. Errors from fn are raised
as usual.
| Option | Default | Meaning |
|---|---|---|
scope |
"local" |
"local" excludes within this process; "db" excludes across every process sharing the table. |
wait_ms |
0 |
How long to wait for the current holder. 0 tries once. |
lease_ms |
none (30000 for db) |
How long fn may hold the lock. At most 15 minutes. |
Names are per tenant. Two tenants using the same name never contend.
The lease is enforced¶
When lease_ms runs out, fn is interrupted, exactly as with
native.deadline.with, and with raises "deadline exceeded".
So a holder that is slow or stuck stops before anyone else can take the lock, and the lock needs
no fencing token. The interrupt covers script code and native calls (HTTP, SQL, stream reads),
but a write that was already in flight can still complete, so make the protected work safe to
repeat or keep the lease comfortably longer than it needs.
A db lock stays valid for at least a second longer than the lease on the clock the table is read
with, so clock skew between machines of under a second cannot let two holders overlap. The lease
counts from the moment with starts asking for the lock, so time spent waiting does not extend it.
Where each scope helps¶
localis shared by every invocation in the process. That matters on Fargate, where one task runs many scripts at once. On Lambda an execution environment runs one invocation at a time, so a local lock only ever excludes the script from itself. Usedbto exclude across Lambda invocations, and across Fargate tasks.dbkeeps one row per lock in thenative.dbtable (under the tenant's partition prefix, in a partition scripts cannot address). It needsdb.table. Waiting polls with a growing, jittered pause; every attempt counts againstmax_data_ops.
Locks are not reentrant: taking a lock you already hold waits for yourself, so use wait_ms = 0
(the default) and check the result when you might.
Deployment¶
Nothing beyond native.db: conditional PutItem and DeleteItem.