better-lock 1.0.0 → 2.0.1-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.eslintrc.js +24 -0
  2. package/.idea/better-lock.iml +1 -7
  3. package/.idea/codeStyles/Project.xml +336 -5
  4. package/.idea/compiler.xml +7 -0
  5. package/.idea/inspectionProfiles/Project_Default.xml +6 -2
  6. package/.idea/jsLibraryMappings.xml +0 -1
  7. package/.idea/modules.xml +1 -1
  8. package/.idea/prettier.xml +8 -0
  9. package/.prettierrc.yaml +1 -3
  10. package/.travis.yml +2 -2
  11. package/README.md +102 -53
  12. package/dist/better_lock.d.ts +50 -0
  13. package/dist/better_lock.js +317 -0
  14. package/dist/better_lock.js.map +1 -0
  15. package/dist/errors.d.ts +44 -0
  16. package/dist/errors.js +131 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +16 -0
  19. package/dist/index.js +28 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/internals.d.ts +75 -0
  22. package/dist/internals.js +75 -0
  23. package/dist/internals.js.map +1 -0
  24. package/dist/options.d.ts +54 -0
  25. package/dist/options.js +3 -0
  26. package/dist/options.js.map +1 -0
  27. package/dist/tools.d.ts +19 -0
  28. package/dist/tools.js +109 -0
  29. package/dist/tools.js.map +1 -0
  30. package/dist/types.d.ts +23 -0
  31. package/dist/types.js +3 -0
  32. package/dist/types.js.map +1 -0
  33. package/jest.config.js +4 -0
  34. package/package.json +30 -8
  35. package/spec/better_lock.spec.ts +614 -0
  36. package/src/better_lock.ts +430 -0
  37. package/src/errors.ts +124 -0
  38. package/src/index.ts +54 -0
  39. package/src/internals.ts +118 -0
  40. package/src/options.ts +63 -0
  41. package/src/tools.ts +111 -0
  42. package/src/types.ts +29 -0
  43. package/tsconfig.json +13 -0
  44. package/.idea/markdown-navigator/profiles_settings.xml +0 -3
  45. package/.idea/markdown-navigator.xml +0 -78
  46. package/.idea/misc.xml +0 -6
  47. package/.idea/watcherTasks.xml +0 -8
  48. package/index.js +0 -19
  49. package/spec/better_lock.spec.js +0 -583
  50. package/src/better_lock.js +0 -400
  51. package/src/errors.js +0 -81
  52. package/src/options.js +0 -104
  53. package/src/tools.js +0 -125
  54. package/src/types.js +0 -110
@@ -1,583 +0,0 @@
1
- const expect = require('chai').expect;
2
-
3
- const { BetterLock } = require('../index');
4
-
5
- describe('BetterLock', () => {
6
- it('can run a simple happy path', testDone => {
7
- const lock = new BetterLock();
8
- const startedAt = new Date();
9
-
10
- lock.acquire(waitWorker(250, 'a', 1, [new Error()]), (a1, a2, a3) => {
11
- expect(new Date() - startedAt).to.be.within(245, 300);
12
- expect(a1).to.equal('a');
13
- expect(a2).to.equal(1);
14
- expect(a3[0]).to.be.instanceOf(Error);
15
- testDone();
16
- });
17
- });
18
-
19
- it('will accept number as a name', testDone => {
20
- const lock = new BetterLock();
21
-
22
- let doneFirst = false;
23
- lock.acquire(15, waitWorker(20), () => {
24
- doneFirst = true;
25
-
26
- testDone();
27
- });
28
-
29
- lock.acquire('15', () => {
30
- expect(doneFirst).to.be.true;
31
- });
32
- lock.acquire(15, () => {
33
- expect(doneFirst).to.be.true;
34
- });
35
- lock.acquire(16, () => {
36
- expect(doneFirst).to.be.false;
37
- });
38
- });
39
-
40
- it('can run locks with different keys concurrently', testDone => {
41
- const lock = new BetterLock();
42
- const startedAt = new Date();
43
- let cbCount = 0;
44
-
45
- lock.acquire(undefined, waitWorker(250, 'a'), res => {
46
- expect(res).to.equal('a');
47
- cb();
48
- });
49
-
50
- lock.acquire('', waitWorker(250, 'b'), res => {
51
- expect(res).to.equal('b');
52
- cb();
53
- });
54
-
55
- lock.acquire('Proper key', waitWorker(250, 'c'), res => {
56
- expect(res).to.equal('c');
57
- cb();
58
- });
59
-
60
- lock.acquire('proper key', waitWorker(250, 'd'), res => {
61
- expect(res).to.equal('d');
62
- cb();
63
- });
64
-
65
- function cb() {
66
- cbCount++;
67
- if (cbCount < 4) {
68
- return;
69
- }
70
-
71
- expect(new Date() - startedAt).to.be.within(245, 300);
72
- testDone();
73
- }
74
- });
75
-
76
- it('can execute multiple jobs one after another', testDone => {
77
- const lock = new BetterLock({
78
- wait_timeout: 100,
79
- execution_timeout: 200, // should never trigger
80
- queue_size: 5,
81
- });
82
-
83
- let called1 = false;
84
-
85
- lock.acquire(waitWorker(50, null, 'ok1'), (err, res) => {
86
- expect(err).to.be.null;
87
- expect(res).to.equal('ok1');
88
- called1 = true;
89
- });
90
-
91
- lock.acquire(waitWorker(150, null, 'ok2'), (err, res) => {
92
- expect(err).to.be.null;
93
- expect(res).to.equal('ok2');
94
- expect(called1).to.be.true;
95
- testDone();
96
- });
97
- });
98
-
99
- it('will correctly tell caller if they can acquire lock', testDone => {
100
- const lock = new BetterLock({});
101
-
102
- expect(lock.canAcquire()).to.be.true;
103
- expect(lock.canAcquire('test')).to.be.true;
104
-
105
- lock.acquire(waitWorker(10), () => {});
106
- lock.acquire('test', waitWorker(30), () => {
107
- expect(lock.canAcquire()).to.be.true;
108
- expect(lock.canAcquire('test')).to.be.true;
109
-
110
- testDone();
111
- });
112
-
113
- expect(lock.canAcquire()).to.be.false;
114
- expect(lock.canAcquire('test')).to.be.false;
115
- });
116
-
117
- it('will log', testDone => {
118
- let seq = 0;
119
- const expected = [
120
- '[MyLock] Enqueued Job #10 [My test]',
121
- '[MyLock] Executing Job #10 [My test]',
122
- '[MyLock] Done called for Job #10 [My test]',
123
- ];
124
-
125
- const lock = new BetterLock({
126
- name: 'MyLock',
127
- log,
128
- });
129
-
130
- BetterLock.LockJob._lastId = 9;
131
- lock.acquire('My test', waitWorker(100), () => {
132
- testDone();
133
- });
134
-
135
- function log(msg) {
136
- expect(msg).to.equal(expected[seq]);
137
- seq++;
138
- }
139
- });
140
-
141
- it('can change default options', () => {
142
- const defaultOptions = BetterLock.DEFAULT_OPTIONS;
143
- BetterLock.DEFAULT_OPTIONS = new BetterLock.Options({
144
- name: 'MyLock',
145
- });
146
-
147
- let seq = 0;
148
- const expected = [
149
- '[MyLock] Enqueued Job #10 [My test]',
150
- '[MyLock] Executing Job #10 [My test]',
151
- '[MyLock] Done called for Job #10 [My test]',
152
- ];
153
-
154
- const lock = new BetterLock({
155
- log,
156
- });
157
-
158
- BetterLock.LockJob._lastId = 9;
159
-
160
- return lock.acquire('My test', waitWorker(100)).then(
161
- () => {
162
- BetterLock.DEFAULT_OPTIONS = defaultOptions;
163
- },
164
- () => {
165
- BetterLock.DEFAULT_OPTIONS = defaultOptions;
166
- }
167
- );
168
-
169
- function log(msg) {
170
- expect(msg).to.equal(expected[seq]);
171
- seq++;
172
- }
173
- });
174
-
175
- it('will accept custom promise tester', () => {
176
- const lock = new BetterLock({
177
- promise_tester: p => p.isPromise === true,
178
- });
179
-
180
- let called = false;
181
-
182
- return Promise.all([
183
- lock.acquire(() => {
184
- return {
185
- then: resolve => {
186
- called = true;
187
- resolve();
188
- },
189
- isPromise: true,
190
- };
191
- }),
192
- ]).then(() => {
193
- expect(called).to.be.true;
194
- });
195
- });
196
-
197
- it('jobs will timeout after waiting in queue for too long', testDone => {
198
- const lock = new BetterLock({
199
- wait_timeout: 50,
200
- execution_timeout: 100, // should never trigger
201
- });
202
-
203
- let cbCount = 0;
204
-
205
- lock.acquire(waitWorker(75, null, 'ok1'), (err, res) => {
206
- expect(err).to.be.null;
207
- expect(res).to.equal('ok1');
208
- cbCount++;
209
- });
210
-
211
- lock.acquire(waitWorker(200000, null, 'ok2'), (err, res) => {
212
- expect(err).to.be.instanceOf(BetterLock.WaitTimeoutError);
213
- cbCount++;
214
- });
215
-
216
- setTimeout(() => {
217
- // Enqueue after some time, this one should run
218
- lock.acquire(waitWorker(50, null, 'ok3'), (err, res) => {
219
- expect(err).to.be.null;
220
- expect(res).to.equal('ok3');
221
- expect(cbCount).to.equal(2);
222
- testDone();
223
- });
224
- }, 50);
225
- });
226
-
227
- it('will timeout long running jobs', testDone => {
228
- const lock = new BetterLock({
229
- wait_timeout: 100, // should never trigger
230
- execution_timeout: 75,
231
- queue_size: 5,
232
- });
233
-
234
- let cbCount = 0;
235
- lock.acquire(waitWorker(100, 'wont', 'be', 'used'), err => {
236
- expect(err).to.be.instanceOf(BetterLock.ExecutionTimeoutError);
237
- cbCount++;
238
- });
239
-
240
- setTimeout(() => {
241
- lock.acquire(waitWorker(50, null, 'ok'), (err, res) => {
242
- expect(cbCount).to.equal(1);
243
- expect(err).to.be.null;
244
- expect(res).to.equal('ok');
245
- testDone();
246
- });
247
- }, 75);
248
- });
249
-
250
- describe('when overflowing', () => {
251
- it('it will kick out the most recently submitted job', testDone => {
252
- const lock = new BetterLock({
253
- wait_timeout: 200, // should never trigger
254
- execution_timeout: 300, // should never trigger
255
- queue_size: 2,
256
- });
257
-
258
- let cbCount = 0;
259
- lock.acquire(waitWorker(25, 1), arg => {
260
- // The executing one
261
- cbCount++;
262
- expect(cbCount).to.equal(2);
263
- expect(arg).to.equal(1);
264
- });
265
- lock.acquire(waitWorker(5, 2), arg => {
266
- cbCount++;
267
- expect(cbCount).to.equal(3);
268
- expect(arg).to.equal(2);
269
- });
270
- lock.acquire(waitWorker(5, 3), arg => {
271
- cbCount++;
272
- expect(cbCount).to.equal(4);
273
- expect(arg).to.equal(3);
274
- testDone();
275
- });
276
- lock.acquire(waitWorker(5, 4), err => {
277
- // This one will trigger the kicking out. And it will be the one kicked out
278
- cbCount++;
279
- expect(cbCount).to.equal(1);
280
- expect(err).to.be.instanceOf(BetterLock.QueueOverflowError);
281
- });
282
- });
283
-
284
- it('will handle queue size of 0', () => {
285
- const lock = new BetterLock({
286
- wait_timeout: 200, // should never trigger
287
- execution_timeout: 300, // should never trigger
288
- queue_size: 0,
289
- });
290
-
291
- return Promise.all([
292
- lock.acquire(waitWorker(25, null, 'result')),
293
- lock.acquire(waitWorker(5, null, 'never get here')).catch(err => err),
294
- ]).then(([res1, res2]) => {
295
- expect(res1).to.equal('result');
296
- expect(res2).to.be.instanceOf(BetterLock.QueueOverflowError);
297
- });
298
- });
299
- });
300
-
301
- it('will extend stack traces', () => {
302
- return (function colorfulFunctionName() {
303
- const lock = new BetterLock({
304
- wait_timeout: 0,
305
- execution_timeout: 5,
306
- queue_size: 1,
307
- });
308
-
309
- return Promise.all([
310
- lock.acquire(waitWorker(25)).catch(err => err),
311
- lock.acquire(waitWorker(120)).catch(err => err),
312
- lock.acquire(waitWorker(120)).catch(err => err),
313
- ]).then(([err1, err2, err3]) => {
314
- expect(err1).to.be.instanceOf(BetterLock.ExecutionTimeoutError);
315
- expect(err1.stack.indexOf('colorfulFunctionName')).to.be.gte(0);
316
-
317
- expect(err2).to.be.instanceOf(BetterLock.WaitTimeoutError);
318
- expect(err2.stack.indexOf('colorfulFunctionName')).to.be.gte(0);
319
-
320
- expect(err3).to.be.instanceOf(BetterLock.QueueOverflowError);
321
- expect(err3.stack.indexOf('colorfulFunctionName')).to.be.gte(0);
322
- });
323
- })();
324
- });
325
-
326
- it('can abort specific key', done => {
327
- const lock = new BetterLock();
328
-
329
- let noCallCount = 0;
330
- const noCall = () => {
331
- noCallCount++;
332
- };
333
-
334
- let result1 = undefined;
335
- lock.acquire(waitWorker(25, null, 'a')).then(res => {
336
- result1 = res;
337
- }, noCall);
338
-
339
- setTimeout(() => {
340
- lock.acquire(noCall).then(noCall, err => {
341
- expect(err).to.be.instanceOf(BetterLock.JobAbortedError);
342
- });
343
- }, 5);
344
-
345
- setTimeout(() => {
346
- lock.abort();
347
- }, 10);
348
-
349
- setTimeout(() => {
350
- expect(noCallCount).to.equal(0);
351
- expect(result1).to.equal('a');
352
-
353
- done();
354
- }, 40);
355
- });
356
-
357
- it('can abort all', () => {
358
- const lock = new BetterLock();
359
-
360
- let noCallCount = 0;
361
- const noCall = () => {
362
- noCallCount++;
363
- };
364
-
365
- let cbCount = 0;
366
- const callback = err => {
367
- cbCount++;
368
- expect(err).to.be.instanceOf(BetterLock.JobAbortedError);
369
- };
370
-
371
- lock.acquire(noCall, callback);
372
- lock.acquire('a', noCall, callback);
373
- lock.acquire('b', noCall, callback);
374
-
375
- lock.abortAll();
376
-
377
- expect(lock.canAcquire()).to.be.true;
378
- expect(lock.canAcquire('a')).to.be.true;
379
- expect(lock.canAcquire('b')).to.be.true;
380
- expect(cbCount).to.equal(3);
381
- });
382
-
383
- describe('if used with promises', () => {
384
- it('will return a viable promise if called without callback', testDone => {
385
- const lock = new BetterLock();
386
-
387
- const variants = [];
388
-
389
- variants.push(
390
- lock.acquire(waitWorker(0, null, 'success')).then(res => {
391
- expect(res).to.equal('success');
392
-
393
- return lock.acquire(waitWorker(0, 'fail')).catch(err => {
394
- expect(err).to.equal('fail');
395
- return 1;
396
- });
397
- })
398
- );
399
-
400
- variants.push(
401
- lock.acquire('with key', waitWorker(0, null, 'success')).then(res => {
402
- expect(res).to.equal('success');
403
-
404
- return lock.acquire('with key', waitWorker(0, 'fail')).catch(err => {
405
- expect(err).to.equal('fail');
406
- return 2;
407
- });
408
- })
409
- );
410
-
411
- variants.push(
412
- lock.acquire('with key and options', waitWorker(0, null, 'success'), {}).then(res => {
413
- expect(res).to.equal('success');
414
-
415
- return lock.acquire('with key and options', waitWorker(0, 'fail')).catch(err => {
416
- expect(err).to.equal('fail');
417
- return 3;
418
- });
419
- })
420
- );
421
-
422
- variants.push(
423
- lock.acquire(/* just options */ waitWorker(0, null, 'success'), {}).then(res => {
424
- expect(res).to.equal('success');
425
-
426
- return lock.acquire(waitWorker(0, 'fail'), {}).catch(err => {
427
- expect(err).to.equal('fail');
428
- return 4;
429
- });
430
- })
431
- );
432
-
433
- Promise.all(variants)
434
- .then(results => {
435
- expect(results).to.eql([1, 2, 3, 4]);
436
- testDone();
437
- })
438
- .catch(testDone);
439
- });
440
-
441
- it('will allow executor to return a promise instead of calling done', testDone => {
442
- const lock = new BetterLock();
443
-
444
- lock.acquire(
445
- () => {
446
- return new Promise((resolve, reject) => {
447
- setTimeout(() => {
448
- resolve('result');
449
- }, 20);
450
- });
451
- },
452
- (err, res) => {
453
- expect(err).to.be.null;
454
- expect(res).to.equal('result');
455
- }
456
- );
457
-
458
- lock.acquire(
459
- () => {
460
- return new Promise((resolve, reject) => {
461
- setTimeout(() => {
462
- reject('error');
463
- }, 20);
464
- });
465
- },
466
- err => {
467
- expect(err).to.equal('error');
468
-
469
- testDone();
470
- }
471
- );
472
- });
473
-
474
- it('will allow executor to directly return a value or throw an error', testDone => {
475
- const lock = new BetterLock();
476
-
477
- lock.acquire(
478
- () => 'result',
479
- (err, res) => {
480
- expect(err).to.be.null;
481
- expect(res).to.equal('result');
482
- }
483
- );
484
-
485
- lock.acquire(
486
- () => {
487
- throw 'error';
488
- },
489
- err => {
490
- expect(err).to.equal('error');
491
-
492
- testDone();
493
- }
494
- );
495
- });
496
- });
497
-
498
- describe('when using in multi-lock mode', () => {
499
- it('can lock on multiple keys in a basic case', () => {
500
- const lock = new BetterLock();
501
- const startedAt = new Date();
502
-
503
- const sequence = [];
504
- return Promise.all([
505
- lock.acquire('a', waitWorker(50, null, 1)).then(res => sequence.push(res)),
506
- waitPromise(35).then(() =>
507
- lock.acquire('b', () => {
508
- sequence.push(2);
509
- return waitPromise(50, 2);
510
- })
511
- ),
512
- waitPromise(15).then(() =>
513
- lock.acquire(['a', 'b'], () => {
514
- sequence.push(3);
515
- return waitPromise(50, 3);
516
- })
517
- ),
518
- ]).then(res => {
519
- expect(res).to.eql([1, 2, 3]);
520
- expect(sequence).to.eql([1, 3, 2]);
521
- expect(new Date() - startedAt).to.be.within(145, 180);
522
- });
523
- });
524
-
525
- it('can handle duplicate keys in the key list', () => {
526
- const lock = new BetterLock({});
527
-
528
- const startedAt = new Date();
529
-
530
- return Promise.all([
531
- lock.acquire(['a', undefined], waitWorker(50, null, '1')),
532
- waitPromise(5).then(() =>
533
- lock.acquire(['a', 'b', undefined, 'c', undefined, 'a'], waitWorker(50, null, '2'))
534
- ),
535
- waitPromise(15).then(() => lock.acquire('c', waitWorker(50, null, '3'))),
536
- ]).then(res => {
537
- expect(res).to.eql(['1', '2', '3']);
538
- expect(new Date() - startedAt).to.be.within(145, 180);
539
- });
540
- });
541
-
542
- it('can wait timeout on partially held locks', () => {
543
- const lock = new BetterLock({
544
- wait_timeout: 20,
545
- execution_timeout: 50,
546
- });
547
-
548
- const startedAt = new Date();
549
-
550
- return Promise.all([
551
- // Hold a, time out execution after 50ms
552
- lock.acquire('a', waitWorker(10000, null, 1)).catch(err => err),
553
- // Wait on a, grab b, then time out on a after 20ms
554
- lock
555
- .acquire(['a', 'b'], waitWorker(50, new Error('Should never be seen')))
556
- .catch(err => err),
557
- // Wait for b, grab it when #2 times out (20ms in).
558
- lock.acquire('b', waitWorker(30, null, 3), { wait_timeout: 30 }).catch(err => err),
559
- ]).then(([res1, res2, res3]) => {
560
- expect(res1).to.be.instanceOf(BetterLock.ExecutionTimeoutError);
561
- expect(res2).to.be.instanceOf(BetterLock.WaitTimeoutError);
562
- expect(res3).to.equal(3);
563
- expect(new Date() - startedAt).to.be.within(45, 60);
564
- });
565
- });
566
- });
567
- });
568
-
569
- function waitWorker(ms, ...args) {
570
- return function(done) {
571
- setTimeout(() => {
572
- done.apply(null, args);
573
- }, ms);
574
- };
575
- }
576
-
577
- function waitPromise(ms, result) {
578
- return new Promise(resolve => {
579
- setTimeout(() => {
580
- resolve(result);
581
- }, ms);
582
- });
583
- }