koatty_store 1.7.0 → 1.8.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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @Author: richen
3
- * @Date: 2024-11-07 14:38:56
3
+ * @Date: 2025-06-09 12:32:48
4
4
  * @License: BSD (3-Clause)
5
5
  * @Copyright (c) - <richenlin(at)gmail.com>
6
6
  * @HomePage: https://koatty.org/
@@ -10,6 +10,7 @@
10
10
  var lodash = require('lodash');
11
11
  var helper = require('koatty_lib');
12
12
  var events = require('events');
13
+ var lruCache = require('lru-cache');
13
14
  var koatty_logger = require('koatty_logger');
14
15
  var ioredis = require('ioredis');
15
16
  var genericPool = require('generic-pool');
@@ -70,7 +71,7 @@ var messages;
70
71
  messages["mutuallyExclusiveNXXX"] = "ERR XX and NX options at the same time are not compatible";
71
72
  })(messages || (messages = {}));
72
73
  class MemoryCache extends events.EventEmitter {
73
- databases = Object.create({});
74
+ databases = new Map();
74
75
  options;
75
76
  currentDBIndex;
76
77
  connected;
@@ -78,18 +79,89 @@ class MemoryCache extends events.EventEmitter {
78
79
  multiMode;
79
80
  cache;
80
81
  responseMessages;
82
+ ttlCheckTimer = null;
81
83
  /**
82
84
  * Creates an instance of MemoryCache.
83
- * @param {*} options
85
+ * @param {MemoryCacheOptions} options
84
86
  * @memberof MemoryCache
85
87
  */
86
88
  constructor(options) {
87
89
  super();
88
- this.options = { ...{ database: "0" }, ...options };
89
- this.currentDBIndex = 0;
90
+ this.options = {
91
+ database: 0,
92
+ maxKeys: 1000,
93
+ evictionPolicy: 'lru',
94
+ ttlCheckInterval: 60000, // 1分钟检查一次过期键
95
+ maxAge: 1000 * 60 * 60, // 默认1小时过期
96
+ ...options
97
+ };
98
+ this.currentDBIndex = options.database || 0;
90
99
  this.connected = false;
91
100
  this.lastSave = Date.now();
92
101
  this.multiMode = false;
102
+ this.responseMessages = [];
103
+ // 初始化数据库和缓存
104
+ if (!this.databases.has(this.currentDBIndex)) {
105
+ this.databases.set(this.currentDBIndex, this.createLRUCache());
106
+ }
107
+ this.cache = this.databases.get(this.currentDBIndex);
108
+ // 启动TTL检查定时器
109
+ this.startTTLCheck();
110
+ }
111
+ /**
112
+ * 创建LRU缓存实例
113
+ */
114
+ createLRUCache() {
115
+ return new lruCache.LRUCache({
116
+ max: this.options.maxKeys || 1000,
117
+ ttl: this.options.maxAge || 1000 * 60 * 60, // 1小时默认
118
+ updateAgeOnGet: true, // 访问时更新age
119
+ dispose: (value, key, reason) => {
120
+ // 键被淘汰时的回调 - 直接使用lru-cache的事件机制
121
+ this.emit('evict', key, value, reason);
122
+ },
123
+ onInsert: (value, key) => {
124
+ // 键被插入时的回调
125
+ this.emit('insert', key, value);
126
+ }
127
+ });
128
+ }
129
+ /**
130
+ * 启动TTL检查定时器
131
+ */
132
+ startTTLCheck() {
133
+ if (this.ttlCheckTimer) {
134
+ clearInterval(this.ttlCheckTimer);
135
+ }
136
+ this.ttlCheckTimer = setInterval(() => {
137
+ this.cleanExpiredKeys();
138
+ }, this.options.ttlCheckInterval || 60000);
139
+ }
140
+ /**
141
+ * 清理过期键
142
+ */
143
+ cleanExpiredKeys() {
144
+ for (const [_dbIndex, cache] of this.databases) {
145
+ const keysToDelete = [];
146
+ cache.forEach((item, key) => {
147
+ if (item.timeout && item.timeout <= Date.now()) {
148
+ keysToDelete.push(key);
149
+ }
150
+ });
151
+ keysToDelete.forEach(key => {
152
+ cache.delete(key);
153
+ this.emit('expire', key);
154
+ });
155
+ }
156
+ }
157
+ /**
158
+ * 停止TTL检查
159
+ */
160
+ stopTTLCheck() {
161
+ if (this.ttlCheckTimer) {
162
+ clearInterval(this.ttlCheckTimer);
163
+ this.ttlCheckTimer = null;
164
+ }
93
165
  }
94
166
  /**
95
167
  *
@@ -98,8 +170,10 @@ class MemoryCache extends events.EventEmitter {
98
170
  * @memberof MemoryCache
99
171
  */
100
172
  createClient() {
101
- this.databases[this.options.database] = Object.create({});
102
- this.cache = this.databases[this.options.database];
173
+ if (!this.databases.has(this.options.database)) {
174
+ this.databases.set(this.options.database, this.createLRUCache());
175
+ }
176
+ this.cache = this.databases.get(this.options.database);
103
177
  this.connected = true;
104
178
  // exit multi mode if we are in it
105
179
  this.discard(null, true);
@@ -115,6 +189,7 @@ class MemoryCache extends events.EventEmitter {
115
189
  */
116
190
  quit() {
117
191
  this.connected = false;
192
+ this.stopTTLCheck();
118
193
  // exit multi mode if we are in it
119
194
  this.discard(null, true);
120
195
  this.emit('end');
@@ -129,6 +204,40 @@ class MemoryCache extends events.EventEmitter {
129
204
  end() {
130
205
  return this.quit();
131
206
  }
207
+ /**
208
+ * 获取缓存统计信息
209
+ */
210
+ info() {
211
+ const stats = {
212
+ databases: this.databases.size,
213
+ currentDB: this.currentDBIndex,
214
+ keys: this.cache ? this.cache.length : 0,
215
+ maxKeys: this.options.maxKeys,
216
+ hits: 0,
217
+ misses: 0,
218
+ memory: this.getMemoryUsage()
219
+ };
220
+ // 如果缓存支持统计信息
221
+ if (this.cache && typeof this.cache.dump === 'function') {
222
+ const dump = this.cache.dump();
223
+ stats.keys = dump.length;
224
+ }
225
+ return stats;
226
+ }
227
+ /**
228
+ * 估算内存使用量
229
+ */
230
+ getMemoryUsage() {
231
+ let totalSize = 0;
232
+ for (const [, cache] of this.databases) {
233
+ cache.forEach((item, key) => {
234
+ // 粗略估算:key长度 + JSON序列化后的大小
235
+ totalSize += key.length * 2; // Unicode字符占2字节
236
+ totalSize += JSON.stringify(item).length * 2;
237
+ });
238
+ }
239
+ return totalSize;
240
+ }
132
241
  /**
133
242
  *
134
243
  *
@@ -175,12 +284,12 @@ class MemoryCache extends events.EventEmitter {
175
284
  if (!helper__namespace.isNumber(dbIndex)) {
176
285
  return this._handleCallback(callback, null, messages.invalidDBIndex);
177
286
  }
178
- if (!this.databases.hasOwnProperty(dbIndex)) {
179
- this.databases[dbIndex] = Object.create({});
287
+ if (!this.databases.has(dbIndex)) {
288
+ this.databases.set(dbIndex, this.createLRUCache());
180
289
  }
181
290
  this.multiMode = false;
182
291
  this.currentDBIndex = dbIndex;
183
- this.cache = this.databases[dbIndex];
292
+ this.cache = this.databases.get(dbIndex);
184
293
  return this._handleCallback(callback, messages.ok);
185
294
  }
186
295
  // ---------------------------------------
@@ -259,7 +368,7 @@ class MemoryCache extends events.EventEmitter {
259
368
  else if (onlyexist) {
260
369
  return this._handleCallback(callback, retVal);
261
370
  }
262
- this.cache[key] = this._makeKey(value.toString(), 'string', pttl);
371
+ this.cache.set(key, this._makeKey(value.toString(), 'string', pttl));
263
372
  return this._handleCallback(callback, messages.ok);
264
373
  }
265
374
  /**
@@ -289,7 +398,8 @@ class MemoryCache extends events.EventEmitter {
289
398
  expire(key, seconds, callback) {
290
399
  let retVal = 0;
291
400
  if (this._hasKey(key)) {
292
- this.cache[key].timeout = Date.now() + seconds * 1000;
401
+ const pttl = seconds * 1000;
402
+ this.cache.set(key, { ...this.cache.get(key), timeout: Date.now() + pttl });
293
403
  retVal = 1;
294
404
  }
295
405
  return this._handleCallback(callback, retVal);
@@ -309,7 +419,7 @@ class MemoryCache extends events.EventEmitter {
309
419
  for (let itr = 0; itr < keys.length; itr++) {
310
420
  const key = keys[itr];
311
421
  if (this._hasKey(key)) {
312
- delete this.cache[key];
422
+ this.cache.delete(key);
313
423
  retVal++;
314
424
  }
315
425
  }
@@ -400,7 +510,7 @@ class MemoryCache extends events.EventEmitter {
400
510
  decrby(key, amount, callback) {
401
511
  let retVal = null;
402
512
  try {
403
- retVal = this._addToKey(key, -amount);
513
+ retVal = this._addToKey(key, 0 - amount);
404
514
  }
405
515
  catch (err) {
406
516
  return this._handleCallback(callback, null, err);
@@ -416,7 +526,7 @@ class MemoryCache extends events.EventEmitter {
416
526
  this._testType(key, 'hash', true, callback);
417
527
  }
418
528
  else {
419
- this.cache[key] = this._makeKey({}, 'hash');
529
+ this.cache.set(key, this._makeKey({}, 'hash'));
420
530
  }
421
531
  if (!this._hasField(key, field)) {
422
532
  retVal = 1;
@@ -479,7 +589,7 @@ class MemoryCache extends events.EventEmitter {
479
589
  for (let itr = 0; itr < fields.length; itr++) {
480
590
  const field = fields[itr];
481
591
  if (this._hasField(key, field)) {
482
- delete this.cache[key].value[field];
592
+ delete this.cache.get(key).value[field];
483
593
  retVal++;
484
594
  }
485
595
  }
@@ -600,22 +710,18 @@ class MemoryCache extends events.EventEmitter {
600
710
  this._testType(key, 'list', true, callback);
601
711
  }
602
712
  else {
603
- this.cache[key] = this._makeKey([], 'list');
713
+ this.cache.set(key, this._makeKey([], 'list'));
604
714
  }
605
- const val = this._getKey(key);
606
- val.push(value);
607
- this._setKey(key, val);
608
- retVal = val.length;
715
+ this._getKey(key).push(value.toString());
716
+ retVal = this._getKey(key).length;
717
+ this.persist(key);
609
718
  return this._handleCallback(callback, retVal);
610
719
  }
611
720
  /**
612
- *
613
- *
614
- * @param {string} key
615
- * @param {(string | number)} value
616
- * @param {Function} [callback]
617
- * @returns {*}
618
- * @memberof MemoryCache
721
+ * List:从左侧推入
722
+ * @param key
723
+ * @param value
724
+ * @param callback
619
725
  */
620
726
  lpush(key, value, callback) {
621
727
  let retVal = 0;
@@ -623,14 +729,31 @@ class MemoryCache extends events.EventEmitter {
623
729
  this._testType(key, 'list', true, callback);
624
730
  }
625
731
  else {
626
- this.cache[key] = this._makeKey([], 'list');
732
+ this.cache.set(key, this._makeKey([], 'list'));
627
733
  }
628
- const val = this._getKey(key);
629
- val.splice(0, 0, value);
630
- this._setKey(key, val);
631
- retVal = val.length;
734
+ const list = this._getKey(key);
735
+ retVal = list.unshift(value);
736
+ this._setKey(key, list);
632
737
  return this._handleCallback(callback, retVal);
633
738
  }
739
+ /**
740
+ * List:获取指定索引的元素
741
+ * @param key
742
+ * @param index
743
+ * @param callback
744
+ */
745
+ lindex(key, index, callback) {
746
+ if (!this._hasKey(key)) {
747
+ return this._handleCallback(callback, null);
748
+ }
749
+ this._testType(key, 'list', true, callback);
750
+ const list = this._getKey(key);
751
+ if (index < 0) {
752
+ index = list.length + index;
753
+ }
754
+ const value = index >= 0 && index < list.length ? list[index] : null;
755
+ return this._handleCallback(callback, value);
756
+ }
634
757
  /**
635
758
  *
636
759
  *
@@ -643,9 +766,11 @@ class MemoryCache extends events.EventEmitter {
643
766
  let retVal = null;
644
767
  if (this._hasKey(key)) {
645
768
  this._testType(key, 'list', true, callback);
646
- const val = this._getKey(key);
647
- retVal = val.shift();
648
- this._setKey(key, val);
769
+ const list = this._getKey(key);
770
+ if (list.length > 0) {
771
+ retVal = list.shift();
772
+ this.persist(key);
773
+ }
649
774
  }
650
775
  return this._handleCallback(callback, retVal);
651
776
  }
@@ -661,9 +786,11 @@ class MemoryCache extends events.EventEmitter {
661
786
  let retVal = null;
662
787
  if (this._hasKey(key)) {
663
788
  this._testType(key, 'list', true, callback);
664
- const val = this._getKey(key);
665
- retVal = val.pop();
666
- this._setKey(key, val);
789
+ const list = this._getKey(key);
790
+ if (list.length > 0) {
791
+ retVal = list.pop();
792
+ this.persist(key);
793
+ }
667
794
  }
668
795
  return this._handleCallback(callback, retVal);
669
796
  }
@@ -681,8 +808,8 @@ class MemoryCache extends events.EventEmitter {
681
808
  const retVal = [];
682
809
  if (this._hasKey(key)) {
683
810
  this._testType(key, 'list', true, callback);
684
- const val = this._getKey(key);
685
- const length = val.length;
811
+ const list = this._getKey(key);
812
+ const length = list.length;
686
813
  if (stop < 0) {
687
814
  stop = length + stop;
688
815
  }
@@ -698,7 +825,7 @@ class MemoryCache extends events.EventEmitter {
698
825
  if (stop >= 0 && stop >= start) {
699
826
  const size = stop - start + 1;
700
827
  for (let itr = start; itr < size; itr++) {
701
- retVal.push(val[itr]);
828
+ retVal.push(list[itr]);
702
829
  }
703
830
  }
704
831
  }
@@ -722,7 +849,7 @@ class MemoryCache extends events.EventEmitter {
722
849
  this._testType(key, 'set', true, callback);
723
850
  }
724
851
  else {
725
- this.cache[key] = this._makeKey([], 'set');
852
+ this.cache.set(key, this._makeKey([], 'set'));
726
853
  }
727
854
  const val = this._getKey(key);
728
855
  const length = val.length;
@@ -794,19 +921,29 @@ class MemoryCache extends events.EventEmitter {
794
921
  * @memberof MemoryCache
795
922
  */
796
923
  spop(key, count, callback) {
797
- let retVal = null;
924
+ let retVal = [];
798
925
  count = count || 1;
799
- if (isNaN(count)) {
800
- return this._handleCallback(callback, null, messages.noint);
926
+ if (typeof count === 'function') {
927
+ callback = count;
928
+ count = 1;
801
929
  }
802
930
  if (this._hasKey(key)) {
803
- retVal = [];
804
931
  this._testType(key, 'set', true, callback);
805
932
  const val = this._getKey(key);
806
- const length = val.length;
807
- count = count > length ? length : count;
808
- for (let itr = 0; itr < count; itr++) {
809
- retVal.push(val.pop());
933
+ const keys = Object.keys(val);
934
+ const keysLength = keys.length;
935
+ if (keysLength) {
936
+ if (count >= keysLength) {
937
+ retVal = keys;
938
+ this.del(key);
939
+ }
940
+ else {
941
+ for (let itr = 0; itr < count; itr++) {
942
+ const randomNum = Math.floor(Math.random() * keys.length);
943
+ retVal.push(keys[randomNum]);
944
+ this.srem(key, keys[randomNum]);
945
+ }
946
+ }
810
947
  }
811
948
  }
812
949
  return this._handleCallback(callback, retVal);
@@ -873,14 +1010,14 @@ class MemoryCache extends events.EventEmitter {
873
1010
  discard(callback, silent) {
874
1011
  // Clear the queue mode, drain the queue, empty the watch list
875
1012
  if (this.multiMode) {
876
- this.cache = this.databases[this.currentDBIndex];
1013
+ this.cache = this.databases.get(this.currentDBIndex);
877
1014
  this.multiMode = false;
878
1015
  this.responseMessages = [];
879
1016
  }
880
- else if (!silent) {
881
- return this._handleCallback(callback, null, messages.nomultidiscard);
1017
+ if (!silent) {
1018
+ return this._handleCallback(callback, messages.ok);
882
1019
  }
883
- return this._handleCallback(callback, messages.ok);
1020
+ return null;
884
1021
  }
885
1022
  // ---------------------------------------
886
1023
  // ## Internal - Key ##
@@ -893,11 +1030,11 @@ class MemoryCache extends events.EventEmitter {
893
1030
  * @returns {*}
894
1031
  * @memberof MemoryCache
895
1032
  */
896
- pttl(key, callback) {
1033
+ pttl(key, _callback) {
897
1034
  let retVal = -2;
898
1035
  if (this._hasKey(key)) {
899
- if (!lodash.isNil(this.cache[key].timeout)) {
900
- retVal = this.cache[key].timeout - Date.now();
1036
+ if (!lodash.isNil(this.cache.get(key)?.timeout)) {
1037
+ retVal = this.cache.get(key).timeout - Date.now();
901
1038
  // Prevent unexpected errors if the actual ttl just happens to be -2 or -1
902
1039
  if (retVal < 0 && retVal > -3) {
903
1040
  retVal = -3;
@@ -907,7 +1044,7 @@ class MemoryCache extends events.EventEmitter {
907
1044
  retVal = -1;
908
1045
  }
909
1046
  }
910
- return this._handleCallback(callback, retVal);
1047
+ return retVal;
911
1048
  }
912
1049
  /**
913
1050
  *
@@ -922,7 +1059,7 @@ class MemoryCache extends events.EventEmitter {
922
1059
  let retVal = 0;
923
1060
  if (this._hasKey(key)) {
924
1061
  if (!lodash.isNil(this._key(key).timeout)) {
925
- this._key(key).timeout = null;
1062
+ this.cache.set(key, { ...this.cache.get(key), timeout: null });
926
1063
  retVal = 1;
927
1064
  }
928
1065
  }
@@ -937,7 +1074,7 @@ class MemoryCache extends events.EventEmitter {
937
1074
  * @memberof MemoryCache
938
1075
  */
939
1076
  _hasKey(key) {
940
- return this.cache.hasOwnProperty(key);
1077
+ return this.cache.has(key);
941
1078
  }
942
1079
  /**
943
1080
  *
@@ -961,8 +1098,8 @@ class MemoryCache extends events.EventEmitter {
961
1098
  * @memberof MemoryCache
962
1099
  */
963
1100
  _key(key) {
964
- this.cache[key].lastAccess = Date.now();
965
- return this.cache[key];
1101
+ this.cache.get(key).lastAccess = Date.now();
1102
+ return this.cache.get(key);
966
1103
  }
967
1104
  /**
968
1105
  *
@@ -987,7 +1124,7 @@ class MemoryCache extends events.EventEmitter {
987
1124
  }
988
1125
  }
989
1126
  else {
990
- this.cache[key] = this._makeKey('0', 'string');
1127
+ this.cache.set(key, this._makeKey('0', 'string'));
991
1128
  }
992
1129
  const val = keyValue + amount;
993
1130
  this._setKey(key, val.toString());
@@ -1040,8 +1177,7 @@ class MemoryCache extends events.EventEmitter {
1040
1177
  * @memberof MemoryCache
1041
1178
  */
1042
1179
  _setKey(key, value) {
1043
- this.cache[key].value = value;
1044
- this.cache[key].lastAccess = Date.now();
1180
+ this.cache.set(key, { ...this.cache.get(key), value: value, lastAccess: Date.now() });
1045
1181
  }
1046
1182
  /**
1047
1183
  *
@@ -1069,7 +1205,7 @@ class MemoryCache extends events.EventEmitter {
1069
1205
  }
1070
1206
  }
1071
1207
  else {
1072
- this.cache[key] = this._makeKey({}, 'hash');
1208
+ this.cache.set(key, this._makeKey({}, 'hash'));
1073
1209
  }
1074
1210
  fieldValue = useFloat ? parseFloat(`${value}`) : parseInt(`${value}`);
1075
1211
  amount = useFloat ? parseFloat(`${amount}`) : parseInt(`${amount}`);
@@ -1178,6 +1314,333 @@ class MemoryCache extends events.EventEmitter {
1178
1314
  }
1179
1315
  return;
1180
1316
  }
1317
+ /**
1318
+ * 字符串追加操作
1319
+ * @param key
1320
+ * @param value
1321
+ * @param callback
1322
+ */
1323
+ append(key, value, callback) {
1324
+ let retVal = 0;
1325
+ if (this._hasKey(key)) {
1326
+ this._testType(key, 'string', true, callback);
1327
+ const existingValue = this._getKey(key);
1328
+ const newValue = existingValue + value;
1329
+ this._setKey(key, newValue);
1330
+ retVal = newValue.length;
1331
+ }
1332
+ else {
1333
+ this.cache.set(key, this._makeKey(value, 'string'));
1334
+ retVal = value.length;
1335
+ }
1336
+ return this._handleCallback(callback, retVal);
1337
+ }
1338
+ /**
1339
+ * 获取字符串长度
1340
+ * @param key
1341
+ * @param callback
1342
+ */
1343
+ strlen(key, callback) {
1344
+ let retVal = 0;
1345
+ if (this._hasKey(key)) {
1346
+ this._testType(key, 'string', true, callback);
1347
+ retVal = this._getKey(key).length;
1348
+ }
1349
+ return this._handleCallback(callback, retVal);
1350
+ }
1351
+ /**
1352
+ * 获取子字符串
1353
+ * @param key
1354
+ * @param start
1355
+ * @param end
1356
+ * @param callback
1357
+ */
1358
+ getrange(key, start, end, callback) {
1359
+ let retVal = '';
1360
+ if (this._hasKey(key)) {
1361
+ this._testType(key, 'string', true, callback);
1362
+ const value = this._getKey(key);
1363
+ retVal = value.substring(start, end + 1);
1364
+ }
1365
+ return this._handleCallback(callback, retVal);
1366
+ }
1367
+ /**
1368
+ * 设置子字符串
1369
+ * @param key
1370
+ * @param offset
1371
+ * @param value
1372
+ * @param callback
1373
+ */
1374
+ setrange(key, offset, value, callback) {
1375
+ let retVal = 0;
1376
+ if (this._hasKey(key)) {
1377
+ this._testType(key, 'string', true, callback);
1378
+ const existingValue = this._getKey(key);
1379
+ const newValue = existingValue.substring(0, offset) + value + existingValue.substring(offset + value.length);
1380
+ this._setKey(key, newValue);
1381
+ retVal = newValue.length;
1382
+ }
1383
+ else {
1384
+ // 如果键不存在,创建一个足够长的字符串
1385
+ const newValue = ''.padEnd(offset, '\0') + value;
1386
+ this.cache.set(key, this._makeKey(newValue, 'string'));
1387
+ retVal = newValue.length;
1388
+ }
1389
+ return this._handleCallback(callback, retVal);
1390
+ }
1391
+ /**
1392
+ * 批量获取
1393
+ * @param keys
1394
+ * @param callback
1395
+ */
1396
+ mget(...keys) {
1397
+ const callback = this._retrieveCallback(keys);
1398
+ const retVal = [];
1399
+ for (const key of keys) {
1400
+ if (this._hasKey(key)) {
1401
+ this._testType(key, 'string', false, callback);
1402
+ retVal.push(this._getKey(key));
1403
+ }
1404
+ else {
1405
+ retVal.push(null);
1406
+ }
1407
+ }
1408
+ return this._handleCallback(callback, retVal);
1409
+ }
1410
+ /**
1411
+ * 批量设置
1412
+ * @param keyValuePairs
1413
+ * @param callback
1414
+ */
1415
+ mset(...keyValuePairs) {
1416
+ const callback = this._retrieveCallback(keyValuePairs);
1417
+ // 确保参数是偶数个
1418
+ if (keyValuePairs.length % 2 !== 0) {
1419
+ return this._handleCallback(callback, null, messages.wrongArgCount.replace('%0', 'mset'));
1420
+ }
1421
+ for (let i = 0; i < keyValuePairs.length; i += 2) {
1422
+ const key = keyValuePairs[i];
1423
+ const value = keyValuePairs[i + 1];
1424
+ this.cache.set(key, this._makeKey(value.toString(), 'string'));
1425
+ }
1426
+ return this._handleCallback(callback, messages.ok);
1427
+ }
1428
+ /**
1429
+ * 获取所有键
1430
+ * @param pattern
1431
+ * @param callback
1432
+ */
1433
+ keys(pattern = '*', callback) {
1434
+ const retVal = [];
1435
+ this.cache.forEach((_item, key) => {
1436
+ if (pattern === '*' || this.matchPattern(key, pattern)) {
1437
+ retVal.push(key);
1438
+ }
1439
+ });
1440
+ return this._handleCallback(callback, retVal);
1441
+ }
1442
+ /**
1443
+ * 简单的模式匹配
1444
+ * @param key
1445
+ * @param pattern
1446
+ */
1447
+ matchPattern(key, pattern) {
1448
+ if (pattern === '*')
1449
+ return true;
1450
+ // 转换glob模式为正则表达式
1451
+ const regexPattern = pattern
1452
+ .replace(/\*/g, '.*')
1453
+ .replace(/\?/g, '.')
1454
+ .replace(/\[([^\]]*)\]/g, '[$1]');
1455
+ const regex = new RegExp(`^${regexPattern}$`);
1456
+ return regex.test(key);
1457
+ }
1458
+ /**
1459
+ * 获取随机键
1460
+ * @param callback
1461
+ */
1462
+ randomkey(callback) {
1463
+ const keys = [];
1464
+ this.cache.forEach((_item, key) => {
1465
+ keys.push(key);
1466
+ });
1467
+ if (keys.length === 0) {
1468
+ return this._handleCallback(callback, null);
1469
+ }
1470
+ const randomIndex = Math.floor(Math.random() * keys.length);
1471
+ return this._handleCallback(callback, keys[randomIndex]);
1472
+ }
1473
+ /**
1474
+ * 重命名键
1475
+ * @param oldKey
1476
+ * @param newKey
1477
+ * @param callback
1478
+ */
1479
+ rename(oldKey, newKey, callback) {
1480
+ if (!this._hasKey(oldKey)) {
1481
+ return this._handleCallback(callback, null, messages.nokey);
1482
+ }
1483
+ const value = this.cache.get(oldKey);
1484
+ this.cache.set(newKey, value);
1485
+ this.cache.delete(oldKey);
1486
+ return this._handleCallback(callback, messages.ok);
1487
+ }
1488
+ /**
1489
+ * 安全重命名键(目标键不存在时才重命名)
1490
+ * @param oldKey
1491
+ * @param newKey
1492
+ * @param callback
1493
+ */
1494
+ renamenx(oldKey, newKey, callback) {
1495
+ if (!this._hasKey(oldKey)) {
1496
+ return this._handleCallback(callback, null, messages.nokey);
1497
+ }
1498
+ if (this._hasKey(newKey)) {
1499
+ return this._handleCallback(callback, 0);
1500
+ }
1501
+ const value = this.cache.get(oldKey);
1502
+ this.cache.set(newKey, value);
1503
+ this.cache.delete(oldKey);
1504
+ return this._handleCallback(callback, 1);
1505
+ }
1506
+ /**
1507
+ * 获取键的类型
1508
+ * @param key
1509
+ * @param callback
1510
+ */
1511
+ type(key, callback) {
1512
+ if (!this._hasKey(key)) {
1513
+ return this._handleCallback(callback, 'none');
1514
+ }
1515
+ const item = this.cache.get(key);
1516
+ return this._handleCallback(callback, item.type);
1517
+ }
1518
+ /**
1519
+ * 清空当前数据库
1520
+ * @param callback
1521
+ */
1522
+ flushdb(callback) {
1523
+ this.cache.clear();
1524
+ return this._handleCallback(callback, messages.ok);
1525
+ }
1526
+ /**
1527
+ * 清空所有数据库
1528
+ * @param callback
1529
+ */
1530
+ flushall(callback) {
1531
+ this.databases.clear();
1532
+ this.cache = this.createLRUCache();
1533
+ this.databases.set(this.currentDBIndex, this.cache);
1534
+ return this._handleCallback(callback, messages.ok);
1535
+ }
1536
+ /**
1537
+ * 获取数据库大小
1538
+ * @param callback
1539
+ */
1540
+ dbsize(callback) {
1541
+ const size = this.cache.size || 0;
1542
+ return this._handleCallback(callback, size);
1543
+ }
1544
+ /**
1545
+ * Sorted Set基础实现 - 添加成员
1546
+ * @param key
1547
+ * @param score
1548
+ * @param member
1549
+ * @param callback
1550
+ */
1551
+ zadd(key, score, member, callback) {
1552
+ let retVal = 0;
1553
+ if (this._hasKey(key)) {
1554
+ this._testType(key, 'zset', true, callback);
1555
+ }
1556
+ else {
1557
+ this.cache.set(key, this._makeKey([], 'zset'));
1558
+ }
1559
+ const zset = this._getKey(key);
1560
+ const existing = zset.find((item) => item.member === member);
1561
+ if (existing) {
1562
+ existing.score = score;
1563
+ }
1564
+ else {
1565
+ zset.push({ score, member });
1566
+ retVal = 1;
1567
+ }
1568
+ // 按分数排序
1569
+ zset.sort((a, b) => a.score - b.score);
1570
+ this._setKey(key, zset);
1571
+ return this._handleCallback(callback, retVal);
1572
+ }
1573
+ /**
1574
+ * Sorted Set - 获取成员分数
1575
+ * @param key
1576
+ * @param member
1577
+ * @param callback
1578
+ */
1579
+ zscore(key, member, callback) {
1580
+ if (!this._hasKey(key)) {
1581
+ return this._handleCallback(callback, null);
1582
+ }
1583
+ this._testType(key, 'zset', true, callback);
1584
+ const zset = this._getKey(key);
1585
+ const item = zset.find((item) => item.member === member);
1586
+ return this._handleCallback(callback, item ? item.score : null);
1587
+ }
1588
+ /**
1589
+ * Sorted Set - 获取范围内的成员
1590
+ * @param key
1591
+ * @param start
1592
+ * @param stop
1593
+ * @param callback
1594
+ */
1595
+ zrange(key, start, stop, callback) {
1596
+ if (!this._hasKey(key)) {
1597
+ return this._handleCallback(callback, []);
1598
+ }
1599
+ this._testType(key, 'zset', true, callback);
1600
+ const zset = this._getKey(key);
1601
+ const length = zset.length;
1602
+ if (stop < 0) {
1603
+ stop = length + stop;
1604
+ }
1605
+ if (start < 0) {
1606
+ start = length + start;
1607
+ }
1608
+ const retVal = zset.slice(start, stop + 1).map((item) => item.member);
1609
+ return this._handleCallback(callback, retVal);
1610
+ }
1611
+ /**
1612
+ * Sorted Set - 获取成员数量
1613
+ * @param key
1614
+ * @param callback
1615
+ */
1616
+ zcard(key, callback) {
1617
+ if (!this._hasKey(key)) {
1618
+ return this._handleCallback(callback, 0);
1619
+ }
1620
+ this._testType(key, 'zset', true, callback);
1621
+ const zset = this._getKey(key);
1622
+ return this._handleCallback(callback, zset.length);
1623
+ }
1624
+ /**
1625
+ * Sorted Set - 删除成员
1626
+ * @param key
1627
+ * @param member
1628
+ * @param callback
1629
+ */
1630
+ zrem(key, member, callback) {
1631
+ let retVal = 0;
1632
+ if (this._hasKey(key)) {
1633
+ this._testType(key, 'zset', true, callback);
1634
+ const zset = this._getKey(key);
1635
+ const index = zset.findIndex((item) => item.member === member);
1636
+ if (index !== -1) {
1637
+ zset.splice(index, 1);
1638
+ retVal = 1;
1639
+ this._setKey(key, zset);
1640
+ }
1641
+ }
1642
+ return this._handleCallback(callback, retVal);
1643
+ }
1181
1644
  }
1182
1645
 
1183
1646
  /*
@@ -1197,7 +1660,12 @@ class MemoryStore {
1197
1660
  * @memberof MemoryStore
1198
1661
  */
1199
1662
  constructor(options) {
1200
- this.options = options;
1663
+ this.options = {
1664
+ maxKeys: 1000,
1665
+ evictionPolicy: 'lru',
1666
+ ttlCheckInterval: 60000, // 1分钟
1667
+ ...options
1668
+ };
1201
1669
  this.client = null;
1202
1670
  }
1203
1671
  /**
@@ -1209,7 +1677,11 @@ class MemoryStore {
1209
1677
  getConnection() {
1210
1678
  if (!this.pool) {
1211
1679
  this.pool = new MemoryCache({
1212
- database: this.options.db
1680
+ database: this.options.db || 0,
1681
+ maxKeys: this.options.maxKeys,
1682
+ maxMemory: this.options.maxMemory,
1683
+ evictionPolicy: this.options.evictionPolicy,
1684
+ ttlCheckInterval: this.options.ttlCheckInterval
1213
1685
  });
1214
1686
  }
1215
1687
  if (!this.client) {
@@ -1225,8 +1697,10 @@ class MemoryStore {
1225
1697
  * @memberof MemoryStore
1226
1698
  */
1227
1699
  async close() {
1228
- this.client.end();
1229
- this.client = null;
1700
+ if (this.client) {
1701
+ this.client.end();
1702
+ this.client = null;
1703
+ }
1230
1704
  }
1231
1705
  /**
1232
1706
  * release
@@ -1269,6 +1743,20 @@ class MemoryStore {
1269
1743
  return -1;
1270
1744
  }
1271
1745
  }
1746
+ /**
1747
+ * 获取缓存统计信息
1748
+ */
1749
+ getStats() {
1750
+ if (this.client) {
1751
+ return this.client.info();
1752
+ }
1753
+ return {
1754
+ keys: 0,
1755
+ memory: 0,
1756
+ hits: 0,
1757
+ misses: 0
1758
+ };
1759
+ }
1272
1760
  }
1273
1761
 
1274
1762
  /*
@@ -1289,6 +1777,9 @@ class RedisStore {
1289
1777
  options;
1290
1778
  pool;
1291
1779
  client;
1780
+ reconnectAttempts = 0;
1781
+ maxReconnectAttempts = 5;
1782
+ reconnectDelay = 1000; // 初始重连延迟1秒
1292
1783
  /**
1293
1784
  * Creates an instance of RedisStore.
1294
1785
  * @param {RedisStoreOpt} options
@@ -1348,7 +1839,7 @@ class RedisStore {
1348
1839
  return opt;
1349
1840
  }
1350
1841
  /**
1351
- * create connection by native
1842
+ * create connection by native with improved error handling
1352
1843
  *
1353
1844
  * @param {number} [connNum=0]
1354
1845
  * @returns {*} {Promise<Redis | Cluster>}
@@ -1360,32 +1851,77 @@ class RedisStore {
1360
1851
  }
1361
1852
  const defer = helper__namespace.getDefer();
1362
1853
  let connection;
1363
- if (!helper__namespace.isEmpty(this.options.clusters)) {
1364
- connection = new ioredis.Cluster([...this.options.clusters], { redisOptions: this.options });
1365
- }
1366
- else {
1367
- connection = new ioredis.Redis(this.options);
1368
- }
1369
- // 去除prefix, 防止重复
1370
- this.options.keyPrefix = "";
1371
- connection.on('end', () => {
1372
- if (connNum < 3) {
1373
- connNum++;
1374
- defer.resolve(this.connect(connNum));
1854
+ try {
1855
+ if (!helper__namespace.isEmpty(this.options.clusters)) {
1856
+ connection = new ioredis.Cluster([...this.options.clusters], {
1857
+ redisOptions: this.options,
1858
+ enableOfflineQueue: false,
1859
+ retryDelayOnFailover: 100
1860
+ });
1375
1861
  }
1376
1862
  else {
1377
- this.close();
1378
- defer.reject('redis connection end');
1863
+ connection = new ioredis.Redis({
1864
+ ...this.options,
1865
+ enableOfflineQueue: false,
1866
+ retryDelayOnFailover: 100,
1867
+ lazyConnect: true
1868
+ });
1379
1869
  }
1380
- });
1381
- connection.on('ready', () => {
1382
- this.client = connection;
1383
- defer.resolve(connection);
1384
- });
1870
+ // 去除prefix, 防止重复
1871
+ this.options.keyPrefix = "";
1872
+ connection.on('error', (err) => {
1873
+ koatty_logger.DefaultLogger.Error(`Redis connection error: ${err.message}`);
1874
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
1875
+ this.scheduleReconnect(connNum);
1876
+ }
1877
+ else {
1878
+ defer.reject(new Error(`Redis connection failed after ${this.maxReconnectAttempts} attempts`));
1879
+ }
1880
+ });
1881
+ connection.on('end', () => {
1882
+ koatty_logger.DefaultLogger.Warn('Redis connection ended');
1883
+ if (connNum < 3) {
1884
+ this.scheduleReconnect(connNum + 1);
1885
+ }
1886
+ else {
1887
+ this.close();
1888
+ defer.reject(new Error('Redis connection end after 3 attempts'));
1889
+ }
1890
+ });
1891
+ connection.on('ready', () => {
1892
+ koatty_logger.DefaultLogger.Info('Redis connection ready');
1893
+ this.client = connection;
1894
+ this.reconnectAttempts = 0; // 重置重连计数
1895
+ defer.resolve(connection);
1896
+ });
1897
+ // 主动连接
1898
+ if (connection instanceof ioredis.Redis) {
1899
+ await connection.connect();
1900
+ }
1901
+ }
1902
+ catch (error) {
1903
+ koatty_logger.DefaultLogger.Error(`Failed to create Redis connection: ${error.message}`);
1904
+ defer.reject(error);
1905
+ }
1385
1906
  return defer.promise;
1386
1907
  }
1387
1908
  /**
1388
- * get connection from pool
1909
+ * 计划重连,使用指数退避策略
1910
+ * @private
1911
+ * @param {number} connNum
1912
+ */
1913
+ scheduleReconnect(connNum) {
1914
+ this.reconnectAttempts++;
1915
+ const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
1916
+ koatty_logger.DefaultLogger.Info(`Scheduling Redis reconnect attempt ${this.reconnectAttempts} in ${delay}ms`);
1917
+ setTimeout(() => {
1918
+ this.connect(connNum).catch(err => {
1919
+ koatty_logger.DefaultLogger.Error(`Reconnect attempt ${this.reconnectAttempts} failed: ${err.message}`);
1920
+ });
1921
+ }, delay);
1922
+ }
1923
+ /**
1924
+ * get connection from pool with improved configuration
1389
1925
  *
1390
1926
  * @returns {*}
1391
1927
  * @memberof RedisStore
@@ -1396,38 +1932,57 @@ class RedisStore {
1396
1932
  create: () => {
1397
1933
  return this.connect();
1398
1934
  },
1399
- destroy: () => {
1400
- return this.close();
1935
+ destroy: (resource) => {
1936
+ if (resource && typeof resource.disconnect === 'function') {
1937
+ resource.disconnect();
1938
+ }
1939
+ return Promise.resolve();
1401
1940
  },
1402
1941
  validate: (resource) => {
1403
- return Promise.resolve(resource.status === 'ready');
1942
+ return Promise.resolve(resource && resource.status === 'ready');
1404
1943
  }
1405
1944
  };
1406
1945
  this.pool = genericPool.createPool(factory, {
1407
1946
  max: this.options.poolSize || 10, // maximum size of the pool
1408
- min: 2 // minimum size of the pool
1947
+ min: Math.min(2, this.options.poolSize || 2), // minimum size of the pool
1948
+ acquireTimeoutMillis: 30000, // 30秒获取连接超时
1949
+ testOnBorrow: true, // 借用时测试连接
1950
+ evictionRunIntervalMillis: 30000, // 30秒检查一次空闲连接
1951
+ idleTimeoutMillis: 300000, // 5分钟空闲超时
1952
+ softIdleTimeoutMillis: 180000 // 3分钟软空闲超时
1409
1953
  });
1410
1954
  this.pool.on('factoryCreateError', function (err) {
1411
- koatty_logger.DefaultLogger.Error(err);
1955
+ koatty_logger.DefaultLogger.Error(`Redis pool create error: ${err.message}`);
1412
1956
  });
1413
1957
  this.pool.on('factoryDestroyError', function (err) {
1414
- koatty_logger.DefaultLogger.Error(err);
1958
+ koatty_logger.DefaultLogger.Error(`Redis pool destroy error: ${err.message}`);
1415
1959
  });
1416
1960
  }
1417
1961
  return this.pool.acquire();
1418
1962
  }
1419
1963
  /**
1420
- * close connection
1964
+ * close connection with proper cleanup
1421
1965
  *
1422
1966
  * @returns {*}
1423
1967
  * @memberof RedisStore
1424
1968
  */
1425
1969
  async close() {
1426
- this.client.disconnect();
1427
- this.client = null;
1428
- this.pool.destroy(this.client);
1429
- this.pool = null;
1430
- return;
1970
+ try {
1971
+ if (this.pool) {
1972
+ await this.pool.drain();
1973
+ await this.pool.clear();
1974
+ this.pool = null;
1975
+ }
1976
+ if (this.client) {
1977
+ if (typeof this.client.disconnect === 'function') {
1978
+ this.client.disconnect();
1979
+ }
1980
+ this.client = null;
1981
+ }
1982
+ }
1983
+ catch (error) {
1984
+ koatty_logger.DefaultLogger.Error(`Error closing Redis connection: ${error.message}`);
1985
+ }
1431
1986
  }
1432
1987
  /**
1433
1988
  *
@@ -1470,16 +2025,16 @@ class RedisStore {
1470
2025
  try {
1471
2026
  conn = await this.defineCommand("getCompare", {
1472
2027
  numberOfKeys: 1,
1473
- lua: `
1474
- local remote_value = redis.call("get",KEYS[1])
1475
-
1476
- if (not remote_value) then
1477
- return 0
1478
- elseif (remote_value == ARGV[1]) then
1479
- return redis.call("del",KEYS[1])
1480
- else
1481
- return -1
1482
- end
2028
+ lua: `
2029
+ local remote_value = redis.call("get",KEYS[1])
2030
+
2031
+ if (not remote_value) then
2032
+ return 0
2033
+ elseif (remote_value == ARGV[1]) then
2034
+ return redis.call("del",KEYS[1])
2035
+ else
2036
+ return -1
2037
+ end
1483
2038
  `
1484
2039
  });
1485
2040
  return conn.getCompare(name, value);
@@ -1519,7 +2074,7 @@ const defaultOptions = {
1519
2074
  class CacheStore {
1520
2075
  client;
1521
2076
  options;
1522
- static instance;
2077
+ static instances = new Map();
1523
2078
  /**
1524
2079
  * Creates an instance of CacheStore.
1525
2080
  * @param {StoreOptions} options
@@ -1539,17 +2094,70 @@ class CacheStore {
1539
2094
  }
1540
2095
  }
1541
2096
  /**
1542
- *
1543
- *
2097
+ * 获取单例实例,支持多配置实例管理
1544
2098
  * @static
1545
- * @returns
2099
+ * @param {StoreOptions} [options]
2100
+ * @param {string} [instanceKey='default'] 实例键名,用于区分不同配置的实例
2101
+ * @returns {CacheStore}
2102
+ */
2103
+ static getInstance(options, instanceKey = 'default') {
2104
+ // 生成配置哈希作为实例键的一部分
2105
+ const configHash = options ? this.generateConfigHash(options) : 'default';
2106
+ const fullKey = `${instanceKey}_${configHash}`;
2107
+ if (this.instances.has(fullKey)) {
2108
+ return this.instances.get(fullKey);
2109
+ }
2110
+ const instance = new CacheStore(options);
2111
+ this.instances.set(fullKey, instance);
2112
+ return instance;
2113
+ }
2114
+ /**
2115
+ * 生成配置哈希
2116
+ * @private
2117
+ * @static
2118
+ * @param {StoreOptions} options
2119
+ * @returns {string}
1546
2120
  */
1547
- static getInstance(options) {
1548
- if (this.instance) {
1549
- return this.instance;
2121
+ static generateConfigHash(options) {
2122
+ const configStr = JSON.stringify({
2123
+ type: options.type,
2124
+ host: options.host,
2125
+ port: options.port,
2126
+ db: options.db,
2127
+ keyPrefix: options.keyPrefix
2128
+ });
2129
+ // 简单哈希函数
2130
+ let hash = 0;
2131
+ for (let i = 0; i < configStr.length; i++) {
2132
+ const char = configStr.charCodeAt(i);
2133
+ hash = ((hash << 5) - hash) + char;
2134
+ hash = hash & hash; // 转换为32位整数
1550
2135
  }
1551
- this.instance = new CacheStore(options);
1552
- return this.instance;
2136
+ return Math.abs(hash).toString(36);
2137
+ }
2138
+ /**
2139
+ * 清理指定实例
2140
+ * @static
2141
+ * @param {string} [instanceKey='default']
2142
+ */
2143
+ static async clearInstance(instanceKey = 'default') {
2144
+ const keysToRemove = Array.from(this.instances.keys()).filter(key => key.startsWith(`${instanceKey}_`));
2145
+ for (const key of keysToRemove) {
2146
+ const instance = this.instances.get(key);
2147
+ if (instance) {
2148
+ await instance.close();
2149
+ this.instances.delete(key);
2150
+ }
2151
+ }
2152
+ }
2153
+ /**
2154
+ * 清理所有实例
2155
+ * @static
2156
+ */
2157
+ static async clearAllInstances() {
2158
+ const promises = Array.from(this.instances.values()).map(instance => instance.close());
2159
+ await Promise.all(promises);
2160
+ this.instances.clear();
1553
2161
  }
1554
2162
  getConnection() {
1555
2163
  return this.client.getConnection();
@@ -1560,11 +2168,13 @@ class CacheStore {
1560
2168
  release(conn) {
1561
2169
  return this.client.release(conn);
1562
2170
  }
1563
- defineCommand(name, scripts) {
1564
- return this.client.defineCommand(name, scripts);
1565
- }
1566
- getCompare(name, value) {
1567
- return this.client.getCompare(name, value);
2171
+ /**
2172
+ * 获取底层实现客户端,用于访问特定实现的功能
2173
+ * 例如:Redis的defineCommand, getCompare等
2174
+ * @returns {MemoryStore | RedisStore}
2175
+ */
2176
+ getRawClient() {
2177
+ return this.client;
1568
2178
  }
1569
2179
  /**
1570
2180
  * handler for native client
@@ -1624,13 +2234,6 @@ class CacheStore {
1624
2234
  expire(name, timeout) {
1625
2235
  return this.wrap('expire', [`${this.options.keyPrefix || ""}${name}`, timeout]);
1626
2236
  }
1627
- /**
1628
- * 删除key
1629
- * @param name
1630
- */
1631
- rm(name) {
1632
- return this.wrap('del', [`${this.options.keyPrefix || ""}${name}`]);
1633
- }
1634
2237
  /**
1635
2238
  *
1636
2239
  *
@@ -1668,8 +2271,8 @@ class CacheStore {
1668
2271
  * @param incr
1669
2272
  * @returns {*}
1670
2273
  */
1671
- incrby(name, incr = 1) {
1672
- return this.wrap('incrby', [`${this.options.keyPrefix || ""}${name}`, incr]);
2274
+ incrby(name, increment) {
2275
+ return this.wrap('incrby', [`${this.options.keyPrefix || ""}${name}`, increment]);
1673
2276
  }
1674
2277
  /**
1675
2278
  * 将 key 所储存的值减去减量
@@ -1677,8 +2280,8 @@ class CacheStore {
1677
2280
  * @param {any} name
1678
2281
  * @param {any} decr
1679
2282
  */
1680
- decrby(name, decr = 1) {
1681
- return this.wrap('decrby', [`${this.options.keyPrefix || ""}${name}`, decr]);
2283
+ decrby(name, decrement) {
2284
+ return this.wrap('decrby', [`${this.options.keyPrefix || ""}${name}`, decrement]);
1682
2285
  }
1683
2286
  /**
1684
2287
  * 哈希写入
@@ -1687,13 +2290,16 @@ class CacheStore {
1687
2290
  * @param value
1688
2291
  * @param timeout
1689
2292
  */
1690
- hset(name, key, value, timeout) {
1691
- const setP = [this.wrap('hset', [`${this.options.keyPrefix || ""}${name}`, key, value])];
1692
- if (typeof timeout !== 'number') {
1693
- timeout = this.options.timeout;
2293
+ async hset(name, key, value, timeout) {
2294
+ const result = await this.wrap('hset', [`${this.options.keyPrefix || ""}${name}`, key, value]);
2295
+ if (typeof timeout === 'number') {
2296
+ await this.set(`${name}:${key}_ex`, 1, timeout);
1694
2297
  }
1695
- setP.push(this.set(`${name}:${key}_ex`, 1, timeout));
1696
- return Promise.all(setP);
2298
+ else {
2299
+ // 如果没有指定timeout,设置一个永久标记,避免hget时误删
2300
+ await this.set(`${name}:${key}_ex`, 1);
2301
+ }
2302
+ return result;
1697
2303
  }
1698
2304
  /**
1699
2305
  * 哈希获取
@@ -1726,7 +2332,7 @@ class CacheStore {
1726
2332
  this.hdel(name, key);
1727
2333
  return 0;
1728
2334
  }
1729
- return dataArr[1] || 0;
2335
+ return Number(dataArr[1]) || 0;
1730
2336
  });
1731
2337
  }
1732
2338
  /**
@@ -1735,10 +2341,10 @@ class CacheStore {
1735
2341
  * @param key
1736
2342
  * @returns {*}
1737
2343
  */
1738
- hdel(name, key) {
1739
- const setP = [this.del(`${name}:${key}_ex`)];
1740
- setP.push(this.wrap('hdel', [`${this.options.keyPrefix || ""}${name}`, key]));
1741
- return Promise.all(setP);
2344
+ async hdel(name, key) {
2345
+ await this.del(`${name}:${key}_ex`);
2346
+ const result = await this.wrap('hdel', [`${this.options.keyPrefix || ""}${name}`, key]);
2347
+ return result;
1742
2348
  }
1743
2349
  /**
1744
2350
  * 返回哈希表 key 中域的数量
@@ -1752,11 +2358,11 @@ class CacheStore {
1752
2358
  * 给哈希表指定key,增加increment
1753
2359
  * @param name
1754
2360
  * @param key
1755
- * @param incr
2361
+ * @param increment
1756
2362
  * @returns {*}
1757
2363
  */
1758
- hincrby(name, key, incr = 1) {
1759
- return this.wrap('hincrby', [`${this.options.keyPrefix || ""}${name}`, key, incr]);
2364
+ hincrby(name, key, increment) {
2365
+ return this.wrap('hincrby', [`${this.options.keyPrefix || ""}${name}`, key, increment]);
1760
2366
  }
1761
2367
  /**
1762
2368
  * 返回哈希表所有key-value
@@ -1845,12 +2451,12 @@ class CacheStore {
1845
2451
  * @param timeout
1846
2452
  * @returns {*}
1847
2453
  */
1848
- sadd(name, value, timeout) {
1849
- const setP = [this.wrap('sadd', [`${this.options.keyPrefix || ""}${name}`, value])];
1850
- if (typeof timeout !== 'number') {
1851
- setP.push(this.wrap('expire', [`${this.options.keyPrefix || ""}${name}`, timeout]));
2454
+ async sadd(name, value, timeout) {
2455
+ const result = await this.wrap('sadd', [`${this.options.keyPrefix || ""}${name}`, value]);
2456
+ if (typeof timeout === 'number') {
2457
+ await this.wrap('expire', [`${this.options.keyPrefix || ""}${name}`, timeout]);
1852
2458
  }
1853
- return Promise.all(setP);
2459
+ return result;
1854
2460
  }
1855
2461
  /**
1856
2462
  * 返回集合的基数(集合中元素的数量)
@@ -1902,7 +2508,7 @@ class CacheStore {
1902
2508
  * @returns {*}
1903
2509
  */
1904
2510
  smove(source, destination, member) {
1905
- return this.wrap('smove', [`${this.options.keyPrefix || ""}${source}`, `${this.options.keyPrefix}${destination}`, member]);
2511
+ return this.wrap('smove', [`${this.options.keyPrefix || ""}${source}`, `${this.options.keyPrefix || ""}${destination}`, member]);
1906
2512
  }
1907
2513
  }
1908
2514