redis-smq-common 1.0.1 → 1.0.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.0.2 (2022-08-10)
4
+
5
+ * Update LockManager tests (321c8c4)
6
+ * Fix "LockManagerExtendError: Acquired lock could not be extended" (fa3a8e5)
7
+
3
8
  ## 1.0.1 (2022-07-07)
4
9
 
5
10
  * Remove unused WorkerRunnerError (48e7206)
@@ -0,0 +1,4 @@
1
+ import { LockManagerError } from './lock-manager.error';
2
+ export declare class LockManagerMethodNotAllowedError extends LockManagerError {
3
+ constructor(message?: string);
4
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LockManagerMethodNotAllowedError = void 0;
4
+ const lock_manager_error_1 = require("./lock-manager.error");
5
+ class LockManagerMethodNotAllowedError extends lock_manager_error_1.LockManagerError {
6
+ constructor(message = `This method can not be used when autoExtend is enabled`) {
7
+ super(message);
8
+ }
9
+ }
10
+ exports.LockManagerMethodNotAllowedError = LockManagerMethodNotAllowedError;
11
+ //# sourceMappingURL=lock-manager-method-not-allowed.error.js.map
@@ -0,0 +1,4 @@
1
+ import { LockManagerError } from './lock-manager.error';
2
+ export declare class LockManagerNotAcquiredError extends LockManagerError {
3
+ constructor(message?: string);
4
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LockManagerNotAcquiredError = void 0;
4
+ const lock_manager_error_1 = require("./lock-manager.error");
5
+ class LockManagerNotAcquiredError extends lock_manager_error_1.LockManagerError {
6
+ constructor(message = `Can not extend a lock which has not been yet acquired. Maybe a pending operation is in progress.`) {
7
+ super(message);
8
+ }
9
+ }
10
+ exports.LockManagerNotAcquiredError = LockManagerNotAcquiredError;
11
+ //# sourceMappingURL=lock-manager-not-acquired.error.js.map
@@ -0,0 +1,4 @@
1
+ import { LockManagerError } from './lock-manager.error';
2
+ export declare class LockManagerNotReleasedError extends LockManagerError {
3
+ constructor(message?: string);
4
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LockManagerNotReleasedError = void 0;
4
+ const lock_manager_error_1 = require("./lock-manager.error");
5
+ class LockManagerNotReleasedError extends lock_manager_error_1.LockManagerError {
6
+ constructor(message = `A lock has been already obtained but not yet released or maybe a pending operation is in progress.`) {
7
+ super(message);
8
+ }
9
+ }
10
+ exports.LockManagerNotReleasedError = LockManagerNotReleasedError;
11
+ //# sourceMappingURL=lock-manager-not-released.error.js.map
@@ -1,12 +1,13 @@
1
1
  /// <reference types="node" />
2
2
  import { ICallback } from '../../types';
3
3
  import { RedisClient } from '../redis-client/redis-client';
4
- declare enum ELockStatus {
4
+ export declare enum ELockStatus {
5
5
  unlocked = 0,
6
6
  locking = 1,
7
7
  locked = 2,
8
8
  releasing = 3,
9
- extending = 4
9
+ extending = 4,
10
+ extended = 5
10
11
  }
11
12
  export declare enum ELuaScript {
12
13
  RELEASE_LOCK = "RELEASE_LOCK",
@@ -22,16 +23,19 @@ export declare class LockManager {
22
23
  protected status: ELockStatus;
23
24
  protected lockingTimer: NodeJS.Timeout | null;
24
25
  protected autoExtendTimer: NodeJS.Timeout | null;
25
- constructor(redisClient: RedisClient, lockKey: string, ttl: number, retryOnFail?: boolean, autoExtend?: boolean);
26
+ protected throwExceptions: boolean;
27
+ constructor(redisClient: RedisClient, lockKey: string, ttl: number, retryOnFail?: boolean, autoExtend?: boolean, throwExceptions?: boolean);
26
28
  protected resetTimers(): void;
27
29
  protected setUnlocked(): void;
28
30
  protected setLocked(): void;
31
+ protected setExtended(): void;
29
32
  protected extend(cb: ICallback<void>): void;
30
33
  protected runAutoExtendTimer(): void;
31
34
  acquireLock(cb: ICallback<void>): void;
32
35
  extendLock(cb: ICallback<void>): void;
33
36
  releaseLock(cb: ICallback<void>): void;
37
+ acquireOrExtend(cb: ICallback<ELockStatus>): void;
34
38
  isLocked(): boolean;
39
+ isReleased(): boolean;
35
40
  getId(): string;
36
41
  }
37
- export {};
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LockManager = exports.ELuaScript = void 0;
3
+ exports.LockManager = exports.ELuaScript = exports.ELockStatus = void 0;
4
4
  const redis_client_1 = require("../redis-client/redis-client");
5
5
  const uuid_1 = require("uuid");
6
- const lock_manager_error_1 = require("./errors/lock-manager.error");
7
6
  const lock_manager_abort_error_1 = require("./errors/lock-manager-abort.error");
8
7
  const lock_manager_extend_error_1 = require("./errors/lock-manager-extend.error");
9
8
  const lock_manager_acquire_error_1 = require("./errors/lock-manager-acquire.error");
10
9
  const fs = require("fs");
10
+ const lock_manager_method_not_allowed_error_1 = require("./errors/lock-manager-method-not-allowed.error");
11
+ const lock_manager_not_acquired_error_1 = require("./errors/lock-manager-not-acquired.error");
12
+ const lock_manager_not_released_error_1 = require("./errors/lock-manager-not-released.error");
11
13
  var ELockStatus;
12
14
  (function (ELockStatus) {
13
15
  ELockStatus[ELockStatus["unlocked"] = 0] = "unlocked";
@@ -15,7 +17,8 @@ var ELockStatus;
15
17
  ELockStatus[ELockStatus["locked"] = 2] = "locked";
16
18
  ELockStatus[ELockStatus["releasing"] = 3] = "releasing";
17
19
  ELockStatus[ELockStatus["extending"] = 4] = "extending";
18
- })(ELockStatus || (ELockStatus = {}));
20
+ ELockStatus[ELockStatus["extended"] = 5] = "extended";
21
+ })(ELockStatus = exports.ELockStatus || (exports.ELockStatus = {}));
19
22
  var ELuaScript;
20
23
  (function (ELuaScript) {
21
24
  ELuaScript["RELEASE_LOCK"] = "RELEASE_LOCK";
@@ -24,16 +27,18 @@ var ELuaScript;
24
27
  redis_client_1.RedisClient.addScript(ELuaScript.RELEASE_LOCK, fs.readFileSync(`${__dirname}/redis-client/lua/release-lock.lua`).toString());
25
28
  redis_client_1.RedisClient.addScript(ELuaScript.EXTEND_LOCK, fs.readFileSync(`${__dirname}/redis-client/lua/extend-lock.lua`).toString());
26
29
  class LockManager {
27
- constructor(redisClient, lockKey, ttl, retryOnFail = false, autoExtend = false) {
30
+ constructor(redisClient, lockKey, ttl, retryOnFail = false, autoExtend = false, throwExceptions = true) {
28
31
  this.status = ELockStatus.unlocked;
29
32
  this.lockingTimer = null;
30
33
  this.autoExtendTimer = null;
34
+ this.throwExceptions = true;
31
35
  this.lockKey = lockKey;
32
36
  this.ttl = ttl;
33
37
  this.retryOnFail = retryOnFail;
34
38
  this.lockId = (0, uuid_1.v4)();
35
39
  this.redisClient = redisClient;
36
40
  this.autoExtend = autoExtend;
41
+ this.throwExceptions = throwExceptions;
37
42
  }
38
43
  resetTimers() {
39
44
  if (this.lockingTimer) {
@@ -51,10 +56,12 @@ class LockManager {
51
56
  setLocked() {
52
57
  this.status = ELockStatus.locked;
53
58
  }
59
+ setExtended() {
60
+ this.status = ELockStatus.extended;
61
+ }
54
62
  extend(cb) {
55
- if (this.status !== ELockStatus.locked) {
56
- cb(new lock_manager_error_1.LockManagerError('The lock is currently not acquired or a pending operation is in progress'));
57
- }
63
+ if (!this.isLocked())
64
+ cb(new lock_manager_not_acquired_error_1.LockManagerNotAcquiredError());
58
65
  else {
59
66
  this.status = ELockStatus.extending;
60
67
  this.redisClient.runScript(ELuaScript.EXTEND_LOCK, [this.lockKey], [this.lockId, this.ttl], (err, reply) => {
@@ -67,7 +74,7 @@ class LockManager {
67
74
  cb(new lock_manager_extend_error_1.LockManagerExtendError());
68
75
  }
69
76
  else {
70
- this.setLocked();
77
+ this.setExtended();
71
78
  cb();
72
79
  }
73
80
  }
@@ -83,14 +90,14 @@ class LockManager {
83
90
  this.autoExtendTimer = setTimeout(() => this.extend((err) => {
84
91
  if (!err)
85
92
  this.runAutoExtendTimer();
86
- else if (!(err instanceof lock_manager_abort_error_1.LockManagerAbortError))
93
+ else if (this.throwExceptions &&
94
+ !(err instanceof lock_manager_abort_error_1.LockManagerAbortError))
87
95
  throw err;
88
96
  }), ms);
89
97
  }
90
98
  acquireLock(cb) {
91
- if (this.status !== ELockStatus.unlocked) {
92
- cb(new lock_manager_error_1.LockManagerError(`The lock is currently not released or a pending operation is in progress`));
93
- }
99
+ if (!this.isReleased())
100
+ cb(new lock_manager_not_released_error_1.LockManagerNotReleasedError());
94
101
  else {
95
102
  this.status = ELockStatus.locking;
96
103
  const lock = () => {
@@ -134,18 +141,17 @@ class LockManager {
134
141
  }
135
142
  }
136
143
  extendLock(cb) {
137
- if (this.autoExtend) {
138
- cb(new lock_manager_error_1.LockManagerError(`Can not extend a lock when autoExtend is enabled`));
139
- }
144
+ if (this.autoExtend)
145
+ cb(new lock_manager_method_not_allowed_error_1.LockManagerMethodNotAllowedError());
140
146
  else
141
147
  this.extend(cb);
142
148
  }
143
149
  releaseLock(cb) {
144
150
  const status = this.status;
145
- if (status === ELockStatus.releasing)
146
- cb(new lock_manager_error_1.LockManagerError('A pending releaseLock() call is in progress'));
147
- else if (status === ELockStatus.unlocked)
151
+ if (status === ELockStatus.unlocked)
148
152
  cb();
153
+ else if (!this.isLocked())
154
+ cb(new lock_manager_not_acquired_error_1.LockManagerNotAcquiredError());
149
155
  else {
150
156
  this.resetTimers();
151
157
  this.status = ELockStatus.releasing;
@@ -159,9 +165,38 @@ class LockManager {
159
165
  });
160
166
  }
161
167
  }
168
+ acquireOrExtend(cb) {
169
+ if (this.autoExtend)
170
+ cb(new lock_manager_method_not_allowed_error_1.LockManagerMethodNotAllowedError());
171
+ else {
172
+ const lock = () => {
173
+ this.acquireLock((err) => {
174
+ if (err)
175
+ cb(err);
176
+ else
177
+ cb(null, ELockStatus.locked);
178
+ });
179
+ };
180
+ if (this.isLocked())
181
+ this.extend((err) => {
182
+ if (err) {
183
+ if (err instanceof lock_manager_extend_error_1.LockManagerExtendError)
184
+ lock();
185
+ else
186
+ cb(err);
187
+ }
188
+ else
189
+ cb(null, ELockStatus.extended);
190
+ });
191
+ else
192
+ lock();
193
+ }
194
+ }
162
195
  isLocked() {
163
- return (this.status === ELockStatus.locked ||
164
- this.status === ELockStatus.extending);
196
+ return (this.status === ELockStatus.locked || this.status === ELockStatus.extended);
197
+ }
198
+ isReleased() {
199
+ return this.status === ELockStatus.unlocked;
165
200
  }
166
201
  getId() {
167
202
  return this.lockId;
@@ -14,16 +14,12 @@ class WorkerRunner extends events_1.EventEmitter {
14
14
  this.onTick = () => {
15
15
  async_1.async.waterfall([
16
16
  (cb) => {
17
- if (!this.lockManager.isLocked()) {
18
- this.lockManager.acquireLock((err) => {
19
- if (!err) {
20
- this.logger.info(`Workers are exclusively running from this instance (Lock ID ${this.lockManager.getId()}).`);
21
- }
22
- cb(err);
23
- });
24
- }
25
- else
26
- cb();
17
+ this.lockManager.acquireOrExtend((err, status) => {
18
+ if (status === lock_manager_1.ELockStatus.locked) {
19
+ this.logger.info(`Workers are exclusively running from this instance (Lock ID ${this.lockManager.getId()}).`);
20
+ }
21
+ cb(err);
22
+ });
27
23
  },
28
24
  (cb) => {
29
25
  this.workerPool.work(cb);
@@ -58,7 +54,7 @@ class WorkerRunner extends events_1.EventEmitter {
58
54
  this.powerManager = new power_manager_1.PowerManager();
59
55
  this.redisClient = redisClient;
60
56
  this.logger = logger;
61
- this.lockManager = new lock_manager_1.LockManager(redisClient, keyLock, 10000, false, true);
57
+ this.lockManager = new lock_manager_1.LockManager(redisClient, keyLock, 60000);
62
58
  this.ticker = new ticker_1.Ticker(this.onTick);
63
59
  this.workerPool = workerPool;
64
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "redis-smq-common",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "RedisSMQ shared components that may be used by integrated applications and extensions.",
5
5
  "author": "Weyoss <weyoss@protonmail.com>",
6
6
  "license": "MIT",