@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.
@@ -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
- yield this.sleep(opts.retryDelay);
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
- const errorMessage = `Error acquiring lock for ${lockKey}: ${error.message}`;
237
- this.logger.error(errorMessage, error.stack);
238
- if (opts.strategy === LockStrategy.THROW) {
239
- throw new Error(errorMessage);
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 = 3600000, // 1 hour default
110
- retryCount = 0, retryDelay = 1000, useExponentialBackoff = false, maxRetryDelay = 60000, logExecution = true, lockKeyPrefix = 'schedule', autoExtendLock = 0, onSuccess, onError, skipIfLocked = true, } = options;
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: lockTtl,
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({