1 | /// <reference types="node" />
|
2 | /// <reference types="bluebird" />
|
3 | import * as Promise from 'bluebird';
|
4 | import { EventEmitter } from 'events';
|
5 | import { Locator } from 'locators';
|
6 | import * as zk from 'node-zookeeper-client';
|
7 | /**
|
8 | * Error thrown by locking action when blocking wait for lock reaches a timeout period
|
9 | */
|
10 | export declare class ZookeeperLockTimeoutError extends Error {
|
11 | lockPath: string;
|
12 | timeout?: number;
|
13 | constructor(message: string, path: string, timeout?: number);
|
14 | }
|
15 | /**
|
16 | * Error thrown by locking action when config.failImmediate == true when a lock is already locked
|
17 | */
|
18 | export declare class ZookeeperLockAlreadyLockedError extends Error {
|
19 | lockPath: string;
|
20 | constructor(message: string, path: string);
|
21 | }
|
22 | export declare class ZookeeperLockConfiguration {
|
23 | /**
|
24 | * locators (https://github.com/metamx/locators) compatible zookeeper server locator
|
25 | */
|
26 | serverLocator?: Locator;
|
27 | /**
|
28 | * prefix which will be placed in front of all locks created from this lock
|
29 | */
|
30 | pathPrefix?: string;
|
31 | /**
|
32 | * zookeeper client session timeout
|
33 | */
|
34 | sessionTimeout?: number;
|
35 | /**
|
36 | * milliseconds, dual function parameter, functioning both as zookeeper lock 'reconnect' delay
|
37 | * as well as internal zookeeper client spinDelay
|
38 | */
|
39 | spinDelay?: number;
|
40 | /**
|
41 | * milliseconds, dual function parameter, functioning both as zookeeper lock 'reconnect' limit
|
42 | * as well as internal zookeeper client retries
|
43 | */
|
44 | retries?: number;
|
45 | /**
|
46 | * when true, all calls to unlock will destroy the lock, detaching all event listeners, in addition
|
47 | * to the normal disconnect. defaults to true to reduce the chance of leaky usage
|
48 | */
|
49 | autoDestroyOnUnlock?: boolean;
|
50 | /**
|
51 | * when true, if the lock is not obtainable immediately, fail with a ZookeeperLockAlreadyLockedError and
|
52 | * disconnect or destroy depending on autoDestroyOnUnlock the lock
|
53 | */
|
54 | failImmediate?: boolean;
|
55 | /**
|
56 | * allowed number of maximum concurrent holders of a lock, defaults to 1 for traditional lock-like
|
57 | * behavior. Note that this value is NOT enforced, it's merely an agreement that all lock clients
|
58 | * agree to follow when working with this lock path, but allows using the zookeeper lock for additional
|
59 | * cluster orchestration roles like controlling the maximum number of concurrent workers
|
60 | */
|
61 | maxConcurrentHolders?: number;
|
62 | /**
|
63 | * if set to true, set a timeout defaulting to 10 seconds to give status updates on the lock while it
|
64 | * is connected to zookeeper, used to help debug working with the locks to detect leaks or what not,
|
65 | * visible by launching the app with the environment variable NODE_DEBUG=zk-lock set
|
66 | */
|
67 | enableTraceLog?: boolean;
|
68 | /**
|
69 | * milliseconds, the rate at which debug trace logs are emitted when enableTraceLog is set to true
|
70 | */
|
71 | traceLogRefresh?: number;
|
72 | /**
|
73 | * milliseconds, the quiet period after a lock is connected until the traceLog will begin reporting
|
74 | * long held locks and suspected connection leaks in a more verbose manner
|
75 | */
|
76 | traceLogQuietPeriod?: number;
|
77 | }
|
78 | export declare class ZookeeperLock extends EventEmitter {
|
79 | static Signals: {
|
80 | LOST: string;
|
81 | TIMEOUT: string;
|
82 | };
|
83 | static States: {
|
84 | ALREADY_LOCKED: string;
|
85 | DESTROYED: string;
|
86 | ERROR: string;
|
87 | LOCKED: string;
|
88 | LOCKING: string;
|
89 | LOST: string;
|
90 | TIMEOUT: string;
|
91 | UNLOCKED: string;
|
92 | UNLOCKING: string;
|
93 | };
|
94 | private static config;
|
95 | path: string;
|
96 | key: string;
|
97 | client: zk.Client;
|
98 | state: string;
|
99 | private config;
|
100 | private retryCount;
|
101 | private timeout;
|
102 | private created;
|
103 | /**
|
104 | * set static config to use by static helper methods
|
105 | * @param config
|
106 | */
|
107 | static initialize: (config: any) => void;
|
108 | /**
|
109 | * create a new lock using the static stored config
|
110 | * @returns {ZookeeperLock}
|
111 | */
|
112 | static lockFactory: () => ZookeeperLock;
|
113 | /**
|
114 | * create a new lock and lock it using the static stored config, with optional timeout
|
115 | * @param key
|
116 | * @param timeout
|
117 | * @returns {Promise<ZookeeperLock>}
|
118 | */
|
119 | static lock: (key: string, timeout?: number) => Promise<ZookeeperLock>;
|
120 | /**
|
121 | * check if a lock exists for a path using the static config
|
122 | * @param key
|
123 | * @returns {Promise<boolean>}
|
124 | */
|
125 | static checkLock: (key: string) => Promise<boolean>;
|
126 | /**
|
127 | * get the numeric part of the lock key
|
128 | * @param path
|
129 | * @returns {number}
|
130 | */
|
131 | private static getSequenceNumber;
|
132 | /**
|
133 | * create a new zk lock
|
134 | * @param config
|
135 | */
|
136 | constructor(config: ZookeeperLockConfiguration);
|
137 | /**
|
138 | * connect underlying zookeeper client, with optional delay
|
139 | * @param [delay=0]
|
140 | * @returns {Promise<any>}
|
141 | */
|
142 | connect: (delay?: number) => Promise<any>;
|
143 | /**
|
144 | * disconnect zookeeper client, and remove all event listeners from it
|
145 | * @returns {Promise<any>}
|
146 | */
|
147 | disconnect: () => Promise<any>;
|
148 | /**
|
149 | * destroy the lock, disconnect and remove all listeners from the 'signal' event emitter
|
150 | * @returns {Promise<any>}
|
151 | */
|
152 | destroy: () => Promise<boolean>;
|
153 | /**
|
154 | * unlock a lock, removing the key from zookeeper, and disconnecting
|
155 | * the zk client and all event listeners. By default this also destroys
|
156 | * the lock and removes event listeners on the locks 'signals' event
|
157 | * @param [destroy=true] - remove listeners from lock in addition
|
158 | * to disconnecting zk client on completion, defaults to true
|
159 | * @returns {Promise<any>}
|
160 | */
|
161 | unlock: (destroy?: boolean) => Promise<any>;
|
162 | /**
|
163 | * wait for a lock to become free for a given key and acquire it, with an optional
|
164 | * timeout upon which the lock will fail. if not currently connected to zookeeper,
|
165 | * this will connect, and on timeout, the lock will disconnect from zookeeper
|
166 | * @param key
|
167 | * @param [timeout]
|
168 | * @returns {Promise<any>}
|
169 | */
|
170 | lock: (key: string, timeout?: number) => Promise<any>;
|
171 | /**
|
172 | * check if a lock exists, connecting to zk client if not connected
|
173 | * @param key
|
174 | * @returns {Promise<boolean>}
|
175 | */
|
176 | checkLocked: (key: string) => Promise<boolean>;
|
177 | private changeState(newState);
|
178 | /**
|
179 | * create a zookeeper client which powers the lock, done when creating a new lock
|
180 | * or zk connection expires
|
181 | * @returns {Promise<any>}
|
182 | */
|
183 | private createClient();
|
184 | private traceLog;
|
185 | private connectHelper;
|
186 | private disconnectHelper;
|
187 | /**
|
188 | * internal method to reconnect, wired up to disconnect event of zk client
|
189 | * @returns {Promise<any>}
|
190 | */
|
191 | private reconnect;
|
192 | private lockHelper;
|
193 | /**
|
194 | * make the zk node that will hold the locks if it doens't already exist
|
195 | * @param path
|
196 | * @returns {Promise<any>}
|
197 | */
|
198 | private makeLockDir;
|
199 | /**
|
200 | * create a lock as a ephemeral sequential child node of the supplied path, prefixed with 'lock-',
|
201 | * to state intent to acquire a lock
|
202 | * @param path
|
203 | * @returns {Promise<any>}
|
204 | */
|
205 | private initLock;
|
206 | /**
|
207 | * loop until lock is available or timeout occurs
|
208 | * @param path
|
209 | * @returns {Promise<any>}
|
210 | */
|
211 | private waitForLock;
|
212 | /**
|
213 | * are we on the happy path to continue locking?
|
214 | * @returns {boolean|zk.Client}
|
215 | */
|
216 | private continueLocking;
|
217 | /**
|
218 | * check for states that result from triggers that resolve the external promise chain of the locking process.
|
219 | * The zookeeper client fires all event handlers when it is disconnected, so events like timeouts, already
|
220 | * locked errors, and even unlocking can cause unintended stray events, so we should just bail from these
|
221 | * handlers rather than trigger unintended rejections from race conditions with the intended external rejections
|
222 | * @returns {boolean}
|
223 | */
|
224 | private shouldRejectPromise;
|
225 | /**
|
226 | * helper method that does the grunt of the work of waiting for the lock. This method does 2 things, first
|
227 | * reads the lock path to compare the locks key to the other keys that are children of the path. if this locks
|
228 | * sequence number is the lowest, the lock has been aquired. If not, this method reactively responds to
|
229 | * children changed events from the zk-client for the path we want to aqcuire the lock for, and recurses to
|
230 | * repeat this process until the sequence is the lowest
|
231 | * @param resolve
|
232 | * @param reject
|
233 | * @param path
|
234 | */
|
235 | private waitForLockHelper;
|
236 | /**
|
237 | * method to filter zk node children to contain only those that are prefixed with 'lock-',
|
238 | * which are assumed to be created by this library
|
239 | * @param children
|
240 | * @returns {string[]|T[]}
|
241 | */
|
242 | private filterLocks;
|
243 | private checkedLockedHelper;
|
244 | }
|