koatty_store 1.7.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: 2024-11-07 14:38:56
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/
@@ -8,6 +8,8 @@
8
8
  import { flatten, isNil, union, isUndefined } from 'lodash';
9
9
  import * as helper from 'koatty_lib';
10
10
  import { EventEmitter } from 'events';
11
+ import { LRUCache } from 'lru-cache';
12
+ import AsyncLock from 'async-lock';
11
13
  import { DefaultLogger } from 'koatty_logger';
12
14
  import { Cluster, Redis } from 'ioredis';
13
15
  import genericPool from 'generic-pool';
@@ -49,26 +51,100 @@ var messages;
49
51
  messages["mutuallyExclusiveNXXX"] = "ERR XX and NX options at the same time are not compatible";
50
52
  })(messages || (messages = {}));
51
53
  class MemoryCache extends EventEmitter {
52
- databases = Object.create({});
53
- options;
54
- currentDBIndex;
55
- connected;
56
- lastSave;
57
- multiMode;
58
- cache;
59
- responseMessages;
60
54
  /**
61
55
  * Creates an instance of MemoryCache.
62
- * @param {*} options
56
+ * @param {MemoryCacheOptions} options
63
57
  * @memberof MemoryCache
64
58
  */
65
59
  constructor(options) {
66
60
  super();
67
- this.options = { ...{ database: "0" }, ...options };
68
- this.currentDBIndex = 0;
61
+ this.databases = new Map();
62
+ this.ttlCheckTimer = null;
63
+ this.options = {
64
+ database: 0,
65
+ maxKeys: 1000,
66
+ evictionPolicy: 'lru',
67
+ ttlCheckInterval: 60000, // 1分钟检查一次过期键
68
+ maxAge: 1000 * 60 * 60, // 默认1小时过期
69
+ ...options
70
+ };
71
+ this.currentDBIndex = options.database || 0;
69
72
  this.connected = false;
70
73
  this.lastSave = Date.now();
71
74
  this.multiMode = false;
75
+ this.responseMessages = [];
76
+ this.lock = new AsyncLock();
77
+ // 初始化数据库和缓存
78
+ if (!this.databases.has(this.currentDBIndex)) {
79
+ this.databases.set(this.currentDBIndex, this.createLRUCache());
80
+ }
81
+ this.cache = this.databases.get(this.currentDBIndex);
82
+ // 启动TTL检查定时器
83
+ this.startTTLCheck();
84
+ }
85
+ /**
86
+ * 创建LRU缓存实例
87
+ */
88
+ createLRUCache() {
89
+ return new LRUCache({
90
+ max: this.options.maxKeys || 1000,
91
+ ttl: this.options.maxAge || 1000 * 60 * 60, // 1小时默认
92
+ updateAgeOnGet: true, // 访问时更新age
93
+ dispose: (value, key, reason) => {
94
+ // 键被淘汰时的回调 - 直接使用lru-cache的事件机制
95
+ this.emit('evict', key, value, reason);
96
+ },
97
+ onInsert: (value, key) => {
98
+ // 键被插入时的回调
99
+ this.emit('insert', key, value);
100
+ }
101
+ });
102
+ }
103
+ /**
104
+ * 启动TTL检查定时器
105
+ */
106
+ startTTLCheck() {
107
+ if (this.ttlCheckTimer) {
108
+ clearInterval(this.ttlCheckTimer);
109
+ }
110
+ this.ttlCheckTimer = setInterval(() => {
111
+ this.cleanExpiredKeys();
112
+ }, this.options.ttlCheckInterval || 60000);
113
+ }
114
+ /**
115
+ * 清理过期键
116
+ */
117
+ cleanExpiredKeys() {
118
+ for (const [_dbIndex, cache] of this.databases) {
119
+ const keysToDelete = [];
120
+ cache.forEach((item, key) => {
121
+ if (item.timeout && item.timeout <= Date.now()) {
122
+ keysToDelete.push(key);
123
+ }
124
+ });
125
+ keysToDelete.forEach(key => {
126
+ cache.delete(key);
127
+ this.emit('expire', key);
128
+ });
129
+ }
130
+ }
131
+ /**
132
+ * 停止TTL检查
133
+ */
134
+ stopTTLCheck() {
135
+ if (this.ttlCheckTimer) {
136
+ clearInterval(this.ttlCheckTimer);
137
+ this.ttlCheckTimer = null;
138
+ }
139
+ }
140
+ /**
141
+ * 清理所有资源
142
+ * @private
143
+ */
144
+ cleanup() {
145
+ this.stopTTLCheck();
146
+ this.databases.clear();
147
+ this.removeAllListeners();
72
148
  }
73
149
  /**
74
150
  *
@@ -77,8 +153,10 @@ class MemoryCache extends EventEmitter {
77
153
  * @memberof MemoryCache
78
154
  */
79
155
  createClient() {
80
- this.databases[this.options.database] = Object.create({});
81
- this.cache = this.databases[this.options.database];
156
+ if (!this.databases.has(this.options.database)) {
157
+ this.databases.set(this.options.database, this.createLRUCache());
158
+ }
159
+ this.cache = this.databases.get(this.options.database);
82
160
  this.connected = true;
83
161
  // exit multi mode if we are in it
84
162
  this.discard(null, true);
@@ -93,6 +171,7 @@ class MemoryCache extends EventEmitter {
93
171
  * @memberof MemoryCache
94
172
  */
95
173
  quit() {
174
+ this.cleanup(); // 调用清理方法
96
175
  this.connected = false;
97
176
  // exit multi mode if we are in it
98
177
  this.discard(null, true);
@@ -108,6 +187,40 @@ class MemoryCache extends EventEmitter {
108
187
  end() {
109
188
  return this.quit();
110
189
  }
190
+ /**
191
+ * 获取缓存统计信息
192
+ */
193
+ info() {
194
+ const stats = {
195
+ databases: this.databases.size,
196
+ currentDB: this.currentDBIndex,
197
+ keys: this.cache ? this.cache.length : 0,
198
+ maxKeys: this.options.maxKeys,
199
+ hits: 0,
200
+ misses: 0,
201
+ memory: this.getMemoryUsage()
202
+ };
203
+ // 如果缓存支持统计信息
204
+ if (this.cache && typeof this.cache.dump === 'function') {
205
+ const dump = this.cache.dump();
206
+ stats.keys = dump.length;
207
+ }
208
+ return stats;
209
+ }
210
+ /**
211
+ * 估算内存使用量
212
+ */
213
+ getMemoryUsage() {
214
+ let totalSize = 0;
215
+ for (const [, cache] of this.databases) {
216
+ cache.forEach((item, key) => {
217
+ // 粗略估算:key长度 + JSON序列化后的大小
218
+ totalSize += key.length * 2; // Unicode字符占2字节
219
+ totalSize += JSON.stringify(item).length * 2;
220
+ });
221
+ }
222
+ return totalSize;
223
+ }
111
224
  /**
112
225
  *
113
226
  *
@@ -154,12 +267,12 @@ class MemoryCache extends EventEmitter {
154
267
  if (!helper.isNumber(dbIndex)) {
155
268
  return this._handleCallback(callback, null, messages.invalidDBIndex);
156
269
  }
157
- if (!this.databases.hasOwnProperty(dbIndex)) {
158
- this.databases[dbIndex] = Object.create({});
270
+ if (!this.databases.has(dbIndex)) {
271
+ this.databases.set(dbIndex, this.createLRUCache());
159
272
  }
160
273
  this.multiMode = false;
161
274
  this.currentDBIndex = dbIndex;
162
- this.cache = this.databases[dbIndex];
275
+ this.cache = this.databases.get(dbIndex);
163
276
  return this._handleCallback(callback, messages.ok);
164
277
  }
165
278
  // ---------------------------------------
@@ -238,7 +351,7 @@ class MemoryCache extends EventEmitter {
238
351
  else if (onlyexist) {
239
352
  return this._handleCallback(callback, retVal);
240
353
  }
241
- this.cache[key] = this._makeKey(value.toString(), 'string', pttl);
354
+ this.cache.set(key, this._makeKey(value.toString(), 'string', pttl));
242
355
  return this._handleCallback(callback, messages.ok);
243
356
  }
244
357
  /**
@@ -268,7 +381,8 @@ class MemoryCache extends EventEmitter {
268
381
  expire(key, seconds, callback) {
269
382
  let retVal = 0;
270
383
  if (this._hasKey(key)) {
271
- this.cache[key].timeout = Date.now() + seconds * 1000;
384
+ const pttl = seconds * 1000;
385
+ this.cache.set(key, { ...this.cache.get(key), timeout: Date.now() + pttl });
272
386
  retVal = 1;
273
387
  }
274
388
  return this._handleCallback(callback, retVal);
@@ -288,7 +402,7 @@ class MemoryCache extends EventEmitter {
288
402
  for (let itr = 0; itr < keys.length; itr++) {
289
403
  const key = keys[itr];
290
404
  if (this._hasKey(key)) {
291
- delete this.cache[key];
405
+ this.cache.delete(key);
292
406
  retVal++;
293
407
  }
294
408
  }
@@ -321,14 +435,18 @@ class MemoryCache extends EventEmitter {
321
435
  * @memberof MemoryCache
322
436
  */
323
437
  incr(key, callback) {
324
- let retVal = null;
325
- try {
326
- retVal = this._addToKey(key, 1);
327
- }
328
- catch (err) {
329
- return this._handleCallback(callback, null, err);
330
- }
331
- 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
+ });
332
450
  }
333
451
  /**
334
452
  *
@@ -340,14 +458,18 @@ class MemoryCache extends EventEmitter {
340
458
  * @memberof MemoryCache
341
459
  */
342
460
  incrby(key, amount, callback) {
343
- let retVal = null;
344
- try {
345
- retVal = this._addToKey(key, amount);
346
- }
347
- catch (err) {
348
- return this._handleCallback(callback, null, err);
349
- }
350
- 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
+ });
351
473
  }
352
474
  /**
353
475
  *
@@ -358,14 +480,18 @@ class MemoryCache extends EventEmitter {
358
480
  * @memberof MemoryCache
359
481
  */
360
482
  decr(key, callback) {
361
- let retVal = null;
362
- try {
363
- retVal = this._addToKey(key, -1);
364
- }
365
- catch (err) {
366
- return this._handleCallback(callback, null, err);
367
- }
368
- 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
+ });
369
495
  }
370
496
  /**
371
497
  *
@@ -377,30 +503,42 @@ class MemoryCache extends EventEmitter {
377
503
  * @memberof MemoryCache
378
504
  */
379
505
  decrby(key, amount, callback) {
380
- let retVal = null;
381
- try {
382
- retVal = this._addToKey(key, -amount);
383
- }
384
- catch (err) {
385
- return this._handleCallback(callback, null, err);
386
- }
387
- 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
+ });
388
518
  }
389
519
  // ---------------------------------------
390
520
  // ## Hash ##
391
521
  // ---------------------------------------
392
- hset(key, field, value, callback) {
522
+ hset(key, field, value, timeout, callback) {
393
523
  let retVal = 0;
394
524
  if (this._hasKey(key)) {
395
525
  this._testType(key, 'hash', true, callback);
396
526
  }
397
527
  else {
398
- this.cache[key] = this._makeKey({}, 'hash');
528
+ this.cache.set(key, this._makeKey({}, 'hash'));
399
529
  }
400
530
  if (!this._hasField(key, field)) {
401
531
  retVal = 1;
402
532
  }
403
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
+ }
404
542
  this.persist(key);
405
543
  return this._handleCallback(callback, retVal);
406
544
  }
@@ -417,8 +555,17 @@ class MemoryCache extends EventEmitter {
417
555
  let retVal = null;
418
556
  if (this._hasKey(key)) {
419
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
+ }
420
567
  if (this._hasField(key, field)) {
421
- retVal = this._getKey(key)[field];
568
+ retVal = hashObj[field];
422
569
  }
423
570
  }
424
571
  return this._handleCallback(callback, retVal);
@@ -436,6 +583,15 @@ class MemoryCache extends EventEmitter {
436
583
  let retVal = 0;
437
584
  if (this._hasKey(key)) {
438
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
+ }
439
595
  if (this._hasField(key, field)) {
440
596
  retVal = 1;
441
597
  }
@@ -455,10 +611,15 @@ class MemoryCache extends EventEmitter {
455
611
  const callback = this._retrieveCallback(fields);
456
612
  if (this._hasKey(key)) {
457
613
  this._testType(key, 'hash', true, callback);
614
+ const hashObj = this.cache.get(key).value;
458
615
  for (let itr = 0; itr < fields.length; itr++) {
459
616
  const field = fields[itr];
460
617
  if (this._hasField(key, field)) {
461
- delete this.cache[key].value[field];
618
+ delete hashObj[field];
619
+ // 清理过期时间记录
620
+ if (hashObj._fieldTTL && hashObj._fieldTTL[field]) {
621
+ delete hashObj._fieldTTL[field];
622
+ }
462
623
  retVal++;
463
624
  }
464
625
  }
@@ -488,14 +649,18 @@ class MemoryCache extends EventEmitter {
488
649
  * @memberof MemoryCache
489
650
  */
490
651
  hincrby(key, field, value, callback) {
491
- let retVal;
492
- try {
493
- retVal = this._addToField(key, field, value, false);
494
- }
495
- catch (err) {
496
- return this._handleCallback(callback, null, err);
497
- }
498
- 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
+ });
499
664
  }
500
665
  /**
501
666
  *
@@ -509,7 +674,11 @@ class MemoryCache extends EventEmitter {
509
674
  let retVals = {};
510
675
  if (this._hasKey(key)) {
511
676
  this._testType(key, 'hash', true, callback);
512
- 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 }), {});
513
682
  }
514
683
  return this._handleCallback(callback, retVals);
515
684
  }
@@ -525,7 +694,9 @@ class MemoryCache extends EventEmitter {
525
694
  let retVals = [];
526
695
  if (this._hasKey(key)) {
527
696
  this._testType(key, 'hash', true, callback);
528
- retVals = Object.keys(this._getKey(key));
697
+ const hashObj = this._getKey(key);
698
+ // 排除内部属性 _fieldTTL
699
+ retVals = Object.keys(hashObj).filter(k => k !== '_fieldTTL');
529
700
  }
530
701
  return this._handleCallback(callback, retVals);
531
702
  }
@@ -541,7 +712,11 @@ class MemoryCache extends EventEmitter {
541
712
  let retVals = [];
542
713
  if (this._hasKey(key)) {
543
714
  this._testType(key, 'hash', true, callback);
544
- 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);
545
720
  }
546
721
  return this._handleCallback(callback, retVals);
547
722
  }
@@ -579,22 +754,18 @@ class MemoryCache extends EventEmitter {
579
754
  this._testType(key, 'list', true, callback);
580
755
  }
581
756
  else {
582
- this.cache[key] = this._makeKey([], 'list');
757
+ this.cache.set(key, this._makeKey([], 'list'));
583
758
  }
584
- const val = this._getKey(key);
585
- val.push(value);
586
- this._setKey(key, val);
587
- retVal = val.length;
759
+ this._getKey(key).push(value.toString());
760
+ retVal = this._getKey(key).length;
761
+ this.persist(key);
588
762
  return this._handleCallback(callback, retVal);
589
763
  }
590
764
  /**
591
- *
592
- *
593
- * @param {string} key
594
- * @param {(string | number)} value
595
- * @param {Function} [callback]
596
- * @returns {*}
597
- * @memberof MemoryCache
765
+ * List:从左侧推入
766
+ * @param key
767
+ * @param value
768
+ * @param callback
598
769
  */
599
770
  lpush(key, value, callback) {
600
771
  let retVal = 0;
@@ -602,14 +773,31 @@ class MemoryCache extends EventEmitter {
602
773
  this._testType(key, 'list', true, callback);
603
774
  }
604
775
  else {
605
- this.cache[key] = this._makeKey([], 'list');
776
+ this.cache.set(key, this._makeKey([], 'list'));
606
777
  }
607
- const val = this._getKey(key);
608
- val.splice(0, 0, value);
609
- this._setKey(key, val);
610
- retVal = val.length;
778
+ const list = this._getKey(key);
779
+ retVal = list.unshift(value);
780
+ this._setKey(key, list);
611
781
  return this._handleCallback(callback, retVal);
612
782
  }
783
+ /**
784
+ * List:获取指定索引的元素
785
+ * @param key
786
+ * @param index
787
+ * @param callback
788
+ */
789
+ lindex(key, index, callback) {
790
+ if (!this._hasKey(key)) {
791
+ return this._handleCallback(callback, null);
792
+ }
793
+ this._testType(key, 'list', true, callback);
794
+ const list = this._getKey(key);
795
+ if (index < 0) {
796
+ index = list.length + index;
797
+ }
798
+ const value = index >= 0 && index < list.length ? list[index] : null;
799
+ return this._handleCallback(callback, value);
800
+ }
613
801
  /**
614
802
  *
615
803
  *
@@ -622,9 +810,11 @@ class MemoryCache extends EventEmitter {
622
810
  let retVal = null;
623
811
  if (this._hasKey(key)) {
624
812
  this._testType(key, 'list', true, callback);
625
- const val = this._getKey(key);
626
- retVal = val.shift();
627
- this._setKey(key, val);
813
+ const list = this._getKey(key);
814
+ if (list.length > 0) {
815
+ retVal = list.shift();
816
+ this.persist(key);
817
+ }
628
818
  }
629
819
  return this._handleCallback(callback, retVal);
630
820
  }
@@ -640,9 +830,11 @@ class MemoryCache extends EventEmitter {
640
830
  let retVal = null;
641
831
  if (this._hasKey(key)) {
642
832
  this._testType(key, 'list', true, callback);
643
- const val = this._getKey(key);
644
- retVal = val.pop();
645
- this._setKey(key, val);
833
+ const list = this._getKey(key);
834
+ if (list.length > 0) {
835
+ retVal = list.pop();
836
+ this.persist(key);
837
+ }
646
838
  }
647
839
  return this._handleCallback(callback, retVal);
648
840
  }
@@ -660,8 +852,8 @@ class MemoryCache extends EventEmitter {
660
852
  const retVal = [];
661
853
  if (this._hasKey(key)) {
662
854
  this._testType(key, 'list', true, callback);
663
- const val = this._getKey(key);
664
- const length = val.length;
855
+ const list = this._getKey(key);
856
+ const length = list.length;
665
857
  if (stop < 0) {
666
858
  stop = length + stop;
667
859
  }
@@ -675,9 +867,8 @@ class MemoryCache extends EventEmitter {
675
867
  stop = length - 1;
676
868
  }
677
869
  if (stop >= 0 && stop >= start) {
678
- const size = stop - start + 1;
679
- for (let itr = start; itr < size; itr++) {
680
- retVal.push(val[itr]);
870
+ for (let itr = start; itr <= stop; itr++) {
871
+ retVal.push(list[itr]);
681
872
  }
682
873
  }
683
874
  }
@@ -701,7 +892,7 @@ class MemoryCache extends EventEmitter {
701
892
  this._testType(key, 'set', true, callback);
702
893
  }
703
894
  else {
704
- this.cache[key] = this._makeKey([], 'set');
895
+ this.cache.set(key, this._makeKey([], 'set'));
705
896
  }
706
897
  const val = this._getKey(key);
707
898
  const length = val.length;
@@ -773,19 +964,29 @@ class MemoryCache extends EventEmitter {
773
964
  * @memberof MemoryCache
774
965
  */
775
966
  spop(key, count, callback) {
776
- let retVal = null;
967
+ let retVal = [];
777
968
  count = count || 1;
778
- if (isNaN(count)) {
779
- return this._handleCallback(callback, null, messages.noint);
969
+ if (typeof count === 'function') {
970
+ callback = count;
971
+ count = 1;
780
972
  }
781
973
  if (this._hasKey(key)) {
782
- retVal = [];
783
974
  this._testType(key, 'set', true, callback);
784
975
  const val = this._getKey(key);
785
- const length = val.length;
786
- count = count > length ? length : count;
787
- for (let itr = 0; itr < count; itr++) {
788
- retVal.push(val.pop());
976
+ const keys = Object.keys(val);
977
+ const keysLength = keys.length;
978
+ if (keysLength) {
979
+ if (count >= keysLength) {
980
+ retVal = keys;
981
+ this.del(key);
982
+ }
983
+ else {
984
+ for (let itr = 0; itr < count; itr++) {
985
+ const randomNum = Math.floor(Math.random() * keys.length);
986
+ retVal.push(keys[randomNum]);
987
+ this.srem(key, keys[randomNum]);
988
+ }
989
+ }
789
990
  }
790
991
  }
791
992
  return this._handleCallback(callback, retVal);
@@ -852,14 +1053,14 @@ class MemoryCache extends EventEmitter {
852
1053
  discard(callback, silent) {
853
1054
  // Clear the queue mode, drain the queue, empty the watch list
854
1055
  if (this.multiMode) {
855
- this.cache = this.databases[this.currentDBIndex];
1056
+ this.cache = this.databases.get(this.currentDBIndex);
856
1057
  this.multiMode = false;
857
1058
  this.responseMessages = [];
858
1059
  }
859
- else if (!silent) {
860
- return this._handleCallback(callback, null, messages.nomultidiscard);
1060
+ if (!silent) {
1061
+ return this._handleCallback(callback, messages.ok);
861
1062
  }
862
- return this._handleCallback(callback, messages.ok);
1063
+ return null;
863
1064
  }
864
1065
  // ---------------------------------------
865
1066
  // ## Internal - Key ##
@@ -872,11 +1073,12 @@ class MemoryCache extends EventEmitter {
872
1073
  * @returns {*}
873
1074
  * @memberof MemoryCache
874
1075
  */
875
- pttl(key, callback) {
1076
+ pttl(key, _callback) {
1077
+ var _a;
876
1078
  let retVal = -2;
877
1079
  if (this._hasKey(key)) {
878
- if (!isNil(this.cache[key].timeout)) {
879
- retVal = this.cache[key].timeout - Date.now();
1080
+ if (!isNil((_a = this.cache.get(key)) === null || _a === void 0 ? void 0 : _a.timeout)) {
1081
+ retVal = this.cache.get(key).timeout - Date.now();
880
1082
  // Prevent unexpected errors if the actual ttl just happens to be -2 or -1
881
1083
  if (retVal < 0 && retVal > -3) {
882
1084
  retVal = -3;
@@ -886,7 +1088,7 @@ class MemoryCache extends EventEmitter {
886
1088
  retVal = -1;
887
1089
  }
888
1090
  }
889
- return this._handleCallback(callback, retVal);
1091
+ return retVal;
890
1092
  }
891
1093
  /**
892
1094
  *
@@ -901,7 +1103,7 @@ class MemoryCache extends EventEmitter {
901
1103
  let retVal = 0;
902
1104
  if (this._hasKey(key)) {
903
1105
  if (!isNil(this._key(key).timeout)) {
904
- this._key(key).timeout = null;
1106
+ this.cache.set(key, { ...this.cache.get(key), timeout: null });
905
1107
  retVal = 1;
906
1108
  }
907
1109
  }
@@ -916,7 +1118,7 @@ class MemoryCache extends EventEmitter {
916
1118
  * @memberof MemoryCache
917
1119
  */
918
1120
  _hasKey(key) {
919
- return this.cache.hasOwnProperty(key);
1121
+ return this.cache.has(key);
920
1122
  }
921
1123
  /**
922
1124
  *
@@ -940,8 +1142,8 @@ class MemoryCache extends EventEmitter {
940
1142
  * @memberof MemoryCache
941
1143
  */
942
1144
  _key(key) {
943
- this.cache[key].lastAccess = Date.now();
944
- return this.cache[key];
1145
+ this.cache.get(key).lastAccess = Date.now();
1146
+ return this.cache.get(key);
945
1147
  }
946
1148
  /**
947
1149
  *
@@ -966,7 +1168,7 @@ class MemoryCache extends EventEmitter {
966
1168
  }
967
1169
  }
968
1170
  else {
969
- this.cache[key] = this._makeKey('0', 'string');
1171
+ this.cache.set(key, this._makeKey('0', 'string'));
970
1172
  }
971
1173
  const val = keyValue + amount;
972
1174
  this._setKey(key, val.toString());
@@ -1019,8 +1221,7 @@ class MemoryCache extends EventEmitter {
1019
1221
  * @memberof MemoryCache
1020
1222
  */
1021
1223
  _setKey(key, value) {
1022
- this.cache[key].value = value;
1023
- this.cache[key].lastAccess = Date.now();
1224
+ this.cache.set(key, { ...this.cache.get(key), value: value, lastAccess: Date.now() });
1024
1225
  }
1025
1226
  /**
1026
1227
  *
@@ -1048,7 +1249,7 @@ class MemoryCache extends EventEmitter {
1048
1249
  }
1049
1250
  }
1050
1251
  else {
1051
- this.cache[key] = this._makeKey({}, 'hash');
1252
+ this.cache.set(key, this._makeKey({}, 'hash'));
1052
1253
  }
1053
1254
  fieldValue = useFloat ? parseFloat(`${value}`) : parseInt(`${value}`);
1054
1255
  amount = useFloat ? parseFloat(`${amount}`) : parseInt(`${amount}`);
@@ -1085,7 +1286,8 @@ class MemoryCache extends EventEmitter {
1085
1286
  if (key && field) {
1086
1287
  const ky = this._getKey(key);
1087
1288
  if (ky) {
1088
- retVal = ky.hasOwnProperty(field);
1289
+ // 排除内部属性 _fieldTTL
1290
+ retVal = field !== '_fieldTTL' && ky.hasOwnProperty(field);
1089
1291
  }
1090
1292
  }
1091
1293
  return retVal;
@@ -1157,6 +1359,333 @@ class MemoryCache extends EventEmitter {
1157
1359
  }
1158
1360
  return;
1159
1361
  }
1362
+ /**
1363
+ * 字符串追加操作
1364
+ * @param key
1365
+ * @param value
1366
+ * @param callback
1367
+ */
1368
+ append(key, value, callback) {
1369
+ let retVal = 0;
1370
+ if (this._hasKey(key)) {
1371
+ this._testType(key, 'string', true, callback);
1372
+ const existingValue = this._getKey(key);
1373
+ const newValue = existingValue + value;
1374
+ this._setKey(key, newValue);
1375
+ retVal = newValue.length;
1376
+ }
1377
+ else {
1378
+ this.cache.set(key, this._makeKey(value, 'string'));
1379
+ retVal = value.length;
1380
+ }
1381
+ return this._handleCallback(callback, retVal);
1382
+ }
1383
+ /**
1384
+ * 获取字符串长度
1385
+ * @param key
1386
+ * @param callback
1387
+ */
1388
+ strlen(key, callback) {
1389
+ let retVal = 0;
1390
+ if (this._hasKey(key)) {
1391
+ this._testType(key, 'string', true, callback);
1392
+ retVal = this._getKey(key).length;
1393
+ }
1394
+ return this._handleCallback(callback, retVal);
1395
+ }
1396
+ /**
1397
+ * 获取子字符串
1398
+ * @param key
1399
+ * @param start
1400
+ * @param end
1401
+ * @param callback
1402
+ */
1403
+ getrange(key, start, end, callback) {
1404
+ let retVal = '';
1405
+ if (this._hasKey(key)) {
1406
+ this._testType(key, 'string', true, callback);
1407
+ const value = this._getKey(key);
1408
+ retVal = value.substring(start, end + 1);
1409
+ }
1410
+ return this._handleCallback(callback, retVal);
1411
+ }
1412
+ /**
1413
+ * 设置子字符串
1414
+ * @param key
1415
+ * @param offset
1416
+ * @param value
1417
+ * @param callback
1418
+ */
1419
+ setrange(key, offset, value, callback) {
1420
+ let retVal = 0;
1421
+ if (this._hasKey(key)) {
1422
+ this._testType(key, 'string', true, callback);
1423
+ const existingValue = this._getKey(key);
1424
+ const newValue = existingValue.substring(0, offset) + value + existingValue.substring(offset + value.length);
1425
+ this._setKey(key, newValue);
1426
+ retVal = newValue.length;
1427
+ }
1428
+ else {
1429
+ // 如果键不存在,创建一个足够长的字符串
1430
+ const newValue = ''.padEnd(offset, '\0') + value;
1431
+ this.cache.set(key, this._makeKey(newValue, 'string'));
1432
+ retVal = newValue.length;
1433
+ }
1434
+ return this._handleCallback(callback, retVal);
1435
+ }
1436
+ /**
1437
+ * 批量获取
1438
+ * @param keys
1439
+ * @param callback
1440
+ */
1441
+ mget(...keys) {
1442
+ const callback = this._retrieveCallback(keys);
1443
+ const retVal = [];
1444
+ for (const key of keys) {
1445
+ if (this._hasKey(key)) {
1446
+ this._testType(key, 'string', false, callback);
1447
+ retVal.push(this._getKey(key));
1448
+ }
1449
+ else {
1450
+ retVal.push(null);
1451
+ }
1452
+ }
1453
+ return this._handleCallback(callback, retVal);
1454
+ }
1455
+ /**
1456
+ * 批量设置
1457
+ * @param keyValuePairs
1458
+ * @param callback
1459
+ */
1460
+ mset(...keyValuePairs) {
1461
+ const callback = this._retrieveCallback(keyValuePairs);
1462
+ // 确保参数是偶数个
1463
+ if (keyValuePairs.length % 2 !== 0) {
1464
+ return this._handleCallback(callback, null, messages.wrongArgCount.replace('%0', 'mset'));
1465
+ }
1466
+ for (let i = 0; i < keyValuePairs.length; i += 2) {
1467
+ const key = keyValuePairs[i];
1468
+ const value = keyValuePairs[i + 1];
1469
+ this.cache.set(key, this._makeKey(value.toString(), 'string'));
1470
+ }
1471
+ return this._handleCallback(callback, messages.ok);
1472
+ }
1473
+ /**
1474
+ * 获取所有键
1475
+ * @param pattern
1476
+ * @param callback
1477
+ */
1478
+ keys(pattern = '*', callback) {
1479
+ const retVal = [];
1480
+ this.cache.forEach((_item, key) => {
1481
+ if (pattern === '*' || this.matchPattern(key, pattern)) {
1482
+ retVal.push(key);
1483
+ }
1484
+ });
1485
+ return this._handleCallback(callback, retVal);
1486
+ }
1487
+ /**
1488
+ * 简单的模式匹配
1489
+ * @param key
1490
+ * @param pattern
1491
+ */
1492
+ matchPattern(key, pattern) {
1493
+ if (pattern === '*')
1494
+ return true;
1495
+ // 转换glob模式为正则表达式
1496
+ const regexPattern = pattern
1497
+ .replace(/\*/g, '.*')
1498
+ .replace(/\?/g, '.')
1499
+ .replace(/\[([^\]]*)\]/g, '[$1]');
1500
+ const regex = new RegExp(`^${regexPattern}$`);
1501
+ return regex.test(key);
1502
+ }
1503
+ /**
1504
+ * 获取随机键
1505
+ * @param callback
1506
+ */
1507
+ randomkey(callback) {
1508
+ const keys = [];
1509
+ this.cache.forEach((_item, key) => {
1510
+ keys.push(key);
1511
+ });
1512
+ if (keys.length === 0) {
1513
+ return this._handleCallback(callback, null);
1514
+ }
1515
+ const randomIndex = Math.floor(Math.random() * keys.length);
1516
+ return this._handleCallback(callback, keys[randomIndex]);
1517
+ }
1518
+ /**
1519
+ * 重命名键
1520
+ * @param oldKey
1521
+ * @param newKey
1522
+ * @param callback
1523
+ */
1524
+ rename(oldKey, newKey, callback) {
1525
+ if (!this._hasKey(oldKey)) {
1526
+ return this._handleCallback(callback, null, messages.nokey);
1527
+ }
1528
+ const value = this.cache.get(oldKey);
1529
+ this.cache.set(newKey, value);
1530
+ this.cache.delete(oldKey);
1531
+ return this._handleCallback(callback, messages.ok);
1532
+ }
1533
+ /**
1534
+ * 安全重命名键(目标键不存在时才重命名)
1535
+ * @param oldKey
1536
+ * @param newKey
1537
+ * @param callback
1538
+ */
1539
+ renamenx(oldKey, newKey, callback) {
1540
+ if (!this._hasKey(oldKey)) {
1541
+ return this._handleCallback(callback, null, messages.nokey);
1542
+ }
1543
+ if (this._hasKey(newKey)) {
1544
+ return this._handleCallback(callback, 0);
1545
+ }
1546
+ const value = this.cache.get(oldKey);
1547
+ this.cache.set(newKey, value);
1548
+ this.cache.delete(oldKey);
1549
+ return this._handleCallback(callback, 1);
1550
+ }
1551
+ /**
1552
+ * 获取键的类型
1553
+ * @param key
1554
+ * @param callback
1555
+ */
1556
+ type(key, callback) {
1557
+ if (!this._hasKey(key)) {
1558
+ return this._handleCallback(callback, 'none');
1559
+ }
1560
+ const item = this.cache.get(key);
1561
+ return this._handleCallback(callback, item.type);
1562
+ }
1563
+ /**
1564
+ * 清空当前数据库
1565
+ * @param callback
1566
+ */
1567
+ flushdb(callback) {
1568
+ this.cache.clear();
1569
+ return this._handleCallback(callback, messages.ok);
1570
+ }
1571
+ /**
1572
+ * 清空所有数据库
1573
+ * @param callback
1574
+ */
1575
+ flushall(callback) {
1576
+ this.databases.clear();
1577
+ this.cache = this.createLRUCache();
1578
+ this.databases.set(this.currentDBIndex, this.cache);
1579
+ return this._handleCallback(callback, messages.ok);
1580
+ }
1581
+ /**
1582
+ * 获取数据库大小
1583
+ * @param callback
1584
+ */
1585
+ dbsize(callback) {
1586
+ const size = this.cache.size || 0;
1587
+ return this._handleCallback(callback, size);
1588
+ }
1589
+ /**
1590
+ * Sorted Set基础实现 - 添加成员
1591
+ * @param key
1592
+ * @param score
1593
+ * @param member
1594
+ * @param callback
1595
+ */
1596
+ zadd(key, score, member, callback) {
1597
+ let retVal = 0;
1598
+ if (this._hasKey(key)) {
1599
+ this._testType(key, 'zset', true, callback);
1600
+ }
1601
+ else {
1602
+ this.cache.set(key, this._makeKey([], 'zset'));
1603
+ }
1604
+ const zset = this._getKey(key);
1605
+ const existing = zset.find((item) => item.member === member);
1606
+ if (existing) {
1607
+ existing.score = score;
1608
+ }
1609
+ else {
1610
+ zset.push({ score, member });
1611
+ retVal = 1;
1612
+ }
1613
+ // 按分数排序
1614
+ zset.sort((a, b) => a.score - b.score);
1615
+ this._setKey(key, zset);
1616
+ return this._handleCallback(callback, retVal);
1617
+ }
1618
+ /**
1619
+ * Sorted Set - 获取成员分数
1620
+ * @param key
1621
+ * @param member
1622
+ * @param callback
1623
+ */
1624
+ zscore(key, member, callback) {
1625
+ if (!this._hasKey(key)) {
1626
+ return this._handleCallback(callback, null);
1627
+ }
1628
+ this._testType(key, 'zset', true, callback);
1629
+ const zset = this._getKey(key);
1630
+ const item = zset.find((item) => item.member === member);
1631
+ return this._handleCallback(callback, item ? item.score : null);
1632
+ }
1633
+ /**
1634
+ * Sorted Set - 获取范围内的成员
1635
+ * @param key
1636
+ * @param start
1637
+ * @param stop
1638
+ * @param callback
1639
+ */
1640
+ zrange(key, start, stop, callback) {
1641
+ if (!this._hasKey(key)) {
1642
+ return this._handleCallback(callback, []);
1643
+ }
1644
+ this._testType(key, 'zset', true, callback);
1645
+ const zset = this._getKey(key);
1646
+ const length = zset.length;
1647
+ if (stop < 0) {
1648
+ stop = length + stop;
1649
+ }
1650
+ if (start < 0) {
1651
+ start = length + start;
1652
+ }
1653
+ const retVal = zset.slice(start, stop + 1).map((item) => item.member);
1654
+ return this._handleCallback(callback, retVal);
1655
+ }
1656
+ /**
1657
+ * Sorted Set - 获取成员数量
1658
+ * @param key
1659
+ * @param callback
1660
+ */
1661
+ zcard(key, callback) {
1662
+ if (!this._hasKey(key)) {
1663
+ return this._handleCallback(callback, 0);
1664
+ }
1665
+ this._testType(key, 'zset', true, callback);
1666
+ const zset = this._getKey(key);
1667
+ return this._handleCallback(callback, zset.length);
1668
+ }
1669
+ /**
1670
+ * Sorted Set - 删除成员
1671
+ * @param key
1672
+ * @param member
1673
+ * @param callback
1674
+ */
1675
+ zrem(key, member, callback) {
1676
+ let retVal = 0;
1677
+ if (this._hasKey(key)) {
1678
+ this._testType(key, 'zset', true, callback);
1679
+ const zset = this._getKey(key);
1680
+ const index = zset.findIndex((item) => item.member === member);
1681
+ if (index !== -1) {
1682
+ zset.splice(index, 1);
1683
+ retVal = 1;
1684
+ this._setKey(key, zset);
1685
+ }
1686
+ }
1687
+ return this._handleCallback(callback, retVal);
1688
+ }
1160
1689
  }
1161
1690
 
1162
1691
  /*
@@ -1167,17 +1696,27 @@ class MemoryCache extends EventEmitter {
1167
1696
  * @LastEditTime: 2023-02-18 23:52:47
1168
1697
  */
1169
1698
  class MemoryStore {
1170
- client;
1171
- pool;
1172
- options;
1173
1699
  /**
1174
1700
  * Creates an instance of MemoryStore.
1175
1701
  * @param {MemoryStoreOpt} options
1176
1702
  * @memberof MemoryStore
1177
1703
  */
1178
1704
  constructor(options) {
1179
- this.options = options;
1180
- this.client = null;
1705
+ this.options = {
1706
+ maxKeys: 1000,
1707
+ evictionPolicy: 'lru',
1708
+ ttlCheckInterval: 60000, // 1分钟
1709
+ ...options
1710
+ };
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();
1181
1720
  }
1182
1721
  /**
1183
1722
  * getConnection
@@ -1186,15 +1725,7 @@ class MemoryStore {
1186
1725
  * @memberof MemoryStore
1187
1726
  */
1188
1727
  getConnection() {
1189
- if (!this.pool) {
1190
- this.pool = new MemoryCache({
1191
- database: this.options.db
1192
- });
1193
- }
1194
- if (!this.client) {
1195
- this.client = this.pool.createClient();
1196
- this.client.status = "ready";
1197
- }
1728
+ // 直接返回 MemoryCache 实例
1198
1729
  return this.client;
1199
1730
  }
1200
1731
  /**
@@ -1204,8 +1735,10 @@ class MemoryStore {
1204
1735
  * @memberof MemoryStore
1205
1736
  */
1206
1737
  async close() {
1207
- this.client.end();
1208
- this.client = null;
1738
+ if (this.client) {
1739
+ this.client.end();
1740
+ this.client = null;
1741
+ }
1209
1742
  }
1210
1743
  /**
1211
1744
  * release
@@ -1215,7 +1748,8 @@ class MemoryStore {
1215
1748
  * @memberof MemoryStore
1216
1749
  */
1217
1750
  async release(_conn) {
1218
- return;
1751
+ // 对于内存存储,不需要释放连接
1752
+ return Promise.resolve();
1219
1753
  }
1220
1754
  /**
1221
1755
  * defineCommand
@@ -1248,6 +1782,20 @@ class MemoryStore {
1248
1782
  return -1;
1249
1783
  }
1250
1784
  }
1785
+ /**
1786
+ * 获取缓存统计信息
1787
+ */
1788
+ getStats() {
1789
+ if (this.client) {
1790
+ return this.client.info();
1791
+ }
1792
+ return {
1793
+ keys: 0,
1794
+ memory: 0,
1795
+ hits: 0,
1796
+ misses: 0
1797
+ };
1798
+ }
1251
1799
  }
1252
1800
 
1253
1801
  /*
@@ -1265,15 +1813,15 @@ class MemoryStore {
1265
1813
  * @class RedisStore
1266
1814
  */
1267
1815
  class RedisStore {
1268
- options;
1269
- pool;
1270
- client;
1271
1816
  /**
1272
1817
  * Creates an instance of RedisStore.
1273
1818
  * @param {RedisStoreOpt} options
1274
1819
  * @memberof RedisStore
1275
1820
  */
1276
1821
  constructor(options) {
1822
+ this.reconnectAttempts = 0;
1823
+ this.maxReconnectAttempts = 5;
1824
+ this.reconnectDelay = 1000; // 初始重连延迟1秒
1277
1825
  this.options = this.parseOpt(options);
1278
1826
  this.pool = null;
1279
1827
  }
@@ -1327,7 +1875,7 @@ class RedisStore {
1327
1875
  return opt;
1328
1876
  }
1329
1877
  /**
1330
- * create connection by native
1878
+ * create connection by native with improved error handling
1331
1879
  *
1332
1880
  * @param {number} [connNum=0]
1333
1881
  * @returns {*} {Promise<Redis | Cluster>}
@@ -1339,32 +1887,77 @@ class RedisStore {
1339
1887
  }
1340
1888
  const defer = helper.getDefer();
1341
1889
  let connection;
1342
- if (!helper.isEmpty(this.options.clusters)) {
1343
- connection = new Cluster([...this.options.clusters], { redisOptions: this.options });
1344
- }
1345
- else {
1346
- connection = new Redis(this.options);
1347
- }
1348
- // 去除prefix, 防止重复
1349
- this.options.keyPrefix = "";
1350
- connection.on('end', () => {
1351
- if (connNum < 3) {
1352
- connNum++;
1353
- defer.resolve(this.connect(connNum));
1890
+ try {
1891
+ if (!helper.isEmpty(this.options.clusters)) {
1892
+ connection = new Cluster([...this.options.clusters], {
1893
+ redisOptions: this.options,
1894
+ enableOfflineQueue: false,
1895
+ retryDelayOnFailover: 100
1896
+ });
1354
1897
  }
1355
1898
  else {
1356
- this.close();
1357
- defer.reject('redis connection end');
1899
+ connection = new Redis({
1900
+ ...this.options,
1901
+ enableOfflineQueue: false,
1902
+ retryDelayOnFailover: 100,
1903
+ lazyConnect: true
1904
+ });
1358
1905
  }
1359
- });
1360
- connection.on('ready', () => {
1361
- this.client = connection;
1362
- defer.resolve(connection);
1363
- });
1906
+ // 去除prefix, 防止重复
1907
+ this.options.keyPrefix = "";
1908
+ connection.on('error', (err) => {
1909
+ DefaultLogger.Error(`Redis connection error: ${err.message}`);
1910
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
1911
+ this.scheduleReconnect(connNum);
1912
+ }
1913
+ else {
1914
+ defer.reject(new Error(`Redis connection failed after ${this.maxReconnectAttempts} attempts`));
1915
+ }
1916
+ });
1917
+ connection.on('end', () => {
1918
+ DefaultLogger.Warn('Redis connection ended');
1919
+ if (connNum < 3) {
1920
+ this.scheduleReconnect(connNum + 1);
1921
+ }
1922
+ else {
1923
+ this.close();
1924
+ defer.reject(new Error('Redis connection end after 3 attempts'));
1925
+ }
1926
+ });
1927
+ connection.on('ready', () => {
1928
+ DefaultLogger.Info('Redis connection ready');
1929
+ this.client = connection;
1930
+ this.reconnectAttempts = 0; // 重置重连计数
1931
+ defer.resolve(connection);
1932
+ });
1933
+ // 主动连接
1934
+ if (connection instanceof Redis) {
1935
+ await connection.connect();
1936
+ }
1937
+ }
1938
+ catch (error) {
1939
+ DefaultLogger.Error(`Failed to create Redis connection: ${error.message}`);
1940
+ defer.reject(error);
1941
+ }
1364
1942
  return defer.promise;
1365
1943
  }
1366
1944
  /**
1367
- * get connection from pool
1945
+ * 计划重连,使用指数退避策略
1946
+ * @private
1947
+ * @param {number} connNum
1948
+ */
1949
+ scheduleReconnect(connNum) {
1950
+ this.reconnectAttempts++;
1951
+ const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
1952
+ DefaultLogger.Info(`Scheduling Redis reconnect attempt ${this.reconnectAttempts} in ${delay}ms`);
1953
+ setTimeout(() => {
1954
+ this.connect(connNum).catch(err => {
1955
+ DefaultLogger.Error(`Reconnect attempt ${this.reconnectAttempts} failed: ${err.message}`);
1956
+ });
1957
+ }, delay);
1958
+ }
1959
+ /**
1960
+ * get connection from pool with improved configuration
1368
1961
  *
1369
1962
  * @returns {*}
1370
1963
  * @memberof RedisStore
@@ -1375,38 +1968,57 @@ class RedisStore {
1375
1968
  create: () => {
1376
1969
  return this.connect();
1377
1970
  },
1378
- destroy: () => {
1379
- return this.close();
1971
+ destroy: (resource) => {
1972
+ if (resource && typeof resource.disconnect === 'function') {
1973
+ resource.disconnect();
1974
+ }
1975
+ return Promise.resolve();
1380
1976
  },
1381
1977
  validate: (resource) => {
1382
- return Promise.resolve(resource.status === 'ready');
1978
+ return Promise.resolve(resource && resource.status === 'ready');
1383
1979
  }
1384
1980
  };
1385
1981
  this.pool = genericPool.createPool(factory, {
1386
1982
  max: this.options.poolSize || 10, // maximum size of the pool
1387
- min: 2 // minimum size of the pool
1983
+ min: Math.min(2, this.options.poolSize || 2), // minimum size of the pool
1984
+ acquireTimeoutMillis: 30000, // 30秒获取连接超时
1985
+ testOnBorrow: true, // 借用时测试连接
1986
+ evictionRunIntervalMillis: 30000, // 30秒检查一次空闲连接
1987
+ idleTimeoutMillis: 300000, // 5分钟空闲超时
1988
+ softIdleTimeoutMillis: 180000 // 3分钟软空闲超时
1388
1989
  });
1389
1990
  this.pool.on('factoryCreateError', function (err) {
1390
- DefaultLogger.Error(err);
1991
+ DefaultLogger.Error(`Redis pool create error: ${err.message}`);
1391
1992
  });
1392
1993
  this.pool.on('factoryDestroyError', function (err) {
1393
- DefaultLogger.Error(err);
1994
+ DefaultLogger.Error(`Redis pool destroy error: ${err.message}`);
1394
1995
  });
1395
1996
  }
1396
1997
  return this.pool.acquire();
1397
1998
  }
1398
1999
  /**
1399
- * close connection
2000
+ * close connection with proper cleanup
1400
2001
  *
1401
2002
  * @returns {*}
1402
2003
  * @memberof RedisStore
1403
2004
  */
1404
2005
  async close() {
1405
- this.client.disconnect();
1406
- this.client = null;
1407
- this.pool.destroy(this.client);
1408
- this.pool = null;
1409
- return;
2006
+ try {
2007
+ if (this.pool) {
2008
+ await this.pool.drain();
2009
+ await this.pool.clear();
2010
+ this.pool = null;
2011
+ }
2012
+ if (this.client) {
2013
+ if (typeof this.client.disconnect === 'function') {
2014
+ this.client.disconnect();
2015
+ }
2016
+ this.client = null;
2017
+ }
2018
+ }
2019
+ catch (error) {
2020
+ DefaultLogger.Error(`Error closing Redis connection: ${error.message}`);
2021
+ }
1410
2022
  }
1411
2023
  /**
1412
2024
  *
@@ -1449,16 +2061,16 @@ class RedisStore {
1449
2061
  try {
1450
2062
  conn = await this.defineCommand("getCompare", {
1451
2063
  numberOfKeys: 1,
1452
- lua: `
1453
- local remote_value = redis.call("get",KEYS[1])
1454
-
1455
- if (not remote_value) then
1456
- return 0
1457
- elseif (remote_value == ARGV[1]) then
1458
- return redis.call("del",KEYS[1])
1459
- else
1460
- return -1
1461
- end
2064
+ lua: `
2065
+ local remote_value = redis.call("get",KEYS[1])
2066
+
2067
+ if (not remote_value) then
2068
+ return 0
2069
+ elseif (remote_value == ARGV[1]) then
2070
+ return redis.call("del",KEYS[1])
2071
+ else
2072
+ return -1
2073
+ end
1462
2074
  `
1463
2075
  });
1464
2076
  return conn.getCompare(name, value);
@@ -1496,9 +2108,6 @@ const defaultOptions = {
1496
2108
  * @class Store
1497
2109
  */
1498
2110
  class CacheStore {
1499
- client;
1500
- options;
1501
- static instance;
1502
2111
  /**
1503
2112
  * Creates an instance of CacheStore.
1504
2113
  * @param {StoreOptions} options
@@ -1518,17 +2127,70 @@ class CacheStore {
1518
2127
  }
1519
2128
  }
1520
2129
  /**
1521
- *
1522
- *
2130
+ * 获取单例实例,支持多配置实例管理
1523
2131
  * @static
1524
- * @returns
2132
+ * @param {StoreOptions} [options]
2133
+ * @param {string} [instanceKey='default'] 实例键名,用于区分不同配置的实例
2134
+ * @returns {CacheStore}
1525
2135
  */
1526
- static getInstance(options) {
1527
- if (this.instance) {
1528
- return this.instance;
2136
+ static getInstance(options, instanceKey = 'default') {
2137
+ // 生成配置哈希作为实例键的一部分
2138
+ const configHash = options ? this.generateConfigHash(options) : 'default';
2139
+ const fullKey = `${instanceKey}_${configHash}`;
2140
+ if (this.instances.has(fullKey)) {
2141
+ return this.instances.get(fullKey);
2142
+ }
2143
+ const instance = new CacheStore(options);
2144
+ this.instances.set(fullKey, instance);
2145
+ return instance;
2146
+ }
2147
+ /**
2148
+ * 生成配置哈希
2149
+ * @private
2150
+ * @static
2151
+ * @param {StoreOptions} options
2152
+ * @returns {string}
2153
+ */
2154
+ static generateConfigHash(options) {
2155
+ const configStr = JSON.stringify({
2156
+ type: options.type,
2157
+ host: options.host,
2158
+ port: options.port,
2159
+ db: options.db,
2160
+ keyPrefix: options.keyPrefix
2161
+ });
2162
+ // 简单哈希函数
2163
+ let hash = 0;
2164
+ for (let i = 0; i < configStr.length; i++) {
2165
+ const char = configStr.charCodeAt(i);
2166
+ hash = ((hash << 5) - hash) + char;
2167
+ hash = hash & hash; // 转换为32位整数
2168
+ }
2169
+ return Math.abs(hash).toString(36);
2170
+ }
2171
+ /**
2172
+ * 清理指定实例
2173
+ * @static
2174
+ * @param {string} [instanceKey='default']
2175
+ */
2176
+ static async clearInstance(instanceKey = 'default') {
2177
+ const keysToRemove = Array.from(this.instances.keys()).filter(key => key.startsWith(`${instanceKey}_`));
2178
+ for (const key of keysToRemove) {
2179
+ const instance = this.instances.get(key);
2180
+ if (instance) {
2181
+ await instance.close();
2182
+ this.instances.delete(key);
2183
+ }
1529
2184
  }
1530
- this.instance = new CacheStore(options);
1531
- return this.instance;
2185
+ }
2186
+ /**
2187
+ * 清理所有实例
2188
+ * @static
2189
+ */
2190
+ static async clearAllInstances() {
2191
+ const promises = Array.from(this.instances.values()).map(instance => instance.close());
2192
+ await Promise.all(promises);
2193
+ this.instances.clear();
1532
2194
  }
1533
2195
  getConnection() {
1534
2196
  return this.client.getConnection();
@@ -1539,11 +2201,13 @@ class CacheStore {
1539
2201
  release(conn) {
1540
2202
  return this.client.release(conn);
1541
2203
  }
1542
- defineCommand(name, scripts) {
1543
- return this.client.defineCommand(name, scripts);
1544
- }
1545
- getCompare(name, value) {
1546
- return this.client.getCompare(name, value);
2204
+ /**
2205
+ * 获取底层实现客户端,用于访问特定实现的功能
2206
+ * 例如:Redis的defineCommand, getCompare等
2207
+ * @returns {MemoryStore | RedisStore}
2208
+ */
2209
+ getRawClient() {
2210
+ return this.client;
1547
2211
  }
1548
2212
  /**
1549
2213
  * handler for native client
@@ -1561,10 +2225,22 @@ class CacheStore {
1561
2225
  return res;
1562
2226
  }
1563
2227
  catch (err) {
1564
- 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;
1565
2232
  }
1566
2233
  finally {
1567
- 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
+ }
1568
2244
  }
1569
2245
  }
1570
2246
  /**
@@ -1603,13 +2279,6 @@ class CacheStore {
1603
2279
  expire(name, timeout) {
1604
2280
  return this.wrap('expire', [`${this.options.keyPrefix || ""}${name}`, timeout]);
1605
2281
  }
1606
- /**
1607
- * 删除key
1608
- * @param name
1609
- */
1610
- rm(name) {
1611
- return this.wrap('del', [`${this.options.keyPrefix || ""}${name}`]);
1612
- }
1613
2282
  /**
1614
2283
  *
1615
2284
  *
@@ -1647,8 +2316,8 @@ class CacheStore {
1647
2316
  * @param incr
1648
2317
  * @returns {*}
1649
2318
  */
1650
- incrby(name, incr = 1) {
1651
- return this.wrap('incrby', [`${this.options.keyPrefix || ""}${name}`, incr]);
2319
+ incrby(name, increment) {
2320
+ return this.wrap('incrby', [`${this.options.keyPrefix || ""}${name}`, increment]);
1652
2321
  }
1653
2322
  /**
1654
2323
  * 将 key 所储存的值减去减量
@@ -1656,8 +2325,8 @@ class CacheStore {
1656
2325
  * @param {any} name
1657
2326
  * @param {any} decr
1658
2327
  */
1659
- decrby(name, decr = 1) {
1660
- return this.wrap('decrby', [`${this.options.keyPrefix || ""}${name}`, decr]);
2328
+ decrby(name, decrement) {
2329
+ return this.wrap('decrby', [`${this.options.keyPrefix || ""}${name}`, decrement]);
1661
2330
  }
1662
2331
  /**
1663
2332
  * 哈希写入
@@ -1666,13 +2335,9 @@ class CacheStore {
1666
2335
  * @param value
1667
2336
  * @param timeout
1668
2337
  */
1669
- hset(name, key, value, timeout) {
1670
- const setP = [this.wrap('hset', [`${this.options.keyPrefix || ""}${name}`, key, value])];
1671
- if (typeof timeout !== 'number') {
1672
- timeout = this.options.timeout;
1673
- }
1674
- setP.push(this.set(`${name}:${key}_ex`, 1, timeout));
1675
- return Promise.all(setP);
2338
+ async hset(name, key, value, timeout) {
2339
+ // MemoryStore 直接支持字段级 TTL,传递 timeout 参数
2340
+ return this.wrap('hset', [`${this.options.keyPrefix || ""}${name}`, key, value, timeout]);
1676
2341
  }
1677
2342
  /**
1678
2343
  * 哈希获取
@@ -1681,15 +2346,8 @@ class CacheStore {
1681
2346
  * @returns {*}
1682
2347
  */
1683
2348
  hget(name, key) {
1684
- const setP = [this.get(`${name}:${key}_ex`)];
1685
- setP.push(this.wrap('hget', [`${this.options.keyPrefix || ""}${name}`, key]));
1686
- return Promise.all(setP).then(dataArr => {
1687
- if (dataArr[0] === null) {
1688
- this.hdel(name, key);
1689
- return null;
1690
- }
1691
- return dataArr[1] || null;
1692
- });
2349
+ // 直接调用底层实现,TTL 检查在 MemoryCache 中处理
2350
+ return this.wrap('hget', [`${this.options.keyPrefix || ""}${name}`, key]);
1693
2351
  }
1694
2352
  /**
1695
2353
  * 查看哈希表 hashKey 中,给定域 key 是否存在
@@ -1698,15 +2356,8 @@ class CacheStore {
1698
2356
  * @returns {*}
1699
2357
  */
1700
2358
  hexists(name, key) {
1701
- const setP = [this.get(`${name}:${key}_ex`)];
1702
- setP.push(this.wrap('hexists', [`${this.options.keyPrefix || ""}${name}`, key]));
1703
- return Promise.all(setP).then(dataArr => {
1704
- if (dataArr[0] === null) {
1705
- this.hdel(name, key);
1706
- return 0;
1707
- }
1708
- return dataArr[1] || 0;
1709
- });
2359
+ // 直接调用底层实现,TTL 检查在 MemoryCache 中处理
2360
+ return this.wrap('hexists', [`${this.options.keyPrefix || ""}${name}`, key]);
1710
2361
  }
1711
2362
  /**
1712
2363
  * 哈希删除
@@ -1714,10 +2365,9 @@ class CacheStore {
1714
2365
  * @param key
1715
2366
  * @returns {*}
1716
2367
  */
1717
- hdel(name, key) {
1718
- const setP = [this.del(`${name}:${key}_ex`)];
1719
- setP.push(this.wrap('hdel', [`${this.options.keyPrefix || ""}${name}`, key]));
1720
- return Promise.all(setP);
2368
+ async hdel(name, key) {
2369
+ // 直接调用底层实现,TTL 清理在 MemoryCache 中处理
2370
+ return this.wrap('hdel', [`${this.options.keyPrefix || ""}${name}`, key]);
1721
2371
  }
1722
2372
  /**
1723
2373
  * 返回哈希表 key 中域的数量
@@ -1731,11 +2381,11 @@ class CacheStore {
1731
2381
  * 给哈希表指定key,增加increment
1732
2382
  * @param name
1733
2383
  * @param key
1734
- * @param incr
2384
+ * @param increment
1735
2385
  * @returns {*}
1736
2386
  */
1737
- hincrby(name, key, incr = 1) {
1738
- return this.wrap('hincrby', [`${this.options.keyPrefix || ""}${name}`, key, incr]);
2387
+ hincrby(name, key, increment) {
2388
+ return this.wrap('hincrby', [`${this.options.keyPrefix || ""}${name}`, key, increment]);
1739
2389
  }
1740
2390
  /**
1741
2391
  * 返回哈希表所有key-value
@@ -1824,12 +2474,12 @@ class CacheStore {
1824
2474
  * @param timeout
1825
2475
  * @returns {*}
1826
2476
  */
1827
- sadd(name, value, timeout) {
1828
- const setP = [this.wrap('sadd', [`${this.options.keyPrefix || ""}${name}`, value])];
1829
- if (typeof timeout !== 'number') {
1830
- setP.push(this.wrap('expire', [`${this.options.keyPrefix || ""}${name}`, timeout]));
2477
+ async sadd(name, value, timeout) {
2478
+ const result = await this.wrap('sadd', [`${this.options.keyPrefix || ""}${name}`, value]);
2479
+ if (typeof timeout === 'number') {
2480
+ await this.wrap('expire', [`${this.options.keyPrefix || ""}${name}`, timeout]);
1831
2481
  }
1832
- return Promise.all(setP);
2482
+ return result;
1833
2483
  }
1834
2484
  /**
1835
2485
  * 返回集合的基数(集合中元素的数量)
@@ -1881,8 +2531,9 @@ class CacheStore {
1881
2531
  * @returns {*}
1882
2532
  */
1883
2533
  smove(source, destination, member) {
1884
- return this.wrap('smove', [`${this.options.keyPrefix || ""}${source}`, `${this.options.keyPrefix}${destination}`, member]);
2534
+ return this.wrap('smove', [`${this.options.keyPrefix || ""}${source}`, `${this.options.keyPrefix || ""}${destination}`, member]);
1885
2535
  }
1886
2536
  }
2537
+ CacheStore.instances = new Map();
1887
2538
 
1888
2539
  export { CacheStore };