durable-locks
Version:
A distributed locking mechanism using Cloudflare Durable Objects.
121 lines (118 loc) • 4.02 kB
JavaScript
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
});
;