Readme

lock: distributed lock

Forked from @stevekrouse/dlock! πŸ™

Example:

Create valimport { acquireLock } from "https://esm.town/v/postpostscript/lock"; using exampleLock = await acquireLock(); // lock will be released when it goes out of context

Full Example

Options:

Create valusing exampleLock = await acquireLock({ id: "lockExample", retries: 0, ttl: 5, // seconds retryTimeout: 1000, // ms });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { sleep } from "https://deno.land/x/sleep/mod.ts";
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON";
import { parentReference } from "https://esm.town/v/stevekrouse/parentReference";
import { searchParams } from "https://esm.town/v/stevekrouse/searchParams";
export async function acquireLock(opts?: LockOptions): Promise<{
release: () => Promise<LockResponse | undefined>;
}> {
let id = opts?.id ?? parentReference().userHandle + "-" + parentReference().valName;
id = id.replace(/[^\w]+/g, "_");
const ttl = opts?.ttl ?? 3;
async function handleError(error: string) {
if (typeof opts.retries === "number" && (opts.retry ?? 0) >= opts.retries) {
throw new LockError(`LockError: failed with message "${error || "unknown error"}"`);
}
await sleep((opts?.retryTimeout ?? 1000) / 1000);
return acquireLock({
...opts,
id,
ttl,
retry: (opts.retry ?? 0) + 1,
});
}
const res = await makeRequest(id, "acquire", {
ttl,
});
const { lease } = res;
if (!lease) {
return handleError(res.error);
}
const acquired = await makeRequest(id, "acquire", {
ttl,
lease,
});
if (!acquired.lease) {
return handleError(res.error);
}
let released = false;
async function release(): Promise<LockResponse | undefined> {
if (released) return;
released = true;
return makeRequest(id, "release", {
lease,
});
}
return {
release,
[Symbol.dispose]: release,
};
}
export class LockError extends Error {}
export type LockOptions = {
id?: string;
ttl?: number;
retry?: number;
retries?: number;
retryTimeout?: number;
};
export type LockResponse = {
lease?: number;
deadline: number;
error?: "string";
};
export async function makeRequest(id: string, method: "release" | "acquire", params): Promise<LockResponse> {
return fetchJSON(
`https://dlock.univalent.net/lock/${id}/${method}?${searchParams(params)}`,
);
}
πŸ‘† This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data β€” all from the browser.