UNPKG

durable-locks

Version:

A distributed locking mechanism using Cloudflare Durable Objects.

121 lines (118 loc) 4.02 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // lib/index.ts var lib_exports = {}; __export(lib_exports, { DurableLock: () => DurableLock, useDurableLock: () => useDurableLock }); module.exports = __toCommonJS(lib_exports); // lib/durable-lock.ts var import_cloudflare_workers = require("cloudflare:workers"); var DurableLock = class extends import_cloudflare_workers.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 }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { DurableLock, useDurableLock });