1 |
|
2 | import { EventEmitter } from "events";
|
3 | import { Redis as IORedisClient, Cluster as IORedisCluster } from "ioredis";
|
4 | declare type Client = IORedisClient | IORedisCluster;
|
5 | export declare type ClientExecutionResult = {
|
6 | client: Client;
|
7 | vote: "for";
|
8 | value: number;
|
9 | } | {
|
10 | client: Client;
|
11 | vote: "against";
|
12 | error: Error;
|
13 | };
|
14 | export declare type ExecutionStats = {
|
15 | readonly membershipSize: number;
|
16 | readonly quorumSize: number;
|
17 | readonly votesFor: Set<Client>;
|
18 | readonly votesAgainst: Map<Client, Error>;
|
19 | };
|
20 | export declare type ExecutionResult = {
|
21 | attempts: ReadonlyArray<Promise<ExecutionStats>>;
|
22 | };
|
23 |
|
24 |
|
25 |
|
26 | export interface Settings {
|
27 | readonly driftFactor: number;
|
28 | readonly retryCount: number;
|
29 | readonly retryDelay: number;
|
30 | readonly retryJitter: number;
|
31 | readonly automaticExtensionThreshold: number;
|
32 | }
|
33 | export declare class ResourceLockedError extends Error {
|
34 | readonly message: string;
|
35 | constructor(message: string);
|
36 | }
|
37 | export declare class ExecutionError extends Error {
|
38 | readonly message: string;
|
39 | readonly attempts: ReadonlyArray<Promise<ExecutionStats>>;
|
40 | constructor(message: string, attempts: ReadonlyArray<Promise<ExecutionStats>>);
|
41 | }
|
42 | export declare class Lock {
|
43 | readonly redlock: Redlock;
|
44 | readonly resources: string[];
|
45 | readonly value: string;
|
46 | readonly attempts: ReadonlyArray<Promise<ExecutionStats>>;
|
47 | expiration: number;
|
48 | constructor(redlock: Redlock, resources: string[], value: string, attempts: ReadonlyArray<Promise<ExecutionStats>>, expiration: number);
|
49 | release(): Promise<ExecutionResult>;
|
50 | extend(duration: number): Promise<Lock>;
|
51 | }
|
52 | export declare type RedlockAbortSignal = AbortSignal & {
|
53 | error?: Error;
|
54 | };
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | export default class Redlock extends EventEmitter {
|
62 | readonly clients: Set<Client>;
|
63 | readonly settings: Settings;
|
64 | readonly scripts: {
|
65 | readonly acquireScript: {
|
66 | value: string;
|
67 | hash: string;
|
68 | };
|
69 | readonly extendScript: {
|
70 | value: string;
|
71 | hash: string;
|
72 | };
|
73 | readonly releaseScript: {
|
74 | value: string;
|
75 | hash: string;
|
76 | };
|
77 | };
|
78 | constructor(clients: Iterable<Client>, settings?: Partial<Settings>, scripts?: {
|
79 | readonly acquireScript?: string | ((script: string) => string);
|
80 | readonly extendScript?: string | ((script: string) => string);
|
81 | readonly releaseScript?: string | ((script: string) => string);
|
82 | });
|
83 | /**
|
84 | * Generate a sha1 hash compatible with redis evalsha.
|
85 | */
|
86 | private _hash;
|
87 | /**
|
88 | * Generate a cryptographically random string.
|
89 | */
|
90 | private _random;
|
91 | /**
|
92 | * This method runs `.quit()` on all client connections.
|
93 | */
|
94 | quit(): Promise<void>;
|
95 | /**
|
96 | * This method acquires a locks on the resources for the duration specified by
|
97 | * the `duration`.
|
98 | */
|
99 | acquire(resources: string[], duration: number, settings?: Partial<Settings>): Promise<Lock>;
|
100 | /**
|
101 | * This method unlocks the provided lock from all servers still persisting it.
|
102 | * It will fail with an error if it is unable to release the lock on a quorum
|
103 | * of nodes, but will make no attempt to restore the lock in the case of a
|
104 | * failure to release. It is safe to re-attempt a release or to ignore the
|
105 | * error, as the lock will automatically expire after its timeout.
|
106 | */
|
107 | release(lock: Lock, settings?: Partial<Settings>): Promise<ExecutionResult>;
|
108 | /**
|
109 | * This method extends a valid lock by the provided `duration`.
|
110 | */
|
111 | extend(existing: Lock, duration: number, settings?: Partial<Settings>): Promise<Lock>;
|
112 | /**
|
113 | * Execute a script on all clients. The resulting promise is resolved or
|
114 | * rejected as soon as this quorum is reached; the resolution or rejection
|
115 | * will contains a `stats` property that is resolved once all votes are in.
|
116 | */
|
117 | private _execute;
|
118 | private _attemptOperation;
|
119 | private _attemptOperationOnClient;
|
120 | /**
|
121 | * Wrap and execute a routine in the context of an auto-extending lock,
|
122 | * returning a promise of the routine's value. In the case that auto-extension
|
123 | * fails, an AbortSignal will be updated to indicate that abortion of the
|
124 | * routine is in order, and to pass along the encountered error.
|
125 | *
|
126 | * @example
|
127 | * ```ts
|
128 | * await redlock.using([senderId, recipientId], 5000, { retryCount: 5 }, async (signal) => {
|
129 | * const senderBalance = await getBalance(senderId);
|
130 | * const recipientBalance = await getBalance(recipientId);
|
131 | *
|
132 | * if (senderBalance < amountToSend) {
|
133 | * throw new Error("Insufficient balance.");
|
134 | * }
|
135 | *
|
136 | *
|
137 | *
|
138 | *
|
139 | *
|
140 | *
|
141 | *
|
142 | * if (signal.aborted) {
|
143 | * throw signal.error;
|
144 | * }
|
145 | *
|
146 | * await setBalances([
|
147 | * {id: senderId, balance: senderBalance - amountToSend},
|
148 | * {id: recipientId, balance: recipientBalance + amountToSend},
|
149 | * ]);
|
150 | * });
|
151 | * ```
|
152 | */
|
153 | using<T>(resources: string[], duration: number, settings: Partial<Settings>, routine?: (signal: RedlockAbortSignal) => Promise<T>): Promise<T>;
|
154 | using<T>(resources: string[], duration: number, routine: (signal: RedlockAbortSignal) => Promise<T>): Promise<T>;
|
155 | }
|
156 | export {};
|