← VolcanoCONTENT HISTORY
WHAT CHANGED
Update to Volcano
Snapshot Oct 24, 2026 · 13:36 UTC · version 0.2.26
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save Volcano and check future changes in Following.
Up to 10 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Use for Volcano project locks, distributed leases, leader election, renewable lock guards, fencing tokens, lock contention, and safe backend worker coordination.",
"included_files": [],
"name": "volcano-locks",
"skill_md_contents": "---\nname: volcano-locks\ndescription: Use for Volcano project locks, distributed leases, leader election, renewable lock guards, fencing tokens, lock contention, and safe backend worker coordination.\n---\n# Volcano Locks Skill\n\n## Role and pairing\n\nUse project locks when backend workers in one project must elect one holder for\nwork such as a migration or scheduled rollup. Pair this skill with `volcano-sdk`\nand `volcano-platform`.\n\nLocks require a service key with `locks.manage` or full access. Never expose the\nkey or lease token in browser code.\n\nUse the project's language:\n\n| Project signal | SDK pattern |\n|---|---|\n| `package.json`, `.ts`, `.js` | JavaScript/TypeScript callback and result envelope |\n| `pyproject.toml`, `requirements.txt`, `.py` | Python context manager and typed exceptions |\n| `Gemfile`, `.gemspec`, `.rb` | Ruby block and typed exceptions |\n\n## Run work while holding a lock\n\nPrefer the automatic lease helper for work that can outlive one lease period.\nIt renews the lease and releases it when the callback, context, or block exits.\n\n### JavaScript and TypeScript\n\n```ts\nconst result = await volcano.locks.withLock(\n 'daily-rollup',\n { ttl: 30 },\n async ({ signal, lease }) => runRollup({ signal, fencingToken: lease.fencingToken }),\n);\n\nif (result.error) throw result.error;\nif (!result.acquired) return { skipped: true };\n```\n\nThe callback must stop when `signal` is aborted. Contention returns\n`{ acquired: false, error: null }`.\n\n### Python\n\n```python\nwith client.locks.with_lock(\"daily-rollup\", ttl=30) as guard:\n run_rollup(fencing_token=guard.lease.fencing_token)\n if guard.lost:\n raise RuntimeError(\"Lock ownership was lost\")\n```\n\n### Ruby\n\n```ruby\nclient.locks.with_lock(\"daily-rollup\", ttl: 30) do |guard|\n run_rollup(fencing_token: guard.lease.fencing_token)\n raise \"Lock ownership was lost\" if guard.lost?\nend\n```\n\nPython and Ruby raise typed SDK exceptions for acquisition or renewal failures.\nStop protected work when the guard reports ownership loss.\n\n## Manage a lease directly\n\n```ts\nconst acquired = await volcano.locks.acquire('migration', { ttl: 30 });\nif (acquired.error) throw acquired.error;\nif (!acquired.acquired || !acquired.lease) return;\n\ntry {\n const renewed = await volcano.locks.renew('migration', acquired.lease, { ttl: 30 });\n if (renewed.error) throw renewed.error;\n} finally {\n const released = await volcano.locks.release('migration', acquired.lease);\n if (released.error) console.error(released.error);\n}\n```\n\n| JavaScript/TypeScript | Python | Ruby |\n|---|---|---|\n| `acquire`, `renew`, `release`, `get`, `forceRelease` | `acquire`, `renew`, `release`, `get`, `force_release` | `acquire`, `renew`, `release`, `get`, `force_release` |\n| returns result envelopes | returns values or raises typed exceptions | returns values or raises typed exceptions |\n\nUse direct methods only when the automatic lease lifecycle does not fit. Python\nand Ruby `renew` return a new immutable lease, so retain the returned value.\nKeep the lease private because its token proves ownership.\n\n## Fencing tokens\n\nA delayed holder can continue working after its lease expires. Pass\n`lease.fencingToken` to the protected storage operation and reject values lower\nthan the highest token already accepted.\n\n```sql\nUPDATE rollup_state\nSET cursor = $1, fencing_token = $2\nWHERE id = $3 AND fencing_token <= $2;\n```\n\nThe lease alone does not block a stale write. The storage check does.\n\n## Inspect and recover\n\n```ts\nconst { state, error } = await volcano.locks.get('migration');\nif (error) throw error;\nif (state?.held) console.log(state.expiresAt, state.fencingToken);\n\nconst released = await volcano.locks.forceRelease('migration');\nif (released.error) throw released.error;\n```\n\nUse `forceRelease` only after stopping the old holder and confirming the\nprotected resource checks fencing tokens. The old holder can run until its next\nrenewal fails.\n\n## Limits and retries\n\n- Keys use letters, digits, `.`, `_`, `:`, and `-`, up to 128 characters.\n- TTL is 5 seconds through 90 days.\n- Renewal replaces the expiry; it does not add time to the old expiry.\n- Lock requests share a 600-per-minute project limit.\n- Rate-limit errors expose `retryAfter` in seconds.\n- Acquisition retries one transport failure or HTTP 503 with the same ownership\n and request IDs. Do not invent a new ID for the same uncertain acquisition.\n\n## Verification\n\n- Start two holders and confirm only one reports `acquired: true`.\n- Confirm the holder releases after success and failure.\n- Confirm the callback stops when renewal aborts its signal.\n- Confirm protected writes reject a lower fencing token.\n"
}SHA-256 of public snapshot: 8b4b7863e3785b47107ad000a9fb77bf527d180e2230fb132493a2a14492fa7e