murlock 1.0.0 → 1.1.0

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 (41) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +38 -8
  3. package/dist/constants/index.js.map +1 -1
  4. package/dist/decorators/murlock.decorator.d.ts +0 -1
  5. package/dist/decorators/murlock.decorator.js +54 -26
  6. package/dist/decorators/murlock.decorator.js.map +1 -1
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/interfaces/murlock-options.interface.d.ts +1 -1
  11. package/dist/murlock.module.js +2 -0
  12. package/dist/murlock.module.js.map +1 -1
  13. package/dist/murlock.service.d.ts +11 -3
  14. package/dist/murlock.service.js +54 -16
  15. package/dist/murlock.service.js.map +1 -1
  16. package/dist/tsconfig.tsbuildinfo +1 -1
  17. package/package.json +2 -1
  18. package/dist/decorators/lock.decorator.d.ts +0 -1
  19. package/dist/decorators/lock.decorator.js +0 -11
  20. package/dist/decorators/lock.decorator.js.map +0 -1
  21. package/dist/interceptors/index.d.ts +0 -1
  22. package/dist/interceptors/index.js +0 -18
  23. package/dist/interceptors/index.js.map +0 -1
  24. package/dist/interceptors/lock.interceptor.d.ts +0 -12
  25. package/dist/interceptors/lock.interceptor.js +0 -66
  26. package/dist/interceptors/lock.interceptor.js.map +0 -1
  27. package/dist/interfaces/lock-meta-data.interface.d.ts +0 -4
  28. package/dist/interfaces/lock-meta-data.interface.js +0 -3
  29. package/dist/interfaces/lock-meta-data.interface.js.map +0 -1
  30. package/dist/interfaces/lock-options.interface.d.ts +0 -4
  31. package/dist/interfaces/lock-options.interface.js +0 -3
  32. package/dist/interfaces/lock-options.interface.js.map +0 -1
  33. package/dist/lock.module.d.ts +0 -5
  34. package/dist/lock.module.js +0 -40
  35. package/dist/lock.module.js.map +0 -1
  36. package/dist/lock.service.d.ts +0 -9
  37. package/dist/lock.service.js +0 -58
  38. package/dist/lock.service.js.map +0 -1
  39. package/dist/lock.spec.d.ts +0 -1
  40. package/dist/lock.spec.js +0 -53
  41. package/dist/lock.spec.js.map +0 -1
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2019 Alex
3
+ Copyright (c) 2023 Eyüp furkan Özmen
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -7,7 +7,8 @@ MurLock is a distributed lock solution designed for the NestJS framework. It pro
7
7
  - **Redis-Based**: Implements a fast and effective lock mechanism using Redis.
8
8
  - **Parameter-Based Locking**: Creates locks based on request parameters or bodies.
9
9
  - **Highly Customizable**: Customize many parameters, such as lock duration.
10
- - **Logging Support**: Detailed logging with internal nestjs-logger.
10
+ - **Retry Mechanism**: Implements an exponential back-off strategy if the lock is not obtained.
11
+ - **Logging: Provides**: logging options for debugging and monitoring.
11
12
  - **OOP and Generic Structure**: Easily integratable and expandable due to its OOP and generic design.
12
13
 
13
14
  ## Installation
@@ -33,6 +34,7 @@ import { MurLockModule } from 'murlock';
33
34
  redisOptions: { host: 'localhost', port: 6379 },
34
35
  wait: 1000,
35
36
  maxAttempts: 3,
37
+ logLevel: 'log',
36
38
  }),
37
39
  ],
38
40
  })
@@ -72,6 +74,7 @@ import { MurLockModule } from 'murlock';
72
74
  redisOptions: configService.get('REDIS_OPTIONS'),
73
75
  wait: configService.get('MURLOCK_WAIT'),
74
76
  maxAttempts: configService.get('MURLOCK_MAX_ATTEMPTS'),
77
+ logLevel: configService.get('LOG_LEVEL')
75
78
  }),
76
79
  inject: [ConfigService],
77
80
  }),
@@ -86,17 +89,38 @@ For more details on usage and configuration, please refer to the API documentati
86
89
 
87
90
  ## API Documentation
88
91
 
89
- For more detailed API documentation, please see the source code, which is thoroughly commented and should provide enough information for most use cases. The key classes and interfaces to be aware of are:
92
+ ### MurLock(releaseTime: number, ...keyParams: string[])
90
93
 
91
- - `MurLockService`: A service that provides methods for locking and unlocking. Normally you would not use this directly, but it is provided for more complex use cases.
94
+ A method decorator to indicate that a particular method should be locked.
92
95
 
93
- - `MurLockModuleOptions`: An interface for the options that can be passed to `MurLockModule.registerSync()`.
96
+ - `releaseTime`: Time in milliseconds after which the lock should be automatically released.
97
+ - `...keyParams`: Method parameters based on which the lock should be made. The format is `paramName.attribute`. If just `paramName` is provided, it will use the `toString` method of that parameter.
94
98
 
95
- - `MurLockModuleAsyncOptions`: An interface for the options that can be passed to `MurLockModule.registerAsync()`.
99
+ ### Configuration Options
96
100
 
97
- - `MurLock()`: A decorator that provides distributed locking functionality.
101
+ - **redisOptions:** Configuration options for the Redis client.
102
+ - **wait:** Time (in milliseconds) to wait before retrying if a lock isn't obtained.
103
+ - **maxAttempts:** Maximum number of attempts to obtain a lock.
104
+ - **logLevel:** Logging level. Can be one of 'none', 'error', 'warn', 'log', or 'debug'.
98
105
 
99
- - `MurLockMetadata`: An interface for the metadata associated with a `@MurLock()` method.
106
+ ### MurLockService
107
+
108
+ A NestJS injectable service to interact with the locking mechanism directly.
109
+
110
+ ## Best Practices
111
+
112
+ - **Short-lived Locks:** Ensure that locks are short-lived to prevent deadlocks and increase the efficiency of your application.
113
+ - **Error Handling:** Always handle errors gracefully. If a lock isn't obtained, it's often better to return a failure or retry after some time.
114
+ - **Logging:** Adjust the `logLevel` based on your environment. Use 'debug' for development and 'error' or 'warn' for production.
115
+
116
+ ## Limitations
117
+
118
+ - **Redis Persistence:** Ensure that your Redis instance has RDB persistence enabled. This ensures that in case of a crash, locks are not lost.
119
+ - **Single Redis Instance:** MurLock is not designed to work with Redis cluster mode. It's essential to ensure that locks are always set to a single instance.
120
+
121
+ ## Contributions & Support
122
+
123
+ We welcome contributions! Please see our contributing guide for more information. For support, raise an issue on our GitHub repository.
100
124
 
101
125
  ## License
102
126
 
@@ -104,4 +128,10 @@ This project is licensed under the [MIT License](LICENSE_FILE_URL).
104
128
 
105
129
  ## Contact
106
130
 
107
- If you have any questions or feedback, feel free to contact me at ozmen.eyupfurkan@gmail.com.
131
+ If you have any questions or feedback, feel free to contact me at ozmen.eyupfurkan@gmail.com.
132
+
133
+ ---
134
+
135
+ We hope you find MurLock useful in your projects. Don't forget to star our repo if you find it helpful!
136
+
137
+ Happy coding! 🚀
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/constants/index.ts"],"names":[],"mappings":";;;AAAa,QAAA,oBAAoB,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAEtD,QAAA,4BAA4B,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/constants/index.ts"],"names":[],"mappings":";;;AAGa,QAAA,oBAAoB,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAKtD,QAAA,4BAA4B,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC"}
@@ -1,2 +1 @@
1
- import 'reflect-metadata';
2
1
  export declare function MurLock(releaseTime: number, ...keyParams: string[]): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
@@ -10,10 +10,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.MurLock = void 0;
13
- require("reflect-metadata");
14
- const murlock_service_1 = require("../murlock.service");
15
13
  const constants_1 = require("../constants");
16
14
  const exceptions_1 = require("../exceptions");
15
+ const murlock_service_1 = require("../murlock.service");
17
16
  function getParameterNames(func) {
18
17
  const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
19
18
  const ARGUMENT_NAMES = /([^\s,]+)/g;
@@ -25,42 +24,71 @@ function MurLock(releaseTime, ...keyParams) {
25
24
  return (target, propertyKey, descriptor) => {
26
25
  const originalMethod = descriptor.value;
27
26
  const methodParameterNames = getParameterNames(originalMethod);
27
+ function constructLockKey(args) {
28
+ const lockKeyElements = [
29
+ target.constructor.name,
30
+ propertyKey,
31
+ ...keyParams.map((keyParam) => {
32
+ const [source, path] = keyParam.split('.');
33
+ const parameterIndex = methodParameterNames.indexOf(source);
34
+ if (parameterIndex >= 0) {
35
+ const parameterValue = args[parameterIndex];
36
+ if (typeof parameterValue === 'undefined' || parameterValue === null) {
37
+ throw new Error(`Parameter ${source} is undefined or null.`);
38
+ }
39
+ if (path && typeof parameterValue === 'object' && parameterValue !== null && path in parameterValue) {
40
+ return parameterValue[path];
41
+ }
42
+ return parameterValue instanceof Object ? parameterValue.toString() : parameterValue;
43
+ }
44
+ throw new Error(`Parameter ${source} not found in method arguments.`);
45
+ }),
46
+ ];
47
+ return lockKeyElements.join(':');
48
+ }
28
49
  descriptor.value = function (...args) {
29
50
  return __awaiter(this, void 0, void 0, function* () {
30
- const lockKeyElements = [
31
- target.constructor.name,
32
- propertyKey,
33
- ...keyParams.map((keyParam) => {
34
- const [source, path] = keyParam.split('.');
35
- const parameterIndex = methodParameterNames.indexOf(source);
36
- if (parameterIndex >= 0) {
37
- const parameterValue = args[parameterIndex];
38
- if (path && typeof parameterValue === 'object' && parameterValue !== null && path in parameterValue) {
39
- return parameterValue[path];
40
- }
41
- return parameterValue instanceof Object ? parameterValue.toString() : parameterValue;
42
- }
43
- }),
44
- ];
45
- const lockKey = lockKeyElements.join(':');
51
+ const lockKey = constructLockKey(args);
46
52
  const murLockService = Reflect.getMetadata(constants_1.MURLOCK_SERVICE_METADATA_KEY, murlock_service_1.MurLockService);
47
- const isLockSuccessful = yield murLockService.lock(lockKey, releaseTime);
48
- if (!isLockSuccessful) {
49
- throw new exceptions_1.MurLockException('Could not obtain lock');
53
+ if (!murLockService) {
54
+ throw new Error('MurLockService is not available.');
50
55
  }
51
- let result;
56
+ yield acquireLock(lockKey, murLockService, releaseTime);
52
57
  try {
53
- result = yield originalMethod.apply(this, args);
58
+ return yield originalMethod.apply(this, args);
54
59
  }
55
60
  finally {
56
- yield murLockService.unlock(lockKey);
61
+ yield releaseLock(lockKey, murLockService);
57
62
  }
58
- return result;
59
63
  });
60
64
  };
61
- Reflect.defineMetadata('MURLOCK_KEY_METADATA', { releaseTime, keyParams }, descriptor.value);
65
+ Reflect.defineMetadata(constants_1.MURLOCK_KEY_METADATA, { releaseTime, keyParams }, descriptor.value);
62
66
  return descriptor;
63
67
  };
64
68
  }
65
69
  exports.MurLock = MurLock;
70
+ function acquireLock(lockKey, murLockService, releaseTime) {
71
+ return __awaiter(this, void 0, void 0, function* () {
72
+ let isLockSuccessful = false;
73
+ try {
74
+ isLockSuccessful = yield murLockService.lock(lockKey, releaseTime);
75
+ }
76
+ catch (error) {
77
+ throw new exceptions_1.MurLockException(`Failed to acquire lock for key ${lockKey}: ${error.message}`);
78
+ }
79
+ if (!isLockSuccessful) {
80
+ throw new exceptions_1.MurLockException(`Could not obtain lock for key ${lockKey}`);
81
+ }
82
+ });
83
+ }
84
+ function releaseLock(lockKey, murLockService) {
85
+ return __awaiter(this, void 0, void 0, function* () {
86
+ try {
87
+ yield murLockService.unlock(lockKey);
88
+ }
89
+ catch (error) {
90
+ throw new exceptions_1.MurLockException(`Failed to release lock for key ${lockKey}: ${error.message}`);
91
+ }
92
+ });
93
+ }
66
94
  //# sourceMappingURL=murlock.decorator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"murlock.decorator.js","sourceRoot":"","sources":["../../lib/decorators/murlock.decorator.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,4BAA0B;AAC1B,wDAAoD;AACpD,4CAA4D;AAC5D,8CAAiD;AAEjD,SAAS,iBAAiB,CAAC,IAAI;IAC7B,MAAM,cAAc,GAAG,kCAAkC,CAAC;IAC1D,MAAM,cAAc,GAAG,YAAY,CAAC;IAEpC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC7F,OAAO,MAAM,IAAI,EAAE,CAAC;AACtB,CAAC;AAED,SAAgB,OAAO,CAAC,WAAmB,EAAE,GAAG,SAAmB;IACjE,OAAO,CACL,MAAW,EACX,WAAmB,EACnB,UAA8B,EAC9B,EAAE;QACF,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC;QACxC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC,cAAc,CAAC,CAAC;QAG/D,UAAU,CAAC,KAAK,GAAG,UAAgB,GAAG,IAAW;;gBAC/C,MAAM,eAAe,GAAG;oBACtB,MAAM,CAAC,WAAW,CAAC,IAAI;oBACvB,WAAW;oBACX,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;wBAC5B,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;wBAC3C,MAAM,cAAc,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;wBAC5D,IAAI,cAAc,IAAI,CAAC,EAAE;4BACvB,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;4BAC5C,IAAI,IAAI,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK,IAAI,IAAI,IAAI,IAAI,cAAc,EAAE;gCACnG,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;6BAC7B;4BACD,OAAO,cAAc,YAAY,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC;yBACtF;oBACH,CAAC,CAAC;iBACH,CAAC;gBACF,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAE1C,MAAM,cAAc,GAAmB,OAAO,CAAC,WAAW,CAAC,wCAA4B,EAAE,gCAAc,CAAC,CAAC;gBAEzG,MAAM,gBAAgB,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;gBACzE,IAAI,CAAC,gBAAgB,EAAE;oBACrB,MAAM,IAAI,6BAAgB,CAAC,uBAAuB,CAAC,CAAC;iBACrD;gBAED,IAAI,MAAM,CAAC;gBACX,IAAI;oBACF,MAAM,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;iBACjD;wBAAS;oBACR,MAAM,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;iBACtC;gBAED,OAAO,MAAM,CAAC;YAChB,CAAC;SAAA,CAAC;QAEF,OAAO,CAAC,cAAc,CACpB,sBAAsB,EACtB,EAAE,WAAW,EAAE,SAAS,EAAE,EAC1B,UAAU,CAAC,KAAK,CACjB,CAAC;QAEF,OAAO,UAAU,CAAC;IACpB,CAAC,CAAC;AACJ,CAAC;AArDD,0BAqDC"}
1
+ {"version":3,"file":"murlock.decorator.js","sourceRoot":"","sources":["../../lib/decorators/murlock.decorator.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from './murlock.service';
2
2
  export * from './murlock.module';
3
3
  export * from './decorators';
4
+ export * from './interfaces';
5
+ export * from './exceptions';
package/dist/index.js CHANGED
@@ -17,4 +17,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./murlock.service"), exports);
18
18
  __exportStar(require("./murlock.module"), exports);
19
19
  __exportStar(require("./decorators"), exports);
20
+ __exportStar(require("./interfaces"), exports);
21
+ __exportStar(require("./exceptions"), exports);
20
22
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,mDAAiC;AACjC,+CAA6B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,mDAAiC;AACjC,+CAA6B;AAC7B,+CAA6B;AAC7B,+CAA6B"}
@@ -1,9 +1,9 @@
1
1
  import { ClientOpts } from 'redis';
2
2
  export interface MurLockModuleOptions {
3
3
  redisOptions: ClientOpts;
4
- ttl: number;
5
4
  wait: number;
6
5
  maxAttempts: number;
6
+ logLevel: 'none' | 'error' | 'warn' | 'log' | 'debug';
7
7
  }
8
8
  export interface MurLockModuleAsyncOptions {
9
9
  imports?: any[];
@@ -12,6 +12,7 @@ const common_1 = require("@nestjs/common");
12
12
  const murlock_service_1 = require("./murlock.service");
13
13
  require("reflect-metadata");
14
14
  const constants_1 = require("./constants");
15
+ const nestjs_cls_1 = require("nestjs-cls");
15
16
  let MurLockModule = exports.MurLockModule = MurLockModule_1 = class MurLockModule {
16
17
  static registerSync(options) {
17
18
  return {
@@ -64,6 +65,7 @@ let MurLockModule = exports.MurLockModule = MurLockModule_1 = class MurLockModul
64
65
  exports.MurLockModule = MurLockModule = MurLockModule_1 = __decorate([
65
66
  (0, common_1.Global)(),
66
67
  (0, common_1.Module)({
68
+ imports: [nestjs_cls_1.ClsModule],
67
69
  providers: [murlock_service_1.MurLockService],
68
70
  exports: [murlock_service_1.MurLockService],
69
71
  })
@@ -1 +1 @@
1
- {"version":3,"file":"murlock.module.js","sourceRoot":"","sources":["../lib/murlock.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAyE;AACzE,uDAAmD;AACnD,4BAA0B;AAE1B,2CAA2D;AAOpD,IAAM,aAAa,6CAAnB,MAAM,aAAa;IACxB,MAAM,CAAC,YAAY,CAAC,OAA6B;QAC/C,OAAO;YACL,MAAM,EAAE,eAAa;YACrB,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,iBAAiB;oBAC1B,QAAQ,EAAE,OAAO;iBAClB;gBACD,gCAAc;gBACd;oBACE,OAAO,EAAE,wCAA4B;oBACrC,UAAU,EAAE,CAAC,cAA8B,EAAE,EAAE;wBAC7C,OAAO,CAAC,cAAc,CAAC,wCAA4B,EAAE,cAAc,EAAE,gCAAc,CAAC,CAAC;wBACrF,OAAO,cAAc,CAAC;oBACxB,CAAC;oBACD,MAAM,EAAE,CAAC,gCAAc,CAAC;iBACzB;aACF;YACD,OAAO,EAAE,CAAC,gCAAc,CAAC;SAC1B,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,OAAkC;QACrD,OAAO;YACL,MAAM,EAAE,eAAa;YACrB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,SAAS,EAAE;gBACT,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC;gBACxC,gCAAc;gBACd;oBACE,OAAO,EAAE,wCAA4B;oBACrC,UAAU,EAAE,CAAC,cAA8B,EAAE,EAAE;wBAC7C,OAAO,CAAC,cAAc,CAAC,wCAA4B,EAAE,cAAc,EAAE,gCAAc,CAAC,CAAC;wBACrF,OAAO,cAAc,CAAC;oBACxB,CAAC;oBACD,MAAM,EAAE,CAAC,gCAAc,CAAC;iBACzB;aACF;YACD,OAAO,EAAE,CAAC,gCAAc,CAAC;SAC1B,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,0BAA0B,CAAC,OAAkC;QAC1E,OAAO;YACL,OAAO,EAAE,iBAAiB;YAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;SAC7B,CAAC;IACJ,CAAC;CACF,CAAA;wBAlDY,aAAa;IALzB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE,CAAC,gCAAc,CAAC;QAC3B,OAAO,EAAE,CAAC,gCAAc,CAAC;KAC1B,CAAC;GACW,aAAa,CAkDzB"}
1
+ {"version":3,"file":"murlock.module.js","sourceRoot":"","sources":["../lib/murlock.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAyE;AACzE,uDAAmD;AACnD,4BAA0B;AAE1B,2CAA2D;AAC3D,2CAAuC;AAQhC,IAAM,aAAa,6CAAnB,MAAM,aAAa;IACxB,MAAM,CAAC,YAAY,CAAC,OAA6B;QAC/C,OAAO;YACL,MAAM,EAAE,eAAa;YACrB,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,iBAAiB;oBAC1B,QAAQ,EAAE,OAAO;iBAClB;gBACD,gCAAc;gBACd;oBACE,OAAO,EAAE,wCAA4B;oBACrC,UAAU,EAAE,CAAC,cAA8B,EAAE,EAAE;wBAC7C,OAAO,CAAC,cAAc,CAAC,wCAA4B,EAAE,cAAc,EAAE,gCAAc,CAAC,CAAC;wBACrF,OAAO,cAAc,CAAC;oBACxB,CAAC;oBACD,MAAM,EAAE,CAAC,gCAAc,CAAC;iBACzB;aACF;YACD,OAAO,EAAE,CAAC,gCAAc,CAAC;SAC1B,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,OAAkC;QACrD,OAAO;YACL,MAAM,EAAE,eAAa;YACrB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,SAAS,EAAE;gBACT,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC;gBACxC,gCAAc;gBACd;oBACE,OAAO,EAAE,wCAA4B;oBACrC,UAAU,EAAE,CAAC,cAA8B,EAAE,EAAE;wBAC7C,OAAO,CAAC,cAAc,CAAC,wCAA4B,EAAE,cAAc,EAAE,gCAAc,CAAC,CAAC;wBACrF,OAAO,cAAc,CAAC;oBACxB,CAAC;oBACD,MAAM,EAAE,CAAC,gCAAc,CAAC;iBACzB;aACF;YACD,OAAO,EAAE,CAAC,gCAAc,CAAC;SAC1B,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,0BAA0B,CAAC,OAAkC;QAC1E,OAAO;YACL,OAAO,EAAE,iBAAiB;YAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;SAC7B,CAAC;IACJ,CAAC;CACF,CAAA;wBAlDY,aAAa;IANzB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,sBAAS,CAAC;QACpB,SAAS,EAAE,CAAC,gCAAc,CAAC;QAC3B,OAAO,EAAE,CAAC,gCAAc,CAAC;KAC1B,CAAC;GACW,aAAa,CAkDzB"}
@@ -1,13 +1,21 @@
1
1
  import { OnApplicationShutdown, OnModuleInit } from '@nestjs/common';
2
2
  import { MurLockModuleOptions } from './interfaces';
3
- export declare class MurLockService implements OnApplicationShutdown, OnModuleInit {
3
+ import { ClsService } from 'nestjs-cls';
4
+ export declare class MurLockService implements OnModuleInit, OnApplicationShutdown {
4
5
  protected readonly options: MurLockModuleOptions;
6
+ private readonly clsService;
5
7
  private readonly logger;
6
8
  private redisClient;
7
- constructor(options: MurLockModuleOptions);
9
+ private readonly lockScript;
10
+ private readonly unlockScript;
11
+ private runScriptAsync;
12
+ constructor(options: MurLockModuleOptions, clsService: ClsService);
8
13
  onModuleInit(): void;
9
14
  onApplicationShutdown(signal?: string): void;
15
+ private generateUuid;
16
+ private getClientId;
10
17
  private sleep;
11
- lock(lockKey: string, releaseTime: number): Promise<boolean>;
18
+ private log;
19
+ lock(lockKey: string, releaseTime: number, attemptsRemaining?: number): Promise<boolean>;
12
20
  unlock(lockKey: string): Promise<void>;
13
21
  }
@@ -25,39 +25,74 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.MurLockService = void 0;
26
26
  const common_1 = require("@nestjs/common");
27
27
  const redis_1 = require("redis");
28
+ const util_1 = require("util");
29
+ const fs_1 = require("fs");
30
+ const path_1 = require("path");
31
+ const nestjs_cls_1 = require("nestjs-cls");
28
32
  let MurLockService = exports.MurLockService = MurLockService_1 = class MurLockService {
29
- constructor(options) {
33
+ constructor(options, clsService) {
30
34
  this.options = options;
35
+ this.clsService = clsService;
31
36
  this.logger = new common_1.Logger(MurLockService_1.name);
37
+ this.lockScript = (0, fs_1.readFileSync)((0, path_1.join)(__dirname, './lua/lock.lua')).toString();
38
+ this.unlockScript = (0, fs_1.readFileSync)((0, path_1.join)(__dirname, './lua/unlock.lua')).toString();
32
39
  }
33
40
  onModuleInit() {
34
41
  this.redisClient = new redis_1.RedisClient(this.options.redisOptions);
35
- this.logger.debug('MurLock Redis Connected.');
36
- this.redisClient.on('error', (err) => this.logger.log('MurLock Redis Client Error', err));
42
+ this.runScriptAsync = (0, util_1.promisify)(this.redisClient.eval).bind(this.redisClient);
43
+ this.redisClient.on('error', (err) => this.log('error', 'MurLock Redis Client Error', err));
37
44
  }
38
45
  onApplicationShutdown(signal) {
39
- this.logger.log('MurLock Redis Connected.');
46
+ this.log('log', 'MurLock Redis Disconnected.');
40
47
  this.redisClient.quit();
41
48
  }
49
+ generateUuid() {
50
+ let d = Date.now();
51
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
52
+ const r = (d + Math.random() * 16) % 16 | 0;
53
+ d = Math.floor(d / 16);
54
+ return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
55
+ });
56
+ }
57
+ getClientId() {
58
+ let clientId = this.clsService.get('clientId');
59
+ if (!clientId) {
60
+ clientId = this.generateUuid();
61
+ this.clsService.set('clientId', clientId);
62
+ }
63
+ return clientId;
64
+ }
42
65
  sleep(ms) {
43
66
  return new Promise((resolve) => setTimeout(resolve, ms));
44
67
  }
45
- lock(lockKey, releaseTime) {
68
+ log(level, message, context) {
69
+ const levels = ['debug', 'log', 'warn', 'error'];
70
+ if (levels.indexOf(level) >= levels.indexOf(this.options.logLevel)) {
71
+ this.logger[level](message, context);
72
+ }
73
+ }
74
+ lock(lockKey, releaseTime, attemptsRemaining = this.options.maxAttempts) {
46
75
  return __awaiter(this, void 0, void 0, function* () {
76
+ const clientId = this.getClientId();
77
+ this.log('debug', `MurLock Client ID is ${clientId}`);
47
78
  const attemptLock = (attemptsRemaining) => __awaiter(this, void 0, void 0, function* () {
48
- if (attemptsRemaining === 0)
49
- return false;
79
+ if (attemptsRemaining === 0) {
80
+ throw new Error(`Failed to obtain lock for key ${lockKey} after ${this.options.maxAttempts} attempts.`);
81
+ }
50
82
  try {
51
- const isLockSuccessful = yield this.redisClient.set(lockKey, 'LOCKED', 'EX', releaseTime, 'NX');
52
- if (isLockSuccessful) {
53
- this.logger.log(`Successfully obtained lock for key ${lockKey}`);
83
+ const isLockSuccessful = yield this.runScriptAsync(this.lockScript, 1, lockKey, clientId, releaseTime);
84
+ if (isLockSuccessful === 1) {
85
+ this.log('log', `Successfully obtained lock for key ${lockKey}`);
54
86
  return true;
55
87
  }
56
- yield this.sleep(this.options.wait);
57
- return attemptLock(attemptsRemaining - 1);
88
+ else {
89
+ this.log('warn', `Failed to obtain lock for key ${lockKey}, retrying in ${this.options.wait} ms...`);
90
+ yield this.sleep(this.options.wait * (this.options.maxAttempts - attemptsRemaining + 1));
91
+ return attemptLock(attemptsRemaining - 1);
92
+ }
58
93
  }
59
94
  catch (error) {
60
- this.logger.error(`Could not obtain lock for key ${lockKey}`, error);
95
+ this.log('error', `Unexpected error when trying to obtain lock for key ${lockKey}:`, error);
61
96
  return false;
62
97
  }
63
98
  });
@@ -66,14 +101,17 @@ let MurLockService = exports.MurLockService = MurLockService_1 = class MurLockSe
66
101
  }
67
102
  unlock(lockKey) {
68
103
  return __awaiter(this, void 0, void 0, function* () {
69
- yield this.redisClient.del(lockKey);
70
- this.logger.log(`Lock released for key ${lockKey}`);
104
+ const clientId = this.getClientId();
105
+ const result = yield this.runScriptAsync(this.unlockScript, 1, lockKey, clientId);
106
+ if (result === 0) {
107
+ throw new Error(`Failed to release lock for key ${lockKey}`);
108
+ }
71
109
  });
72
110
  }
73
111
  };
74
112
  exports.MurLockService = MurLockService = MurLockService_1 = __decorate([
75
113
  (0, common_1.Injectable)(),
76
114
  __param(0, (0, common_1.Inject)('MURLOCK_OPTIONS')),
77
- __metadata("design:paramtypes", [Object])
115
+ __metadata("design:paramtypes", [Object, nestjs_cls_1.ClsService])
78
116
  ], MurLockService);
79
117
  //# sourceMappingURL=murlock.service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"murlock.service.js","sourceRoot":"","sources":["../lib/murlock.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAgG;AAChG,iCAAoC;AAK7B,IAAM,cAAc,+CAApB,MAAM,cAAc;IAGzB,YAC6B,OAAgD;QAA7B,YAAO,GAAP,OAAO,CAAsB;QAH5D,WAAM,GAAG,IAAI,eAAM,CAAC,gBAAc,CAAC,IAAI,CAAC,CAAC;IAIvD,CAAC;IACJ,YAAY;QACV,IAAI,CAAC,WAAW,GAAG,IAAI,mBAAW,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAE9D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAE9C,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5F,CAAC;IACD,qBAAqB,CAAC,MAAe;QACnC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QAC5C,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAEO,KAAK,CAAC,EAAU;QACtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC;IAEK,IAAI,CAAC,OAAe,EAAE,WAAmB;;YAC7C,MAAM,WAAW,GAAG,CAAO,iBAAyB,EAAoB,EAAE;gBACxE,IAAI,iBAAiB,KAAK,CAAC;oBAAE,OAAO,KAAK,CAAC;gBAE1C,IAAI;oBACF,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;oBAChG,IAAI,gBAAgB,EAAE;wBACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,sCAAsC,OAAO,EAAE,CAAC,CAAC;wBACjE,OAAO,IAAI,CAAC;qBACb;oBACD,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;oBACpC,OAAO,WAAW,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC;iBAC3C;gBACD,OAAO,KAAK,EAAE;oBACZ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;oBACrE,OAAO,KAAK,CAAC;iBACd;YACH,CAAC,CAAA,CAAC;YAEF,OAAO,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC/C,CAAC;KAAA;IAEK,MAAM,CAAC,OAAe;;YAC1B,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;QACtD,CAAC;KAAA;CACF,CAAA;yBAhDY,cAAc;IAD1B,IAAA,mBAAU,GAAE;IAKR,WAAA,IAAA,eAAM,EAAC,iBAAiB,CAAC,CAAA;;GAJjB,cAAc,CAgD1B"}
1
+ {"version":3,"file":"murlock.service.js","sourceRoot":"","sources":["../lib/murlock.service.ts"],"names":[],"mappings":"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"}