koatty_store 1.8.0 → 1.9.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/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @Author: richen
3
- * @Date: 2025-06-09 12:32:48
3
+ * @Date: 2025-11-02 08:37:33
4
4
  * @License: BSD (3-Clause)
5
5
  * @Copyright (c) - <richenlin(at)gmail.com>
6
6
  * @HomePage: https://koatty.org/
@@ -9,6 +9,7 @@ import { flatten, isNil, union, isUndefined } from 'lodash';
9
9
  import * as helper from 'koatty_lib';
10
10
  import { EventEmitter } from 'events';
11
11
  import { LRUCache } from 'lru-cache';
12
+ import AsyncLock from 'async-lock';
12
13
  import { DefaultLogger } from 'koatty_logger';
13
14
  import { Cluster, Redis } from 'ioredis';
14
15
  import genericPool from 'generic-pool';
@@ -50,15 +51,6 @@ var messages;
50
51
  messages["mutuallyExclusiveNXXX"] = "ERR XX and NX options at the same time are not compatible";
51
52
  })(messages || (messages = {}));
52
53
  class MemoryCache extends EventEmitter {
53
- databases = new Map();
54
- options;
55
- currentDBIndex;
56
- connected;
57
- lastSave;
58
- multiMode;
59
- cache;
60
- responseMessages;
61
- ttlCheckTimer = null;
62
54
  /**
63
55
  * Creates an instance of MemoryCache.
64
56
  * @param {MemoryCacheOptions} options
@@ -66,6 +58,8 @@ class MemoryCache extends EventEmitter {
66
58
  */
67
59
  constructor(options) {
68
60
  super();
61
+ this.databases = new Map();
62
+ this.ttlCheckTimer = null;
69
63
  this.options = {
70
64
  database: 0,
71
65
  maxKeys: 1000,
@@ -79,6 +73,7 @@ class MemoryCache extends EventEmitter {
79
73
  this.lastSave = Date.now();
80
74
  this.multiMode = false;
81
75
  this.responseMessages = [];
76
+ this.lock = new AsyncLock();
82
77
  // 初始化数据库和缓存
83
78
  if (!this.databases.has(this.currentDBIndex)) {
84
79
  this.databases.set(this.currentDBIndex, this.createLRUCache());
@@ -142,6 +137,15 @@ class MemoryCache extends EventEmitter {
142
137
  this.ttlCheckTimer = null;
143
138
  }
144
139
  }
140
+ /**
141
+ * 清理所有资源
142
+ * @private
143
+ */
144
+ cleanup() {
145
+ this.stopTTLCheck();
146
+ this.databases.clear();
147
+ this.removeAllListeners();
148
+ }
145
149
  /**
146
150
  *
147
151
  *
@@ -167,8 +171,8 @@ class MemoryCache extends EventEmitter {
167
171
  * @memberof MemoryCache
168
172
  */
169
173
  quit() {
174
+ this.cleanup(); // 调用清理方法
170
175
  this.connected = false;
171
- this.stopTTLCheck();
172
176
  // exit multi mode if we are in it
173
177
  this.discard(null, true);
174
178
  this.emit('end');
@@ -431,14 +435,18 @@ class MemoryCache extends EventEmitter {
431
435
  * @memberof MemoryCache
432
436
  */
433
437
  incr(key, callback) {
434
- let retVal = null;
435
- try {
436
- retVal = this._addToKey(key, 1);
437
- }
438
- catch (err) {
439
- return this._handleCallback(callback, null, err);
440
- }
441
- return this._handleCallback(callback, retVal);
438
+ // 使用锁保护原子操作
439
+ const lockKey = `incr:${key}`;
440
+ return this.lock.acquire(lockKey, () => {
441
+ let retVal = null;
442
+ try {
443
+ retVal = this._addToKey(key, 1);
444
+ }
445
+ catch (err) {
446
+ return this._handleCallback(callback, null, err);
447
+ }
448
+ return this._handleCallback(callback, retVal);
449
+ });
442
450
  }
443
451
  /**
444
452
  *
@@ -450,14 +458,18 @@ class MemoryCache extends EventEmitter {
450
458
  * @memberof MemoryCache
451
459
  */
452
460
  incrby(key, amount, callback) {
453
- let retVal = null;
454
- try {
455
- retVal = this._addToKey(key, amount);
456
- }
457
- catch (err) {
458
- return this._handleCallback(callback, null, err);
459
- }
460
- return this._handleCallback(callback, retVal);
461
+ // 使用锁保护原子操作
462
+ const lockKey = `incrby:${key}`;
463
+ return this.lock.acquire(lockKey, () => {
464
+ let retVal = null;
465
+ try {
466
+ retVal = this._addToKey(key, amount);
467
+ }
468
+ catch (err) {
469
+ return this._handleCallback(callback, null, err);
470
+ }
471
+ return this._handleCallback(callback, retVal);
472
+ });
461
473
  }
462
474
  /**
463
475
  *
@@ -468,14 +480,18 @@ class MemoryCache extends EventEmitter {
468
480
  * @memberof MemoryCache
469
481
  */
470
482
  decr(key, callback) {
471
- let retVal = null;
472
- try {
473
- retVal = this._addToKey(key, -1);
474
- }
475
- catch (err) {
476
- return this._handleCallback(callback, null, err);
477
- }
478
- return this._handleCallback(callback, retVal);
483
+ // 使用锁保护原子操作
484
+ const lockKey = `decr:${key}`;
485
+ return this.lock.acquire(lockKey, () => {
486
+ let retVal = null;
487
+ try {
488
+ retVal = this._addToKey(key, -1);
489
+ }
490
+ catch (err) {
491
+ return this._handleCallback(callback, null, err);
492
+ }
493
+ return this._handleCallback(callback, retVal);
494
+ });
479
495
  }
480
496
  /**
481
497
  *
@@ -487,19 +503,23 @@ class MemoryCache extends EventEmitter {
487
503
  * @memberof MemoryCache
488
504
  */
489
505
  decrby(key, amount, callback) {
490
- let retVal = null;
491
- try {
492
- retVal = this._addToKey(key, 0 - amount);
493
- }
494
- catch (err) {
495
- return this._handleCallback(callback, null, err);
496
- }
497
- return this._handleCallback(callback, retVal);
506
+ // 使用锁保护原子操作
507
+ const lockKey = `decrby:${key}`;
508
+ return this.lock.acquire(lockKey, () => {
509
+ let retVal = null;
510
+ try {
511
+ retVal = this._addToKey(key, 0 - amount);
512
+ }
513
+ catch (err) {
514
+ return this._handleCallback(callback, null, err);
515
+ }
516
+ return this._handleCallback(callback, retVal);
517
+ });
498
518
  }
499
519
  // ---------------------------------------
500
520
  // ## Hash ##
501
521
  // ---------------------------------------
502
- hset(key, field, value, callback) {
522
+ hset(key, field, value, timeout, callback) {
503
523
  let retVal = 0;
504
524
  if (this._hasKey(key)) {
505
525
  this._testType(key, 'hash', true, callback);
@@ -511,6 +531,14 @@ class MemoryCache extends EventEmitter {
511
531
  retVal = 1;
512
532
  }
513
533
  this._setField(key, field, value.toString());
534
+ // 如果指定了 timeout,存储字段级别的过期时间
535
+ if (typeof timeout === 'number') {
536
+ const hashObj = this.cache.get(key).value;
537
+ if (!hashObj._fieldTTL) {
538
+ hashObj._fieldTTL = {};
539
+ }
540
+ hashObj._fieldTTL[field] = Date.now() + (timeout * 1000);
541
+ }
514
542
  this.persist(key);
515
543
  return this._handleCallback(callback, retVal);
516
544
  }
@@ -527,8 +555,17 @@ class MemoryCache extends EventEmitter {
527
555
  let retVal = null;
528
556
  if (this._hasKey(key)) {
529
557
  this._testType(key, 'hash', true, callback);
558
+ // 检查字段级别的过期时间
559
+ const hashObj = this._getKey(key);
560
+ if (hashObj && hashObj._fieldTTL && hashObj._fieldTTL[field]) {
561
+ if (hashObj._fieldTTL[field] <= Date.now()) {
562
+ // 过期,删除字段
563
+ this.hdel(key, field);
564
+ return this._handleCallback(callback, null);
565
+ }
566
+ }
530
567
  if (this._hasField(key, field)) {
531
- retVal = this._getKey(key)[field];
568
+ retVal = hashObj[field];
532
569
  }
533
570
  }
534
571
  return this._handleCallback(callback, retVal);
@@ -546,6 +583,15 @@ class MemoryCache extends EventEmitter {
546
583
  let retVal = 0;
547
584
  if (this._hasKey(key)) {
548
585
  this._testType(key, 'hash', true, callback);
586
+ // 检查字段级别的过期时间
587
+ const hashObj = this._getKey(key);
588
+ if (hashObj && hashObj._fieldTTL && hashObj._fieldTTL[field]) {
589
+ if (hashObj._fieldTTL[field] <= Date.now()) {
590
+ // 过期,删除字段
591
+ this.hdel(key, field);
592
+ return this._handleCallback(callback, 0);
593
+ }
594
+ }
549
595
  if (this._hasField(key, field)) {
550
596
  retVal = 1;
551
597
  }
@@ -565,10 +611,15 @@ class MemoryCache extends EventEmitter {
565
611
  const callback = this._retrieveCallback(fields);
566
612
  if (this._hasKey(key)) {
567
613
  this._testType(key, 'hash', true, callback);
614
+ const hashObj = this.cache.get(key).value;
568
615
  for (let itr = 0; itr < fields.length; itr++) {
569
616
  const field = fields[itr];
570
617
  if (this._hasField(key, field)) {
571
- delete this.cache.get(key).value[field];
618
+ delete hashObj[field];
619
+ // 清理过期时间记录
620
+ if (hashObj._fieldTTL && hashObj._fieldTTL[field]) {
621
+ delete hashObj._fieldTTL[field];
622
+ }
572
623
  retVal++;
573
624
  }
574
625
  }
@@ -598,14 +649,18 @@ class MemoryCache extends EventEmitter {
598
649
  * @memberof MemoryCache
599
650
  */
600
651
  hincrby(key, field, value, callback) {
601
- let retVal;
602
- try {
603
- retVal = this._addToField(key, field, value, false);
604
- }
605
- catch (err) {
606
- return this._handleCallback(callback, null, err);
607
- }
608
- return this._handleCallback(callback, retVal);
652
+ // 使用锁保护原子操作,使用组合 key
653
+ const lockKey = `hincrby:${key}:${field}`;
654
+ return this.lock.acquire(lockKey, () => {
655
+ let retVal;
656
+ try {
657
+ retVal = this._addToField(key, field, value, false);
658
+ }
659
+ catch (err) {
660
+ return this._handleCallback(callback, null, err);
661
+ }
662
+ return this._handleCallback(callback, retVal);
663
+ });
609
664
  }
610
665
  /**
611
666
  *
@@ -619,7 +674,11 @@ class MemoryCache extends EventEmitter {
619
674
  let retVals = {};
620
675
  if (this._hasKey(key)) {
621
676
  this._testType(key, 'hash', true, callback);
622
- retVals = this._getKey(key);
677
+ const hashObj = this._getKey(key);
678
+ // 排除内部属性 _fieldTTL
679
+ retVals = Object.entries(hashObj)
680
+ .filter(([k]) => k !== '_fieldTTL')
681
+ .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
623
682
  }
624
683
  return this._handleCallback(callback, retVals);
625
684
  }
@@ -635,7 +694,9 @@ class MemoryCache extends EventEmitter {
635
694
  let retVals = [];
636
695
  if (this._hasKey(key)) {
637
696
  this._testType(key, 'hash', true, callback);
638
- retVals = Object.keys(this._getKey(key));
697
+ const hashObj = this._getKey(key);
698
+ // 排除内部属性 _fieldTTL
699
+ retVals = Object.keys(hashObj).filter(k => k !== '_fieldTTL');
639
700
  }
640
701
  return this._handleCallback(callback, retVals);
641
702
  }
@@ -651,7 +712,11 @@ class MemoryCache extends EventEmitter {
651
712
  let retVals = [];
652
713
  if (this._hasKey(key)) {
653
714
  this._testType(key, 'hash', true, callback);
654
- retVals = Object.values(this._getKey(key));
715
+ const hashObj = this._getKey(key);
716
+ // 排除内部属性 _fieldTTL 的值
717
+ retVals = Object.entries(hashObj)
718
+ .filter(([k]) => k !== '_fieldTTL')
719
+ .map(([, v]) => v);
655
720
  }
656
721
  return this._handleCallback(callback, retVals);
657
722
  }
@@ -802,8 +867,7 @@ class MemoryCache extends EventEmitter {
802
867
  stop = length - 1;
803
868
  }
804
869
  if (stop >= 0 && stop >= start) {
805
- const size = stop - start + 1;
806
- for (let itr = start; itr < size; itr++) {
870
+ for (let itr = start; itr <= stop; itr++) {
807
871
  retVal.push(list[itr]);
808
872
  }
809
873
  }
@@ -1010,9 +1074,10 @@ class MemoryCache extends EventEmitter {
1010
1074
  * @memberof MemoryCache
1011
1075
  */
1012
1076
  pttl(key, _callback) {
1077
+ var _a;
1013
1078
  let retVal = -2;
1014
1079
  if (this._hasKey(key)) {
1015
- if (!isNil(this.cache.get(key)?.timeout)) {
1080
+ if (!isNil((_a = this.cache.get(key)) === null || _a === void 0 ? void 0 : _a.timeout)) {
1016
1081
  retVal = this.cache.get(key).timeout - Date.now();
1017
1082
  // Prevent unexpected errors if the actual ttl just happens to be -2 or -1
1018
1083
  if (retVal < 0 && retVal > -3) {
@@ -1221,7 +1286,8 @@ class MemoryCache extends EventEmitter {
1221
1286
  if (key && field) {
1222
1287
  const ky = this._getKey(key);
1223
1288
  if (ky) {
1224
- retVal = ky.hasOwnProperty(field);
1289
+ // 排除内部属性 _fieldTTL
1290
+ retVal = field !== '_fieldTTL' && ky.hasOwnProperty(field);
1225
1291
  }
1226
1292
  }
1227
1293
  return retVal;
@@ -1630,9 +1696,6 @@ class MemoryCache extends EventEmitter {
1630
1696
  * @LastEditTime: 2023-02-18 23:52:47
1631
1697
  */
1632
1698
  class MemoryStore {
1633
- client;
1634
- pool;
1635
- options;
1636
1699
  /**
1637
1700
  * Creates an instance of MemoryStore.
1638
1701
  * @param {MemoryStoreOpt} options
@@ -1645,7 +1708,15 @@ class MemoryStore {
1645
1708
  ttlCheckInterval: 60000, // 1分钟
1646
1709
  ...options
1647
1710
  };
1648
- this.client = null;
1711
+ // 直接创建 MemoryCache 实例
1712
+ this.client = new MemoryCache({
1713
+ database: this.options.db || 0,
1714
+ maxKeys: this.options.maxKeys,
1715
+ maxMemory: this.options.maxMemory,
1716
+ evictionPolicy: this.options.evictionPolicy,
1717
+ ttlCheckInterval: this.options.ttlCheckInterval
1718
+ });
1719
+ this.client.createClient();
1649
1720
  }
1650
1721
  /**
1651
1722
  * getConnection
@@ -1654,19 +1725,7 @@ class MemoryStore {
1654
1725
  * @memberof MemoryStore
1655
1726
  */
1656
1727
  getConnection() {
1657
- if (!this.pool) {
1658
- this.pool = new MemoryCache({
1659
- database: this.options.db || 0,
1660
- maxKeys: this.options.maxKeys,
1661
- maxMemory: this.options.maxMemory,
1662
- evictionPolicy: this.options.evictionPolicy,
1663
- ttlCheckInterval: this.options.ttlCheckInterval
1664
- });
1665
- }
1666
- if (!this.client) {
1667
- this.client = this.pool.createClient();
1668
- this.client.status = "ready";
1669
- }
1728
+ // 直接返回 MemoryCache 实例
1670
1729
  return this.client;
1671
1730
  }
1672
1731
  /**
@@ -1689,7 +1748,8 @@ class MemoryStore {
1689
1748
  * @memberof MemoryStore
1690
1749
  */
1691
1750
  async release(_conn) {
1692
- return;
1751
+ // 对于内存存储,不需要释放连接
1752
+ return Promise.resolve();
1693
1753
  }
1694
1754
  /**
1695
1755
  * defineCommand
@@ -1753,18 +1813,15 @@ class MemoryStore {
1753
1813
  * @class RedisStore
1754
1814
  */
1755
1815
  class RedisStore {
1756
- options;
1757
- pool;
1758
- client;
1759
- reconnectAttempts = 0;
1760
- maxReconnectAttempts = 5;
1761
- reconnectDelay = 1000; // 初始重连延迟1秒
1762
1816
  /**
1763
1817
  * Creates an instance of RedisStore.
1764
1818
  * @param {RedisStoreOpt} options
1765
1819
  * @memberof RedisStore
1766
1820
  */
1767
1821
  constructor(options) {
1822
+ this.reconnectAttempts = 0;
1823
+ this.maxReconnectAttempts = 5;
1824
+ this.reconnectDelay = 1000; // 初始重连延迟1秒
1768
1825
  this.options = this.parseOpt(options);
1769
1826
  this.pool = null;
1770
1827
  }
@@ -2051,9 +2108,6 @@ const defaultOptions = {
2051
2108
  * @class Store
2052
2109
  */
2053
2110
  class CacheStore {
2054
- client;
2055
- options;
2056
- static instances = new Map();
2057
2111
  /**
2058
2112
  * Creates an instance of CacheStore.
2059
2113
  * @param {StoreOptions} options
@@ -2171,10 +2225,22 @@ class CacheStore {
2171
2225
  return res;
2172
2226
  }
2173
2227
  catch (err) {
2174
- throw err;
2228
+ // 添加详细的错误信息
2229
+ const error = new Error(`Cache operation failed: ${name}(${data.slice(0, 2).join(', ')}${data.length > 2 ? '...' : ''}) - ${err.message}`);
2230
+ error.stack = err.stack;
2231
+ throw error;
2175
2232
  }
2176
2233
  finally {
2177
- this.release(conn);
2234
+ // 安全地释放连接
2235
+ if (conn) {
2236
+ try {
2237
+ await this.release(conn);
2238
+ }
2239
+ catch (releaseErr) {
2240
+ // 记录但不抛出,避免掩盖原始错误
2241
+ DefaultLogger.Error(`Failed to release connection: ${releaseErr.message}`);
2242
+ }
2243
+ }
2178
2244
  }
2179
2245
  }
2180
2246
  /**
@@ -2270,15 +2336,8 @@ class CacheStore {
2270
2336
  * @param timeout
2271
2337
  */
2272
2338
  async hset(name, key, value, timeout) {
2273
- const result = await this.wrap('hset', [`${this.options.keyPrefix || ""}${name}`, key, value]);
2274
- if (typeof timeout === 'number') {
2275
- await this.set(`${name}:${key}_ex`, 1, timeout);
2276
- }
2277
- else {
2278
- // 如果没有指定timeout,设置一个永久标记,避免hget时误删
2279
- await this.set(`${name}:${key}_ex`, 1);
2280
- }
2281
- return result;
2339
+ // MemoryStore 直接支持字段级 TTL,传递 timeout 参数
2340
+ return this.wrap('hset', [`${this.options.keyPrefix || ""}${name}`, key, value, timeout]);
2282
2341
  }
2283
2342
  /**
2284
2343
  * 哈希获取
@@ -2287,15 +2346,8 @@ class CacheStore {
2287
2346
  * @returns {*}
2288
2347
  */
2289
2348
  hget(name, key) {
2290
- const setP = [this.get(`${name}:${key}_ex`)];
2291
- setP.push(this.wrap('hget', [`${this.options.keyPrefix || ""}${name}`, key]));
2292
- return Promise.all(setP).then(dataArr => {
2293
- if (dataArr[0] === null) {
2294
- this.hdel(name, key);
2295
- return null;
2296
- }
2297
- return dataArr[1] || null;
2298
- });
2349
+ // 直接调用底层实现,TTL 检查在 MemoryCache 中处理
2350
+ return this.wrap('hget', [`${this.options.keyPrefix || ""}${name}`, key]);
2299
2351
  }
2300
2352
  /**
2301
2353
  * 查看哈希表 hashKey 中,给定域 key 是否存在
@@ -2304,15 +2356,8 @@ class CacheStore {
2304
2356
  * @returns {*}
2305
2357
  */
2306
2358
  hexists(name, key) {
2307
- const setP = [this.get(`${name}:${key}_ex`)];
2308
- setP.push(this.wrap('hexists', [`${this.options.keyPrefix || ""}${name}`, key]));
2309
- return Promise.all(setP).then(dataArr => {
2310
- if (dataArr[0] === null) {
2311
- this.hdel(name, key);
2312
- return 0;
2313
- }
2314
- return Number(dataArr[1]) || 0;
2315
- });
2359
+ // 直接调用底层实现,TTL 检查在 MemoryCache 中处理
2360
+ return this.wrap('hexists', [`${this.options.keyPrefix || ""}${name}`, key]);
2316
2361
  }
2317
2362
  /**
2318
2363
  * 哈希删除
@@ -2321,9 +2366,8 @@ class CacheStore {
2321
2366
  * @returns {*}
2322
2367
  */
2323
2368
  async hdel(name, key) {
2324
- await this.del(`${name}:${key}_ex`);
2325
- const result = await this.wrap('hdel', [`${this.options.keyPrefix || ""}${name}`, key]);
2326
- return result;
2369
+ // 直接调用底层实现,TTL 清理在 MemoryCache 中处理
2370
+ return this.wrap('hdel', [`${this.options.keyPrefix || ""}${name}`, key]);
2327
2371
  }
2328
2372
  /**
2329
2373
  * 返回哈希表 key 中域的数量
@@ -2490,5 +2534,6 @@ class CacheStore {
2490
2534
  return this.wrap('smove', [`${this.options.keyPrefix || ""}${source}`, `${this.options.keyPrefix || ""}${destination}`, member]);
2491
2535
  }
2492
2536
  }
2537
+ CacheStore.instances = new Map();
2493
2538
 
2494
2539
  export { CacheStore };
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koatty_store",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Cache store for koatty.",
5
5
  "scripts": {
6
6
  "build": "npm run build:js && npm run build:dts && npm run build:doc && npm run build:cp",
@@ -65,6 +65,7 @@
65
65
  "@rollup/plugin-commonjs": "^28.x.x",
66
66
  "@rollup/plugin-json": "^6.x.x",
67
67
  "@rollup/plugin-node-resolve": "^15.x.x",
68
+ "@types/async-lock": "^1.4.2",
68
69
  "@types/jest": "^29.x.x",
69
70
  "@types/koa": "^2.x.x",
70
71
  "@types/lodash": "^4.x.x",
@@ -91,12 +92,13 @@
91
92
  "typescript": "^5.x.x"
92
93
  },
93
94
  "dependencies": {
95
+ "async-lock": "^1.4.1",
94
96
  "generic-pool": "^3.9.0",
95
- "ioredis": "^5.4.2",
97
+ "ioredis": "^5.8.2",
96
98
  "koatty_lib": "^1.x.x",
97
99
  "koatty_logger": "^2.x.x",
98
100
  "lodash": "^4.17.21",
99
- "lru-cache": "^11.1.0"
101
+ "lru-cache": "^11.2.2"
100
102
  },
101
103
  "peerDependencies": {
102
104
  "koatty_lib": "^1.x.x",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koatty_store",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Cache store for koatty.",
5
5
  "scripts": {
6
6
  "build": "npm run build:js && npm run build:dts && npm run build:doc && npm run build:cp",
@@ -65,6 +65,7 @@
65
65
  "@rollup/plugin-commonjs": "^28.x.x",
66
66
  "@rollup/plugin-json": "^6.x.x",
67
67
  "@rollup/plugin-node-resolve": "^15.x.x",
68
+ "@types/async-lock": "^1.4.2",
68
69
  "@types/jest": "^29.x.x",
69
70
  "@types/koa": "^2.x.x",
70
71
  "@types/lodash": "^4.x.x",
@@ -91,12 +92,13 @@
91
92
  "typescript": "^5.x.x"
92
93
  },
93
94
  "dependencies": {
95
+ "async-lock": "^1.4.1",
94
96
  "generic-pool": "^3.9.0",
95
- "ioredis": "^5.4.2",
97
+ "ioredis": "^5.8.2",
96
98
  "koatty_lib": "^1.x.x",
97
99
  "koatty_logger": "^2.x.x",
98
100
  "lodash": "^4.17.21",
99
- "lru-cache": "^11.1.0"
101
+ "lru-cache": "^11.2.2"
100
102
  },
101
103
  "peerDependencies": {
102
104
  "koatty_lib": "^1.x.x",
@@ -1,25 +0,0 @@
1
- {
2
- // 使用 IntelliSense 了解相关属性。
3
- // 悬停以查看现有属性的描述。
4
- // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5
- "version": "0.2.0",
6
- "configurations": [
7
- {
8
- "name": "Launch Typescript Project",
9
- "type": "node",
10
- "request": "launch",
11
- "args": [
12
- "${workspaceRoot}/test/test.ts"
13
- ],
14
- "runtimeArgs": [
15
- "--nolazy",
16
- "-r",
17
- "ts-node/register"
18
- ],
19
- "sourceMaps": true,
20
- "cwd": "${workspaceRoot}",
21
- "protocol": "inspector",
22
- "internalConsoleOptions": "neverOpen"
23
- }
24
- ]
25
- }