monsqlize 1.3.1 → 2.0.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.
- package/CHANGELOG.md +415 -146
- package/LICENSE +201 -21
- package/README.md +537 -928
- package/changelogs/README.md +160 -0
- package/changelogs/v2.0.0.md +222 -0
- package/dist/cjs/index.cjs +10600 -0
- package/dist/cjs/mongodb/common/transaction-aware.cjs +10 -0
- package/dist/cjs/transaction/CacheLockManager.cjs +100 -0
- package/dist/cjs/transaction/Transaction.cjs +158 -0
- package/dist/cjs/transaction/TransactionManager.cjs +298 -0
- package/dist/esm/index.mjs +10650 -0
- package/dist/types/base.d.ts +81 -0
- package/dist/types/collection.d.ts +1031 -0
- package/dist/types/expression.d.ts +115 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/lock.d.ts +74 -0
- package/dist/types/model.d.ts +526 -0
- package/dist/types/mongodb.d.ts +49 -0
- package/dist/types/monsqlize.d.ts +491 -0
- package/dist/types/pool.d.ts +84 -0
- package/dist/types/runtime.d.ts +362 -0
- package/dist/types/saga.d.ts +143 -0
- package/dist/types/slow-query-log.d.ts +126 -0
- package/dist/types/sync.d.ts +103 -0
- package/dist/types/transaction.d.ts +132 -0
- package/package.json +69 -67
- package/index.d.ts +0 -1289
- package/lib/cache.js +0 -491
- package/lib/common/cursor.js +0 -58
- package/lib/common/docs-urls.js +0 -72
- package/lib/common/index-options.js +0 -222
- package/lib/common/log.js +0 -60
- package/lib/common/namespace.js +0 -21
- package/lib/common/normalize.js +0 -33
- package/lib/common/page-result.js +0 -42
- package/lib/common/runner.js +0 -56
- package/lib/common/server-features.js +0 -231
- package/lib/common/shape-builders.js +0 -26
- package/lib/common/validation.js +0 -112
- package/lib/connect.js +0 -76
- package/lib/constants.js +0 -54
- package/lib/count-queue.js +0 -187
- package/lib/distributed-cache-invalidator.js +0 -259
- package/lib/errors.js +0 -167
- package/lib/index.js +0 -461
- package/lib/infrastructure/ssh-tunnel-ssh2.js +0 -211
- package/lib/infrastructure/ssh-tunnel.js +0 -40
- package/lib/infrastructure/uri-parser.js +0 -35
- package/lib/lock/Lock.js +0 -66
- package/lib/lock/errors.js +0 -27
- package/lib/lock/index.js +0 -12
- package/lib/logger.js +0 -224
- package/lib/model/examples/test.js +0 -114
- package/lib/mongodb/common/accessor-helpers.js +0 -58
- package/lib/mongodb/common/agg-pipeline.js +0 -32
- package/lib/mongodb/common/iid.js +0 -27
- package/lib/mongodb/common/lexicographic-expr.js +0 -52
- package/lib/mongodb/common/shape.js +0 -31
- package/lib/mongodb/common/sort.js +0 -38
- package/lib/mongodb/common/transaction-aware.js +0 -24
- package/lib/mongodb/connect.js +0 -233
- package/lib/mongodb/index.js +0 -591
- package/lib/mongodb/management/admin-ops.js +0 -199
- package/lib/mongodb/management/bookmark-ops.js +0 -166
- package/lib/mongodb/management/cache-ops.js +0 -49
- package/lib/mongodb/management/collection-ops.js +0 -386
- package/lib/mongodb/management/database-ops.js +0 -201
- package/lib/mongodb/management/index-ops.js +0 -474
- package/lib/mongodb/management/index.js +0 -16
- package/lib/mongodb/management/namespace.js +0 -30
- package/lib/mongodb/management/validation-ops.js +0 -267
- package/lib/mongodb/queries/aggregate.js +0 -142
- package/lib/mongodb/queries/chain.js +0 -630
- package/lib/mongodb/queries/count.js +0 -98
- package/lib/mongodb/queries/distinct.js +0 -77
- package/lib/mongodb/queries/find-and-count.js +0 -192
- package/lib/mongodb/queries/find-by-ids.js +0 -235
- package/lib/mongodb/queries/find-one-by-id.js +0 -170
- package/lib/mongodb/queries/find-one.js +0 -70
- package/lib/mongodb/queries/find-page.js +0 -577
- package/lib/mongodb/queries/find.js +0 -170
- package/lib/mongodb/queries/index.js +0 -50
- package/lib/mongodb/queries/watch.js +0 -537
- package/lib/mongodb/writes/delete-many.js +0 -190
- package/lib/mongodb/writes/delete-one.js +0 -182
- package/lib/mongodb/writes/find-one-and-delete.js +0 -202
- package/lib/mongodb/writes/find-one-and-replace.js +0 -238
- package/lib/mongodb/writes/find-one-and-update.js +0 -239
- package/lib/mongodb/writes/increment-one.js +0 -252
- package/lib/mongodb/writes/index.js +0 -41
- package/lib/mongodb/writes/insert-batch.js +0 -507
- package/lib/mongodb/writes/insert-many.js +0 -227
- package/lib/mongodb/writes/insert-one.js +0 -180
- package/lib/mongodb/writes/replace-one.js +0 -215
- package/lib/mongodb/writes/result-handler.js +0 -236
- package/lib/mongodb/writes/update-many.js +0 -221
- package/lib/mongodb/writes/update-one.js +0 -223
- package/lib/mongodb/writes/upsert-one.js +0 -206
- package/lib/multi-level-cache.js +0 -189
- package/lib/operators.js +0 -330
- package/lib/redis-cache-adapter.js +0 -237
- package/lib/slow-query-log/base-storage.js +0 -69
- package/lib/slow-query-log/batch-queue.js +0 -96
- package/lib/slow-query-log/config-manager.js +0 -195
- package/lib/slow-query-log/index.js +0 -237
- package/lib/slow-query-log/mongodb-storage.js +0 -323
- package/lib/slow-query-log/query-hash.js +0 -38
- package/lib/transaction/CacheLockManager.js +0 -161
- package/lib/transaction/DistributedCacheLockManager.js +0 -474
- package/lib/transaction/Transaction.js +0 -314
- package/lib/transaction/TransactionManager.js +0 -266
- package/lib/transaction/index.js +0 -10
- package/lib/utils/objectid-converter.js +0 -566
|
@@ -1,323 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,38 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 缓存锁管理器
|
|
3
|
-
* 用于在事务执行期间锁定缓存键,防止脏数据写入缓存
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
class CacheLockManager {
|
|
7
|
-
constructor(options = {}) {
|
|
8
|
-
this.locks = new Map(); // key -> { sessionId, expiresAt }
|
|
9
|
-
this.maxDuration = options.maxDuration || 300000; // 5分钟默认
|
|
10
|
-
this.lockCleanupInterval = options.lockCleanupInterval || 10000; // 10秒清理一次
|
|
11
|
-
this.cleanupTimer = null;
|
|
12
|
-
|
|
13
|
-
// 启动自动清理
|
|
14
|
-
this._startCleanup();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* 添加缓存锁
|
|
19
|
-
* @param {string} key - 缓存键(支持通配符 *)
|
|
20
|
-
* @param {Object} session - MongoDB session 对象
|
|
21
|
-
*/
|
|
22
|
-
addLock(key, session) {
|
|
23
|
-
if (!session || !session.id) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const sessionId = session.id.toString();
|
|
28
|
-
const expiresAt = Date.now() + this.maxDuration;
|
|
29
|
-
|
|
30
|
-
this.locks.set(key, {
|
|
31
|
-
sessionId,
|
|
32
|
-
expiresAt,
|
|
33
|
-
lockedAt: Date.now()
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 检查缓存键是否被锁定
|
|
39
|
-
* @param {string} key - 缓存键
|
|
40
|
-
* @returns {boolean}
|
|
41
|
-
*/
|
|
42
|
-
isLocked(key) {
|
|
43
|
-
// 精确匹配
|
|
44
|
-
if (this.locks.has(key)) {
|
|
45
|
-
const lock = this.locks.get(key);
|
|
46
|
-
if (Date.now() < lock.expiresAt) {
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
// 过期自动删除
|
|
50
|
-
this.locks.delete(key);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// 通配符匹配
|
|
54
|
-
for (const [lockKey, lock] of this.locks.entries()) {
|
|
55
|
-
if (lockKey.includes('*')) {
|
|
56
|
-
const pattern = lockKey.replace(/\*/g, '.*');
|
|
57
|
-
const regex = new RegExp(`^${pattern}$`);
|
|
58
|
-
if (regex.test(key)) {
|
|
59
|
-
if (Date.now() < lock.expiresAt) {
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
// 过期自动删除
|
|
63
|
-
this.locks.delete(lockKey);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* 释放指定 session 的所有锁
|
|
73
|
-
* @param {Object} session - MongoDB session 对象
|
|
74
|
-
*/
|
|
75
|
-
releaseLocks(session) {
|
|
76
|
-
if (!session || !session.id) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const sessionId = session.id.toString();
|
|
81
|
-
const keysToDelete = [];
|
|
82
|
-
|
|
83
|
-
for (const [key, lock] of this.locks.entries()) {
|
|
84
|
-
if (lock.sessionId === sessionId) {
|
|
85
|
-
keysToDelete.push(key);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
keysToDelete.forEach(key => this.locks.delete(key));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* 清理过期的锁
|
|
94
|
-
* @private
|
|
95
|
-
*/
|
|
96
|
-
_cleanupExpiredLocks() {
|
|
97
|
-
const now = Date.now();
|
|
98
|
-
const keysToDelete = [];
|
|
99
|
-
|
|
100
|
-
for (const [key, lock] of this.locks.entries()) {
|
|
101
|
-
if (now >= lock.expiresAt) {
|
|
102
|
-
keysToDelete.push(key);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (keysToDelete.length > 0) {
|
|
107
|
-
keysToDelete.forEach(key => this.locks.delete(key));
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* 启动自动清理定时器
|
|
113
|
-
* @private
|
|
114
|
-
*/
|
|
115
|
-
_startCleanup() {
|
|
116
|
-
if (this.cleanupTimer) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
this.cleanupTimer = setInterval(() => {
|
|
121
|
-
this._cleanupExpiredLocks();
|
|
122
|
-
}, this.lockCleanupInterval);
|
|
123
|
-
|
|
124
|
-
// 防止定时器阻止进程退出
|
|
125
|
-
if (this.cleanupTimer.unref) {
|
|
126
|
-
this.cleanupTimer.unref();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* 停止自动清理
|
|
132
|
-
*/
|
|
133
|
-
stop() {
|
|
134
|
-
if (this.cleanupTimer) {
|
|
135
|
-
clearInterval(this.cleanupTimer);
|
|
136
|
-
this.cleanupTimer = null;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 获取当前锁的统计信息
|
|
142
|
-
*/
|
|
143
|
-
getStats() {
|
|
144
|
-
return {
|
|
145
|
-
totalLocks: this.locks.size,
|
|
146
|
-
activeLocks: Array.from(this.locks.values()).filter(
|
|
147
|
-
lock => Date.now() < lock.expiresAt
|
|
148
|
-
).length
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* 清除所有锁(用于测试)
|
|
154
|
-
*/
|
|
155
|
-
clear() {
|
|
156
|
-
this.locks.clear();
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
module.exports = CacheLockManager;
|
|
161
|
-
|