UNPKG

durable-locks

Version:

A distributed locking mechanism using Cloudflare Durable Objects.

93 lines (92 loc) 2.92 kB
// lib/durable-lock.ts import { DurableObject } from "cloudflare:workers"; var DurableLock = class extends DurableObject { async alarm() { await this.ctx.storage.deleteAll(); } async fetch(request) { const url = new URL(request.url); const ttl = url.searchParams.get("ttl"); const lease = url.searchParams.get("lease"); let res; switch (url.pathname) { case "/acquire": if (ttl === null) return new Response(null); res = await this.acquire( parseInt(ttl), lease ? parseInt(lease) : void 0 ); return new Response(JSON.stringify(res)); case "/release": if (lease === null) return new Response(null); res = await this.release(parseInt(lease)); return new Response(JSON.stringify(res)); case "/isLocked": res = await this.isLocked(); return new Response(JSON.stringify(res)); default: throw new Error("Invalid pathname"); } } async acquire(ttl, lease) { const lock = await this.ctx.storage.get("lock") || { lease: 0, deadline: 0 }; const deadline = Date.now() + ttl; if (!lease) { if (Date.now() < lock.deadline) { return { success: false, reason: "BORROWED" }; } const lease2 = lock.lease + 1; await this.ctx.storage.put("lock", { lease: lease2, deadline }); await this.ctx.storage.setAlarm(this.hardDeadline()); return { success: true, lease: lease2, deadline }; } if (lease !== lock.lease) { return { success: false, reason: "NO_LONGER_VALID" }; } await this.ctx.storage.put("lock", { lease, deadline }); return { success: true, lease, deadline }; } async release(lease) { const lock = await this.ctx.storage.get("lock"); if (!lock || lease !== lock.lease || lock.deadline < Date.now()) { return { success: false, reason: "NO_LONGER_VALID" }; } await this.ctx.storage.put("lock", { lease, deadline: 0 }); return { success: true }; } async isLocked() { const lock = await this.ctx.storage.get("lock"); return lock ? lock.deadline > Date.now() : false; } hardDeadline() { return new Date(Date.now() + 1e3 * 86400); } }; function useDurableLock(namespace, id) { const uniqueId = namespace.idFromName(id); const durableLock = namespace.get(uniqueId); async function acquire(ttl, lease) { const res = await durableLock.fetch( `http://localhost/acquire?ttl=${ttl}&lease=${lease}` ); return await res.json(); } async function release(lease) { const res = await durableLock.fetch( `http://localhost/release?lease=${lease}` ); return await res.json(); } async function isLocked() { const res = await durableLock.fetch(`http://localhost/isLocked`); return await res.json(); } return { acquire, release, isLocked }; } export { DurableLock, useDurableLock };