@nest-omni/core 4.1.3-0 → 4.1.3-10
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/audit/audit.module.d.ts +10 -0
- package/audit/audit.module.js +15 -0
- package/audit/controllers/audit.controller.d.ts +24 -0
- package/audit/controllers/audit.controller.js +24 -0
- package/audit/decorators/audit-controller.decorator.d.ts +8 -0
- package/audit/decorators/audit-controller.decorator.js +9 -0
- package/audit/decorators/audit-operation.decorator.d.ts +45 -0
- package/audit/decorators/audit-operation.decorator.js +49 -0
- package/audit/decorators/entity-audit.decorator.d.ts +8 -0
- package/audit/decorators/entity-audit.decorator.js +9 -0
- package/audit/dto/audit-log-query.dto.d.ts +3 -0
- package/audit/dto/audit-log-query.dto.js +3 -0
- package/audit/dto/begin-transaction.dto.d.ts +3 -0
- package/audit/dto/begin-transaction.dto.js +3 -0
- package/audit/dto/compare-entities.dto.d.ts +3 -0
- package/audit/dto/compare-entities.dto.js +3 -0
- package/audit/dto/pre-check-restore.dto.d.ts +3 -0
- package/audit/dto/pre-check-restore.dto.js +3 -0
- package/audit/dto/restore-entity.dto.d.ts +3 -0
- package/audit/dto/restore-entity.dto.js +3 -0
- package/audit/entities/entity-audit-log.entity.d.ts +3 -0
- package/audit/entities/entity-audit-log.entity.js +3 -0
- package/audit/entities/entity-transaction.entity.d.ts +3 -0
- package/audit/entities/entity-transaction.entity.js +3 -0
- package/audit/entities/manual-operation-log.entity.d.ts +4 -0
- package/audit/entities/manual-operation-log.entity.js +4 -0
- package/audit/entities/operation-template.entity.d.ts +4 -0
- package/audit/entities/operation-template.entity.js +4 -0
- package/audit/enums/audit.enums.d.ts +17 -2
- package/audit/enums/audit.enums.js +15 -0
- package/audit/index.js +10 -0
- package/audit/interceptors/audit.interceptor.d.ts +15 -0
- package/audit/interceptors/audit.interceptor.js +23 -1
- package/audit/interfaces/audit.interfaces.d.ts +42 -0
- package/audit/services/audit-context.service.d.ts +15 -0
- package/audit/services/audit-context.service.js +15 -0
- package/audit/services/audit-strategy.service.d.ts +6 -0
- package/audit/services/audit-strategy.service.js +13 -0
- package/audit/services/entity-audit.service.d.ts +57 -0
- package/audit/services/entity-audit.service.js +91 -0
- package/audit/services/manual-audit-log.service.d.ts +124 -0
- package/audit/services/manual-audit-log.service.js +138 -0
- package/audit/services/multi-database.service.d.ts +12 -0
- package/audit/services/multi-database.service.js +12 -0
- package/audit/services/operation-description.service.d.ts +59 -0
- package/audit/services/operation-description.service.js +76 -2
- package/audit/services/transaction-audit.service.d.ts +30 -0
- package/audit/services/transaction-audit.service.js +47 -0
- package/audit/subscribers/entity-audit.subscriber.d.ts +15 -0
- package/audit/subscribers/entity-audit.subscriber.js +29 -1
- package/cache/cache-metrics.service.d.ts +67 -0
- package/cache/cache-metrics.service.js +68 -4
- package/cache/cache-serialization.service.d.ts +31 -0
- package/cache/cache-serialization.service.js +25 -0
- package/cache/cache.constants.d.ts +9 -0
- package/cache/cache.constants.js +9 -0
- package/cache/cache.health.d.ts +26 -0
- package/cache/cache.health.js +30 -0
- package/cache/cache.module.d.ts +86 -0
- package/cache/cache.module.js +71 -0
- package/cache/cache.service.d.ts +140 -0
- package/cache/cache.service.js +157 -0
- package/cache/cache.warmup.service.d.ts +39 -0
- package/cache/cache.warmup.service.js +32 -0
- package/cache/decorators/cache-evict.decorator.d.ts +47 -0
- package/cache/decorators/cache-evict.decorator.js +56 -0
- package/cache/decorators/cache-put.decorator.d.ts +34 -0
- package/cache/decorators/cache-put.decorator.js +39 -0
- package/cache/decorators/cacheable.decorator.d.ts +40 -0
- package/cache/decorators/cacheable.decorator.js +55 -0
- package/cache/dependencies/callback.dependency.d.ts +33 -0
- package/cache/dependencies/callback.dependency.js +39 -1
- package/cache/dependencies/chain.dependency.d.ts +28 -0
- package/cache/dependencies/chain.dependency.js +34 -0
- package/cache/dependencies/db.dependency.d.ts +45 -0
- package/cache/dependencies/db.dependency.js +48 -1
- package/cache/dependencies/file.dependency.d.ts +32 -0
- package/cache/dependencies/file.dependency.js +34 -0
- package/cache/dependencies/tag.dependency.d.ts +36 -0
- package/cache/dependencies/tag.dependency.js +36 -0
- package/cache/dependencies/time.dependency.d.ts +43 -0
- package/cache/dependencies/time.dependency.js +43 -0
- package/cache/examples/basic-usage.d.ts +15 -0
- package/cache/examples/basic-usage.js +62 -8
- package/cache/index.js +9 -0
- package/cache/interfaces/cache-dependency.interface.d.ts +53 -0
- package/cache/interfaces/cache-options.interface.d.ts +81 -0
- package/cache/interfaces/cache-options.interface.js +6 -0
- package/cache/interfaces/cache-provider.interface.d.ts +78 -0
- package/cache/providers/base-cache.provider.d.ts +14 -0
- package/cache/providers/base-cache.provider.js +16 -0
- package/cache/providers/cls-cache.provider.d.ts +20 -0
- package/cache/providers/cls-cache.provider.js +28 -0
- package/cache/providers/memory-cache.provider.d.ts +23 -0
- package/cache/providers/memory-cache.provider.js +26 -0
- package/cache/providers/redis-cache.provider.d.ts +26 -0
- package/cache/providers/redis-cache.provider.js +29 -0
- package/cache/utils/dependency-manager.util.d.ts +52 -0
- package/cache/utils/dependency-manager.util.js +59 -0
- package/cache/utils/key-generator.util.d.ts +42 -0
- package/cache/utils/key-generator.util.js +53 -1
- package/common/abstract.entity.d.ts +14 -0
- package/common/abstract.entity.js +14 -0
- package/common/boilerplate.polyfill.d.ts +142 -4
- package/common/boilerplate.polyfill.js +24 -131
- package/common/dto/dto-container.d.ts +16 -0
- package/common/dto/dto-container.js +20 -0
- package/common/dto/dto-decorators.d.ts +18 -0
- package/common/dto/dto-decorators.js +14 -0
- package/common/dto/dto-extensions.d.ts +11 -0
- package/common/dto/dto-extensions.js +9 -0
- package/common/dto/dto-service-accessor.d.ts +17 -0
- package/common/dto/dto-service-accessor.js +18 -0
- package/common/dto/dto-transformer.d.ts +12 -0
- package/common/dto/dto-transformer.js +9 -0
- package/common/dto/index.js +2 -0
- package/common/examples/paginate-and-map.example.d.ts +6 -0
- package/common/examples/paginate-and-map.example.js +26 -0
- package/common/utils.d.ts +15 -0
- package/common/utils.js +15 -0
- package/constants/language-code.js +1 -0
- package/decorators/field.decorators.js +8 -1
- package/decorators/property.decorators.js +1 -0
- package/decorators/public-route.decorator.js +1 -0
- package/decorators/transform.decorators.d.ts +27 -0
- package/decorators/transform.decorators.js +29 -0
- package/decorators/translate.decorator.js +1 -0
- package/decorators/user.decorator.js +1 -0
- package/decorators/validator.decorators.d.ts +8 -18
- package/decorators/validator.decorators.js +22 -190
- package/filters/constraint-errors.js +1 -0
- package/helpers/common.helper.d.ts +13 -0
- package/helpers/common.helper.js +13 -0
- package/http-client/config/http-client.config.d.ts +15 -0
- package/http-client/config/http-client.config.js +25 -9
- package/http-client/decorators/http-client.decorators.d.ts +63 -0
- package/http-client/decorators/http-client.decorators.js +71 -3
- package/http-client/entities/http-log.entity.d.ts +229 -0
- package/http-client/entities/http-log.entity.js +6 -1
- package/http-client/errors/http-client.errors.d.ts +57 -0
- package/http-client/errors/http-client.errors.js +58 -0
- package/http-client/examples/advanced-usage.example.d.ts +41 -0
- package/http-client/examples/advanced-usage.example.js +68 -24
- package/http-client/examples/auth-with-waiting-lock.example.d.ts +31 -0
- package/http-client/examples/auth-with-waiting-lock.example.js +52 -5
- package/http-client/examples/basic-usage.example.d.ts +60 -0
- package/http-client/examples/basic-usage.example.js +60 -0
- package/http-client/examples/multi-api-configuration.example.d.ts +60 -0
- package/http-client/examples/multi-api-configuration.example.js +76 -5
- package/http-client/http-client.module.d.ts +13 -0
- package/http-client/http-client.module.js +19 -0
- package/http-client/index.js +8 -0
- package/http-client/interfaces/api-client-config.interface.d.ts +125 -0
- package/http-client/interfaces/api-client-config.interface.js +3 -0
- package/http-client/interfaces/http-client-config.interface.d.ts +60 -0
- package/http-client/services/api-client-registry.service.d.ts +57 -0
- package/http-client/services/api-client-registry.service.js +84 -1
- package/http-client/services/cache.service.d.ts +52 -0
- package/http-client/services/cache.service.js +72 -3
- package/http-client/services/circuit-breaker.service.d.ts +46 -0
- package/http-client/services/circuit-breaker.service.js +52 -0
- package/http-client/services/http-client.service.d.ts +67 -0
- package/http-client/services/http-client.service.js +105 -4
- package/http-client/services/http-log-query.service.d.ts +83 -0
- package/http-client/services/http-log-query.service.js +122 -1
- package/http-client/services/http-replay.service.d.ts +101 -0
- package/http-client/services/http-replay.service.js +86 -0
- package/http-client/services/log-cleanup.service.d.ts +63 -0
- package/http-client/services/log-cleanup.service.js +54 -2
- package/http-client/services/logging.service.d.ts +40 -0
- package/http-client/services/logging.service.js +53 -0
- package/http-client/utils/call-stack-extractor.util.d.ts +37 -0
- package/http-client/utils/call-stack-extractor.util.js +48 -0
- package/http-client/utils/context-extractor.util.d.ts +49 -0
- package/http-client/utils/context-extractor.util.js +52 -0
- package/http-client/utils/curl-generator.util.d.ts +21 -0
- package/http-client/utils/curl-generator.util.js +44 -3
- package/http-client/utils/request-id.util.d.ts +18 -0
- package/http-client/utils/request-id.util.js +20 -0
- package/http-client/utils/retry-recorder.util.d.ts +42 -0
- package/http-client/utils/retry-recorder.util.js +44 -0
- package/i18n/en_US/validation.json +2 -1
- package/i18n/zh_CN/validation.json +2 -1
- package/index.js +8 -0
- package/interceptors/translation-interceptor.service.js +5 -0
- package/package.json +1 -1
- package/providers/context.provider.js +2 -0
- package/providers/generator.provider.d.ts +4 -0
- package/providers/generator.provider.js +4 -0
- package/redis-lock/comprehensive-lock-cleanup.service.d.ts +94 -0
- package/redis-lock/comprehensive-lock-cleanup.service.js +253 -0
- package/redis-lock/examples/lock-strategy.examples.d.ts +89 -0
- package/redis-lock/examples/lock-strategy.examples.js +130 -15
- package/redis-lock/index.d.ts +2 -0
- package/redis-lock/index.js +8 -1
- package/redis-lock/lock-heartbeat.service.d.ts +78 -0
- package/redis-lock/lock-heartbeat.service.js +222 -0
- package/redis-lock/redis-lock.decorator.d.ts +101 -0
- package/redis-lock/redis-lock.decorator.js +120 -0
- package/redis-lock/redis-lock.module.d.ts +66 -0
- package/redis-lock/redis-lock.module.js +175 -70
- package/redis-lock/redis-lock.service.d.ts +260 -0
- package/redis-lock/redis-lock.service.js +244 -4
- package/setup/bootstrap.setup.js +20 -0
- package/setup/mode.setup.d.ts +44 -0
- package/setup/mode.setup.js +44 -0
- package/setup/schedule.decorator.d.ts +227 -0
- package/setup/schedule.decorator.js +219 -6
- package/setup/worker.decorator.d.ts +86 -0
- package/setup/worker.decorator.js +88 -0
- package/shared/serviceRegistryModule.js +9 -1
- package/shared/services/api-config.service.d.ts +3 -0
- package/shared/services/api-config.service.js +20 -9
- package/validator-json/decorators.d.ts +17 -0
- package/validator-json/decorators.js +17 -2
- package/validator-json/default.d.ts +6 -0
- package/validator-json/default.js +30 -2
- package/validator-json/defaultConverters.js +1 -0
- package/validator-json/options.d.ts +23 -0
- package/validators/common-validators.d.ts +143 -0
- package/validators/common-validators.js +249 -0
- package/validators/custom-validate.examples.d.ts +96 -0
- package/validators/custom-validate.examples.js +400 -0
- package/validators/custom-validate.validator.d.ts +134 -0
- package/validators/custom-validate.validator.js +214 -0
- package/validators/index.d.ts +2 -0
- package/validators/index.js +2 -0
- package/validators/is-exists.validator.d.ts +18 -4
- package/validators/is-exists.validator.js +67 -6
- package/validators/is-unique.validator.d.ts +32 -5
- package/validators/is-unique.validator.js +99 -17
- package/validators/skip-empty.validator.d.ts +5 -0
- package/validators/skip-empty.validator.js +5 -0
- package/vault/interfaces/vault-options.interface.d.ts +9 -0
- package/vault/vault-config.loader.d.ts +30 -0
- package/vault/vault-config.loader.js +48 -1
- package/vault/vault-config.service.d.ts +53 -0
- package/vault/vault-config.service.js +57 -0
- package/vault/vault.module.d.ts +4 -0
- package/vault/vault.module.js +4 -0
- package/decorators/examples/validation-decorators.example.d.ts +0 -69
- package/decorators/examples/validation-decorators.example.js +0 -331
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lock Strategy Examples
|
|
4
|
+
*
|
|
5
|
+
* This file demonstrates the different lock acquisition strategies available in Redis Lock module.
|
|
6
|
+
*/
|
|
2
7
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
8
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
9
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -23,18 +28,33 @@ exports.CombinedStrategiesExample = exports.WaitStrategyExample = exports.ThrowS
|
|
|
23
28
|
const common_1 = require("@nestjs/common");
|
|
24
29
|
const schedule_1 = require("@nestjs/schedule");
|
|
25
30
|
const index_1 = require("../index");
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Strategy 1: SKIP (Default)
|
|
33
|
+
// ============================================================================
|
|
34
|
+
/**
|
|
35
|
+
* SKIP strategy - Skip execution if lock cannot be acquired
|
|
36
|
+
* This is the default behavior and is useful for scheduled tasks where you don't
|
|
37
|
+
* want to queue up requests if a previous execution is still running.
|
|
38
|
+
*/
|
|
26
39
|
let SkipStrategyExample = SkipStrategyExample_1 = class SkipStrategyExample {
|
|
27
40
|
constructor(lockService) {
|
|
28
41
|
this.lockService = lockService;
|
|
29
42
|
this.logger = new common_1.Logger(SkipStrategyExample_1.name);
|
|
30
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Example 1: Using decorator with SKIP strategy (default)
|
|
46
|
+
*/
|
|
31
47
|
scheduledTask() {
|
|
32
48
|
return __awaiter(this, void 0, void 0, function* () {
|
|
33
49
|
this.logger.log('Executing scheduled task...');
|
|
50
|
+
// This will skip if another instance is already running
|
|
34
51
|
yield this.simulateLongRunning();
|
|
35
52
|
this.logger.log('Scheduled task completed');
|
|
36
53
|
});
|
|
37
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Example 2: Using service directly with SKIP strategy
|
|
57
|
+
*/
|
|
38
58
|
processWithSkip() {
|
|
39
59
|
return __awaiter(this, void 0, void 0, function* () {
|
|
40
60
|
const lockResult = yield this.lockService.acquireLock('process-task', {
|
|
@@ -55,13 +75,16 @@ let SkipStrategyExample = SkipStrategyExample_1 = class SkipStrategyExample {
|
|
|
55
75
|
}
|
|
56
76
|
});
|
|
57
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Example 3: SKIP with retries
|
|
80
|
+
*/
|
|
58
81
|
processWithRetries() {
|
|
59
82
|
return __awaiter(this, void 0, void 0, function* () {
|
|
60
83
|
const lockResult = yield this.lockService.acquireLock('retry-task', {
|
|
61
84
|
strategy: index_1.LockStrategy.SKIP,
|
|
62
85
|
ttl: 30000,
|
|
63
|
-
retryCount: 3,
|
|
64
|
-
retryDelay: 500,
|
|
86
|
+
retryCount: 3, // Try 3 times
|
|
87
|
+
retryDelay: 500, // Wait 500ms between retries
|
|
65
88
|
});
|
|
66
89
|
if (!lockResult.acquired) {
|
|
67
90
|
this.logger.warn('Failed to acquire lock after 3 retries');
|
|
@@ -84,10 +107,11 @@ let SkipStrategyExample = SkipStrategyExample_1 = class SkipStrategyExample {
|
|
|
84
107
|
};
|
|
85
108
|
exports.SkipStrategyExample = SkipStrategyExample;
|
|
86
109
|
__decorate([
|
|
87
|
-
(0, schedule_1.Cron)('* * * * *')
|
|
110
|
+
(0, schedule_1.Cron)('* * * * *') // Every minute
|
|
111
|
+
,
|
|
88
112
|
(0, index_1.UseRedisLock)('scheduled-task', {
|
|
89
113
|
ttl: 60000,
|
|
90
|
-
strategy: index_1.LockStrategy.SKIP,
|
|
114
|
+
strategy: index_1.LockStrategy.SKIP, // Can be omitted as it's the default
|
|
91
115
|
}),
|
|
92
116
|
__metadata("design:type", Function),
|
|
93
117
|
__metadata("design:paramtypes", []),
|
|
@@ -97,18 +121,33 @@ exports.SkipStrategyExample = SkipStrategyExample = SkipStrategyExample_1 = __de
|
|
|
97
121
|
(0, common_1.Injectable)(),
|
|
98
122
|
__metadata("design:paramtypes", [index_1.RedisLockService])
|
|
99
123
|
], SkipStrategyExample);
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Strategy 2: THROW
|
|
126
|
+
// ============================================================================
|
|
127
|
+
/**
|
|
128
|
+
* THROW strategy - Throw an error if lock cannot be acquired
|
|
129
|
+
* Use this for critical operations that must have exclusive access.
|
|
130
|
+
* If the lock cannot be acquired, an exception will be thrown.
|
|
131
|
+
*/
|
|
100
132
|
let ThrowStrategyExample = ThrowStrategyExample_1 = class ThrowStrategyExample {
|
|
101
133
|
constructor(lockService) {
|
|
102
134
|
this.lockService = lockService;
|
|
103
135
|
this.logger = new common_1.Logger(ThrowStrategyExample_1.name);
|
|
104
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Example 1: Using decorator with THROW strategy
|
|
139
|
+
*/
|
|
105
140
|
criticalOperation() {
|
|
106
141
|
return __awaiter(this, void 0, void 0, function* () {
|
|
107
142
|
this.logger.log('Executing critical operation...');
|
|
143
|
+
// If lock cannot be acquired, an error will be thrown
|
|
108
144
|
yield this.processData();
|
|
109
145
|
this.logger.log('Critical operation completed');
|
|
110
146
|
});
|
|
111
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Example 2: Using service with error handling
|
|
150
|
+
*/
|
|
112
151
|
processWithThrow() {
|
|
113
152
|
return __awaiter(this, void 0, void 0, function* () {
|
|
114
153
|
try {
|
|
@@ -127,20 +166,26 @@ let ThrowStrategyExample = ThrowStrategyExample_1 = class ThrowStrategyExample {
|
|
|
127
166
|
}
|
|
128
167
|
catch (error) {
|
|
129
168
|
this.logger.error(`Failed to acquire lock: ${error.message}`);
|
|
169
|
+
// Handle the error appropriately
|
|
130
170
|
throw new Error('Operation cannot proceed without exclusive access');
|
|
131
171
|
}
|
|
132
172
|
});
|
|
133
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Example 3: Critical payment processing
|
|
176
|
+
*/
|
|
134
177
|
processPayment(orderId, amount) {
|
|
135
178
|
return __awaiter(this, void 0, void 0, function* () {
|
|
136
179
|
const lockKey = `payment:${orderId}`;
|
|
137
180
|
try {
|
|
181
|
+
// Must have exclusive access for payment processing
|
|
138
182
|
const lockResult = yield this.lockService.acquireLock(lockKey, {
|
|
139
183
|
strategy: index_1.LockStrategy.THROW,
|
|
140
184
|
ttl: 60000,
|
|
141
185
|
});
|
|
142
186
|
try {
|
|
143
187
|
this.logger.log(`Processing payment for order ${orderId}...`);
|
|
188
|
+
// Payment logic here
|
|
144
189
|
yield this.chargePayment(orderId, amount);
|
|
145
190
|
yield this.updateOrderStatus(orderId, 'paid');
|
|
146
191
|
return { success: true };
|
|
@@ -162,11 +207,13 @@ let ThrowStrategyExample = ThrowStrategyExample_1 = class ThrowStrategyExample {
|
|
|
162
207
|
}
|
|
163
208
|
chargePayment(orderId, amount) {
|
|
164
209
|
return __awaiter(this, void 0, void 0, function* () {
|
|
210
|
+
// Simulate payment processing
|
|
165
211
|
yield new Promise((resolve) => setTimeout(resolve, 1000));
|
|
166
212
|
});
|
|
167
213
|
}
|
|
168
214
|
updateOrderStatus(orderId, status) {
|
|
169
215
|
return __awaiter(this, void 0, void 0, function* () {
|
|
216
|
+
// Update order status
|
|
170
217
|
yield new Promise((resolve) => setTimeout(resolve, 500));
|
|
171
218
|
});
|
|
172
219
|
}
|
|
@@ -185,24 +232,39 @@ exports.ThrowStrategyExample = ThrowStrategyExample = ThrowStrategyExample_1 = _
|
|
|
185
232
|
(0, common_1.Injectable)(),
|
|
186
233
|
__metadata("design:paramtypes", [index_1.RedisLockService])
|
|
187
234
|
], ThrowStrategyExample);
|
|
235
|
+
// ============================================================================
|
|
236
|
+
// Strategy 3: WAIT
|
|
237
|
+
// ============================================================================
|
|
238
|
+
/**
|
|
239
|
+
* WAIT strategy - Block/wait until lock can be acquired
|
|
240
|
+
* Use this when you want to queue execution and ensure the operation eventually runs.
|
|
241
|
+
* The function will wait indefinitely (or until waitTimeout) for the lock to become available.
|
|
242
|
+
*/
|
|
188
243
|
let WaitStrategyExample = WaitStrategyExample_1 = class WaitStrategyExample {
|
|
189
244
|
constructor(lockService) {
|
|
190
245
|
this.lockService = lockService;
|
|
191
246
|
this.logger = new common_1.Logger(WaitStrategyExample_1.name);
|
|
192
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Example 1: Using decorator with WAIT strategy
|
|
250
|
+
*/
|
|
193
251
|
sequentialTask() {
|
|
194
252
|
return __awaiter(this, void 0, void 0, function* () {
|
|
195
253
|
this.logger.log('Executing sequential task...');
|
|
254
|
+
// This will wait until lock is available
|
|
196
255
|
yield this.processInOrder();
|
|
197
256
|
this.logger.log('Sequential task completed');
|
|
198
257
|
});
|
|
199
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Example 2: WAIT with timeout
|
|
261
|
+
*/
|
|
200
262
|
processWithWaitTimeout() {
|
|
201
263
|
return __awaiter(this, void 0, void 0, function* () {
|
|
202
264
|
const lockResult = yield this.lockService.acquireLock('wait-task', {
|
|
203
265
|
strategy: index_1.LockStrategy.WAIT,
|
|
204
|
-
waitTimeout: 30000,
|
|
205
|
-
retryDelay: 200,
|
|
266
|
+
waitTimeout: 30000, // Wait maximum 30 seconds
|
|
267
|
+
retryDelay: 200, // Check every 200ms
|
|
206
268
|
ttl: 60000,
|
|
207
269
|
});
|
|
208
270
|
if (!lockResult.acquired) {
|
|
@@ -219,16 +281,21 @@ let WaitStrategyExample = WaitStrategyExample_1 = class WaitStrategyExample {
|
|
|
219
281
|
}
|
|
220
282
|
});
|
|
221
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Example 3: Sequential data processing
|
|
286
|
+
* Perfect for operations that must be processed in order
|
|
287
|
+
*/
|
|
222
288
|
processDataSequentially(userId, data) {
|
|
223
289
|
return __awaiter(this, void 0, void 0, function* () {
|
|
224
290
|
const lockKey = `user-data:${userId}`;
|
|
225
291
|
const lockResult = yield this.lockService.acquireLock(lockKey, {
|
|
226
292
|
strategy: index_1.LockStrategy.WAIT,
|
|
227
293
|
retryDelay: 50,
|
|
228
|
-
ttl: 120000,
|
|
294
|
+
ttl: 120000, // 2 minutes
|
|
229
295
|
});
|
|
230
296
|
try {
|
|
231
297
|
this.logger.log(`Processing data for user ${userId}...`);
|
|
298
|
+
// These operations will execute sequentially across all instances
|
|
232
299
|
yield this.validateData(data);
|
|
233
300
|
yield this.saveData(userId, data);
|
|
234
301
|
yield this.notifyUser(userId);
|
|
@@ -239,6 +306,10 @@ let WaitStrategyExample = WaitStrategyExample_1 = class WaitStrategyExample {
|
|
|
239
306
|
}
|
|
240
307
|
});
|
|
241
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Example 4: Queue-like behavior
|
|
311
|
+
* Multiple requests will queue and execute one by one
|
|
312
|
+
*/
|
|
242
313
|
processQueue(queueName, item) {
|
|
243
314
|
return __awaiter(this, void 0, void 0, function* () {
|
|
244
315
|
const lockKey = `queue:${queueName}`;
|
|
@@ -258,15 +329,19 @@ let WaitStrategyExample = WaitStrategyExample_1 = class WaitStrategyExample {
|
|
|
258
329
|
}
|
|
259
330
|
});
|
|
260
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* Example 5: Database migration with WAIT
|
|
334
|
+
* Ensures migrations run sequentially across all instances
|
|
335
|
+
*/
|
|
261
336
|
runMigration(migrationName) {
|
|
262
337
|
return __awaiter(this, void 0, void 0, function* () {
|
|
263
338
|
const lockKey = `migration:${migrationName}`;
|
|
264
339
|
this.logger.log(`Waiting to run migration ${migrationName}...`);
|
|
265
340
|
const lockResult = yield this.lockService.acquireLock(lockKey, {
|
|
266
341
|
strategy: index_1.LockStrategy.WAIT,
|
|
267
|
-
waitTimeout: 300000,
|
|
268
|
-
retryDelay: 1000,
|
|
269
|
-
ttl: 600000,
|
|
342
|
+
waitTimeout: 300000, // Wait up to 5 minutes
|
|
343
|
+
retryDelay: 1000, // Check every second
|
|
344
|
+
ttl: 600000, // Hold lock for 10 minutes max
|
|
270
345
|
});
|
|
271
346
|
if (!lockResult.acquired) {
|
|
272
347
|
throw new Error(`Migration ${migrationName} timed out waiting for lock`);
|
|
@@ -318,7 +393,7 @@ __decorate([
|
|
|
318
393
|
(0, index_1.UseRedisLock)('sequential-task', {
|
|
319
394
|
ttl: 30000,
|
|
320
395
|
strategy: index_1.LockStrategy.WAIT,
|
|
321
|
-
retryDelay: 100,
|
|
396
|
+
retryDelay: 100, // Check every 100ms
|
|
322
397
|
}),
|
|
323
398
|
__metadata("design:type", Function),
|
|
324
399
|
__metadata("design:paramtypes", []),
|
|
@@ -328,16 +403,50 @@ exports.WaitStrategyExample = WaitStrategyExample = WaitStrategyExample_1 = __de
|
|
|
328
403
|
(0, common_1.Injectable)(),
|
|
329
404
|
__metadata("design:paramtypes", [index_1.RedisLockService])
|
|
330
405
|
], WaitStrategyExample);
|
|
406
|
+
// ============================================================================
|
|
407
|
+
// Comparison: When to use each strategy
|
|
408
|
+
// ============================================================================
|
|
409
|
+
/**
|
|
410
|
+
* USE SKIP WHEN:
|
|
411
|
+
* - Running scheduled tasks where you don't want overlapping executions
|
|
412
|
+
* - Cron jobs that should skip if previous run is still in progress
|
|
413
|
+
* - Non-critical background tasks
|
|
414
|
+
* - Operations where it's okay to skip if busy
|
|
415
|
+
*
|
|
416
|
+
* USE THROW WHEN:
|
|
417
|
+
* - Critical operations that require exclusive access
|
|
418
|
+
* - Financial transactions
|
|
419
|
+
* - Operations where failure to acquire lock is an error
|
|
420
|
+
* - When you want to alert/fail fast if resource is busy
|
|
421
|
+
*
|
|
422
|
+
* USE WAIT WHEN:
|
|
423
|
+
* - Operations that must execute eventually
|
|
424
|
+
* - Sequential processing requirements
|
|
425
|
+
* - Queue-like behavior needed
|
|
426
|
+
* - Database migrations
|
|
427
|
+
* - Operations where order matters
|
|
428
|
+
* - When you want to ensure the operation runs (with optional timeout)
|
|
429
|
+
*/
|
|
430
|
+
/**
|
|
431
|
+
* Example: Combining strategies in a single service
|
|
432
|
+
*/
|
|
331
433
|
let CombinedStrategiesExample = CombinedStrategiesExample_1 = class CombinedStrategiesExample {
|
|
332
434
|
constructor(lockService) {
|
|
333
435
|
this.lockService = lockService;
|
|
334
436
|
this.logger = new common_1.Logger(CombinedStrategiesExample_1.name);
|
|
335
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Background cleanup - SKIP if already running
|
|
440
|
+
*/
|
|
336
441
|
cleanupOldData() {
|
|
337
442
|
return __awaiter(this, void 0, void 0, function* () {
|
|
338
443
|
this.logger.log('Running cleanup...');
|
|
444
|
+
// Cleanup logic
|
|
339
445
|
});
|
|
340
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* Payment processing - THROW if cannot get exclusive access
|
|
449
|
+
*/
|
|
341
450
|
processPayment(orderId) {
|
|
342
451
|
return __awaiter(this, void 0, void 0, function* () {
|
|
343
452
|
const lockResult = yield this.lockService.acquireLock(`payment:${orderId}`, {
|
|
@@ -345,24 +454,29 @@ let CombinedStrategiesExample = CombinedStrategiesExample_1 = class CombinedStra
|
|
|
345
454
|
ttl: 60000,
|
|
346
455
|
});
|
|
347
456
|
try {
|
|
457
|
+
// Payment logic
|
|
348
458
|
}
|
|
349
459
|
finally {
|
|
350
460
|
yield this.lockService.releaseLock(`payment:${orderId}`, lockResult.lockValue);
|
|
351
461
|
}
|
|
352
462
|
});
|
|
353
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* Report generation - WAIT to ensure it runs eventually
|
|
466
|
+
*/
|
|
354
467
|
generateReport(reportId) {
|
|
355
468
|
return __awaiter(this, void 0, void 0, function* () {
|
|
356
469
|
const lockResult = yield this.lockService.acquireLock(`report:${reportId}`, {
|
|
357
470
|
strategy: index_1.LockStrategy.WAIT,
|
|
358
|
-
waitTimeout: 180000,
|
|
471
|
+
waitTimeout: 180000, // Wait up to 3 minutes
|
|
359
472
|
retryDelay: 500,
|
|
360
|
-
ttl: 300000,
|
|
473
|
+
ttl: 300000, // 5 minutes
|
|
361
474
|
});
|
|
362
475
|
if (!lockResult.acquired) {
|
|
363
476
|
throw new Error('Report generation timed out');
|
|
364
477
|
}
|
|
365
478
|
try {
|
|
479
|
+
// Report generation logic
|
|
366
480
|
}
|
|
367
481
|
finally {
|
|
368
482
|
yield this.lockService.releaseLock(`report:${reportId}`, lockResult.lockValue);
|
|
@@ -372,10 +486,11 @@ let CombinedStrategiesExample = CombinedStrategiesExample_1 = class CombinedStra
|
|
|
372
486
|
};
|
|
373
487
|
exports.CombinedStrategiesExample = CombinedStrategiesExample;
|
|
374
488
|
__decorate([
|
|
375
|
-
(0, schedule_1.Cron)('0 */6 * * *')
|
|
489
|
+
(0, schedule_1.Cron)('0 */6 * * *') // Every 6 hours
|
|
490
|
+
,
|
|
376
491
|
(0, index_1.UseRedisLock)('cleanup-task', {
|
|
377
492
|
strategy: index_1.LockStrategy.SKIP,
|
|
378
|
-
ttl: 3600000,
|
|
493
|
+
ttl: 3600000, // 1 hour
|
|
379
494
|
}),
|
|
380
495
|
__metadata("design:type", Function),
|
|
381
496
|
__metadata("design:paramtypes", []),
|
package/redis-lock/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { RedisLockModule, type RedisLockModuleOptions, REDIS_LOCK_SERVICE, } from './redis-lock.module';
|
|
2
2
|
export { RedisLockService, LockStrategy } from './redis-lock.service';
|
|
3
|
+
export { LockHeartbeatService } from './lock-heartbeat.service';
|
|
4
|
+
export { ComprehensiveLockCleanupService } from './comprehensive-lock-cleanup.service';
|
|
3
5
|
export { UseRedisLock, UseRedisLockOrSkip, UseRedisLockSmart, UseRedisLockOrSkipSmart, setLockService, getLockService, } from './redis-lock.decorator';
|
|
4
6
|
export type { LockOptions, LockResult } from './redis-lock.service';
|
package/redis-lock/index.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getLockService = exports.setLockService = exports.UseRedisLockOrSkipSmart = exports.UseRedisLockSmart = exports.UseRedisLockOrSkip = exports.UseRedisLock = exports.LockStrategy = exports.RedisLockService = exports.REDIS_LOCK_SERVICE = exports.RedisLockModule = void 0;
|
|
3
|
+
exports.getLockService = exports.setLockService = exports.UseRedisLockOrSkipSmart = exports.UseRedisLockSmart = exports.UseRedisLockOrSkip = exports.UseRedisLock = exports.ComprehensiveLockCleanupService = exports.LockHeartbeatService = exports.LockStrategy = exports.RedisLockService = exports.REDIS_LOCK_SERVICE = exports.RedisLockModule = void 0;
|
|
4
|
+
// Module
|
|
4
5
|
var redis_lock_module_1 = require("./redis-lock.module");
|
|
5
6
|
Object.defineProperty(exports, "RedisLockModule", { enumerable: true, get: function () { return redis_lock_module_1.RedisLockModule; } });
|
|
6
7
|
Object.defineProperty(exports, "REDIS_LOCK_SERVICE", { enumerable: true, get: function () { return redis_lock_module_1.REDIS_LOCK_SERVICE; } });
|
|
8
|
+
// Service
|
|
7
9
|
var redis_lock_service_1 = require("./redis-lock.service");
|
|
8
10
|
Object.defineProperty(exports, "RedisLockService", { enumerable: true, get: function () { return redis_lock_service_1.RedisLockService; } });
|
|
9
11
|
Object.defineProperty(exports, "LockStrategy", { enumerable: true, get: function () { return redis_lock_service_1.LockStrategy; } });
|
|
12
|
+
var lock_heartbeat_service_1 = require("./lock-heartbeat.service");
|
|
13
|
+
Object.defineProperty(exports, "LockHeartbeatService", { enumerable: true, get: function () { return lock_heartbeat_service_1.LockHeartbeatService; } });
|
|
14
|
+
var comprehensive_lock_cleanup_service_1 = require("./comprehensive-lock-cleanup.service");
|
|
15
|
+
Object.defineProperty(exports, "ComprehensiveLockCleanupService", { enumerable: true, get: function () { return comprehensive_lock_cleanup_service_1.ComprehensiveLockCleanupService; } });
|
|
16
|
+
// Decorators
|
|
10
17
|
var redis_lock_decorator_1 = require("./redis-lock.decorator");
|
|
11
18
|
Object.defineProperty(exports, "UseRedisLock", { enumerable: true, get: function () { return redis_lock_decorator_1.UseRedisLock; } });
|
|
12
19
|
Object.defineProperty(exports, "UseRedisLockOrSkip", { enumerable: true, get: function () { return redis_lock_decorator_1.UseRedisLockOrSkip; } });
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|
2
|
+
import type Redis from 'ioredis';
|
|
3
|
+
/**
|
|
4
|
+
* Lock heartbeat service
|
|
5
|
+
*
|
|
6
|
+
* Provides heartbeat mechanism for distributed lock instances.
|
|
7
|
+
* Each instance sends periodic heartbeats to Redis to indicate it's alive.
|
|
8
|
+
* Dead instances can be detected by checking if their heartbeat has expired.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* constructor(private heartbeatService: LockHeartbeatService) {}
|
|
13
|
+
*
|
|
14
|
+
* async isInstanceAlive(instanceId: string): Promise<boolean> {
|
|
15
|
+
* return this.heartbeatService.isInstanceAlive(instanceId);
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare class LockHeartbeatService implements OnModuleInit, OnModuleDestroy {
|
|
20
|
+
private readonly logger;
|
|
21
|
+
private readonly instanceId;
|
|
22
|
+
private redis;
|
|
23
|
+
private heartbeatTimer;
|
|
24
|
+
/**
|
|
25
|
+
* Heartbeat interval in milliseconds
|
|
26
|
+
* @default 10000 (10 seconds)
|
|
27
|
+
*/
|
|
28
|
+
private readonly heartbeatInterval;
|
|
29
|
+
/**
|
|
30
|
+
* Heartbeat TTL in seconds (3x interval for safety)
|
|
31
|
+
* @default 30 (30 seconds)
|
|
32
|
+
*/
|
|
33
|
+
private readonly heartbeatTtl;
|
|
34
|
+
constructor();
|
|
35
|
+
onModuleInit(): Promise<void>;
|
|
36
|
+
onModuleDestroy(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Set Redis client (required for dependency injection)
|
|
39
|
+
*/
|
|
40
|
+
setRedisClient(redis: Redis): void;
|
|
41
|
+
/**
|
|
42
|
+
* Send heartbeat to Redis
|
|
43
|
+
* Stores instance information with TTL
|
|
44
|
+
*/
|
|
45
|
+
private sendHeartbeat;
|
|
46
|
+
/**
|
|
47
|
+
* Remove heartbeat record from Redis
|
|
48
|
+
*/
|
|
49
|
+
private removeHeartbeat;
|
|
50
|
+
/**
|
|
51
|
+
* Check if an instance is alive by checking its heartbeat
|
|
52
|
+
*
|
|
53
|
+
* @param instanceId - Instance identifier to check
|
|
54
|
+
* @returns True if instance is alive (heartbeat exists), false otherwise
|
|
55
|
+
*/
|
|
56
|
+
isInstanceAlive(instanceId: string): Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Get heartbeat information for an instance
|
|
59
|
+
*
|
|
60
|
+
* @param instanceId - Instance identifier
|
|
61
|
+
* @returns Heartbeat data or null if not found
|
|
62
|
+
*/
|
|
63
|
+
getHeartbeatInfo(instanceId: string): Promise<{
|
|
64
|
+
instanceId: string;
|
|
65
|
+
timestamp: number;
|
|
66
|
+
pid: number;
|
|
67
|
+
} | null>;
|
|
68
|
+
/**
|
|
69
|
+
* Get all active instances (with heartbeat)
|
|
70
|
+
*
|
|
71
|
+
* @returns Array of instance IDs that are currently alive
|
|
72
|
+
*/
|
|
73
|
+
getActiveInstances(): Promise<string[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Get current instance ID
|
|
76
|
+
*/
|
|
77
|
+
getInstanceId(): string;
|
|
78
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
12
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
13
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
14
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
15
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
16
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
17
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
var LockHeartbeatService_1;
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.LockHeartbeatService = void 0;
|
|
23
|
+
const common_1 = require("@nestjs/common");
|
|
24
|
+
/**
|
|
25
|
+
* Lock heartbeat service
|
|
26
|
+
*
|
|
27
|
+
* Provides heartbeat mechanism for distributed lock instances.
|
|
28
|
+
* Each instance sends periodic heartbeats to Redis to indicate it's alive.
|
|
29
|
+
* Dead instances can be detected by checking if their heartbeat has expired.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* constructor(private heartbeatService: LockHeartbeatService) {}
|
|
34
|
+
*
|
|
35
|
+
* async isInstanceAlive(instanceId: string): Promise<boolean> {
|
|
36
|
+
* return this.heartbeatService.isInstanceAlive(instanceId);
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
let LockHeartbeatService = LockHeartbeatService_1 = class LockHeartbeatService {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.logger = new common_1.Logger(LockHeartbeatService_1.name);
|
|
43
|
+
this.redis = null;
|
|
44
|
+
this.heartbeatTimer = null;
|
|
45
|
+
/**
|
|
46
|
+
* Heartbeat interval in milliseconds
|
|
47
|
+
* @default 10000 (10 seconds)
|
|
48
|
+
*/
|
|
49
|
+
this.heartbeatInterval = 10000;
|
|
50
|
+
/**
|
|
51
|
+
* Heartbeat TTL in seconds (3x interval for safety)
|
|
52
|
+
* @default 30 (30 seconds)
|
|
53
|
+
*/
|
|
54
|
+
this.heartbeatTtl = 30;
|
|
55
|
+
this.instanceId = this.getInstanceId();
|
|
56
|
+
}
|
|
57
|
+
onModuleInit() {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
if (!this.redis) {
|
|
60
|
+
this.logger.warn('LockHeartbeatService initialized but no Redis client provided. Heartbeat functionality will not work.');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Send initial heartbeat
|
|
64
|
+
yield this.sendHeartbeat();
|
|
65
|
+
// Start periodic heartbeat
|
|
66
|
+
this.heartbeatTimer = setInterval(() => {
|
|
67
|
+
this.sendHeartbeat().catch((error) => {
|
|
68
|
+
this.logger.error('Failed to send heartbeat:', error);
|
|
69
|
+
});
|
|
70
|
+
}, this.heartbeatInterval);
|
|
71
|
+
this.logger.log(`Heartbeat started for instance: ${this.instanceId}`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
onModuleDestroy() {
|
|
75
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
+
// Stop heartbeat timer
|
|
77
|
+
if (this.heartbeatTimer) {
|
|
78
|
+
clearInterval(this.heartbeatTimer);
|
|
79
|
+
this.heartbeatTimer = null;
|
|
80
|
+
}
|
|
81
|
+
// Remove heartbeat record
|
|
82
|
+
yield this.removeHeartbeat();
|
|
83
|
+
this.logger.log('Heartbeat stopped');
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Set Redis client (required for dependency injection)
|
|
88
|
+
*/
|
|
89
|
+
setRedisClient(redis) {
|
|
90
|
+
this.redis = redis;
|
|
91
|
+
this.logger.log('Redis client set for LockHeartbeatService');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Send heartbeat to Redis
|
|
95
|
+
* Stores instance information with TTL
|
|
96
|
+
*/
|
|
97
|
+
sendHeartbeat() {
|
|
98
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
if (!this.redis) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const key = `heartbeat:${this.instanceId}`;
|
|
103
|
+
const data = {
|
|
104
|
+
instanceId: this.instanceId,
|
|
105
|
+
timestamp: Date.now(),
|
|
106
|
+
pid: process.pid,
|
|
107
|
+
};
|
|
108
|
+
try {
|
|
109
|
+
// Set heartbeat with TTL (3x interval for safety)
|
|
110
|
+
yield this.redis.setex(key, this.heartbeatTtl, JSON.stringify(data));
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
this.logger.error('Error sending heartbeat:', error);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Remove heartbeat record from Redis
|
|
119
|
+
*/
|
|
120
|
+
removeHeartbeat() {
|
|
121
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
if (!this.redis) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const key = `heartbeat:${this.instanceId}`;
|
|
126
|
+
try {
|
|
127
|
+
yield this.redis.del(key);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
this.logger.error('Error removing heartbeat:', error);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if an instance is alive by checking its heartbeat
|
|
136
|
+
*
|
|
137
|
+
* @param instanceId - Instance identifier to check
|
|
138
|
+
* @returns True if instance is alive (heartbeat exists), false otherwise
|
|
139
|
+
*/
|
|
140
|
+
isInstanceAlive(instanceId) {
|
|
141
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
if (!this.redis) {
|
|
143
|
+
this.logger.warn('Redis client not available, cannot check instance status');
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const key = `heartbeat:${instanceId}`;
|
|
147
|
+
try {
|
|
148
|
+
const exists = yield this.redis.exists(key);
|
|
149
|
+
return exists === 1;
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
this.logger.error(`Error checking instance ${instanceId} status:`, error);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get heartbeat information for an instance
|
|
159
|
+
*
|
|
160
|
+
* @param instanceId - Instance identifier
|
|
161
|
+
* @returns Heartbeat data or null if not found
|
|
162
|
+
*/
|
|
163
|
+
getHeartbeatInfo(instanceId) {
|
|
164
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
165
|
+
if (!this.redis) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const key = `heartbeat:${instanceId}`;
|
|
169
|
+
try {
|
|
170
|
+
const data = yield this.redis.get(key);
|
|
171
|
+
if (!data) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return JSON.parse(data);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
this.logger.error(`Error getting heartbeat info for ${instanceId}:`, error);
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get all active instances (with heartbeat)
|
|
184
|
+
*
|
|
185
|
+
* @returns Array of instance IDs that are currently alive
|
|
186
|
+
*/
|
|
187
|
+
getActiveInstances() {
|
|
188
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
189
|
+
if (!this.redis) {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const keys = yield this.redis.keys('heartbeat:*');
|
|
194
|
+
return keys.map((key) => key.replace('heartbeat:', ''));
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
this.logger.error('Error getting active instances:', error);
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Get current instance ID
|
|
204
|
+
*/
|
|
205
|
+
getInstanceId() {
|
|
206
|
+
// 1. Kubernetes Pod name
|
|
207
|
+
if (process.env.HOSTNAME) {
|
|
208
|
+
return process.env.HOSTNAME;
|
|
209
|
+
}
|
|
210
|
+
// 2. Custom instance ID
|
|
211
|
+
if (process.env.INSTANCE_ID) {
|
|
212
|
+
return process.env.INSTANCE_ID;
|
|
213
|
+
}
|
|
214
|
+
// 3. Generate unique ID
|
|
215
|
+
return `instance-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
exports.LockHeartbeatService = LockHeartbeatService;
|
|
219
|
+
exports.LockHeartbeatService = LockHeartbeatService = LockHeartbeatService_1 = __decorate([
|
|
220
|
+
(0, common_1.Injectable)(),
|
|
221
|
+
__metadata("design:paramtypes", [])
|
|
222
|
+
], LockHeartbeatService);
|