monsqlize 1.0.1 → 1.0.5

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 (66) hide show
  1. package/CHANGELOG.md +204 -2464
  2. package/README.md +735 -1198
  3. package/index.d.ts +942 -18
  4. package/lib/cache.js +8 -8
  5. package/lib/common/validation.js +64 -1
  6. package/lib/connect.js +3 -3
  7. package/lib/errors.js +10 -0
  8. package/lib/index.js +173 -9
  9. package/lib/infrastructure/ssh-tunnel-ssh2.js +211 -0
  10. package/lib/infrastructure/ssh-tunnel.js +40 -0
  11. package/lib/infrastructure/uri-parser.js +35 -0
  12. package/lib/lock/Lock.js +66 -0
  13. package/lib/lock/errors.js +27 -0
  14. package/lib/lock/index.js +12 -0
  15. package/lib/logger.js +1 -1
  16. package/lib/model/examples/test.js +225 -29
  17. package/lib/model/features/soft-delete.js +348 -0
  18. package/lib/model/features/version.js +156 -0
  19. package/lib/model/index.js +756 -0
  20. package/lib/mongodb/common/accessor-helpers.js +17 -3
  21. package/lib/mongodb/connect.js +68 -13
  22. package/lib/mongodb/index.js +153 -6
  23. package/lib/mongodb/management/collection-ops.js +4 -4
  24. package/lib/mongodb/management/index-ops.js +18 -18
  25. package/lib/mongodb/management/validation-ops.js +3 -3
  26. package/lib/mongodb/queries/aggregate.js +14 -5
  27. package/lib/mongodb/queries/chain.js +52 -45
  28. package/lib/mongodb/queries/count.js +16 -6
  29. package/lib/mongodb/queries/distinct.js +15 -6
  30. package/lib/mongodb/queries/find-and-count.js +22 -13
  31. package/lib/mongodb/queries/find-by-ids.js +5 -5
  32. package/lib/mongodb/queries/find-one-by-id.js +1 -1
  33. package/lib/mongodb/queries/find-one.js +12 -3
  34. package/lib/mongodb/queries/find-page.js +12 -0
  35. package/lib/mongodb/queries/find.js +15 -6
  36. package/lib/mongodb/queries/watch.js +11 -2
  37. package/lib/mongodb/writes/common/batch-retry.js +64 -0
  38. package/lib/mongodb/writes/delete-batch.js +322 -0
  39. package/lib/mongodb/writes/delete-many.js +20 -11
  40. package/lib/mongodb/writes/delete-one.js +18 -9
  41. package/lib/mongodb/writes/find-one-and-delete.js +19 -10
  42. package/lib/mongodb/writes/find-one-and-replace.js +36 -20
  43. package/lib/mongodb/writes/find-one-and-update.js +36 -20
  44. package/lib/mongodb/writes/increment-one.js +22 -7
  45. package/lib/mongodb/writes/index.js +17 -13
  46. package/lib/mongodb/writes/insert-batch.js +46 -37
  47. package/lib/mongodb/writes/insert-many.js +22 -13
  48. package/lib/mongodb/writes/insert-one.js +18 -9
  49. package/lib/mongodb/writes/replace-one.js +33 -17
  50. package/lib/mongodb/writes/result-handler.js +14 -14
  51. package/lib/mongodb/writes/update-batch.js +358 -0
  52. package/lib/mongodb/writes/update-many.js +34 -18
  53. package/lib/mongodb/writes/update-one.js +33 -17
  54. package/lib/mongodb/writes/upsert-one.js +25 -9
  55. package/lib/operators.js +1 -1
  56. package/lib/redis-cache-adapter.js +3 -3
  57. package/lib/slow-query-log/base-storage.js +69 -0
  58. package/lib/slow-query-log/batch-queue.js +96 -0
  59. package/lib/slow-query-log/config-manager.js +195 -0
  60. package/lib/slow-query-log/index.js +237 -0
  61. package/lib/slow-query-log/mongodb-storage.js +323 -0
  62. package/lib/slow-query-log/query-hash.js +38 -0
  63. package/lib/transaction/DistributedCacheLockManager.js +240 -5
  64. package/lib/transaction/Transaction.js +1 -1
  65. package/lib/utils/objectid-converter.js +566 -0
  66. package/package.json +18 -6
@@ -0,0 +1,323 @@
1
+ /**
2
+ * MongoDB慢查询日志存储实现
3
+ * 实现方案B(更新记录去重)
4
+ *
5
+ * @version 1.3.0
6
+ * @since 2025-12-22
7
+ */
8
+
9
+ const { ISlowQueryLogStorage } = require('./base-storage');
10
+ const { generateQueryHash } = require('./query-hash');
11
+
12
+ class MongoDBSlowQueryLogStorage extends ISlowQueryLogStorage {
13
+ /**
14
+ * 创建MongoDB存储实例
15
+ * @param {Object} config - 存储配置
16
+ * @param {string} [config.uri] - MongoDB连接URI(独立连接时)
17
+ * @param {string} [config.database='admin'] - 存储数据库
18
+ * @param {string} [config.collection='slow_query_logs'] - 存储集合
19
+ * @param {number} [config.ttl=604800] - TTL过期时间(秒)
20
+ * @param {string} [config.ttlField='lastSeen'] - TTL字段
21
+ * @param {Object} [client] - MongoDB客户端(复用连接时)
22
+ * @param {Object} [logger] - 日志记录器
23
+ */
24
+ constructor(config, client, logger) {
25
+ super();
26
+ this.config = config;
27
+ this.client = client; // 可能是复用的业务连接
28
+ this.ownClient = null; // 独立连接(如果创建)
29
+ this.collection = null;
30
+ this.logger = logger || console;
31
+ this.initialized = false;
32
+ }
33
+
34
+ /**
35
+ * 初始化存储(连接+创建索引)
36
+ * @returns {Promise<void>}
37
+ */
38
+ async initialize() {
39
+ if (this.initialized) {
40
+ return;
41
+ }
42
+
43
+ try {
44
+ // 如果没有传入client,创建独立连接
45
+ if (!this.client) {
46
+ if (!this.config.uri) {
47
+ throw new Error('MongoDB URI is required when client is not provided');
48
+ }
49
+
50
+ const { MongoClient } = require('mongodb');
51
+ this.ownClient = await MongoClient.connect(this.config.uri);
52
+ this.client = this.ownClient;
53
+
54
+ if (this.logger.info) {
55
+ this.logger.info(`[SlowQueryLog] Connected to MongoDB: ${this.config.uri}`);
56
+ }
57
+ }
58
+
59
+ // 获取数据库和集合
60
+ const dbName = this.config.database || 'admin';
61
+ const collName = this.config.collection || 'slow_query_logs';
62
+
63
+ this.db = this.client.db(dbName);
64
+ this.collection = this.db.collection(collName);
65
+
66
+ // 创建索引
67
+ await this.setupIndexes();
68
+
69
+ this.initialized = true;
70
+
71
+ if (this.logger.info) {
72
+ this.logger.info(
73
+ `[SlowQueryLog] Initialized MongoDB storage: ${dbName}.${collName}`
74
+ );
75
+ }
76
+ } catch (err) {
77
+ if (this.logger.error) {
78
+ this.logger.error('[SlowQueryLog] Failed to initialize MongoDB storage:', err);
79
+ }
80
+ throw err;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 创建索引
86
+ * @returns {Promise<void>}
87
+ */
88
+ async setupIndexes() {
89
+ try {
90
+ // 索引1:queryHash唯一索引(去重关键)
91
+ await this.collection.createIndex(
92
+ { queryHash: 1 },
93
+ { unique: true, name: 'idx_queryHash_unique' }
94
+ );
95
+
96
+ // 索引2:lastSeen TTL索引(自动过期)
97
+ const ttl = this.config.ttl || 7 * 24 * 3600;
98
+ const ttlField = this.config.ttlField || 'lastSeen';
99
+
100
+ await this.collection.createIndex(
101
+ { [ttlField]: 1 },
102
+ {
103
+ name: 'idx_lastSeen_ttl',
104
+ expireAfterSeconds: ttl
105
+ }
106
+ );
107
+
108
+ // 索引3:按集合查询(辅助索引)
109
+ await this.collection.createIndex(
110
+ { db: 1, collection: 1 },
111
+ { name: 'idx_db_collection' }
112
+ );
113
+
114
+ // 索引4:按执行次数查询(找高频慢查询)
115
+ await this.collection.createIndex(
116
+ { count: -1 },
117
+ { name: 'idx_count_desc' }
118
+ );
119
+
120
+ if (this.logger.debug) {
121
+ this.logger.debug('[SlowQueryLog] MongoDB indexes created successfully');
122
+ }
123
+ } catch (err) {
124
+ // 索引创建失败不应该阻塞功能
125
+ if (this.logger.warn) {
126
+ this.logger.warn('[SlowQueryLog] Failed to create some indexes:', err.message);
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * 保存单条慢查询日志(upsert)
133
+ * @param {Object} log - 慢查询日志对象
134
+ * @returns {Promise<void>}
135
+ */
136
+ async save(log) {
137
+ if (!this.initialized) {
138
+ await this.initialize();
139
+ }
140
+
141
+ const queryHash = generateQueryHash(log);
142
+ await this.upsert(queryHash, log);
143
+ }
144
+
145
+ /**
146
+ * 批量保存慢查询日志(bulkWrite + upsert)
147
+ * @param {Object[]} logs - 慢查询日志数组
148
+ * @returns {Promise<void>}
149
+ */
150
+ async saveBatch(logs) {
151
+ if (!this.initialized) {
152
+ await this.initialize();
153
+ }
154
+
155
+ if (!logs || logs.length === 0) {
156
+ return;
157
+ }
158
+
159
+ try {
160
+ // 构建bulkWrite操作数组
161
+ const operations = logs.map(log => {
162
+ const queryHash = generateQueryHash(log);
163
+ const timestamp = log.timestamp || new Date();
164
+ const executionTimeMs = log.executionTimeMs || log.ms || 0;
165
+
166
+ return {
167
+ updateOne: {
168
+ filter: { queryHash },
169
+ update: {
170
+ $set: {
171
+ lastSeen: timestamp,
172
+ lastExecution: {
173
+ executionTimeMs,
174
+ timestamp,
175
+ metadata: log.metadata || {}
176
+ }
177
+ },
178
+ $inc: {
179
+ count: 1,
180
+ totalTimeMs: executionTimeMs
181
+ },
182
+ $min: { minTimeMs: executionTimeMs },
183
+ $max: { maxTimeMs: executionTimeMs },
184
+ $setOnInsert: {
185
+ queryHash,
186
+ db: log.db,
187
+ collection: log.collection || log.coll,
188
+ operation: log.operation || log.op,
189
+ queryShape: log.queryShape || {},
190
+ type: log.type || 'mongodb',
191
+ firstSeen: timestamp
192
+ }
193
+ },
194
+ upsert: true
195
+ }
196
+ };
197
+ });
198
+
199
+ // 批量写入(ordered=false 允许部分失败)
200
+ const result = await this.collection.bulkWrite(operations, { ordered: false });
201
+
202
+ if (this.logger.debug) {
203
+ this.logger.debug(
204
+ `[SlowQueryLog] Batch saved: ${result.upsertedCount} inserted, ` +
205
+ `${result.modifiedCount} updated`
206
+ );
207
+ }
208
+ } catch (err) {
209
+ if (this.logger.error) {
210
+ this.logger.error('[SlowQueryLog] Failed to save batch:', err);
211
+ }
212
+ throw err;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * 单条upsert操作
218
+ * @param {string} queryHash - 查询Hash
219
+ * @param {Object} log - 慢查询日志对象
220
+ * @returns {Promise<void>}
221
+ */
222
+ async upsert(queryHash, log) {
223
+ const timestamp = log.timestamp || new Date();
224
+ const executionTimeMs = log.executionTimeMs || log.ms || 0;
225
+
226
+ await this.collection.updateOne(
227
+ { queryHash },
228
+ {
229
+ $set: {
230
+ lastSeen: timestamp,
231
+ lastExecution: {
232
+ executionTimeMs,
233
+ timestamp,
234
+ metadata: log.metadata || {}
235
+ }
236
+ },
237
+ $inc: {
238
+ count: 1,
239
+ totalTimeMs: executionTimeMs
240
+ },
241
+ $min: { minTimeMs: executionTimeMs },
242
+ $max: { maxTimeMs: executionTimeMs },
243
+ $setOnInsert: {
244
+ queryHash,
245
+ db: log.db,
246
+ collection: log.collection || log.coll,
247
+ operation: log.operation || log.op,
248
+ queryShape: log.queryShape || {},
249
+ type: log.type || 'mongodb',
250
+ firstSeen: timestamp
251
+ }
252
+ },
253
+ { upsert: true }
254
+ );
255
+ }
256
+
257
+ /**
258
+ * 查询慢查询日志
259
+ * @param {Object} filter - 查询条件
260
+ * @param {Object} options - 查询选项
261
+ * @returns {Promise<Object[]>}
262
+ */
263
+ async query(filter = {}, options = {}) {
264
+ if (!this.initialized) {
265
+ await this.initialize();
266
+ }
267
+
268
+ try {
269
+ const cursor = this.collection.find(filter);
270
+
271
+ if (options.sort) {
272
+ cursor.sort(options.sort);
273
+ }
274
+ if (options.limit) {
275
+ cursor.limit(options.limit);
276
+ }
277
+ if (options.skip) {
278
+ cursor.skip(options.skip);
279
+ }
280
+
281
+ const results = await cursor.toArray();
282
+
283
+ // 计算avgTimeMs(动态计算)
284
+ return results.map(doc => ({
285
+ ...doc,
286
+ avgTimeMs: doc.count > 0 ? Math.round(doc.totalTimeMs / doc.count) : 0
287
+ }));
288
+ } catch (err) {
289
+ if (this.logger.error) {
290
+ this.logger.error('[SlowQueryLog] Failed to query:', err);
291
+ }
292
+ return [];
293
+ }
294
+ }
295
+
296
+ /**
297
+ * 关闭连接
298
+ * @returns {Promise<void>}
299
+ */
300
+ async close() {
301
+ // 只关闭自己创建的连接,不关闭复用的连接
302
+ if (this.ownClient) {
303
+ try {
304
+ await this.ownClient.close();
305
+ if (this.logger.info) {
306
+ this.logger.info('[SlowQueryLog] MongoDB connection closed');
307
+ }
308
+ } catch (err) {
309
+ if (this.logger.error) {
310
+ this.logger.error('[SlowQueryLog] Failed to close MongoDB connection:', err);
311
+ }
312
+ }
313
+ this.ownClient = null;
314
+ }
315
+
316
+ this.client = null;
317
+ this.collection = null;
318
+ this.initialized = false;
319
+ }
320
+ }
321
+
322
+ module.exports = { MongoDBSlowQueryLogStorage };
323
+
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 慢查询Hash生成工具
3
+ * 用于生成queryHash(去重标识)
4
+ *
5
+ * @version 1.3.0
6
+ * @since 2025-12-22
7
+ */
8
+
9
+ const crypto = require('crypto');
10
+
11
+ /**
12
+ * 生成慢查询的唯一Hash标识
13
+ * @param {Object} log - 慢查询日志对象
14
+ * @param {string} log.db - 数据库名
15
+ * @param {string} log.collection - 集合名
16
+ * @param {string} log.operation - 操作类型
17
+ * @param {Object} log.queryShape - 查询模式(已脱敏)
18
+ * @returns {string} 16位Hash字符串
19
+ */
20
+ function generateQueryHash(log) {
21
+ // 构建唯一键(不包含executionTimeMs、timestamp等动态字段)
22
+ const key = JSON.stringify({
23
+ db: log.db || '',
24
+ collection: log.collection || log.coll || '',
25
+ operation: log.operation || log.op || '',
26
+ queryShape: log.queryShape || {}
27
+ });
28
+
29
+ // 生成SHA256 Hash,取前16位
30
+ return crypto
31
+ .createHash('sha256')
32
+ .update(key)
33
+ .digest('hex')
34
+ .substring(0, 16);
35
+ }
36
+
37
+ module.exports = { generateQueryHash };
38
+
@@ -2,17 +2,26 @@
2
2
  * 分布式缓存锁管理器
3
3
  * 基于 Redis 实现跨实例的事务缓存锁,确保事务隔离性
4
4
  *
5
- * @example
6
- * const lockManager = new DistributedCacheLockManager({
7
- * redis: redisInstance,
8
- * lockKeyPrefix: 'monsqlize:cache:lock:'
9
- * });
5
+ * v1.4.0 新增:业务级分布式锁支持
6
+ * - withLock(): 自动管理锁生命周期
7
+ * - acquireLock(): 手动获取锁(阻塞重试)
8
+ * - tryAcquireLock(): 尝试获取锁(不阻塞)
10
9
  *
10
+ * @example
11
+ * // 事务缓存锁(原有功能)
11
12
  * await lockManager.addLock('user:1', session);
12
13
  * const isLocked = await lockManager.isLocked('user:1');
13
14
  * await lockManager.releaseLocks(session);
15
+ *
16
+ * // 业务锁(v1.4.0 新增)
17
+ * await lockManager.withLock('inventory:SKU123', async () => {
18
+ * // 临界区代码
19
+ * });
14
20
  */
15
21
 
22
+ const Lock = require('../lock/Lock');
23
+ const { LockAcquireError } = require('../lock/errors');
24
+
16
25
  class DistributedCacheLockManager {
17
26
  /**
18
27
  * @param {Object} options
@@ -233,6 +242,232 @@ class DistributedCacheLockManager {
233
242
  this.logger.info('[DistributedCacheLockManager] Stopped');
234
243
  }
235
244
  }
245
+
246
+ // ==================== 业务锁 API (v1.4.0) ====================
247
+
248
+ /**
249
+ * 业务锁:自动管理锁生命周期(推荐)
250
+ * @param {string} key - 锁的唯一标识
251
+ * @param {Function} callback - 获取锁后执行的函数
252
+ * @param {Object} [options] - 锁选项
253
+ * @param {number} [options.ttl=10000] - 锁过期时间(毫秒)
254
+ * @param {number} [options.retryTimes=3] - 重试次数
255
+ * @param {number} [options.retryDelay=100] - 重试间隔(毫秒)
256
+ * @param {boolean} [options.fallbackToNoLock=false] - Redis不可用时降级为无锁执行
257
+ * @returns {Promise<*>} callback 的返回值
258
+ */
259
+ async withLock(key, callback, options = {}) {
260
+ try {
261
+ const lock = await this.acquireLock(key, options);
262
+ try {
263
+ return await callback();
264
+ } finally {
265
+ // 释放失败不应阻塞业务(锁会在 TTL 后自动过期)
266
+ await lock.release().catch(err => {
267
+ if (this.logger) {
268
+ this.logger.warn(`[Lock] Release failed: ${key}`, err);
269
+ }
270
+ });
271
+ }
272
+ } catch (error) {
273
+ // Redis 连接问题检测
274
+ if (this._isRedisConnectionError(error)) {
275
+ if (options.fallbackToNoLock) {
276
+ if (this.logger) {
277
+ this.logger.warn(`[Lock] Redis unavailable, proceeding without lock: ${key}`);
278
+ }
279
+ return callback();
280
+ }
281
+ throw new LockAcquireError(`Redis unavailable: ${error.message}`);
282
+ }
283
+ throw error;
284
+ }
285
+ }
286
+
287
+ /**
288
+ * 业务锁:手动获取锁(阻塞重试)
289
+ * @param {string} key - 锁的唯一标识
290
+ * @param {Object} [options] - 锁选项
291
+ * @returns {Promise<Lock>}
292
+ */
293
+ async acquireLock(key, options = {}) {
294
+ const ttl = options.ttl || 10000; // 默认 10 秒
295
+ const retryTimes = options.retryTimes ?? 3;
296
+ const retryDelay = options.retryDelay || 100;
297
+ const lockId = this._generateLockId();
298
+ const fullKey = this.lockKeyPrefix + key;
299
+
300
+ for (let attempt = 0; attempt <= retryTimes; attempt++) {
301
+ try {
302
+ // 使用 SET NX PX 原子操作(毫秒级 TTL)
303
+ const result = await this.redis.set(
304
+ fullKey,
305
+ lockId,
306
+ 'PX', ttl, // PX = 毫秒
307
+ 'NX'
308
+ );
309
+
310
+ if (result === 'OK') {
311
+ this.stats.locksAcquired++;
312
+ if (this.logger) {
313
+ this.logger.debug(`[Lock] Acquired: ${key}`);
314
+ }
315
+ return new Lock(key, lockId, this, ttl);
316
+ }
317
+
318
+ // 最后一次尝试失败
319
+ if (attempt === retryTimes) {
320
+ break;
321
+ }
322
+
323
+ // 等待后重试
324
+ await this._sleep(retryDelay);
325
+ } catch (error) {
326
+ // 如果是最后一次尝试,抛出错误
327
+ if (attempt === retryTimes) {
328
+ throw error;
329
+ }
330
+ // 否则等待后重试
331
+ await this._sleep(retryDelay);
332
+ }
333
+ }
334
+
335
+ this.stats.errors++;
336
+ throw new LockAcquireError(`Failed to acquire lock: ${key}`);
337
+ }
338
+
339
+ /**
340
+ * 业务锁:尝试获取锁(不阻塞)
341
+ * @param {string} key - 锁的唯一标识
342
+ * @param {Object} [options] - 锁选项(不包含 retryTimes)
343
+ * @returns {Promise<Lock|null>} Lock对象或null
344
+ */
345
+ async tryAcquireLock(key, options = {}) {
346
+ const ttl = options.ttl || 10000;
347
+ const lockId = this._generateLockId();
348
+ const fullKey = this.lockKeyPrefix + key;
349
+
350
+ try {
351
+ const result = await this.redis.set(
352
+ fullKey,
353
+ lockId,
354
+ 'PX', ttl,
355
+ 'NX'
356
+ );
357
+
358
+ if (result === 'OK') {
359
+ this.stats.locksAcquired++;
360
+ if (this.logger) {
361
+ this.logger.debug(`[Lock] Acquired (try): ${key}`);
362
+ }
363
+ return new Lock(key, lockId, this, ttl);
364
+ }
365
+
366
+ return null;
367
+ } catch (error) {
368
+ if (this.logger) {
369
+ this.logger.error(`[Lock] Try acquire error: ${key}`, error.message);
370
+ }
371
+ return null;
372
+ }
373
+ }
374
+
375
+ /**
376
+ * 释放单个锁(由 Lock 对象调用)
377
+ * @param {string} key - 锁标识
378
+ * @param {string} lockId - 锁ID
379
+ * @returns {Promise<boolean>}
380
+ */
381
+ async releaseLock(key, lockId) {
382
+ const fullKey = this.lockKeyPrefix + key;
383
+
384
+ try {
385
+ // 使用 Lua 脚本确保原子性(只释放自己的锁)
386
+ const luaScript = `
387
+ if redis.call("get", KEYS[1]) == ARGV[1] then
388
+ return redis.call("del", KEYS[1])
389
+ else
390
+ return 0
391
+ end
392
+ `;
393
+
394
+ const result = await this.redis.eval(luaScript, 1, fullKey, lockId);
395
+
396
+ if (result === 1) {
397
+ this.stats.locksReleased++;
398
+ if (this.logger) {
399
+ this.logger.debug(`[Lock] Released: ${key}`);
400
+ }
401
+ return true;
402
+ }
403
+
404
+ return false;
405
+ } catch (error) {
406
+ this.stats.errors++;
407
+ if (this.logger) {
408
+ this.logger.error(`[Lock] Release error: ${key}`, error.message);
409
+ }
410
+ return false;
411
+ }
412
+ }
413
+
414
+ /**
415
+ * 续期(延长锁的过期时间)
416
+ * @param {string} key - 锁标识
417
+ * @param {string} lockId - 锁ID
418
+ * @param {number} ttl - 新的过期时间(毫秒)
419
+ * @returns {Promise<boolean>}
420
+ */
421
+ async renewLock(key, lockId, ttl) {
422
+ const fullKey = this.lockKeyPrefix + key;
423
+
424
+ try {
425
+ // 使用 Lua 脚本确保只续期自己的锁
426
+ const luaScript = `
427
+ if redis.call("get", KEYS[1]) == ARGV[1] then
428
+ return redis.call("pexpire", KEYS[1], ARGV[2])
429
+ else
430
+ return 0
431
+ end
432
+ `;
433
+
434
+ const result = await this.redis.eval(luaScript, 1, fullKey, lockId, ttl);
435
+ return result === 1;
436
+ } catch (error) {
437
+ if (this.logger) {
438
+ this.logger.error(`[Lock] Renew error: ${key}`, error.message);
439
+ }
440
+ return false;
441
+ }
442
+ }
443
+
444
+ /**
445
+ * 生成唯一锁ID
446
+ * @private
447
+ */
448
+ _generateLockId() {
449
+ return `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
450
+ }
451
+
452
+ /**
453
+ * 延迟
454
+ * @private
455
+ */
456
+ _sleep(ms) {
457
+ return new Promise(resolve => setTimeout(resolve, ms));
458
+ }
459
+
460
+ /**
461
+ * 检测是否是 Redis 连接错误
462
+ * @private
463
+ */
464
+ _isRedisConnectionError(error) {
465
+ const msg = error.message || '';
466
+ return msg.includes('ECONNREFUSED') ||
467
+ msg.includes('ETIMEDOUT') ||
468
+ msg.includes('ENOTFOUND') ||
469
+ msg.includes('Connection is closed');
470
+ }
236
471
  }
237
472
 
238
473
  module.exports = DistributedCacheLockManager;
@@ -216,7 +216,7 @@ class Transaction {
216
216
  if (this.lockManager && this.session) {
217
217
  this.lockManager.releaseLocks(this.session);
218
218
  this.lockedKeys.clear();
219
- this.logger?.debug(`[Transaction] Released all cache locks`);
219
+ this.logger?.debug('[Transaction] Released all cache locks');
220
220
  }
221
221
  }
222
222