@abtnode/db-cache 1.17.8-beta-20260108-224855-28496abb → 1.17.8-beta-20260111-112953-aed5ff39

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.mjs CHANGED
@@ -21,20 +21,20 @@ function ulid() {
21
21
  return timeStr + rand;
22
22
  }
23
23
 
24
- var __defProp$4 = Object.defineProperty;
25
- var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
26
- var __publicField$4 = (obj, key, value) => {
27
- __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
24
+ var __defProp$5 = Object.defineProperty;
25
+ var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
26
+ var __publicField$5 = (obj, key, value) => {
27
+ __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
28
28
  return value;
29
29
  };
30
30
  const _RedisAdapter = class _RedisAdapter {
31
31
  constructor() {
32
- __publicField$4(this, "opts", null);
33
- __publicField$4(this, "defaultTtl", 1e3 * 60 * 60);
34
- __publicField$4(this, "url", "");
35
- __publicField$4(this, "prefix", "");
36
- __publicField$4(this, "prefixKey", (key) => `${this.prefix}:${key}`);
37
- __publicField$4(this, "prefixKeyGroup", (key) => `${this.prefix}:group:${key}`);
32
+ __publicField$5(this, "opts", null);
33
+ __publicField$5(this, "defaultTtl", 1e3 * 60 * 60);
34
+ __publicField$5(this, "url", "");
35
+ __publicField$5(this, "prefix", "");
36
+ __publicField$5(this, "prefixKey", (key) => `${this.prefix}:${key}`);
37
+ __publicField$5(this, "prefixKeyGroup", (key) => `${this.prefix}:group:${key}`);
38
38
  }
39
39
  clearAll() {
40
40
  throw new Error("Method not implemented.");
@@ -177,10 +177,16 @@ const _RedisAdapter = class _RedisAdapter {
177
177
  const client = this.getClient();
178
178
  await client.flushAll();
179
179
  }
180
+ /**
181
+ * 获取 LRU 缓存统计信息(Redis 不使用 LRU 缓存)
182
+ */
183
+ getLruCacheStats() {
184
+ return null;
185
+ }
180
186
  };
181
187
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
- __publicField$4(_RedisAdapter, "clients", /* @__PURE__ */ new Map());
183
- __publicField$4(_RedisAdapter, "initPromises", /* @__PURE__ */ new Map());
188
+ __publicField$5(_RedisAdapter, "clients", /* @__PURE__ */ new Map());
189
+ __publicField$5(_RedisAdapter, "initPromises", /* @__PURE__ */ new Map());
184
190
  let RedisAdapter = _RedisAdapter;
185
191
 
186
192
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -209,6 +215,363 @@ async function withRetry(fn, {
209
215
  }
210
216
  }
211
217
 
218
+ var __defProp$4 = Object.defineProperty;
219
+ var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
220
+ var __publicField$4 = (obj, key, value) => {
221
+ __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
222
+ return value;
223
+ };
224
+ const lruCacheModule = require("lru-cache");
225
+ const LRUCacheLib = lruCacheModule?.LRUCache || lruCacheModule?.default || lruCacheModule;
226
+ const isTestEnv = process.env.NODE_ENV === "test";
227
+ const eventHub = isTestEnv ? require("@arcblock/event-hub/single") : require("@arcblock/event-hub");
228
+ const LRU_CACHE_SYNC_EVENT = "db-cache:lru:sync";
229
+ const LRU_CACHE_DELETE_EVENT = "db-cache:lru:delete";
230
+ const LRU_CACHE_CLEAR_EVENT = "db-cache:lru:clear";
231
+ const shouldEnableSync = () => {
232
+ if (isTestEnv) {
233
+ return false;
234
+ }
235
+ return process.env.NODE_APP_INSTANCE !== void 0;
236
+ };
237
+ const _LruCache = class _LruCache {
238
+ constructor(options) {
239
+ __publicField$4(this, "cacheKey");
240
+ __publicField$4(this, "maxSize");
241
+ __publicField$4(this, "enableSync");
242
+ this.cacheKey = `lru:${options.prefix}`;
243
+ this.maxSize = options.maxSize ?? 1e4;
244
+ this.enableSync = (options.enableSync ?? true) && shouldEnableSync();
245
+ if (!_LruCache.caches.has(this.cacheKey)) {
246
+ _LruCache.caches.set(
247
+ this.cacheKey,
248
+ new LRUCacheLib({
249
+ max: this.maxSize,
250
+ ttl: 0
251
+ // 我们自己管理 TTL
252
+ })
253
+ );
254
+ _LruCache.groupCaches.set(this.cacheKey, /* @__PURE__ */ new Map());
255
+ if (this.enableSync && !_LruCache.listenerSetup.has(this.cacheKey)) {
256
+ this._setupSyncListener();
257
+ _LruCache.listenerSetup.set(this.cacheKey, true);
258
+ }
259
+ }
260
+ }
261
+ getCache() {
262
+ const cache = _LruCache.caches.get(this.cacheKey);
263
+ if (!cache) {
264
+ throw new Error("LRU cache not initialized");
265
+ }
266
+ return cache;
267
+ }
268
+ getGroupCaches() {
269
+ const groupCaches = _LruCache.groupCaches.get(this.cacheKey);
270
+ if (!groupCaches) {
271
+ throw new Error("LRU group cache not initialized");
272
+ }
273
+ return groupCaches;
274
+ }
275
+ getOrCreateGroupCache(key) {
276
+ const groupCaches = this.getGroupCaches();
277
+ if (!groupCaches.has(key)) {
278
+ groupCaches.set(
279
+ key,
280
+ new LRUCacheLib({
281
+ max: this.maxSize,
282
+ ttl: 0
283
+ })
284
+ );
285
+ }
286
+ return groupCaches.get(key);
287
+ }
288
+ /**
289
+ * 设置同步监听器,接收其他 worker 广播的缓存数据
290
+ */
291
+ _setupSyncListener() {
292
+ const { cacheKey, maxSize } = this;
293
+ eventHub.on(LRU_CACHE_SYNC_EVENT, (payload) => {
294
+ const { channel, key, value, expiresAt, isGroup, subKey } = payload;
295
+ if (channel !== cacheKey)
296
+ return;
297
+ const entry = { value, expiresAt };
298
+ if (isGroup && subKey !== void 0) {
299
+ const groupCaches = _LruCache.groupCaches.get(cacheKey);
300
+ if (groupCaches) {
301
+ const groupCache = groupCaches.get(key) || new LRUCacheLib({ max: maxSize, ttl: 0 });
302
+ groupCache.set(subKey, entry);
303
+ groupCaches.set(key, groupCache);
304
+ }
305
+ } else {
306
+ const cache = _LruCache.caches.get(cacheKey);
307
+ if (cache) {
308
+ cache.set(key, entry);
309
+ }
310
+ }
311
+ });
312
+ eventHub.on(LRU_CACHE_DELETE_EVENT, (payload) => {
313
+ const { channel, keys, isGroup, groupKey } = payload;
314
+ if (channel !== cacheKey)
315
+ return;
316
+ if (isGroup && groupKey !== void 0) {
317
+ const groupCaches = _LruCache.groupCaches.get(cacheKey);
318
+ if (groupCaches) {
319
+ const groupCache = groupCaches.get(groupKey);
320
+ if (groupCache) {
321
+ for (const key of keys) {
322
+ groupCache.delete(key);
323
+ }
324
+ }
325
+ }
326
+ } else {
327
+ const cache = _LruCache.caches.get(cacheKey);
328
+ if (cache) {
329
+ for (const key of keys) {
330
+ cache.delete(key);
331
+ const groupCaches = _LruCache.groupCaches.get(cacheKey);
332
+ if (groupCaches) {
333
+ groupCaches.delete(key);
334
+ }
335
+ }
336
+ }
337
+ }
338
+ });
339
+ eventHub.on(LRU_CACHE_CLEAR_EVENT, (payload) => {
340
+ const { channel } = payload;
341
+ if (channel !== cacheKey)
342
+ return;
343
+ const cache = _LruCache.caches.get(cacheKey);
344
+ if (cache) {
345
+ cache.clear();
346
+ }
347
+ const groupCaches = _LruCache.groupCaches.get(cacheKey);
348
+ if (groupCaches) {
349
+ groupCaches.clear();
350
+ }
351
+ });
352
+ }
353
+ /**
354
+ * 广播缓存数据给其他 worker
355
+ */
356
+ _broadcastSync(key, value, expiresAt, isGroup = false, subKey) {
357
+ if (this.enableSync) {
358
+ eventHub.broadcast(LRU_CACHE_SYNC_EVENT, {
359
+ channel: this.cacheKey,
360
+ key,
361
+ value,
362
+ expiresAt,
363
+ isGroup,
364
+ subKey
365
+ });
366
+ }
367
+ }
368
+ /**
369
+ * 广播删除缓存给其他 worker
370
+ */
371
+ _broadcastDelete(keys, isGroup = false, groupKey) {
372
+ if (this.enableSync && keys.length > 0) {
373
+ eventHub.broadcast(LRU_CACHE_DELETE_EVENT, {
374
+ channel: this.cacheKey,
375
+ keys,
376
+ isGroup,
377
+ groupKey
378
+ });
379
+ }
380
+ }
381
+ /**
382
+ * 广播清空缓存给其他 worker
383
+ */
384
+ _broadcastClear() {
385
+ if (this.enableSync) {
386
+ eventHub.broadcast(LRU_CACHE_CLEAR_EVENT, {
387
+ channel: this.cacheKey
388
+ });
389
+ }
390
+ }
391
+ isExpired(entry) {
392
+ if (!entry)
393
+ return true;
394
+ if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {
395
+ return true;
396
+ }
397
+ return false;
398
+ }
399
+ /* ---------------------- 基础 API ---------------------- */
400
+ /**
401
+ * 设置缓存
402
+ * @param key 缓存键
403
+ * @param value 序列化后的值
404
+ * @param expiresAt 过期时间戳,null 表示永不过期
405
+ */
406
+ set(key, value, expiresAt) {
407
+ const cache = this.getCache();
408
+ const entry = { value, expiresAt };
409
+ cache.set(key, entry);
410
+ this._broadcastSync(key, value, expiresAt);
411
+ }
412
+ /**
413
+ * 获取缓存
414
+ * @param key 缓存键
415
+ * @returns 序列化的值,未命中或过期返回 null
416
+ */
417
+ get(key) {
418
+ const cache = this.getCache();
419
+ const entry = cache.get(key);
420
+ if (!entry)
421
+ return null;
422
+ if (this.isExpired(entry)) {
423
+ cache.delete(key);
424
+ return null;
425
+ }
426
+ return entry.value;
427
+ }
428
+ /**
429
+ * 检查缓存是否存在且未过期
430
+ */
431
+ has(key) {
432
+ const cache = this.getCache();
433
+ const entry = cache.get(key);
434
+ if (!entry)
435
+ return false;
436
+ if (this.isExpired(entry)) {
437
+ cache.delete(key);
438
+ return false;
439
+ }
440
+ return true;
441
+ }
442
+ /**
443
+ * 删除缓存
444
+ */
445
+ del(key) {
446
+ const cache = this.getCache();
447
+ cache.delete(key);
448
+ const groupCaches = this.getGroupCaches();
449
+ groupCaches.delete(key);
450
+ this._broadcastDelete([key]);
451
+ }
452
+ /**
453
+ * 按前缀删除缓存
454
+ * @returns 删除的数量
455
+ */
456
+ delByPrefix(prefix) {
457
+ const cache = this.getCache();
458
+ const deletedKeys = [];
459
+ for (const key of cache.keys()) {
460
+ if (key.startsWith(prefix)) {
461
+ cache.delete(key);
462
+ deletedKeys.push(key);
463
+ }
464
+ }
465
+ const groupCaches = this.getGroupCaches();
466
+ for (const key of groupCaches.keys()) {
467
+ if (key.startsWith(prefix)) {
468
+ groupCaches.delete(key);
469
+ }
470
+ }
471
+ this._broadcastDelete(deletedKeys);
472
+ return deletedKeys.length;
473
+ }
474
+ /* ---------------------- 分组 API ---------------------- */
475
+ /**
476
+ * 设置分组缓存
477
+ */
478
+ groupSet(key, subKey, value, expiresAt) {
479
+ const groupCache = this.getOrCreateGroupCache(key);
480
+ const entry = { value, expiresAt };
481
+ groupCache.set(subKey, entry);
482
+ this._broadcastSync(key, value, expiresAt, true, subKey);
483
+ }
484
+ /**
485
+ * 获取分组缓存
486
+ */
487
+ groupGet(key, subKey) {
488
+ const groupCaches = this.getGroupCaches();
489
+ const groupCache = groupCaches.get(key);
490
+ if (!groupCache)
491
+ return null;
492
+ const entry = groupCache.get(subKey);
493
+ if (!entry)
494
+ return null;
495
+ if (this.isExpired(entry)) {
496
+ groupCache.delete(subKey);
497
+ return null;
498
+ }
499
+ return entry.value;
500
+ }
501
+ /**
502
+ * 检查分组缓存是否存在
503
+ */
504
+ groupHas(key, subKey) {
505
+ const groupCaches = this.getGroupCaches();
506
+ const groupCache = groupCaches.get(key);
507
+ if (!groupCache)
508
+ return false;
509
+ const entry = groupCache.get(subKey);
510
+ if (!entry)
511
+ return false;
512
+ if (this.isExpired(entry)) {
513
+ groupCache.delete(subKey);
514
+ return false;
515
+ }
516
+ return true;
517
+ }
518
+ /**
519
+ * 删除分组缓存
520
+ */
521
+ groupDel(key, subKey) {
522
+ const groupCaches = this.getGroupCaches();
523
+ const groupCache = groupCaches.get(key);
524
+ if (groupCache) {
525
+ groupCache.delete(subKey);
526
+ this._broadcastDelete([subKey], true, key);
527
+ }
528
+ }
529
+ /* ---------------------- 管理 API ---------------------- */
530
+ /**
531
+ * 清空所有缓存
532
+ */
533
+ clear() {
534
+ const cache = this.getCache();
535
+ cache.clear();
536
+ const groupCaches = this.getGroupCaches();
537
+ groupCaches.clear();
538
+ this._broadcastClear();
539
+ }
540
+ /**
541
+ * 获取缓存统计信息
542
+ */
543
+ getStats() {
544
+ const cache = this.getCache();
545
+ const groupCaches = this.getGroupCaches();
546
+ let totalGroupSize = 0;
547
+ for (const groupCache of groupCaches.values()) {
548
+ totalGroupSize += groupCache.size;
549
+ }
550
+ return {
551
+ size: cache.size,
552
+ // 普通缓存条目数
553
+ groupCount: totalGroupSize,
554
+ // 分组缓存条目数
555
+ maxSize: this.maxSize
556
+ // 最大容量
557
+ };
558
+ }
559
+ /**
560
+ * 关闭缓存(清理资源)
561
+ *
562
+ * 注意:不会广播 close 事件给其他 worker,因为每个 worker 有独立的生命周期
563
+ */
564
+ close() {
565
+ _LruCache.caches.delete(this.cacheKey);
566
+ _LruCache.groupCaches.delete(this.cacheKey);
567
+ _LruCache.listenerSetup.delete(this.cacheKey);
568
+ }
569
+ };
570
+ __publicField$4(_LruCache, "caches", /* @__PURE__ */ new Map());
571
+ __publicField$4(_LruCache, "groupCaches", /* @__PURE__ */ new Map());
572
+ __publicField$4(_LruCache, "listenerSetup", /* @__PURE__ */ new Map());
573
+ let LruCache = _LruCache;
574
+
212
575
  var __defProp$3 = Object.defineProperty;
213
576
  var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
214
577
  var __publicField$3 = (obj, key, value) => {
@@ -274,6 +637,8 @@ const _SqliteAdapter = class _SqliteAdapter {
274
637
  __publicField$3(this, "defaultTtl", 1e3 * 60 * 60);
275
638
  __publicField$3(this, "cleanupInterval", 5 * 60 * 1e3);
276
639
  __publicField$3(this, "dbPath", "");
640
+ __publicField$3(this, "enableLruCache", false);
641
+ __publicField$3(this, "lruCache", null);
277
642
  }
278
643
  /* ------------------------------- init ------------------------------- */
279
644
  async ensure() {
@@ -283,6 +648,28 @@ const _SqliteAdapter = class _SqliteAdapter {
283
648
  this.prefix = this.opts.prefix;
284
649
  this.defaultTtl = this.opts.ttl;
285
650
  this.cleanupInterval = this.opts.cleanupInterval ?? 5 * 60 * 1e3;
651
+ this.enableLruCache = this.opts.enableLruCache ?? false;
652
+ }
653
+ if (this.enableLruCache && !this.lruCache) {
654
+ const lruCacheKey = `sqlite:${this.dbPath}:${this.prefix}`;
655
+ if (!_SqliteAdapter.lruCaches.has(lruCacheKey)) {
656
+ const requestedSize = this.opts.lruMaxSize ?? 100;
657
+ const actualSize = Math.min(requestedSize, _SqliteAdapter.MAX_LRU_CACHE_SIZE);
658
+ if (requestedSize > _SqliteAdapter.MAX_LRU_CACHE_SIZE) {
659
+ console.warn(
660
+ `[SqliteAdapter] Requested LRU cache size ${requestedSize} exceeds maximum ${_SqliteAdapter.MAX_LRU_CACHE_SIZE}, using ${actualSize} instead`
661
+ );
662
+ }
663
+ _SqliteAdapter.lruCaches.set(
664
+ lruCacheKey,
665
+ new LruCache({
666
+ prefix: lruCacheKey,
667
+ maxSize: actualSize,
668
+ enableSync: this.opts.enableSync ?? true
669
+ })
670
+ );
671
+ }
672
+ this.lruCache = _SqliteAdapter.lruCaches.get(lruCacheKey);
286
673
  }
287
674
  if (_SqliteAdapter.clients.has(this.dbPath))
288
675
  return;
@@ -345,7 +732,11 @@ const _SqliteAdapter = class _SqliteAdapter {
345
732
  const storageKey = this.prefixKey(key);
346
733
  const effectiveTtl = opts.ttl !== void 0 ? opts.ttl : this.defaultTtl;
347
734
  const expiresAt = effectiveTtl > 0 ? Date.now() + effectiveTtl : null;
735
+ const serializedValue = this.serialize(value);
348
736
  if (opts.nx) {
737
+ if (this.lruCache?.has(storageKey)) {
738
+ return false;
739
+ }
349
740
  const existing = await dbGet(
350
741
  client,
351
742
  "SELECT value FROM kvcache WHERE key = ? AND subKey = ?",
@@ -364,13 +755,22 @@ const _SqliteAdapter = class _SqliteAdapter {
364
755
  value = excluded.value,
365
756
  expiresAt = excluded.expiresAt
366
757
  `,
367
- [storageKey, "", this.serialize(value), expiresAt]
758
+ [storageKey, "", serializedValue, expiresAt]
368
759
  );
760
+ if (this.lruCache) {
761
+ this.lruCache.set(storageKey, serializedValue, expiresAt);
762
+ }
369
763
  return true;
370
764
  }
371
765
  async get(key) {
372
- const client = this.getClient();
373
766
  const storageKey = this.prefixKey(key);
767
+ if (this.lruCache) {
768
+ const cached = this.lruCache.get(storageKey);
769
+ if (cached !== null) {
770
+ return this.deserialize(cached);
771
+ }
772
+ }
773
+ const client = this.getClient();
374
774
  const row = await dbGet(
375
775
  client,
376
776
  'SELECT value, "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
@@ -382,16 +782,25 @@ const _SqliteAdapter = class _SqliteAdapter {
382
782
  await this.del(key);
383
783
  return null;
384
784
  }
785
+ if (this.lruCache) {
786
+ this.lruCache.set(storageKey, row.value, row.expiresAt);
787
+ }
385
788
  return this.deserialize(row.value);
386
789
  }
387
790
  async del(key) {
388
- const client = this.getClient();
389
791
  const storageKey = this.prefixKey(key);
792
+ if (this.lruCache) {
793
+ this.lruCache.del(storageKey);
794
+ }
795
+ const client = this.getClient();
390
796
  await dbRun(client, "DELETE FROM kvcache WHERE key = ?", [storageKey]);
391
797
  }
392
798
  async has(key) {
393
- const client = this.getClient();
394
799
  const storageKey = this.prefixKey(key);
800
+ if (this.lruCache?.has(storageKey)) {
801
+ return true;
802
+ }
803
+ const client = this.getClient();
395
804
  const row = await dbGet(
396
805
  client,
397
806
  'SELECT "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
@@ -411,11 +820,7 @@ const _SqliteAdapter = class _SqliteAdapter {
411
820
  const storageKey = this.prefixKey(key);
412
821
  const effectiveTtl = opts.ttl !== void 0 ? opts.ttl : this.defaultTtl;
413
822
  const expiresAt = effectiveTtl > 0 ? Date.now() + effectiveTtl : null;
414
- if (opts.nx) {
415
- const exists = await this.groupHas(key, subKey);
416
- if (exists)
417
- return;
418
- }
823
+ const serializedValue = this.serialize(value);
419
824
  await dbRun(
420
825
  client,
421
826
  `
@@ -425,12 +830,21 @@ const _SqliteAdapter = class _SqliteAdapter {
425
830
  value = excluded.value,
426
831
  expiresAt = excluded.expiresAt
427
832
  `,
428
- [storageKey, subKey, this.serialize(value), expiresAt]
833
+ [storageKey, subKey, serializedValue, expiresAt]
429
834
  );
835
+ if (this.lruCache) {
836
+ this.lruCache.groupSet(storageKey, subKey, serializedValue, expiresAt);
837
+ }
430
838
  }
431
839
  async groupGet(key, subKey) {
432
- const client = this.getClient();
433
840
  const storageKey = this.prefixKey(key);
841
+ if (this.lruCache) {
842
+ const cached = this.lruCache.groupGet(storageKey, subKey);
843
+ if (cached !== null) {
844
+ return this.deserialize(cached);
845
+ }
846
+ }
847
+ const client = this.getClient();
434
848
  const row = await dbGet(
435
849
  client,
436
850
  'SELECT value, "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
@@ -442,11 +856,17 @@ const _SqliteAdapter = class _SqliteAdapter {
442
856
  await this.groupDel(key, subKey);
443
857
  return null;
444
858
  }
859
+ if (this.lruCache) {
860
+ this.lruCache.groupSet(storageKey, subKey, row.value, row.expiresAt);
861
+ }
445
862
  return this.deserialize(row.value);
446
863
  }
447
864
  async groupHas(key, subKey) {
448
- const client = this.getClient();
449
865
  const storageKey = this.prefixKey(key);
866
+ if (this.lruCache?.groupHas(storageKey, subKey)) {
867
+ return true;
868
+ }
869
+ const client = this.getClient();
450
870
  const row = await dbGet(
451
871
  client,
452
872
  'SELECT "expiresAt" FROM kvcache WHERE key = ? AND subKey = ?',
@@ -461,12 +881,21 @@ const _SqliteAdapter = class _SqliteAdapter {
461
881
  return true;
462
882
  }
463
883
  async groupDel(key, subKey) {
464
- const client = this.getClient();
465
884
  const storageKey = this.prefixKey(key);
885
+ if (this.lruCache) {
886
+ this.lruCache.groupDel(storageKey, subKey);
887
+ }
888
+ const client = this.getClient();
466
889
  await dbRun(client, "DELETE FROM kvcache WHERE key = ? AND subKey = ?", [storageKey, subKey]);
467
890
  }
468
891
  /* ------------------------------- misc ------------------------------- */
469
892
  async close() {
893
+ if (this.lruCache) {
894
+ this.lruCache.close();
895
+ const lruCacheKey = `sqlite:${this.dbPath}:${this.prefix}`;
896
+ _SqliteAdapter.lruCaches.delete(lruCacheKey);
897
+ this.lruCache = null;
898
+ }
470
899
  if (this.dbPath) {
471
900
  const timer = _SqliteAdapter.cleanupTimers.get(this.dbPath);
472
901
  if (timer) {
@@ -482,14 +911,29 @@ const _SqliteAdapter = class _SqliteAdapter {
482
911
  }
483
912
  }
484
913
  async flushAll() {
914
+ if (this.lruCache) {
915
+ this.lruCache.clear();
916
+ }
485
917
  const client = this.getClient();
486
918
  const run = promisify(client.run.bind(client));
487
919
  await run("DELETE FROM kvcache");
488
920
  }
921
+ /**
922
+ * 获取 LRU 缓存统计信息
923
+ */
924
+ getLruCacheStats() {
925
+ if (!this.lruCache) {
926
+ return null;
927
+ }
928
+ return this.lruCache.getStats();
929
+ }
489
930
  };
490
931
  __publicField$3(_SqliteAdapter, "clients", /* @__PURE__ */ new Map());
491
932
  __publicField$3(_SqliteAdapter, "initPromises", /* @__PURE__ */ new Map());
492
933
  __publicField$3(_SqliteAdapter, "cleanupTimers", /* @__PURE__ */ new Map());
934
+ __publicField$3(_SqliteAdapter, "lruCaches", /* @__PURE__ */ new Map());
935
+ /** LRU 缓存的最大条数上限 */
936
+ __publicField$3(_SqliteAdapter, "MAX_LRU_CACHE_SIZE", 100);
493
937
  let SqliteAdapter = _SqliteAdapter;
494
938
 
495
939
  var __defProp$2 = Object.defineProperty;
@@ -641,6 +1085,14 @@ class BaseDBCache {
641
1085
  await this.adapter.ensure();
642
1086
  return this.adapter.flushAll();
643
1087
  }
1088
+ /**
1089
+ * 获取 LRU 缓存统计信息
1090
+ * @returns 缓存统计信息,如果未启用 LRU 缓存则返回 null
1091
+ */
1092
+ getLruCacheStats() {
1093
+ this.initAdapter();
1094
+ return this.adapter.getLruCacheStats();
1095
+ }
644
1096
  }
645
1097
 
646
1098
  var __defProp$1 = Object.defineProperty;
@@ -851,4 +1303,4 @@ const getAbtNodeRedisAndSQLiteUrl = () => {
851
1303
  return params;
852
1304
  };
853
1305
 
854
- export { LockDBCache as DBCache, getAbtNodeRedisAndSQLiteUrl, withRetry };
1306
+ export { LockDBCache as DBCache, LruCache, getAbtNodeRedisAndSQLiteUrl, withRetry };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@abtnode/db-cache",
3
- "version": "1.17.8-beta-20260108-224855-28496abb",
4
- "description": "Db cache use redis or sqlite as backend",
3
+ "version": "1.17.8-beta-20260111-112953-aed5ff39",
4
+ "description": "Db cache use redis, sqlite or memory as backend",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,7 +27,9 @@
27
27
  "author": "",
28
28
  "license": "ISC",
29
29
  "dependencies": {
30
+ "@arcblock/event-hub": "^1.28.4",
30
31
  "@types/proper-lockfile": "^4.1.4",
32
+ "lru-cache": "^11.1.0",
31
33
  "proper-lockfile": "^4.1.2",
32
34
  "redis": "^5.1.1",
33
35
  "sqlite3": "^5.1.7"
@@ -42,5 +44,5 @@
42
44
  "prettier": "^3.3.2",
43
45
  "unbuild": "^2.0.0"
44
46
  },
45
- "gitHead": "e4407556f8c8f909afdfc6abc05228b0b495dcbf"
47
+ "gitHead": "b4c76fd49b8c98c9554c37c798f58ddf334fed4c"
46
48
  }