@nest-omni/core 4.1.3-10 → 4.1.3-11
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.js +25 -2
- package/audit/decorators/audit-controller.decorator.d.ts +1 -1
- package/audit/decorators/audit-controller.decorator.js +2 -2
- package/audit/decorators/entity-audit.decorator.d.ts +69 -2
- package/audit/decorators/entity-audit.decorator.js +127 -4
- package/audit/decorators/index.d.ts +1 -0
- package/audit/decorators/index.js +1 -0
- package/audit/entities/entity-audit-log.entity.d.ts +5 -0
- package/audit/entities/entity-audit-log.entity.js +30 -1
- package/audit/entities/entity-transaction.entity.d.ts +7 -0
- package/audit/entities/entity-transaction.entity.js +30 -1
- package/audit/entities/index.d.ts +2 -0
- package/audit/entities/index.js +2 -0
- package/audit/enums/audit.enums.d.ts +30 -5
- package/audit/enums/audit.enums.js +34 -6
- package/audit/index.d.ts +3 -1
- package/audit/index.js +21 -2
- package/audit/interfaces/audit.interfaces.d.ts +140 -2
- package/audit/services/entity-audit.service.d.ts +72 -3
- package/audit/services/entity-audit.service.js +210 -6
- package/audit/services/index.d.ts +2 -0
- package/audit/services/index.js +2 -0
- package/http-client/http-client.module.js +1 -5
- package/package.json +1 -1
- package/redis-lock/lock-heartbeat.service.d.ts +2 -2
- package/redis-lock/lock-heartbeat.service.js +4 -4
- package/redis-lock/redis-lock.service.d.ts +18 -0
- package/redis-lock/redis-lock.service.js +38 -8
- package/setup/schedule.decorator.js +18 -8
- package/shared/serviceRegistryModule.js +18 -13
|
@@ -72,13 +72,16 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
|
|
|
72
72
|
this.redis = null;
|
|
73
73
|
this.defaultOptions = {
|
|
74
74
|
ttl: 300000, // 5 minutes
|
|
75
|
-
retryCount: 0
|
|
76
|
-
retryDelay: 100
|
|
75
|
+
retryCount: 3, // Increased from 0 to provide reasonable retry attempts
|
|
76
|
+
retryDelay: 500, // Increased from 100 to reduce retry frequency
|
|
77
77
|
keyPrefix: 'lock',
|
|
78
78
|
throwOnFailure: false,
|
|
79
79
|
strategy: LockStrategy.SKIP,
|
|
80
80
|
waitTimeout: undefined,
|
|
81
81
|
autoExtend: 0, // No auto-extension by default
|
|
82
|
+
useExponentialBackoff: true, // Enable exponential backoff by default
|
|
83
|
+
maxRetryDelay: 30000, // Maximum retry delay of 30 seconds
|
|
84
|
+
retryJitter: 100, // Add random jitter to avoid synchronized retries
|
|
82
85
|
};
|
|
83
86
|
// Set global instance when created
|
|
84
87
|
if (!RedisLockService_1.globalInstance) {
|
|
@@ -169,6 +172,7 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
|
|
|
169
172
|
*/
|
|
170
173
|
acquireLock(key_1) {
|
|
171
174
|
return __awaiter(this, arguments, void 0, function* (key, options = {}) {
|
|
175
|
+
var _a, _b, _c, _d, _e;
|
|
172
176
|
const redis = yield this.getRedis();
|
|
173
177
|
const opts = Object.assign(Object.assign({}, this.defaultOptions), options);
|
|
174
178
|
// Handle backward compatibility: if throwOnFailure is true, use THROW strategy
|
|
@@ -229,16 +233,42 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
|
|
|
229
233
|
}
|
|
230
234
|
// Wait before next attempt (except for last attempt)
|
|
231
235
|
if (attempts < maxAttempts) {
|
|
232
|
-
|
|
236
|
+
// Add random jitter to avoid synchronized retries
|
|
237
|
+
const jitter = opts.retryJitter ? Math.random() * opts.retryJitter : 0;
|
|
238
|
+
const totalDelay = opts.retryDelay + jitter;
|
|
239
|
+
yield this.sleep(totalDelay);
|
|
233
240
|
}
|
|
234
241
|
}
|
|
235
242
|
catch (error) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
243
|
+
// Enhanced error classification and handling
|
|
244
|
+
if (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('ECONNREFUSED')) ||
|
|
245
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('ECONNRESET')) ||
|
|
246
|
+
error.code === 'ECONNREFUSED') {
|
|
247
|
+
// Connection error, will retry - log at debug level
|
|
248
|
+
this.logger.debug(`Redis connection error, will retry: ${error.message}`);
|
|
249
|
+
}
|
|
250
|
+
else if (((_c = error.message) === null || _c === void 0 ? void 0 : _c.includes('READONLY')) ||
|
|
251
|
+
((_d = error.message) === null || _d === void 0 ? void 0 : _d.includes('LOADING')) ||
|
|
252
|
+
((_e = error.message) === null || _e === void 0 ? void 0 : _e.includes('MASTERDOWN'))) {
|
|
253
|
+
// Redis server error, log at warn level
|
|
254
|
+
this.logger.warn(`Redis server error: ${error.message}`);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// Other errors, log at error level
|
|
258
|
+
const errorMessage = `Error acquiring lock for ${lockKey}: ${error.message}`;
|
|
259
|
+
this.logger.error(errorMessage, error.stack);
|
|
260
|
+
if (opts.strategy === LockStrategy.THROW) {
|
|
261
|
+
throw new Error(errorMessage);
|
|
262
|
+
}
|
|
263
|
+
return { acquired: false, error: errorMessage };
|
|
264
|
+
}
|
|
265
|
+
// For retryable errors, continue with retry logic
|
|
266
|
+
attempts++;
|
|
267
|
+
if (attempts < maxAttempts) {
|
|
268
|
+
const jitter = opts.retryJitter ? Math.random() * opts.retryJitter : 0;
|
|
269
|
+
const totalDelay = opts.retryDelay + jitter;
|
|
270
|
+
yield this.sleep(totalDelay);
|
|
240
271
|
}
|
|
241
|
-
return { acquired: false, error: errorMessage };
|
|
242
272
|
}
|
|
243
273
|
}
|
|
244
274
|
// Max attempts reached
|
|
@@ -106,8 +106,18 @@ function createWorkerDecoratorWithLock(baseDecorator, decoratorName, lockKey, op
|
|
|
106
106
|
}
|
|
107
107
|
// Get or create global instance - auto-configures from environment variables
|
|
108
108
|
const lockService = redis_lock_1.RedisLockService.getOrCreateGlobalInstance();
|
|
109
|
-
const { lockTtl =
|
|
110
|
-
retryCount =
|
|
109
|
+
const { lockTtl = 600000, // 10 minutes default (reduced from 1 hour)
|
|
110
|
+
retryCount = 3, retryDelay = 500, useExponentialBackoff = true, maxRetryDelay = 30000, logExecution = true, lockKeyPrefix = 'schedule', autoExtendLock = 0, onSuccess, onError, skipIfLocked = true, } = options;
|
|
111
|
+
// Configuration validation and adjustment
|
|
112
|
+
const validatedLockTtl = lockTtl >= 3600000
|
|
113
|
+
? (logger.warn(`lockTtl (${lockTtl}ms) is quite long for a scheduled task, consider if this is appropriate for ${className}.${methodName}`), lockTtl)
|
|
114
|
+
: lockTtl;
|
|
115
|
+
const validatedRetryCount = retryCount > 10
|
|
116
|
+
? (logger.warn(`retryCount (${retryCount}) is too high, limiting to 10 for task ${className}.${methodName}`), 10)
|
|
117
|
+
: retryCount;
|
|
118
|
+
const validatedRetryDelay = retryDelay < 100
|
|
119
|
+
? (logger.warn(`retryDelay (${retryDelay}ms) is too low, setting to 100ms for task ${className}.${methodName}`), 100)
|
|
120
|
+
: retryDelay;
|
|
111
121
|
const fullLockKey = `${lockKeyPrefix}:${resolvedLockKey}`;
|
|
112
122
|
const startTime = Date.now();
|
|
113
123
|
// Log task start
|
|
@@ -117,7 +127,7 @@ function createWorkerDecoratorWithLock(baseDecorator, decoratorName, lockKey, op
|
|
|
117
127
|
try {
|
|
118
128
|
// Acquire lock
|
|
119
129
|
const lockResult = yield lockService.acquireLock(resolvedLockKey, {
|
|
120
|
-
ttl:
|
|
130
|
+
ttl: validatedLockTtl,
|
|
121
131
|
keyPrefix: lockKeyPrefix,
|
|
122
132
|
autoExtend: autoExtendLock,
|
|
123
133
|
});
|
|
@@ -133,8 +143,8 @@ function createWorkerDecoratorWithLock(baseDecorator, decoratorName, lockKey, op
|
|
|
133
143
|
try {
|
|
134
144
|
// Execute with retry logic
|
|
135
145
|
const result = yield executeWithRetry(() => originalMethod.apply(this, args), {
|
|
136
|
-
retryCount,
|
|
137
|
-
retryDelay,
|
|
146
|
+
retryCount: validatedRetryCount,
|
|
147
|
+
retryDelay: validatedRetryDelay,
|
|
138
148
|
useExponentialBackoff,
|
|
139
149
|
maxRetryDelay,
|
|
140
150
|
logger,
|
|
@@ -309,11 +319,11 @@ function WorkerIntervalWithLock(timeout, lockKeyOrOptions, lockTtl) {
|
|
|
309
319
|
let options;
|
|
310
320
|
if (typeof lockKeyOrOptions === 'string') {
|
|
311
321
|
lockKey = lockKeyOrOptions;
|
|
312
|
-
options = { lockTtl: lockTtl || Math.floor(timeout * 0.8) };
|
|
322
|
+
options = { lockTtl: lockTtl || Math.max(Math.floor(timeout * 0.8), 10000) };
|
|
313
323
|
}
|
|
314
324
|
else {
|
|
315
325
|
lockKey = generateLockKey(target.constructor.name, String(propertyKey));
|
|
316
|
-
options = Object.assign({ lockTtl: Math.floor(timeout * 0.8) }, lockKeyOrOptions);
|
|
326
|
+
options = Object.assign({ lockTtl: Math.max(Math.floor(timeout * 0.8), 10000) }, lockKeyOrOptions);
|
|
317
327
|
}
|
|
318
328
|
return createWorkerDecoratorWithLock((0, schedule_1.Interval)(timeout), 'WorkerIntervalWithLock', lockKey, options)(target, propertyKey, descriptor);
|
|
319
329
|
};
|
|
@@ -406,7 +416,7 @@ function WorkerCronSmart(cronTime, options = {}, cronOptions) {
|
|
|
406
416
|
function WorkerIntervalSmart(timeout, options = {}) {
|
|
407
417
|
return function (target, propertyKey, descriptor) {
|
|
408
418
|
const lockKey = generateLockKey(target.constructor.name, String(propertyKey));
|
|
409
|
-
return createWorkerDecoratorWithLock((0, schedule_1.Interval)(timeout), 'WorkerIntervalSmart', lockKey, Object.assign({ lockTtl: Math.floor(timeout * 0.8), logExecution: true }, options))(target, propertyKey, descriptor);
|
|
419
|
+
return createWorkerDecoratorWithLock((0, schedule_1.Interval)(timeout), 'WorkerIntervalSmart', lockKey, Object.assign({ lockTtl: Math.max(Math.floor(timeout * 0.8), 10000), logExecution: true }, options))(target, propertyKey, descriptor);
|
|
410
420
|
};
|
|
411
421
|
}
|
|
412
422
|
/**
|
|
@@ -61,19 +61,6 @@ const modules = [
|
|
|
61
61
|
expandVariables: false,
|
|
62
62
|
envFilePath: [process.env.ENV_FILE_PATH, process.env.BASE_ENV_FILE_PATH],
|
|
63
63
|
}),
|
|
64
|
-
nestjs_cls_1.ClsModule.forRoot({
|
|
65
|
-
global: true,
|
|
66
|
-
middleware: { mount: true, saveReq: true },
|
|
67
|
-
plugins: [
|
|
68
|
-
new transactional_1.ClsPluginTransactional({
|
|
69
|
-
imports: [typeorm_1.TypeOrmModule],
|
|
70
|
-
adapter: new transactional_adapter_typeorm_1.TransactionalAdapterTypeOrm({
|
|
71
|
-
dataSourceToken: typeorm_2.DataSource,
|
|
72
|
-
}),
|
|
73
|
-
enableTransactionProxy: true,
|
|
74
|
-
}),
|
|
75
|
-
],
|
|
76
|
-
}),
|
|
77
64
|
nestjs_pino_1.LoggerModule.forRootAsync({
|
|
78
65
|
inject: [services_1.ApiConfigService],
|
|
79
66
|
useFactory: (config) => config.pinoConfig,
|
|
@@ -105,6 +92,24 @@ if (services_1.ApiConfigService.toBoolean(process.env.DB_ENABLED, true)) {
|
|
|
105
92
|
inject: [services_1.ApiConfigService],
|
|
106
93
|
useFactory: (config) => config.typeormConfig,
|
|
107
94
|
}));
|
|
95
|
+
modules.push(nestjs_cls_1.ClsModule.forRootAsync({
|
|
96
|
+
imports: [typeorm_1.TypeOrmModule],
|
|
97
|
+
useFactory(args) {
|
|
98
|
+
return {
|
|
99
|
+
middleware: { mount: true, saveReq: true },
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
global: true,
|
|
103
|
+
plugins: [
|
|
104
|
+
new transactional_1.ClsPluginTransactional({
|
|
105
|
+
imports: [typeorm_1.TypeOrmModule],
|
|
106
|
+
adapter: new transactional_adapter_typeorm_1.TransactionalAdapterTypeOrm({
|
|
107
|
+
dataSourceToken: (0, typeorm_1.getDataSourceToken)(),
|
|
108
|
+
}),
|
|
109
|
+
enableTransactionProxy: true,
|
|
110
|
+
}),
|
|
111
|
+
]
|
|
112
|
+
}));
|
|
108
113
|
}
|
|
109
114
|
if (services_1.ApiConfigService.toBoolean(process.env.BULL_ENABLED)) {
|
|
110
115
|
modules.push(bull_1.BullModule.forRootAsync({
|