@abtnode/db-cache 1.17.8-beta-20260109-075740-5f484e08 → 1.17.8-beta-20260113-015027-32a1cec4
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/dist/index.cjs +478 -25
- package/dist/index.d.cts +132 -1
- package/dist/index.d.mts +132 -1
- package/dist/index.d.ts +132 -1
- package/dist/index.mjs +478 -26
- package/package.json +5 -3
package/dist/index.cjs
CHANGED
|
@@ -42,20 +42,20 @@ function ulid() {
|
|
|
42
42
|
return timeStr + rand;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
var __defProp$
|
|
46
|
-
var __defNormalProp$
|
|
47
|
-
var __publicField$
|
|
48
|
-
__defNormalProp$
|
|
45
|
+
var __defProp$5 = Object.defineProperty;
|
|
46
|
+
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
47
|
+
var __publicField$5 = (obj, key, value) => {
|
|
48
|
+
__defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
49
49
|
return value;
|
|
50
50
|
};
|
|
51
51
|
const _RedisAdapter = class _RedisAdapter {
|
|
52
52
|
constructor() {
|
|
53
|
-
__publicField$
|
|
54
|
-
__publicField$
|
|
55
|
-
__publicField$
|
|
56
|
-
__publicField$
|
|
57
|
-
__publicField$
|
|
58
|
-
__publicField$
|
|
53
|
+
__publicField$5(this, "opts", null);
|
|
54
|
+
__publicField$5(this, "defaultTtl", 1e3 * 60 * 60);
|
|
55
|
+
__publicField$5(this, "url", "");
|
|
56
|
+
__publicField$5(this, "prefix", "");
|
|
57
|
+
__publicField$5(this, "prefixKey", (key) => `${this.prefix}:${key}`);
|
|
58
|
+
__publicField$5(this, "prefixKeyGroup", (key) => `${this.prefix}:group:${key}`);
|
|
59
59
|
}
|
|
60
60
|
clearAll() {
|
|
61
61
|
throw new Error("Method not implemented.");
|
|
@@ -198,10 +198,16 @@ const _RedisAdapter = class _RedisAdapter {
|
|
|
198
198
|
const client = this.getClient();
|
|
199
199
|
await client.flushAll();
|
|
200
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* 获取 LRU 缓存统计信息(Redis 不使用 LRU 缓存)
|
|
203
|
+
*/
|
|
204
|
+
getLruCacheStats() {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
201
207
|
};
|
|
202
208
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
203
|
-
__publicField$
|
|
204
|
-
__publicField$
|
|
209
|
+
__publicField$5(_RedisAdapter, "clients", /* @__PURE__ */ new Map());
|
|
210
|
+
__publicField$5(_RedisAdapter, "initPromises", /* @__PURE__ */ new Map());
|
|
205
211
|
let RedisAdapter = _RedisAdapter;
|
|
206
212
|
|
|
207
213
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -230,6 +236,363 @@ async function withRetry(fn, {
|
|
|
230
236
|
}
|
|
231
237
|
}
|
|
232
238
|
|
|
239
|
+
var __defProp$4 = Object.defineProperty;
|
|
240
|
+
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
241
|
+
var __publicField$4 = (obj, key, value) => {
|
|
242
|
+
__defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
243
|
+
return value;
|
|
244
|
+
};
|
|
245
|
+
const lruCacheModule = require("lru-cache");
|
|
246
|
+
const LRUCacheLib = lruCacheModule?.LRUCache || lruCacheModule?.default || lruCacheModule;
|
|
247
|
+
const isTestEnv = process.env.NODE_ENV === "test";
|
|
248
|
+
const eventHub = isTestEnv ? require("@arcblock/event-hub/single") : require("@arcblock/event-hub");
|
|
249
|
+
const LRU_CACHE_SYNC_EVENT = "db-cache:lru:sync";
|
|
250
|
+
const LRU_CACHE_DELETE_EVENT = "db-cache:lru:delete";
|
|
251
|
+
const LRU_CACHE_CLEAR_EVENT = "db-cache:lru:clear";
|
|
252
|
+
const shouldEnableSync = () => {
|
|
253
|
+
if (isTestEnv) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
return process.env.NODE_APP_INSTANCE !== void 0;
|
|
257
|
+
};
|
|
258
|
+
const _LruCache = class _LruCache {
|
|
259
|
+
constructor(options) {
|
|
260
|
+
__publicField$4(this, "cacheKey");
|
|
261
|
+
__publicField$4(this, "maxSize");
|
|
262
|
+
__publicField$4(this, "enableSync");
|
|
263
|
+
this.cacheKey = `lru:${options.prefix}`;
|
|
264
|
+
this.maxSize = options.maxSize ?? 1e4;
|
|
265
|
+
this.enableSync = (options.enableSync ?? true) && shouldEnableSync();
|
|
266
|
+
if (!_LruCache.caches.has(this.cacheKey)) {
|
|
267
|
+
_LruCache.caches.set(
|
|
268
|
+
this.cacheKey,
|
|
269
|
+
new LRUCacheLib({
|
|
270
|
+
max: this.maxSize,
|
|
271
|
+
ttl: 0
|
|
272
|
+
// 我们自己管理 TTL
|
|
273
|
+
})
|
|
274
|
+
);
|
|
275
|
+
_LruCache.groupCaches.set(this.cacheKey, /* @__PURE__ */ new Map());
|
|
276
|
+
if (this.enableSync && !_LruCache.listenerSetup.has(this.cacheKey)) {
|
|
277
|
+
this._setupSyncListener();
|
|
278
|
+
_LruCache.listenerSetup.set(this.cacheKey, true);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
getCache() {
|
|
283
|
+
const cache = _LruCache.caches.get(this.cacheKey);
|
|
284
|
+
if (!cache) {
|
|
285
|
+
throw new Error("LRU cache not initialized");
|
|
286
|
+
}
|
|
287
|
+
return cache;
|
|
288
|
+
}
|
|
289
|
+
getGroupCaches() {
|
|
290
|
+
const groupCaches = _LruCache.groupCaches.get(this.cacheKey);
|
|
291
|
+
if (!groupCaches) {
|
|
292
|
+
throw new Error("LRU group cache not initialized");
|
|
293
|
+
}
|
|
294
|
+
return groupCaches;
|
|
295
|
+
}
|
|
296
|
+
getOrCreateGroupCache(key) {
|
|
297
|
+
const groupCaches = this.getGroupCaches();
|
|
298
|
+
if (!groupCaches.has(key)) {
|
|
299
|
+
groupCaches.set(
|
|
300
|
+
key,
|
|
301
|
+
new LRUCacheLib({
|
|
302
|
+
max: this.maxSize,
|
|
303
|
+
ttl: 0
|
|
304
|
+
})
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return groupCaches.get(key);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* 设置同步监听器,接收其他 worker 广播的缓存数据
|
|
311
|
+
*/
|
|
312
|
+
_setupSyncListener() {
|
|
313
|
+
const { cacheKey, maxSize } = this;
|
|
314
|
+
eventHub.on(LRU_CACHE_SYNC_EVENT, (payload) => {
|
|
315
|
+
const { channel, key, value, expiresAt, isGroup, subKey } = payload;
|
|
316
|
+
if (channel !== cacheKey)
|
|
317
|
+
return;
|
|
318
|
+
const entry = { value, expiresAt };
|
|
319
|
+
if (isGroup && subKey !== void 0) {
|
|
320
|
+
const groupCaches = _LruCache.groupCaches.get(cacheKey);
|
|
321
|
+
if (groupCaches) {
|
|
322
|
+
const groupCache = groupCaches.get(key) || new LRUCacheLib({ max: maxSize, ttl: 0 });
|
|
323
|
+
groupCache.set(subKey, entry);
|
|
324
|
+
groupCaches.set(key, groupCache);
|
|
325
|
+
}
|
|
326
|
+
} else {
|
|
327
|
+
const cache = _LruCache.caches.get(cacheKey);
|
|
328
|
+
if (cache) {
|
|
329
|
+
cache.set(key, entry);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
eventHub.on(LRU_CACHE_DELETE_EVENT, (payload) => {
|
|
334
|
+
const { channel, keys, isGroup, groupKey } = payload;
|
|
335
|
+
if (channel !== cacheKey)
|
|
336
|
+
return;
|
|
337
|
+
if (isGroup && groupKey !== void 0) {
|
|
338
|
+
const groupCaches = _LruCache.groupCaches.get(cacheKey);
|
|
339
|
+
if (groupCaches) {
|
|
340
|
+
const groupCache = groupCaches.get(groupKey);
|
|
341
|
+
if (groupCache) {
|
|
342
|
+
for (const key of keys) {
|
|
343
|
+
groupCache.delete(key);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} else {
|
|
348
|
+
const cache = _LruCache.caches.get(cacheKey);
|
|
349
|
+
if (cache) {
|
|
350
|
+
for (const key of keys) {
|
|
351
|
+
cache.delete(key);
|
|
352
|
+
const groupCaches = _LruCache.groupCaches.get(cacheKey);
|
|
353
|
+
if (groupCaches) {
|
|
354
|
+
groupCaches.delete(key);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
eventHub.on(LRU_CACHE_CLEAR_EVENT, (payload) => {
|
|
361
|
+
const { channel } = payload;
|
|
362
|
+
if (channel !== cacheKey)
|
|
363
|
+
return;
|
|
364
|
+
const cache = _LruCache.caches.get(cacheKey);
|
|
365
|
+
if (cache) {
|
|
366
|
+
cache.clear();
|
|
367
|
+
}
|
|
368
|
+
const groupCaches = _LruCache.groupCaches.get(cacheKey);
|
|
369
|
+
if (groupCaches) {
|
|
370
|
+
groupCaches.clear();
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* 广播缓存数据给其他 worker
|
|
376
|
+
*/
|
|
377
|
+
_broadcastSync(key, value, expiresAt, isGroup = false, subKey) {
|
|
378
|
+
if (this.enableSync) {
|
|
379
|
+
eventHub.broadcast(LRU_CACHE_SYNC_EVENT, {
|
|
380
|
+
channel: this.cacheKey,
|
|
381
|
+
key,
|
|
382
|
+
value,
|
|
383
|
+
expiresAt,
|
|
384
|
+
isGroup,
|
|
385
|
+
subKey
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* 广播删除缓存给其他 worker
|
|
391
|
+
*/
|
|
392
|
+
_broadcastDelete(keys, isGroup = false, groupKey) {
|
|
393
|
+
if (this.enableSync && keys.length > 0) {
|
|
394
|
+
eventHub.broadcast(LRU_CACHE_DELETE_EVENT, {
|
|
395
|
+
channel: this.cacheKey,
|
|
396
|
+
keys,
|
|
397
|
+
isGroup,
|
|
398
|
+
groupKey
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* 广播清空缓存给其他 worker
|
|
404
|
+
*/
|
|
405
|
+
_broadcastClear() {
|
|
406
|
+
if (this.enableSync) {
|
|
407
|
+
eventHub.broadcast(LRU_CACHE_CLEAR_EVENT, {
|
|
408
|
+
channel: this.cacheKey
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
isExpired(entry) {
|
|
413
|
+
if (!entry)
|
|
414
|
+
return true;
|
|
415
|
+
if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
/* ---------------------- 基础 API ---------------------- */
|
|
421
|
+
/**
|
|
422
|
+
* 设置缓存
|
|
423
|
+
* @param key 缓存键
|
|
424
|
+
* @param value 序列化后的值
|
|
425
|
+
* @param expiresAt 过期时间戳,null 表示永不过期
|
|
426
|
+
*/
|
|
427
|
+
set(key, value, expiresAt) {
|
|
428
|
+
const cache = this.getCache();
|
|
429
|
+
const entry = { value, expiresAt };
|
|
430
|
+
cache.set(key, entry);
|
|
431
|
+
this._broadcastSync(key, value, expiresAt);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* 获取缓存
|
|
435
|
+
* @param key 缓存键
|
|
436
|
+
* @returns 序列化的值,未命中或过期返回 null
|
|
437
|
+
*/
|
|
438
|
+
get(key) {
|
|
439
|
+
const cache = this.getCache();
|
|
440
|
+
const entry = cache.get(key);
|
|
441
|
+
if (!entry)
|
|
442
|
+
return null;
|
|
443
|
+
if (this.isExpired(entry)) {
|
|
444
|
+
cache.delete(key);
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
return entry.value;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* 检查缓存是否存在且未过期
|
|
451
|
+
*/
|
|
452
|
+
has(key) {
|
|
453
|
+
const cache = this.getCache();
|
|
454
|
+
const entry = cache.get(key);
|
|
455
|
+
if (!entry)
|
|
456
|
+
return false;
|
|
457
|
+
if (this.isExpired(entry)) {
|
|
458
|
+
cache.delete(key);
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* 删除缓存
|
|
465
|
+
*/
|
|
466
|
+
del(key) {
|
|
467
|
+
const cache = this.getCache();
|
|
468
|
+
cache.delete(key);
|
|
469
|
+
const groupCaches = this.getGroupCaches();
|
|
470
|
+
groupCaches.delete(key);
|
|
471
|
+
this._broadcastDelete([key]);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* 按前缀删除缓存
|
|
475
|
+
* @returns 删除的数量
|
|
476
|
+
*/
|
|
477
|
+
delByPrefix(prefix) {
|
|
478
|
+
const cache = this.getCache();
|
|
479
|
+
const deletedKeys = [];
|
|
480
|
+
for (const key of cache.keys()) {
|
|
481
|
+
if (key.startsWith(prefix)) {
|
|
482
|
+
cache.delete(key);
|
|
483
|
+
deletedKeys.push(key);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const groupCaches = this.getGroupCaches();
|
|
487
|
+
for (const key of groupCaches.keys()) {
|
|
488
|
+
if (key.startsWith(prefix)) {
|
|
489
|
+
groupCaches.delete(key);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
this._broadcastDelete(deletedKeys);
|
|
493
|
+
return deletedKeys.length;
|
|
494
|
+
}
|
|
495
|
+
/* ---------------------- 分组 API ---------------------- */
|
|
496
|
+
/**
|
|
497
|
+
* 设置分组缓存
|
|
498
|
+
*/
|
|
499
|
+
groupSet(key, subKey, value, expiresAt) {
|
|
500
|
+
const groupCache = this.getOrCreateGroupCache(key);
|
|
501
|
+
const entry = { value, expiresAt };
|
|
502
|
+
groupCache.set(subKey, entry);
|
|
503
|
+
this._broadcastSync(key, value, expiresAt, true, subKey);
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* 获取分组缓存
|
|
507
|
+
*/
|
|
508
|
+
groupGet(key, subKey) {
|
|
509
|
+
const groupCaches = this.getGroupCaches();
|
|
510
|
+
const groupCache = groupCaches.get(key);
|
|
511
|
+
if (!groupCache)
|
|
512
|
+
return null;
|
|
513
|
+
const entry = groupCache.get(subKey);
|
|
514
|
+
if (!entry)
|
|
515
|
+
return null;
|
|
516
|
+
if (this.isExpired(entry)) {
|
|
517
|
+
groupCache.delete(subKey);
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
return entry.value;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* 检查分组缓存是否存在
|
|
524
|
+
*/
|
|
525
|
+
groupHas(key, subKey) {
|
|
526
|
+
const groupCaches = this.getGroupCaches();
|
|
527
|
+
const groupCache = groupCaches.get(key);
|
|
528
|
+
if (!groupCache)
|
|
529
|
+
return false;
|
|
530
|
+
const entry = groupCache.get(subKey);
|
|
531
|
+
if (!entry)
|
|
532
|
+
return false;
|
|
533
|
+
if (this.isExpired(entry)) {
|
|
534
|
+
groupCache.delete(subKey);
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* 删除分组缓存
|
|
541
|
+
*/
|
|
542
|
+
groupDel(key, subKey) {
|
|
543
|
+
const groupCaches = this.getGroupCaches();
|
|
544
|
+
const groupCache = groupCaches.get(key);
|
|
545
|
+
if (groupCache) {
|
|
546
|
+
groupCache.delete(subKey);
|
|
547
|
+
this._broadcastDelete([subKey], true, key);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
/* ---------------------- 管理 API ---------------------- */
|
|
551
|
+
/**
|
|
552
|
+
* 清空所有缓存
|
|
553
|
+
*/
|
|
554
|
+
clear() {
|
|
555
|
+
const cache = this.getCache();
|
|
556
|
+
cache.clear();
|
|
557
|
+
const groupCaches = this.getGroupCaches();
|
|
558
|
+
groupCaches.clear();
|
|
559
|
+
this._broadcastClear();
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* 获取缓存统计信息
|
|
563
|
+
*/
|
|
564
|
+
getStats() {
|
|
565
|
+
const cache = this.getCache();
|
|
566
|
+
const groupCaches = this.getGroupCaches();
|
|
567
|
+
let totalGroupSize = 0;
|
|
568
|
+
for (const groupCache of groupCaches.values()) {
|
|
569
|
+
totalGroupSize += groupCache.size;
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
size: cache.size,
|
|
573
|
+
// 普通缓存条目数
|
|
574
|
+
groupCount: totalGroupSize,
|
|
575
|
+
// 分组缓存条目数
|
|
576
|
+
maxSize: this.maxSize
|
|
577
|
+
// 最大容量
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* 关闭缓存(清理资源)
|
|
582
|
+
*
|
|
583
|
+
* 注意:不会广播 close 事件给其他 worker,因为每个 worker 有独立的生命周期
|
|
584
|
+
*/
|
|
585
|
+
close() {
|
|
586
|
+
_LruCache.caches.delete(this.cacheKey);
|
|
587
|
+
_LruCache.groupCaches.delete(this.cacheKey);
|
|
588
|
+
_LruCache.listenerSetup.delete(this.cacheKey);
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
__publicField$4(_LruCache, "caches", /* @__PURE__ */ new Map());
|
|
592
|
+
__publicField$4(_LruCache, "groupCaches", /* @__PURE__ */ new Map());
|
|
593
|
+
__publicField$4(_LruCache, "listenerSetup", /* @__PURE__ */ new Map());
|
|
594
|
+
let LruCache = _LruCache;
|
|
595
|
+
|
|
233
596
|
var __defProp$3 = Object.defineProperty;
|
|
234
597
|
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
235
598
|
var __publicField$3 = (obj, key, value) => {
|
|
@@ -295,6 +658,8 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
295
658
|
__publicField$3(this, "defaultTtl", 1e3 * 60 * 60);
|
|
296
659
|
__publicField$3(this, "cleanupInterval", 5 * 60 * 1e3);
|
|
297
660
|
__publicField$3(this, "dbPath", "");
|
|
661
|
+
__publicField$3(this, "enableLruCache", false);
|
|
662
|
+
__publicField$3(this, "lruCache", null);
|
|
298
663
|
}
|
|
299
664
|
/* ------------------------------- init ------------------------------- */
|
|
300
665
|
async ensure() {
|
|
@@ -304,6 +669,28 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
304
669
|
this.prefix = this.opts.prefix;
|
|
305
670
|
this.defaultTtl = this.opts.ttl;
|
|
306
671
|
this.cleanupInterval = this.opts.cleanupInterval ?? 5 * 60 * 1e3;
|
|
672
|
+
this.enableLruCache = this.opts.enableLruCache ?? false;
|
|
673
|
+
}
|
|
674
|
+
if (this.enableLruCache && !this.lruCache) {
|
|
675
|
+
const lruCacheKey = `sqlite:${this.dbPath}:${this.prefix}`;
|
|
676
|
+
if (!_SqliteAdapter.lruCaches.has(lruCacheKey)) {
|
|
677
|
+
const requestedSize = this.opts.lruMaxSize ?? 100;
|
|
678
|
+
const actualSize = Math.min(requestedSize, _SqliteAdapter.MAX_LRU_CACHE_SIZE);
|
|
679
|
+
if (requestedSize > _SqliteAdapter.MAX_LRU_CACHE_SIZE) {
|
|
680
|
+
console.warn(
|
|
681
|
+
`[SqliteAdapter] Requested LRU cache size ${requestedSize} exceeds maximum ${_SqliteAdapter.MAX_LRU_CACHE_SIZE}, using ${actualSize} instead`
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
_SqliteAdapter.lruCaches.set(
|
|
685
|
+
lruCacheKey,
|
|
686
|
+
new LruCache({
|
|
687
|
+
prefix: lruCacheKey,
|
|
688
|
+
maxSize: actualSize,
|
|
689
|
+
enableSync: this.opts.enableSync ?? true
|
|
690
|
+
})
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
this.lruCache = _SqliteAdapter.lruCaches.get(lruCacheKey);
|
|
307
694
|
}
|
|
308
695
|
if (_SqliteAdapter.clients.has(this.dbPath))
|
|
309
696
|
return;
|
|
@@ -366,7 +753,11 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
366
753
|
const storageKey = this.prefixKey(key);
|
|
367
754
|
const effectiveTtl = opts.ttl !== void 0 ? opts.ttl : this.defaultTtl;
|
|
368
755
|
const expiresAt = effectiveTtl > 0 ? Date.now() + effectiveTtl : null;
|
|
756
|
+
const serializedValue = this.serialize(value);
|
|
369
757
|
if (opts.nx) {
|
|
758
|
+
if (this.lruCache?.has(storageKey)) {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
370
761
|
const existing = await dbGet(
|
|
371
762
|
client,
|
|
372
763
|
"SELECT value FROM kvcache WHERE key = ? AND subKey = ?",
|
|
@@ -385,13 +776,22 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
385
776
|
value = excluded.value,
|
|
386
777
|
expiresAt = excluded.expiresAt
|
|
387
778
|
`,
|
|
388
|
-
[storageKey, "",
|
|
779
|
+
[storageKey, "", serializedValue, expiresAt]
|
|
389
780
|
);
|
|
781
|
+
if (this.lruCache) {
|
|
782
|
+
this.lruCache.set(storageKey, serializedValue, expiresAt);
|
|
783
|
+
}
|
|
390
784
|
return true;
|
|
391
785
|
}
|
|
392
786
|
async get(key) {
|
|
393
|
-
const client = this.getClient();
|
|
394
787
|
const storageKey = this.prefixKey(key);
|
|
788
|
+
if (this.lruCache) {
|
|
789
|
+
const cached = this.lruCache.get(storageKey);
|
|
790
|
+
if (cached !== null) {
|
|
791
|
+
return this.deserialize(cached);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
const client = this.getClient();
|
|
395
795
|
const row = await dbGet(
|
|
396
796
|
client,
|
|
397
797
|
'SELECT value, "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
@@ -403,16 +803,25 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
403
803
|
await this.del(key);
|
|
404
804
|
return null;
|
|
405
805
|
}
|
|
806
|
+
if (this.lruCache) {
|
|
807
|
+
this.lruCache.set(storageKey, row.value, row.expiresAt);
|
|
808
|
+
}
|
|
406
809
|
return this.deserialize(row.value);
|
|
407
810
|
}
|
|
408
811
|
async del(key) {
|
|
409
|
-
const client = this.getClient();
|
|
410
812
|
const storageKey = this.prefixKey(key);
|
|
813
|
+
if (this.lruCache) {
|
|
814
|
+
this.lruCache.del(storageKey);
|
|
815
|
+
}
|
|
816
|
+
const client = this.getClient();
|
|
411
817
|
await dbRun(client, "DELETE FROM kvcache WHERE key = ?", [storageKey]);
|
|
412
818
|
}
|
|
413
819
|
async has(key) {
|
|
414
|
-
const client = this.getClient();
|
|
415
820
|
const storageKey = this.prefixKey(key);
|
|
821
|
+
if (this.lruCache?.has(storageKey)) {
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
const client = this.getClient();
|
|
416
825
|
const row = await dbGet(
|
|
417
826
|
client,
|
|
418
827
|
'SELECT "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
@@ -432,11 +841,7 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
432
841
|
const storageKey = this.prefixKey(key);
|
|
433
842
|
const effectiveTtl = opts.ttl !== void 0 ? opts.ttl : this.defaultTtl;
|
|
434
843
|
const expiresAt = effectiveTtl > 0 ? Date.now() + effectiveTtl : null;
|
|
435
|
-
|
|
436
|
-
const exists = await this.groupHas(key, subKey);
|
|
437
|
-
if (exists)
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
844
|
+
const serializedValue = this.serialize(value);
|
|
440
845
|
await dbRun(
|
|
441
846
|
client,
|
|
442
847
|
`
|
|
@@ -446,12 +851,21 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
446
851
|
value = excluded.value,
|
|
447
852
|
expiresAt = excluded.expiresAt
|
|
448
853
|
`,
|
|
449
|
-
[storageKey, subKey,
|
|
854
|
+
[storageKey, subKey, serializedValue, expiresAt]
|
|
450
855
|
);
|
|
856
|
+
if (this.lruCache) {
|
|
857
|
+
this.lruCache.groupSet(storageKey, subKey, serializedValue, expiresAt);
|
|
858
|
+
}
|
|
451
859
|
}
|
|
452
860
|
async groupGet(key, subKey) {
|
|
453
|
-
const client = this.getClient();
|
|
454
861
|
const storageKey = this.prefixKey(key);
|
|
862
|
+
if (this.lruCache) {
|
|
863
|
+
const cached = this.lruCache.groupGet(storageKey, subKey);
|
|
864
|
+
if (cached !== null) {
|
|
865
|
+
return this.deserialize(cached);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
const client = this.getClient();
|
|
455
869
|
const row = await dbGet(
|
|
456
870
|
client,
|
|
457
871
|
'SELECT value, "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
@@ -463,11 +877,17 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
463
877
|
await this.groupDel(key, subKey);
|
|
464
878
|
return null;
|
|
465
879
|
}
|
|
880
|
+
if (this.lruCache) {
|
|
881
|
+
this.lruCache.groupSet(storageKey, subKey, row.value, row.expiresAt);
|
|
882
|
+
}
|
|
466
883
|
return this.deserialize(row.value);
|
|
467
884
|
}
|
|
468
885
|
async groupHas(key, subKey) {
|
|
469
|
-
const client = this.getClient();
|
|
470
886
|
const storageKey = this.prefixKey(key);
|
|
887
|
+
if (this.lruCache?.groupHas(storageKey, subKey)) {
|
|
888
|
+
return true;
|
|
889
|
+
}
|
|
890
|
+
const client = this.getClient();
|
|
471
891
|
const row = await dbGet(
|
|
472
892
|
client,
|
|
473
893
|
'SELECT "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
|
|
@@ -482,12 +902,21 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
482
902
|
return true;
|
|
483
903
|
}
|
|
484
904
|
async groupDel(key, subKey) {
|
|
485
|
-
const client = this.getClient();
|
|
486
905
|
const storageKey = this.prefixKey(key);
|
|
906
|
+
if (this.lruCache) {
|
|
907
|
+
this.lruCache.groupDel(storageKey, subKey);
|
|
908
|
+
}
|
|
909
|
+
const client = this.getClient();
|
|
487
910
|
await dbRun(client, "DELETE FROM kvcache WHERE key = ? AND subKey = ?", [storageKey, subKey]);
|
|
488
911
|
}
|
|
489
912
|
/* ------------------------------- misc ------------------------------- */
|
|
490
913
|
async close() {
|
|
914
|
+
if (this.lruCache) {
|
|
915
|
+
this.lruCache.close();
|
|
916
|
+
const lruCacheKey = `sqlite:${this.dbPath}:${this.prefix}`;
|
|
917
|
+
_SqliteAdapter.lruCaches.delete(lruCacheKey);
|
|
918
|
+
this.lruCache = null;
|
|
919
|
+
}
|
|
491
920
|
if (this.dbPath) {
|
|
492
921
|
const timer = _SqliteAdapter.cleanupTimers.get(this.dbPath);
|
|
493
922
|
if (timer) {
|
|
@@ -503,14 +932,29 @@ const _SqliteAdapter = class _SqliteAdapter {
|
|
|
503
932
|
}
|
|
504
933
|
}
|
|
505
934
|
async flushAll() {
|
|
935
|
+
if (this.lruCache) {
|
|
936
|
+
this.lruCache.clear();
|
|
937
|
+
}
|
|
506
938
|
const client = this.getClient();
|
|
507
939
|
const run = node_util.promisify(client.run.bind(client));
|
|
508
940
|
await run("DELETE FROM kvcache");
|
|
509
941
|
}
|
|
942
|
+
/**
|
|
943
|
+
* 获取 LRU 缓存统计信息
|
|
944
|
+
*/
|
|
945
|
+
getLruCacheStats() {
|
|
946
|
+
if (!this.lruCache) {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
return this.lruCache.getStats();
|
|
950
|
+
}
|
|
510
951
|
};
|
|
511
952
|
__publicField$3(_SqliteAdapter, "clients", /* @__PURE__ */ new Map());
|
|
512
953
|
__publicField$3(_SqliteAdapter, "initPromises", /* @__PURE__ */ new Map());
|
|
513
954
|
__publicField$3(_SqliteAdapter, "cleanupTimers", /* @__PURE__ */ new Map());
|
|
955
|
+
__publicField$3(_SqliteAdapter, "lruCaches", /* @__PURE__ */ new Map());
|
|
956
|
+
/** LRU 缓存的最大条数上限 */
|
|
957
|
+
__publicField$3(_SqliteAdapter, "MAX_LRU_CACHE_SIZE", 100);
|
|
514
958
|
let SqliteAdapter = _SqliteAdapter;
|
|
515
959
|
|
|
516
960
|
var __defProp$2 = Object.defineProperty;
|
|
@@ -662,6 +1106,14 @@ class BaseDBCache {
|
|
|
662
1106
|
await this.adapter.ensure();
|
|
663
1107
|
return this.adapter.flushAll();
|
|
664
1108
|
}
|
|
1109
|
+
/**
|
|
1110
|
+
* 获取 LRU 缓存统计信息
|
|
1111
|
+
* @returns 缓存统计信息,如果未启用 LRU 缓存则返回 null
|
|
1112
|
+
*/
|
|
1113
|
+
getLruCacheStats() {
|
|
1114
|
+
this.initAdapter();
|
|
1115
|
+
return this.adapter.getLruCacheStats();
|
|
1116
|
+
}
|
|
665
1117
|
}
|
|
666
1118
|
|
|
667
1119
|
var __defProp$1 = Object.defineProperty;
|
|
@@ -873,5 +1325,6 @@ const getAbtNodeRedisAndSQLiteUrl = () => {
|
|
|
873
1325
|
};
|
|
874
1326
|
|
|
875
1327
|
exports.DBCache = LockDBCache;
|
|
1328
|
+
exports.LruCache = LruCache;
|
|
876
1329
|
exports.getAbtNodeRedisAndSQLiteUrl = getAbtNodeRedisAndSQLiteUrl;
|
|
877
1330
|
exports.withRetry = withRetry;
|