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