koatty_store 1.6.2 → 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: 2023-12-20 19:11:59
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');
@@ -33,1430 +34,1997 @@ function _interopNamespaceDefault(e) {
33
34
 
34
35
  var helper__namespace = /*#__PURE__*/_interopNamespaceDefault(helper);
35
36
 
36
- /*
37
- * @Description:
38
- * @Usage:
39
- * @Author: richen
40
- * @Date: 2021-12-02 11:03:20
41
- * @LastEditTime: 2023-12-20 19:04:29
42
- */
43
- /**
44
- *
45
- *
46
- * @enum {number}
47
- */
48
- var messages;
49
- (function (messages) {
50
- messages["ok"] = "OK";
51
- messages["queued"] = "QUEUED";
52
- messages["pong"] = "PONG";
53
- messages["noint"] = "ERR value is not an integer or out of range";
54
- messages["nofloat"] = "ERR value is not an float or out of range";
55
- messages["nokey"] = "ERR no such key";
56
- messages["nomultiinmulti"] = "ERR MULTI calls can not be nested";
57
- messages["nomultiexec"] = "ERR EXEC without MULTI";
58
- messages["nomultidiscard"] = "ERR DISCARD without MULTI";
59
- messages["busykey"] = "ERR target key name is busy";
60
- messages["syntax"] = "ERR syntax error";
61
- messages["unsupported"] = "MemoryCache does not support that operation";
62
- messages["wrongTypeOp"] = "WRONGTYPE Operation against a key holding the wrong kind of value";
63
- messages["wrongPayload"] = "DUMP payload version or checksum are wrong";
64
- messages["wrongArgCount"] = "ERR wrong number of arguments for '%0' command";
65
- messages["bitopnotWrongCount"] = "ERR BITOP NOT must be called with a single source key";
66
- messages["indexOutOfRange"] = "ERR index out of range";
67
- messages["invalidLexRange"] = "ERR min or max not valid string range item";
68
- messages["invalidDBIndex"] = "ERR invalid DB index";
69
- messages["invalidDBIndexNX"] = "ERR invalid DB index, '%0' does not exist";
70
- messages["mutuallyExclusiveNXXX"] = "ERR XX and NX options at the same time are not compatible";
71
- })(messages || (messages = {}));
72
- class MemoryCache extends events.EventEmitter {
73
- /**
74
- * Creates an instance of MemoryCache.
75
- * @param {*} options
76
- * @memberof MemoryCache
77
- */
78
- constructor(options) {
79
- super();
80
- this.databases = Object.create({});
81
- this.options = { ...{ database: "0" }, ...options };
82
- this.currentDBIndex = 0;
83
- this.connected = false;
84
- this.lastSave = Date.now();
85
- this.multiMode = false;
86
- }
87
- /**
88
- *
89
- *
90
- * @returns {*}
91
- * @memberof MemoryCache
92
- */
93
- createClient() {
94
- this.databases[this.options.database] = Object.create({});
95
- this.cache = this.databases[this.options.database];
96
- this.connected = true;
97
- // exit multi mode if we are in it
98
- this.discard(null, true);
99
- this.emit('connect');
100
- this.emit('ready');
101
- return this;
102
- }
103
- /**
104
- *
105
- *
106
- * @returns {*}
107
- * @memberof MemoryCache
108
- */
109
- quit() {
110
- this.connected = false;
111
- // exit multi mode if we are in it
112
- this.discard(null, true);
113
- this.emit('end');
114
- return this;
115
- }
116
- /**
117
- *
118
- *
119
- * @returns {*}
120
- * @memberof MemoryCache
121
- */
122
- end() {
123
- return this.quit();
124
- }
125
- /**
126
- *
127
- *
128
- * @param {string} message
129
- * @param {Function} [callback]
130
- * @returns {*}
131
- * @memberof MemoryCache
132
- */
133
- echo(message, callback) {
134
- return this._handleCallback(callback, message);
135
- }
136
- /**
137
- *
138
- *
139
- * @param {string} message
140
- * @param {Function} [callback]
141
- * @returns {*}
142
- * @memberof MemoryCache
143
- */
144
- ping(message, callback) {
145
- message = message || messages.pong;
146
- return this._handleCallback(callback, message);
147
- }
148
- /**
149
- *
150
- *
151
- * @param {string} password
152
- * @param {Function} [callback]
153
- * @returns {*}
154
- * @memberof MemoryCache
155
- */
156
- auth(password, callback) {
157
- return this._handleCallback(callback, messages.ok);
158
- }
159
- /**
160
- *
161
- *
162
- * @param {number} dbIndex
163
- * @param {Function} [callback]
164
- * @returns {*}
165
- * @memberof MemoryCache
166
- */
167
- select(dbIndex, callback) {
168
- if (!helper__namespace.isNumber(dbIndex)) {
169
- return this._handleCallback(callback, null, messages.invalidDBIndex);
170
- }
171
- if (!this.databases.hasOwnProperty(dbIndex)) {
172
- this.databases[dbIndex] = Object.create({});
173
- }
174
- this.multiMode = false;
175
- this.currentDBIndex = dbIndex;
176
- this.cache = this.databases[dbIndex];
177
- return this._handleCallback(callback, messages.ok);
178
- }
179
- // ---------------------------------------
180
- // Keys
181
- // ---------------------------------------
182
- get(key, callback) {
183
- let retVal = null;
184
- if (this._hasKey(key)) {
185
- this._testType(key, 'string', true, callback);
186
- retVal = this._getKey(key);
187
- }
188
- return this._handleCallback(callback, retVal);
189
- }
190
- /**
191
- * set(key, value, ttl, pttl, notexist, onlyexist, callback)
192
- *
193
- * @param {string} key
194
- * @param {(string | number)} value
195
- * @param {...any[]} params
196
- * @returns {*}
197
- * @memberof MemoryCache
198
- */
199
- set(key, value, ...params) {
200
- const retVal = null;
201
- params = lodash.flatten(params);
202
- const callback = this._retrieveCallback(params);
203
- let ttl, pttl, notexist, onlyexist;
204
- // parse parameters
205
- while (params.length > 0) {
206
- const param = params.shift();
207
- switch (param.toString().toLowerCase()) {
208
- case 'nx':
209
- notexist = true;
210
- break;
211
- case 'xx':
212
- onlyexist = true;
213
- break;
214
- case 'ex':
215
- if (params.length === 0) {
216
- return this._handleCallback(callback, null, messages.syntax);
217
- }
218
- ttl = parseInt(params.shift());
219
- if (isNaN(ttl)) {
220
- return this._handleCallback(callback, null, messages.noint);
221
- }
222
- break;
223
- case 'px':
224
- if (params.length === 0) {
225
- return this._handleCallback(callback, null, messages.syntax);
226
- }
227
- pttl = parseInt(params.shift());
228
- if (isNaN(pttl)) {
229
- return this._handleCallback(callback, null, messages.noint);
230
- }
231
- break;
232
- default:
233
- return this._handleCallback(callback, null, messages.syntax);
234
- }
235
- }
236
- if (!lodash.isNil(ttl) && !lodash.isNil(pttl)) {
237
- return this._handleCallback(callback, null, messages.syntax);
238
- }
239
- if (notexist && onlyexist) {
240
- return this._handleCallback(callback, null, messages.syntax);
241
- }
242
- pttl = pttl || ttl * 1000 || null;
243
- if (!lodash.isNil(pttl)) {
244
- pttl = Date.now() + pttl;
245
- }
246
- if (this._hasKey(key)) {
247
- this._testType(key, 'string', true, callback);
248
- if (notexist) {
249
- return this._handleCallback(callback, retVal);
250
- }
251
- }
252
- else if (onlyexist) {
253
- return this._handleCallback(callback, retVal);
254
- }
255
- this.cache[key] = this._makeKey(value.toString(), 'string', pttl);
256
- return this._handleCallback(callback, messages.ok);
257
- }
258
- /**
259
- *
260
- *
261
- * @param {string} key
262
- * @param {Function} [callback]
263
- * @returns {*}
264
- * @memberof MemoryCache
265
- */
266
- ttl(key, callback) {
267
- let retVal = this.pttl(key);
268
- if (retVal >= 0 || retVal <= -3) {
269
- retVal = Math.floor(retVal / 1000);
270
- }
271
- return this._handleCallback(callback, retVal);
272
- }
273
- /**
274
- *
275
- *
276
- * @param {string} key
277
- * @param {number} seconds
278
- * @param {Function} [callback]
279
- * @returns {*}
280
- * @memberof MemoryCache
281
- */
282
- expire(key, seconds, callback) {
283
- let retVal = 0;
284
- if (this._hasKey(key)) {
285
- this.cache[key].timeout = Date.now() + seconds * 1000;
286
- retVal = 1;
287
- }
288
- return this._handleCallback(callback, retVal);
289
- }
290
- /**
291
- *
292
- *
293
- * @param {...any[]} keys
294
- * @returns {*}
295
- * @memberof MemoryCache
296
- */
297
- del(...keys) {
298
- let retVal = 0;
299
- const callback = this._retrieveCallback(keys);
300
- // Flatten the array in case an array was passed
301
- keys = lodash.flatten(keys);
302
- for (let itr = 0; itr < keys.length; itr++) {
303
- const key = keys[itr];
304
- if (this._hasKey(key)) {
305
- delete this.cache[key];
306
- retVal++;
307
- }
308
- }
309
- return this._handleCallback(callback, retVal);
310
- }
311
- /**
312
- *
313
- *
314
- * @param {...any[]} keys
315
- * @returns {*}
316
- * @memberof MemoryCache
317
- */
318
- exists(...keys) {
319
- let retVal = 0;
320
- const callback = this._retrieveCallback(keys);
321
- for (let itr = 0; itr < keys.length; itr++) {
322
- const key = keys[itr];
323
- if (this._hasKey(key)) {
324
- retVal++;
325
- }
326
- }
327
- return this._handleCallback(callback, retVal);
328
- }
329
- /**
330
- *
331
- *
332
- * @param {string} key
333
- * @param {Function} [callback]
334
- * @returns {*}
335
- * @memberof MemoryCache
336
- */
337
- incr(key, callback) {
338
- let retVal = null;
339
- try {
340
- retVal = this._addToKey(key, 1);
341
- }
342
- catch (err) {
343
- return this._handleCallback(callback, null, err);
344
- }
345
- return this._handleCallback(callback, retVal);
346
- }
347
- /**
348
- *
349
- *
350
- * @param {string} key
351
- * @param {number} amount
352
- * @param {Function} [callback]
353
- * @returns {*}
354
- * @memberof MemoryCache
355
- */
356
- incrby(key, amount, callback) {
357
- let retVal = null;
358
- try {
359
- retVal = this._addToKey(key, amount);
360
- }
361
- catch (err) {
362
- return this._handleCallback(callback, null, err);
363
- }
364
- return this._handleCallback(callback, retVal);
365
- }
366
- /**
367
- *
368
- *
369
- * @param {string} key
370
- * @param {Function} [callback]
371
- * @returns {*}
372
- * @memberof MemoryCache
373
- */
374
- decr(key, callback) {
375
- let retVal = null;
376
- try {
377
- retVal = this._addToKey(key, -1);
378
- }
379
- catch (err) {
380
- return this._handleCallback(callback, null, err);
381
- }
382
- return this._handleCallback(callback, retVal);
383
- }
384
- /**
385
- *
386
- *
387
- * @param {string} key
388
- * @param {number} amount
389
- * @param {Function} [callback]
390
- * @returns {*}
391
- * @memberof MemoryCache
392
- */
393
- decrby(key, amount, callback) {
394
- let retVal = null;
395
- try {
396
- retVal = this._addToKey(key, -amount);
397
- }
398
- catch (err) {
399
- return this._handleCallback(callback, null, err);
400
- }
401
- return this._handleCallback(callback, retVal);
402
- }
403
- // ---------------------------------------
404
- // ## Hash ##
405
- // ---------------------------------------
406
- hset(key, field, value, callback) {
407
- let retVal = 0;
408
- if (this._hasKey(key)) {
409
- this._testType(key, 'hash', true, callback);
410
- }
411
- else {
412
- this.cache[key] = this._makeKey({}, 'hash');
413
- }
414
- if (!this._hasField(key, field)) {
415
- retVal = 1;
416
- }
417
- this._setField(key, field, value.toString());
418
- this.persist(key);
419
- return this._handleCallback(callback, retVal);
420
- }
421
- /**
422
- *
423
- *
424
- * @param {string} key
425
- * @param {string} field
426
- * @param {Function} [callback]
427
- * @returns {*}
428
- * @memberof MemoryCache
429
- */
430
- hget(key, field, callback) {
431
- let retVal = null;
432
- if (this._hasKey(key)) {
433
- this._testType(key, 'hash', true, callback);
434
- if (this._hasField(key, field)) {
435
- retVal = this._getKey(key)[field];
436
- }
437
- }
438
- return this._handleCallback(callback, retVal);
439
- }
440
- /**
441
- *
442
- *
443
- * @param {string} key
444
- * @param {string} field
445
- * @param {Function} [callback]
446
- * @returns {*}
447
- * @memberof MemoryCache
448
- */
449
- hexists(key, field, callback) {
450
- let retVal = 0;
451
- if (this._hasKey(key)) {
452
- this._testType(key, 'hash', true, callback);
453
- if (this._hasField(key, field)) {
454
- retVal = 1;
455
- }
456
- }
457
- return this._handleCallback(callback, retVal);
458
- }
459
- /**
460
- *
461
- *
462
- * @param {string} key
463
- * @param {...any[]} fields
464
- * @returns {*}
465
- * @memberof MemoryCache
466
- */
467
- hdel(key, ...fields) {
468
- let retVal = 0;
469
- const callback = this._retrieveCallback(fields);
470
- if (this._hasKey(key)) {
471
- this._testType(key, 'hash', true, callback);
472
- for (let itr = 0; itr < fields.length; itr++) {
473
- const field = fields[itr];
474
- if (this._hasField(key, field)) {
475
- delete this.cache[key].value[field];
476
- retVal++;
477
- }
478
- }
479
- }
480
- return this._handleCallback(callback, retVal);
481
- }
482
- /**
483
- *
484
- *
485
- * @param {string} key
486
- * @param {Function} [callback]
487
- * @returns {*}
488
- * @memberof MemoryCache
489
- */
490
- hlen(key, callback) {
491
- const retVal = this.hkeys(key).length;
492
- return this._handleCallback(callback, retVal);
493
- }
494
- /**
495
- *
496
- *
497
- * @param {string} key
498
- * @param {string} field
499
- * @param {*} value
500
- * @param {Function} [callback]
501
- * @returns {*}
502
- * @memberof MemoryCache
503
- */
504
- hincrby(key, field, value, callback) {
505
- let retVal;
506
- try {
507
- retVal = this._addToField(key, field, value, false);
508
- }
509
- catch (err) {
510
- return this._handleCallback(callback, null, err);
511
- }
512
- return this._handleCallback(callback, retVal);
513
- }
514
- /**
515
- *
516
- *
517
- * @param {string} key
518
- * @param {Function} [callback]
519
- * @returns {*}
520
- * @memberof MemoryCache
521
- */
522
- hgetall(key, callback) {
523
- let retVals = {};
524
- if (this._hasKey(key)) {
525
- this._testType(key, 'hash', true, callback);
526
- retVals = this._getKey(key);
527
- }
528
- return this._handleCallback(callback, retVals);
529
- }
530
- /**
531
- *
532
- *
533
- * @param {string} key
534
- * @param {Function} [callback]
535
- * @returns {*}
536
- * @memberof MemoryCache
537
- */
538
- hkeys(key, callback) {
539
- let retVals = [];
540
- if (this._hasKey(key)) {
541
- this._testType(key, 'hash', true, callback);
542
- retVals = Object.keys(this._getKey(key));
543
- }
544
- return this._handleCallback(callback, retVals);
545
- }
546
- /**
547
- *
548
- *
549
- * @param {string} key
550
- * @param {Function} [callback]
551
- * @returns {*}
552
- * @memberof MemoryCache
553
- */
554
- hvals(key, callback) {
555
- let retVals = [];
556
- if (this._hasKey(key)) {
557
- this._testType(key, 'hash', true, callback);
558
- retVals = Object.values(this._getKey(key));
559
- }
560
- return this._handleCallback(callback, retVals);
561
- }
562
- // ---------------------------------------
563
- // Lists (Array / Queue / Stack)
564
- // ---------------------------------------
565
- /**
566
- *
567
- *
568
- * @param {string} key
569
- * @param {Function} [callback]
570
- * @returns {*}
571
- * @memberof MemoryCache
572
- */
573
- llen(key, callback) {
574
- let retVal = 0;
575
- if (this._hasKey(key)) {
576
- this._testType(key, 'list', true, callback);
577
- retVal = this._getKey(key).length || 0;
578
- }
579
- return this._handleCallback(callback, retVal);
580
- }
581
- /**
582
- *
583
- *
584
- * @param {string} key
585
- * @param {(string | number)} value
586
- * @param {Function} [callback]
587
- * @returns {*}
588
- * @memberof MemoryCache
589
- */
590
- rpush(key, value, callback) {
591
- let retVal = 0;
592
- if (this._hasKey(key)) {
593
- this._testType(key, 'list', true, callback);
594
- }
595
- else {
596
- this.cache[key] = this._makeKey([], 'list');
597
- }
598
- const val = this._getKey(key);
599
- val.push(value);
600
- this._setKey(key, val);
601
- retVal = val.length;
602
- return this._handleCallback(callback, retVal);
603
- }
604
- /**
605
- *
606
- *
607
- * @param {string} key
608
- * @param {(string | number)} value
609
- * @param {Function} [callback]
610
- * @returns {*}
611
- * @memberof MemoryCache
612
- */
613
- lpush(key, value, callback) {
614
- let retVal = 0;
615
- if (this._hasKey(key)) {
616
- this._testType(key, 'list', true, callback);
617
- }
618
- else {
619
- this.cache[key] = this._makeKey([], 'list');
620
- }
621
- const val = this._getKey(key);
622
- val.splice(0, 0, value);
623
- this._setKey(key, val);
624
- retVal = val.length;
625
- return this._handleCallback(callback, retVal);
626
- }
627
- /**
628
- *
629
- *
630
- * @param {string} key
631
- * @param {Function} [callback]
632
- * @returns {*}
633
- * @memberof MemoryCache
634
- */
635
- lpop(key, callback) {
636
- let retVal = null;
637
- if (this._hasKey(key)) {
638
- this._testType(key, 'list', true, callback);
639
- const val = this._getKey(key);
640
- retVal = val.shift();
641
- this._setKey(key, val);
642
- }
643
- return this._handleCallback(callback, retVal);
644
- }
645
- /**
646
- *
647
- *
648
- * @param {string} key
649
- * @param {Function} [callback]
650
- * @returns {*}
651
- * @memberof MemoryCache
652
- */
653
- rpop(key, callback) {
654
- let retVal = null;
655
- if (this._hasKey(key)) {
656
- this._testType(key, 'list', true, callback);
657
- const val = this._getKey(key);
658
- retVal = val.pop();
659
- this._setKey(key, val);
660
- }
661
- return this._handleCallback(callback, retVal);
662
- }
663
- /**
664
- *
665
- *
666
- * @param {string} key
667
- * @param {number} start
668
- * @param {number} stop
669
- * @param {Function} [callback]
670
- * @returns {*}
671
- * @memberof MemoryCache
672
- */
673
- lrange(key, start, stop, callback) {
674
- const retVal = [];
675
- if (this._hasKey(key)) {
676
- this._testType(key, 'list', true, callback);
677
- const val = this._getKey(key);
678
- const length = val.length;
679
- if (stop < 0) {
680
- stop = length + stop;
681
- }
682
- if (start < 0) {
683
- start = length + start;
684
- }
685
- if (start < 0) {
686
- start = 0;
687
- }
688
- if (stop >= length) {
689
- stop = length - 1;
690
- }
691
- if (stop >= 0 && stop >= start) {
692
- const size = stop - start + 1;
693
- for (let itr = start; itr < size; itr++) {
694
- retVal.push(val[itr]);
695
- }
696
- }
697
- }
698
- return this._handleCallback(callback, retVal);
699
- }
700
- // ---------------------------------------
701
- // ## Sets (Unique Lists)##
702
- // ---------------------------------------
703
- /**
704
- *
705
- *
706
- * @param {string} key
707
- * @param {...any[]} members
708
- * @returns {*}
709
- * @memberof MemoryCache
710
- */
711
- sadd(key, ...members) {
712
- let retVal = 0;
713
- const callback = this._retrieveCallback(members);
714
- if (this._hasKey(key)) {
715
- this._testType(key, 'set', true, callback);
716
- }
717
- else {
718
- this.cache[key] = this._makeKey([], 'set');
719
- }
720
- const val = this._getKey(key);
721
- const length = val.length;
722
- const nval = lodash.union(val, members);
723
- const newlength = nval.length;
724
- retVal = newlength - length;
725
- this._setKey(key, nval);
726
- return this._handleCallback(callback, retVal);
727
- }
728
- /**
729
- *
730
- *
731
- * @param {string} key
732
- * @param {Function} [callback]
733
- * @returns {*}
734
- * @memberof MemoryCache
735
- */
736
- scard(key, callback) {
737
- let retVal = 0;
738
- if (this._hasKey(key)) {
739
- this._testType(key, 'set', true, callback);
740
- retVal = this._getKey(key).length;
741
- }
742
- return this._handleCallback(callback, retVal);
743
- }
744
- /**
745
- *
746
- *
747
- * @param {string} key
748
- * @param {string} member
749
- * @param {Function} [callback]
750
- * @returns {*}
751
- * @memberof MemoryCache
752
- */
753
- sismember(key, member, callback) {
754
- let retVal = 0;
755
- if (this._hasKey(key)) {
756
- this._testType(key, 'set', true, callback);
757
- const val = this._getKey(key);
758
- if (val.includes(member)) {
759
- retVal = 1;
760
- }
761
- }
762
- return this._handleCallback(callback, retVal);
763
- }
764
- /**
765
- *
766
- *
767
- * @param {string} key
768
- * @param {Function} [callback]
769
- * @returns {*}
770
- * @memberof MemoryCache
771
- */
772
- smembers(key, callback) {
773
- let retVal = [];
774
- if (this._hasKey(key)) {
775
- this._testType(key, 'set', true, callback);
776
- retVal = this._getKey(key);
777
- }
778
- return this._handleCallback(callback, retVal);
779
- }
780
- /**
781
- *
782
- *
783
- * @param {string} key
784
- * @param {number} [count]
785
- * @param {Function} [callback]
786
- * @returns {*}
787
- * @memberof MemoryCache
788
- */
789
- spop(key, count, callback) {
790
- let retVal = null;
791
- count = count || 1;
792
- if (isNaN(count)) {
793
- return this._handleCallback(callback, null, messages.noint);
794
- }
795
- if (this._hasKey(key)) {
796
- retVal = [];
797
- this._testType(key, 'set', true, callback);
798
- const val = this._getKey(key);
799
- const length = val.length;
800
- count = count > length ? length : count;
801
- for (let itr = 0; itr < count; itr++) {
802
- retVal.push(val.pop());
803
- }
804
- }
805
- return this._handleCallback(callback, retVal);
806
- }
807
- /**
808
- *
809
- *
810
- * @param {string} key
811
- * @param {...any[]} members
812
- * @returns {*}
813
- * @memberof MemoryCache
814
- */
815
- srem(key, ...members) {
816
- let retVal = 0;
817
- const callback = this._retrieveCallback(members);
818
- if (this._hasKey(key)) {
819
- this._testType(key, 'set', true, callback);
820
- const val = this._getKey(key);
821
- for (const index in members) {
822
- if (members.hasOwnProperty(index)) {
823
- const member = members[index];
824
- const idx = val.indexOf(member);
825
- if (idx !== -1) {
826
- val.splice(idx, 1);
827
- retVal++;
828
- }
829
- }
830
- }
831
- this._setKey(key, val);
832
- }
833
- return this._handleCallback(callback, retVal);
834
- }
835
- /**
836
- *
837
- *
838
- * @param {string} sourcekey
839
- * @param {string} destkey
840
- * @param {string} member
841
- * @param {Function} [callback]
842
- * @returns {*}
843
- * @memberof MemoryCache
844
- */
845
- smove(sourcekey, destkey, member, callback) {
846
- let retVal = 0;
847
- if (this._hasKey(sourcekey)) {
848
- this._testType(sourcekey, 'set', true, callback);
849
- const val = this._getKey(sourcekey);
850
- const idx = val.indexOf(member);
851
- if (idx !== -1) {
852
- this.sadd(destkey, member);
853
- val.splice(idx, 1);
854
- retVal = 1;
855
- }
856
- }
857
- return this._handleCallback(callback, retVal);
858
- }
859
- // ---------------------------------------
860
- // ## Transactions (Atomic) ##
861
- // ---------------------------------------
862
- // TODO: Transaction Queues watch and unwatch
863
- // https://redis.io/topics/transactions
864
- // This can be accomplished by temporarily swapping this.cache to a temporary copy of the current statement
865
- // holding and then using __.merge on actual this.cache with the temp storage.
866
- discard(callback, silent) {
867
- // Clear the queue mode, drain the queue, empty the watch list
868
- if (this.multiMode) {
869
- this.cache = this.databases[this.currentDBIndex];
870
- this.multiMode = false;
871
- this.responseMessages = [];
872
- }
873
- else if (!silent) {
874
- return this._handleCallback(callback, null, messages.nomultidiscard);
875
- }
876
- return this._handleCallback(callback, messages.ok);
877
- }
878
- // ---------------------------------------
879
- // ## Internal - Key ##
880
- // ---------------------------------------
881
- /**
882
- *
883
- *
884
- * @param {string} key
885
- * @param {Function} [callback]
886
- * @returns {*}
887
- * @memberof MemoryCache
888
- */
889
- pttl(key, callback) {
890
- let retVal = -2;
891
- if (this._hasKey(key)) {
892
- if (!lodash.isNil(this.cache[key].timeout)) {
893
- retVal = this.cache[key].timeout - Date.now();
894
- // Prevent unexpected errors if the actual ttl just happens to be -2 or -1
895
- if (retVal < 0 && retVal > -3) {
896
- retVal = -3;
897
- }
898
- }
899
- else {
900
- retVal = -1;
901
- }
902
- }
903
- return this._handleCallback(callback, retVal);
904
- }
905
- /**
906
- *
907
- *
908
- * @private
909
- * @param {string} key
910
- * @param {Function} [callback]
911
- * @returns {*}
912
- * @memberof MemoryCache
913
- */
914
- persist(key, callback) {
915
- let retVal = 0;
916
- if (this._hasKey(key)) {
917
- if (!lodash.isNil(this._key(key).timeout)) {
918
- this._key(key).timeout = null;
919
- retVal = 1;
920
- }
921
- }
922
- return this._handleCallback(callback, retVal);
923
- }
924
- /**
925
- *
926
- *
927
- * @private
928
- * @param {string} key
929
- * @returns {*} {boolean}
930
- * @memberof MemoryCache
931
- */
932
- _hasKey(key) {
933
- return this.cache.hasOwnProperty(key);
934
- }
935
- /**
936
- *
937
- *
938
- * @private
939
- * @param {*} value
940
- * @param {string} type
941
- * @param {number} timeout
942
- * @returns {*}
943
- * @memberof MemoryCache
944
- */
945
- _makeKey(value, type, timeout) {
946
- return { value: value, type: type, timeout: timeout || null, lastAccess: Date.now() };
947
- }
948
- /**
949
- *
950
- *
951
- * @private
952
- * @param {string} key
953
- * @returns {*}
954
- * @memberof MemoryCache
955
- */
956
- _key(key) {
957
- this.cache[key].lastAccess = Date.now();
958
- return this.cache[key];
959
- }
960
- /**
961
- *
962
- *
963
- * @private
964
- * @param {string} key
965
- * @param {number} amount
966
- * @param {Function} [callback]
967
- * @returns {*}
968
- * @memberof MemoryCache
969
- */
970
- _addToKey(key, amount, callback) {
971
- let keyValue = 0;
972
- if (isNaN(amount) || lodash.isNil(amount)) {
973
- return this._handleCallback(callback, null, messages.noint);
974
- }
975
- if (this._hasKey(key)) {
976
- this._testType(key, 'string', true, callback);
977
- keyValue = parseInt(this._getKey(key));
978
- if (isNaN(keyValue) || lodash.isNil(keyValue)) {
979
- return this._handleCallback(callback, null, messages.noint);
980
- }
981
- }
982
- else {
983
- this.cache[key] = this._makeKey('0', 'string');
984
- }
985
- const val = keyValue + amount;
986
- this._setKey(key, val.toString());
987
- return val;
988
- }
989
- /**
990
- *
991
- *
992
- * @private
993
- * @param {string} key
994
- * @param {string} type
995
- * @param {boolean} [throwError]
996
- * @param {Function} [callback]
997
- * @returns {*}
998
- * @memberof MemoryCache
999
- */
1000
- _testType(key, type, throwError, callback) {
1001
- throwError = !!throwError;
1002
- const keyType = this._key(key).type;
1003
- if (keyType !== type) {
1004
- if (throwError) {
1005
- return this._handleCallback(callback, null, messages.wrongTypeOp);
1006
- }
1007
- return false;
1008
- }
1009
- return true;
1010
- }
1011
- /**
1012
- *
1013
- *
1014
- * @private
1015
- * @param {string} key
1016
- * @returns {*}
1017
- * @memberof MemoryCache
1018
- */
1019
- _getKey(key) {
1020
- const _key = this._key(key) || {};
1021
- if (_key.timeout && _key.timeout <= Date.now()) {
1022
- this.del(key);
1023
- return null;
1024
- }
1025
- return _key.value;
1026
- }
1027
- /**
1028
- *
1029
- *
1030
- * @private
1031
- * @param {string} key
1032
- * @param {(number | string)} value
1033
- * @memberof MemoryCache
1034
- */
1035
- _setKey(key, value) {
1036
- this.cache[key].value = value;
1037
- this.cache[key].lastAccess = Date.now();
1038
- }
1039
- /**
1040
- *
1041
- *
1042
- * @private
1043
- * @param {string} key
1044
- * @param {string} field
1045
- * @param {number} [amount]
1046
- * @param {boolean} [useFloat]
1047
- * @param {Function} [callback]
1048
- * @returns {*}
1049
- * @memberof MemoryCache
1050
- */
1051
- _addToField(key, field, amount, useFloat, callback) {
1052
- useFloat = useFloat || false;
1053
- let fieldValue = useFloat ? 0.0 : 0;
1054
- let value = 0;
1055
- if (isNaN(amount) || lodash.isNil(amount)) {
1056
- return this._handleCallback(callback, null, useFloat ? messages.nofloat : messages.noint);
1057
- }
1058
- if (this._hasKey(key)) {
1059
- this._testType(key, 'hash', true, callback);
1060
- if (this._hasField(key, field)) {
1061
- value = this._getField(key, field);
1062
- }
1063
- }
1064
- else {
1065
- this.cache[key] = this._makeKey({}, 'hash');
1066
- }
1067
- fieldValue = useFloat ? parseFloat(`${value}`) : parseInt(`${value}`);
1068
- amount = useFloat ? parseFloat(`${amount}`) : parseInt(`${amount}`);
1069
- if (isNaN(fieldValue) || lodash.isNil(fieldValue)) {
1070
- return this._handleCallback(callback, null, useFloat ? messages.nofloat : messages.noint);
1071
- }
1072
- fieldValue += amount;
1073
- this._setField(key, field, fieldValue.toString());
1074
- return fieldValue;
1075
- }
1076
- /**
1077
- *
1078
- *
1079
- * @private
1080
- * @param {string} key
1081
- * @param {string} field
1082
- * @returns {*}
1083
- * @memberof MemoryCache
1084
- */
1085
- _getField(key, field) {
1086
- return this._getKey(key)[field];
1087
- }
1088
- /**
1089
- *
1090
- *
1091
- * @private
1092
- * @param {string} key
1093
- * @param {string} field
1094
- * @returns {*} {boolean}
1095
- * @memberof MemoryCache
1096
- */
1097
- _hasField(key, field) {
1098
- let retVal = false;
1099
- if (key && field) {
1100
- const ky = this._getKey(key);
1101
- if (ky) {
1102
- retVal = ky.hasOwnProperty(field);
1103
- }
1104
- }
1105
- return retVal;
1106
- }
1107
- /**
1108
- *
1109
- *
1110
- * @param {string} key
1111
- * @param {string} field
1112
- * @param {*} value
1113
- * @memberof MemoryCache
1114
- */
1115
- _setField(key, field, value) {
1116
- this._getKey(key)[field] = value;
1117
- }
1118
- /**
1119
- *
1120
- *
1121
- * @private
1122
- * @param {Function} [callback]
1123
- * @param {(any)} [message]
1124
- * @param {*} [error]
1125
- * @param {boolean} [nolog]
1126
- * @returns {*}
1127
- * @memberof MemoryCache
1128
- */
1129
- _handleCallback(callback, message, error, nolog) {
1130
- let err = error;
1131
- let msg = message;
1132
- nolog = lodash.isNil(nolog) ? true : nolog;
1133
- if (nolog) {
1134
- err = this._logReturn(error);
1135
- msg = this._logReturn(message);
1136
- }
1137
- if (typeof callback === 'function') {
1138
- callback(err, msg);
1139
- return;
1140
- }
1141
- if (err) {
1142
- throw new Error(err);
1143
- }
1144
- return msg;
1145
- }
1146
- _logReturn(message) {
1147
- if (!lodash.isUndefined(message)) {
1148
- if (this.multiMode) {
1149
- if (!lodash.isNil(this.responseMessages)) {
1150
- this.responseMessages.push(message);
1151
- if (message === messages.ok) {
1152
- message = messages.queued;
1153
- }
1154
- }
1155
- }
1156
- return message;
1157
- }
1158
- return;
1159
- }
1160
- /**
1161
- *
1162
- *
1163
- * @private
1164
- * @param {any[]} [params]
1165
- * @returns {*}
1166
- * @memberof MemoryCache
1167
- */
1168
- _retrieveCallback(params) {
1169
- if (Array.isArray(params) && params.length > 0 && typeof params[params.length - 1] === 'function') {
1170
- return params.pop();
1171
- }
1172
- return;
1173
- }
37
+ /*
38
+ * @Description:
39
+ * @Usage:
40
+ * @Author: richen
41
+ * @Date: 2021-12-02 11:03:20
42
+ * @LastEditTime: 2023-12-20 19:04:29
43
+ */
44
+ /**
45
+ *
46
+ *
47
+ * @enum {number}
48
+ */
49
+ var messages;
50
+ (function (messages) {
51
+ messages["ok"] = "OK";
52
+ messages["queued"] = "QUEUED";
53
+ messages["pong"] = "PONG";
54
+ messages["noint"] = "ERR value is not an integer or out of range";
55
+ messages["nofloat"] = "ERR value is not an float or out of range";
56
+ messages["nokey"] = "ERR no such key";
57
+ messages["nomultiinmulti"] = "ERR MULTI calls can not be nested";
58
+ messages["nomultiexec"] = "ERR EXEC without MULTI";
59
+ messages["nomultidiscard"] = "ERR DISCARD without MULTI";
60
+ messages["busykey"] = "ERR target key name is busy";
61
+ messages["syntax"] = "ERR syntax error";
62
+ messages["unsupported"] = "MemoryCache does not support that operation";
63
+ messages["wrongTypeOp"] = "WRONGTYPE Operation against a key holding the wrong kind of value";
64
+ messages["wrongPayload"] = "DUMP payload version or checksum are wrong";
65
+ messages["wrongArgCount"] = "ERR wrong number of arguments for '%0' command";
66
+ messages["bitopnotWrongCount"] = "ERR BITOP NOT must be called with a single source key";
67
+ messages["indexOutOfRange"] = "ERR index out of range";
68
+ messages["invalidLexRange"] = "ERR min or max not valid string range item";
69
+ messages["invalidDBIndex"] = "ERR invalid DB index";
70
+ messages["invalidDBIndexNX"] = "ERR invalid DB index, '%0' does not exist";
71
+ messages["mutuallyExclusiveNXXX"] = "ERR XX and NX options at the same time are not compatible";
72
+ })(messages || (messages = {}));
73
+ class MemoryCache extends events.EventEmitter {
74
+ databases = new Map();
75
+ options;
76
+ currentDBIndex;
77
+ connected;
78
+ lastSave;
79
+ multiMode;
80
+ cache;
81
+ responseMessages;
82
+ ttlCheckTimer = null;
83
+ /**
84
+ * Creates an instance of MemoryCache.
85
+ * @param {MemoryCacheOptions} options
86
+ * @memberof MemoryCache
87
+ */
88
+ constructor(options) {
89
+ super();
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;
99
+ this.connected = false;
100
+ this.lastSave = Date.now();
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
+ }
165
+ }
166
+ /**
167
+ *
168
+ *
169
+ * @returns {*}
170
+ * @memberof MemoryCache
171
+ */
172
+ createClient() {
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);
177
+ this.connected = true;
178
+ // exit multi mode if we are in it
179
+ this.discard(null, true);
180
+ this.emit('connect');
181
+ this.emit('ready');
182
+ return this;
183
+ }
184
+ /**
185
+ *
186
+ *
187
+ * @returns {*}
188
+ * @memberof MemoryCache
189
+ */
190
+ quit() {
191
+ this.connected = false;
192
+ this.stopTTLCheck();
193
+ // exit multi mode if we are in it
194
+ this.discard(null, true);
195
+ this.emit('end');
196
+ return this;
197
+ }
198
+ /**
199
+ *
200
+ *
201
+ * @returns {*}
202
+ * @memberof MemoryCache
203
+ */
204
+ end() {
205
+ return this.quit();
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
+ }
241
+ /**
242
+ *
243
+ *
244
+ * @param {string} message
245
+ * @param {Function} [callback]
246
+ * @returns {*}
247
+ * @memberof MemoryCache
248
+ */
249
+ echo(message, callback) {
250
+ return this._handleCallback(callback, message);
251
+ }
252
+ /**
253
+ *
254
+ *
255
+ * @param {string} message
256
+ * @param {Function} [callback]
257
+ * @returns {*}
258
+ * @memberof MemoryCache
259
+ */
260
+ ping(message, callback) {
261
+ message = message || messages.pong;
262
+ return this._handleCallback(callback, message);
263
+ }
264
+ /**
265
+ *
266
+ *
267
+ * @param {string} password
268
+ * @param {Function} [callback]
269
+ * @returns {*}
270
+ * @memberof MemoryCache
271
+ */
272
+ auth(password, callback) {
273
+ return this._handleCallback(callback, messages.ok);
274
+ }
275
+ /**
276
+ *
277
+ *
278
+ * @param {number} dbIndex
279
+ * @param {Function} [callback]
280
+ * @returns {*}
281
+ * @memberof MemoryCache
282
+ */
283
+ select(dbIndex, callback) {
284
+ if (!helper__namespace.isNumber(dbIndex)) {
285
+ return this._handleCallback(callback, null, messages.invalidDBIndex);
286
+ }
287
+ if (!this.databases.has(dbIndex)) {
288
+ this.databases.set(dbIndex, this.createLRUCache());
289
+ }
290
+ this.multiMode = false;
291
+ this.currentDBIndex = dbIndex;
292
+ this.cache = this.databases.get(dbIndex);
293
+ return this._handleCallback(callback, messages.ok);
294
+ }
295
+ // ---------------------------------------
296
+ // Keys
297
+ // ---------------------------------------
298
+ get(key, callback) {
299
+ let retVal = null;
300
+ if (this._hasKey(key)) {
301
+ this._testType(key, 'string', true, callback);
302
+ retVal = this._getKey(key);
303
+ }
304
+ return this._handleCallback(callback, retVal);
305
+ }
306
+ /**
307
+ * set(key, value, ttl, pttl, notexist, onlyexist, callback)
308
+ *
309
+ * @param {string} key
310
+ * @param {(string | number)} value
311
+ * @param {...any[]} params
312
+ * @returns {*}
313
+ * @memberof MemoryCache
314
+ */
315
+ set(key, value, ...params) {
316
+ const retVal = null;
317
+ params = lodash.flatten(params);
318
+ const callback = this._retrieveCallback(params);
319
+ let ttl, pttl, notexist, onlyexist;
320
+ // parse parameters
321
+ while (params.length > 0) {
322
+ const param = params.shift();
323
+ switch (param.toString().toLowerCase()) {
324
+ case 'nx':
325
+ notexist = true;
326
+ break;
327
+ case 'xx':
328
+ onlyexist = true;
329
+ break;
330
+ case 'ex':
331
+ if (params.length === 0) {
332
+ return this._handleCallback(callback, null, messages.syntax);
333
+ }
334
+ ttl = parseInt(params.shift());
335
+ if (isNaN(ttl)) {
336
+ return this._handleCallback(callback, null, messages.noint);
337
+ }
338
+ break;
339
+ case 'px':
340
+ if (params.length === 0) {
341
+ return this._handleCallback(callback, null, messages.syntax);
342
+ }
343
+ pttl = parseInt(params.shift());
344
+ if (isNaN(pttl)) {
345
+ return this._handleCallback(callback, null, messages.noint);
346
+ }
347
+ break;
348
+ default:
349
+ return this._handleCallback(callback, null, messages.syntax);
350
+ }
351
+ }
352
+ if (!lodash.isNil(ttl) && !lodash.isNil(pttl)) {
353
+ return this._handleCallback(callback, null, messages.syntax);
354
+ }
355
+ if (notexist && onlyexist) {
356
+ return this._handleCallback(callback, null, messages.syntax);
357
+ }
358
+ pttl = pttl || ttl * 1000 || null;
359
+ if (!lodash.isNil(pttl)) {
360
+ pttl = Date.now() + pttl;
361
+ }
362
+ if (this._hasKey(key)) {
363
+ this._testType(key, 'string', true, callback);
364
+ if (notexist) {
365
+ return this._handleCallback(callback, retVal);
366
+ }
367
+ }
368
+ else if (onlyexist) {
369
+ return this._handleCallback(callback, retVal);
370
+ }
371
+ this.cache.set(key, this._makeKey(value.toString(), 'string', pttl));
372
+ return this._handleCallback(callback, messages.ok);
373
+ }
374
+ /**
375
+ *
376
+ *
377
+ * @param {string} key
378
+ * @param {Function} [callback]
379
+ * @returns {*}
380
+ * @memberof MemoryCache
381
+ */
382
+ ttl(key, callback) {
383
+ let retVal = this.pttl(key);
384
+ if (retVal >= 0 || retVal <= -3) {
385
+ retVal = Math.floor(retVal / 1000);
386
+ }
387
+ return this._handleCallback(callback, retVal);
388
+ }
389
+ /**
390
+ *
391
+ *
392
+ * @param {string} key
393
+ * @param {number} seconds
394
+ * @param {Function} [callback]
395
+ * @returns {*}
396
+ * @memberof MemoryCache
397
+ */
398
+ expire(key, seconds, callback) {
399
+ let retVal = 0;
400
+ if (this._hasKey(key)) {
401
+ const pttl = seconds * 1000;
402
+ this.cache.set(key, { ...this.cache.get(key), timeout: Date.now() + pttl });
403
+ retVal = 1;
404
+ }
405
+ return this._handleCallback(callback, retVal);
406
+ }
407
+ /**
408
+ *
409
+ *
410
+ * @param {...any[]} keys
411
+ * @returns {*}
412
+ * @memberof MemoryCache
413
+ */
414
+ del(...keys) {
415
+ let retVal = 0;
416
+ const callback = this._retrieveCallback(keys);
417
+ // Flatten the array in case an array was passed
418
+ keys = lodash.flatten(keys);
419
+ for (let itr = 0; itr < keys.length; itr++) {
420
+ const key = keys[itr];
421
+ if (this._hasKey(key)) {
422
+ this.cache.delete(key);
423
+ retVal++;
424
+ }
425
+ }
426
+ return this._handleCallback(callback, retVal);
427
+ }
428
+ /**
429
+ *
430
+ *
431
+ * @param {...any[]} keys
432
+ * @returns {*}
433
+ * @memberof MemoryCache
434
+ */
435
+ exists(...keys) {
436
+ let retVal = 0;
437
+ const callback = this._retrieveCallback(keys);
438
+ for (let itr = 0; itr < keys.length; itr++) {
439
+ const key = keys[itr];
440
+ if (this._hasKey(key)) {
441
+ retVal++;
442
+ }
443
+ }
444
+ return this._handleCallback(callback, retVal);
445
+ }
446
+ /**
447
+ *
448
+ *
449
+ * @param {string} key
450
+ * @param {Function} [callback]
451
+ * @returns {*}
452
+ * @memberof MemoryCache
453
+ */
454
+ incr(key, callback) {
455
+ let retVal = null;
456
+ try {
457
+ retVal = this._addToKey(key, 1);
458
+ }
459
+ catch (err) {
460
+ return this._handleCallback(callback, null, err);
461
+ }
462
+ return this._handleCallback(callback, retVal);
463
+ }
464
+ /**
465
+ *
466
+ *
467
+ * @param {string} key
468
+ * @param {number} amount
469
+ * @param {Function} [callback]
470
+ * @returns {*}
471
+ * @memberof MemoryCache
472
+ */
473
+ incrby(key, amount, callback) {
474
+ let retVal = null;
475
+ try {
476
+ retVal = this._addToKey(key, amount);
477
+ }
478
+ catch (err) {
479
+ return this._handleCallback(callback, null, err);
480
+ }
481
+ return this._handleCallback(callback, retVal);
482
+ }
483
+ /**
484
+ *
485
+ *
486
+ * @param {string} key
487
+ * @param {Function} [callback]
488
+ * @returns {*}
489
+ * @memberof MemoryCache
490
+ */
491
+ decr(key, callback) {
492
+ let retVal = null;
493
+ try {
494
+ retVal = this._addToKey(key, -1);
495
+ }
496
+ catch (err) {
497
+ return this._handleCallback(callback, null, err);
498
+ }
499
+ return this._handleCallback(callback, retVal);
500
+ }
501
+ /**
502
+ *
503
+ *
504
+ * @param {string} key
505
+ * @param {number} amount
506
+ * @param {Function} [callback]
507
+ * @returns {*}
508
+ * @memberof MemoryCache
509
+ */
510
+ decrby(key, amount, callback) {
511
+ let retVal = null;
512
+ try {
513
+ retVal = this._addToKey(key, 0 - amount);
514
+ }
515
+ catch (err) {
516
+ return this._handleCallback(callback, null, err);
517
+ }
518
+ return this._handleCallback(callback, retVal);
519
+ }
520
+ // ---------------------------------------
521
+ // ## Hash ##
522
+ // ---------------------------------------
523
+ hset(key, field, value, callback) {
524
+ let retVal = 0;
525
+ if (this._hasKey(key)) {
526
+ this._testType(key, 'hash', true, callback);
527
+ }
528
+ else {
529
+ this.cache.set(key, this._makeKey({}, 'hash'));
530
+ }
531
+ if (!this._hasField(key, field)) {
532
+ retVal = 1;
533
+ }
534
+ this._setField(key, field, value.toString());
535
+ this.persist(key);
536
+ return this._handleCallback(callback, retVal);
537
+ }
538
+ /**
539
+ *
540
+ *
541
+ * @param {string} key
542
+ * @param {string} field
543
+ * @param {Function} [callback]
544
+ * @returns {*}
545
+ * @memberof MemoryCache
546
+ */
547
+ hget(key, field, callback) {
548
+ let retVal = null;
549
+ if (this._hasKey(key)) {
550
+ this._testType(key, 'hash', true, callback);
551
+ if (this._hasField(key, field)) {
552
+ retVal = this._getKey(key)[field];
553
+ }
554
+ }
555
+ return this._handleCallback(callback, retVal);
556
+ }
557
+ /**
558
+ *
559
+ *
560
+ * @param {string} key
561
+ * @param {string} field
562
+ * @param {Function} [callback]
563
+ * @returns {*}
564
+ * @memberof MemoryCache
565
+ */
566
+ hexists(key, field, callback) {
567
+ let retVal = 0;
568
+ if (this._hasKey(key)) {
569
+ this._testType(key, 'hash', true, callback);
570
+ if (this._hasField(key, field)) {
571
+ retVal = 1;
572
+ }
573
+ }
574
+ return this._handleCallback(callback, retVal);
575
+ }
576
+ /**
577
+ *
578
+ *
579
+ * @param {string} key
580
+ * @param {...any[]} fields
581
+ * @returns {*}
582
+ * @memberof MemoryCache
583
+ */
584
+ hdel(key, ...fields) {
585
+ let retVal = 0;
586
+ const callback = this._retrieveCallback(fields);
587
+ if (this._hasKey(key)) {
588
+ this._testType(key, 'hash', true, callback);
589
+ for (let itr = 0; itr < fields.length; itr++) {
590
+ const field = fields[itr];
591
+ if (this._hasField(key, field)) {
592
+ delete this.cache.get(key).value[field];
593
+ retVal++;
594
+ }
595
+ }
596
+ }
597
+ return this._handleCallback(callback, retVal);
598
+ }
599
+ /**
600
+ *
601
+ *
602
+ * @param {string} key
603
+ * @param {Function} [callback]
604
+ * @returns {*}
605
+ * @memberof MemoryCache
606
+ */
607
+ hlen(key, callback) {
608
+ const retVal = this.hkeys(key).length;
609
+ return this._handleCallback(callback, retVal);
610
+ }
611
+ /**
612
+ *
613
+ *
614
+ * @param {string} key
615
+ * @param {string} field
616
+ * @param {*} value
617
+ * @param {Function} [callback]
618
+ * @returns {*}
619
+ * @memberof MemoryCache
620
+ */
621
+ hincrby(key, field, value, callback) {
622
+ let retVal;
623
+ try {
624
+ retVal = this._addToField(key, field, value, false);
625
+ }
626
+ catch (err) {
627
+ return this._handleCallback(callback, null, err);
628
+ }
629
+ return this._handleCallback(callback, retVal);
630
+ }
631
+ /**
632
+ *
633
+ *
634
+ * @param {string} key
635
+ * @param {Function} [callback]
636
+ * @returns {*}
637
+ * @memberof MemoryCache
638
+ */
639
+ hgetall(key, callback) {
640
+ let retVals = {};
641
+ if (this._hasKey(key)) {
642
+ this._testType(key, 'hash', true, callback);
643
+ retVals = this._getKey(key);
644
+ }
645
+ return this._handleCallback(callback, retVals);
646
+ }
647
+ /**
648
+ *
649
+ *
650
+ * @param {string} key
651
+ * @param {Function} [callback]
652
+ * @returns {*}
653
+ * @memberof MemoryCache
654
+ */
655
+ hkeys(key, callback) {
656
+ let retVals = [];
657
+ if (this._hasKey(key)) {
658
+ this._testType(key, 'hash', true, callback);
659
+ retVals = Object.keys(this._getKey(key));
660
+ }
661
+ return this._handleCallback(callback, retVals);
662
+ }
663
+ /**
664
+ *
665
+ *
666
+ * @param {string} key
667
+ * @param {Function} [callback]
668
+ * @returns {*}
669
+ * @memberof MemoryCache
670
+ */
671
+ hvals(key, callback) {
672
+ let retVals = [];
673
+ if (this._hasKey(key)) {
674
+ this._testType(key, 'hash', true, callback);
675
+ retVals = Object.values(this._getKey(key));
676
+ }
677
+ return this._handleCallback(callback, retVals);
678
+ }
679
+ // ---------------------------------------
680
+ // Lists (Array / Queue / Stack)
681
+ // ---------------------------------------
682
+ /**
683
+ *
684
+ *
685
+ * @param {string} key
686
+ * @param {Function} [callback]
687
+ * @returns {*}
688
+ * @memberof MemoryCache
689
+ */
690
+ llen(key, callback) {
691
+ let retVal = 0;
692
+ if (this._hasKey(key)) {
693
+ this._testType(key, 'list', true, callback);
694
+ retVal = this._getKey(key).length || 0;
695
+ }
696
+ return this._handleCallback(callback, retVal);
697
+ }
698
+ /**
699
+ *
700
+ *
701
+ * @param {string} key
702
+ * @param {(string | number)} value
703
+ * @param {Function} [callback]
704
+ * @returns {*}
705
+ * @memberof MemoryCache
706
+ */
707
+ rpush(key, value, callback) {
708
+ let retVal = 0;
709
+ if (this._hasKey(key)) {
710
+ this._testType(key, 'list', true, callback);
711
+ }
712
+ else {
713
+ this.cache.set(key, this._makeKey([], 'list'));
714
+ }
715
+ this._getKey(key).push(value.toString());
716
+ retVal = this._getKey(key).length;
717
+ this.persist(key);
718
+ return this._handleCallback(callback, retVal);
719
+ }
720
+ /**
721
+ * List:从左侧推入
722
+ * @param key
723
+ * @param value
724
+ * @param callback
725
+ */
726
+ lpush(key, value, callback) {
727
+ let retVal = 0;
728
+ if (this._hasKey(key)) {
729
+ this._testType(key, 'list', true, callback);
730
+ }
731
+ else {
732
+ this.cache.set(key, this._makeKey([], 'list'));
733
+ }
734
+ const list = this._getKey(key);
735
+ retVal = list.unshift(value);
736
+ this._setKey(key, list);
737
+ return this._handleCallback(callback, retVal);
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
+ }
757
+ /**
758
+ *
759
+ *
760
+ * @param {string} key
761
+ * @param {Function} [callback]
762
+ * @returns {*}
763
+ * @memberof MemoryCache
764
+ */
765
+ lpop(key, callback) {
766
+ let retVal = null;
767
+ if (this._hasKey(key)) {
768
+ this._testType(key, 'list', true, callback);
769
+ const list = this._getKey(key);
770
+ if (list.length > 0) {
771
+ retVal = list.shift();
772
+ this.persist(key);
773
+ }
774
+ }
775
+ return this._handleCallback(callback, retVal);
776
+ }
777
+ /**
778
+ *
779
+ *
780
+ * @param {string} key
781
+ * @param {Function} [callback]
782
+ * @returns {*}
783
+ * @memberof MemoryCache
784
+ */
785
+ rpop(key, callback) {
786
+ let retVal = null;
787
+ if (this._hasKey(key)) {
788
+ this._testType(key, 'list', true, callback);
789
+ const list = this._getKey(key);
790
+ if (list.length > 0) {
791
+ retVal = list.pop();
792
+ this.persist(key);
793
+ }
794
+ }
795
+ return this._handleCallback(callback, retVal);
796
+ }
797
+ /**
798
+ *
799
+ *
800
+ * @param {string} key
801
+ * @param {number} start
802
+ * @param {number} stop
803
+ * @param {Function} [callback]
804
+ * @returns {*}
805
+ * @memberof MemoryCache
806
+ */
807
+ lrange(key, start, stop, callback) {
808
+ const retVal = [];
809
+ if (this._hasKey(key)) {
810
+ this._testType(key, 'list', true, callback);
811
+ const list = this._getKey(key);
812
+ const length = list.length;
813
+ if (stop < 0) {
814
+ stop = length + stop;
815
+ }
816
+ if (start < 0) {
817
+ start = length + start;
818
+ }
819
+ if (start < 0) {
820
+ start = 0;
821
+ }
822
+ if (stop >= length) {
823
+ stop = length - 1;
824
+ }
825
+ if (stop >= 0 && stop >= start) {
826
+ const size = stop - start + 1;
827
+ for (let itr = start; itr < size; itr++) {
828
+ retVal.push(list[itr]);
829
+ }
830
+ }
831
+ }
832
+ return this._handleCallback(callback, retVal);
833
+ }
834
+ // ---------------------------------------
835
+ // ## Sets (Unique Lists)##
836
+ // ---------------------------------------
837
+ /**
838
+ *
839
+ *
840
+ * @param {string} key
841
+ * @param {...any[]} members
842
+ * @returns {*}
843
+ * @memberof MemoryCache
844
+ */
845
+ sadd(key, ...members) {
846
+ let retVal = 0;
847
+ const callback = this._retrieveCallback(members);
848
+ if (this._hasKey(key)) {
849
+ this._testType(key, 'set', true, callback);
850
+ }
851
+ else {
852
+ this.cache.set(key, this._makeKey([], 'set'));
853
+ }
854
+ const val = this._getKey(key);
855
+ const length = val.length;
856
+ const nval = lodash.union(val, members);
857
+ const newlength = nval.length;
858
+ retVal = newlength - length;
859
+ this._setKey(key, nval);
860
+ return this._handleCallback(callback, retVal);
861
+ }
862
+ /**
863
+ *
864
+ *
865
+ * @param {string} key
866
+ * @param {Function} [callback]
867
+ * @returns {*}
868
+ * @memberof MemoryCache
869
+ */
870
+ scard(key, callback) {
871
+ let retVal = 0;
872
+ if (this._hasKey(key)) {
873
+ this._testType(key, 'set', true, callback);
874
+ retVal = this._getKey(key).length;
875
+ }
876
+ return this._handleCallback(callback, retVal);
877
+ }
878
+ /**
879
+ *
880
+ *
881
+ * @param {string} key
882
+ * @param {string} member
883
+ * @param {Function} [callback]
884
+ * @returns {*}
885
+ * @memberof MemoryCache
886
+ */
887
+ sismember(key, member, callback) {
888
+ let retVal = 0;
889
+ if (this._hasKey(key)) {
890
+ this._testType(key, 'set', true, callback);
891
+ const val = this._getKey(key);
892
+ if (val.includes(member)) {
893
+ retVal = 1;
894
+ }
895
+ }
896
+ return this._handleCallback(callback, retVal);
897
+ }
898
+ /**
899
+ *
900
+ *
901
+ * @param {string} key
902
+ * @param {Function} [callback]
903
+ * @returns {*}
904
+ * @memberof MemoryCache
905
+ */
906
+ smembers(key, callback) {
907
+ let retVal = [];
908
+ if (this._hasKey(key)) {
909
+ this._testType(key, 'set', true, callback);
910
+ retVal = this._getKey(key);
911
+ }
912
+ return this._handleCallback(callback, retVal);
913
+ }
914
+ /**
915
+ *
916
+ *
917
+ * @param {string} key
918
+ * @param {number} [count]
919
+ * @param {Function} [callback]
920
+ * @returns {*}
921
+ * @memberof MemoryCache
922
+ */
923
+ spop(key, count, callback) {
924
+ let retVal = [];
925
+ count = count || 1;
926
+ if (typeof count === 'function') {
927
+ callback = count;
928
+ count = 1;
929
+ }
930
+ if (this._hasKey(key)) {
931
+ this._testType(key, 'set', true, callback);
932
+ const val = this._getKey(key);
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
+ }
947
+ }
948
+ }
949
+ return this._handleCallback(callback, retVal);
950
+ }
951
+ /**
952
+ *
953
+ *
954
+ * @param {string} key
955
+ * @param {...any[]} members
956
+ * @returns {*}
957
+ * @memberof MemoryCache
958
+ */
959
+ srem(key, ...members) {
960
+ let retVal = 0;
961
+ const callback = this._retrieveCallback(members);
962
+ if (this._hasKey(key)) {
963
+ this._testType(key, 'set', true, callback);
964
+ const val = this._getKey(key);
965
+ for (const index in members) {
966
+ if (members.hasOwnProperty(index)) {
967
+ const member = members[index];
968
+ const idx = val.indexOf(member);
969
+ if (idx !== -1) {
970
+ val.splice(idx, 1);
971
+ retVal++;
972
+ }
973
+ }
974
+ }
975
+ this._setKey(key, val);
976
+ }
977
+ return this._handleCallback(callback, retVal);
978
+ }
979
+ /**
980
+ *
981
+ *
982
+ * @param {string} sourcekey
983
+ * @param {string} destkey
984
+ * @param {string} member
985
+ * @param {Function} [callback]
986
+ * @returns {*}
987
+ * @memberof MemoryCache
988
+ */
989
+ smove(sourcekey, destkey, member, callback) {
990
+ let retVal = 0;
991
+ if (this._hasKey(sourcekey)) {
992
+ this._testType(sourcekey, 'set', true, callback);
993
+ const val = this._getKey(sourcekey);
994
+ const idx = val.indexOf(member);
995
+ if (idx !== -1) {
996
+ this.sadd(destkey, member);
997
+ val.splice(idx, 1);
998
+ retVal = 1;
999
+ }
1000
+ }
1001
+ return this._handleCallback(callback, retVal);
1002
+ }
1003
+ // ---------------------------------------
1004
+ // ## Transactions (Atomic) ##
1005
+ // ---------------------------------------
1006
+ // TODO: Transaction Queues watch and unwatch
1007
+ // https://redis.io/topics/transactions
1008
+ // This can be accomplished by temporarily swapping this.cache to a temporary copy of the current statement
1009
+ // holding and then using __.merge on actual this.cache with the temp storage.
1010
+ discard(callback, silent) {
1011
+ // Clear the queue mode, drain the queue, empty the watch list
1012
+ if (this.multiMode) {
1013
+ this.cache = this.databases.get(this.currentDBIndex);
1014
+ this.multiMode = false;
1015
+ this.responseMessages = [];
1016
+ }
1017
+ if (!silent) {
1018
+ return this._handleCallback(callback, messages.ok);
1019
+ }
1020
+ return null;
1021
+ }
1022
+ // ---------------------------------------
1023
+ // ## Internal - Key ##
1024
+ // ---------------------------------------
1025
+ /**
1026
+ *
1027
+ *
1028
+ * @param {string} key
1029
+ * @param {Function} [callback]
1030
+ * @returns {*}
1031
+ * @memberof MemoryCache
1032
+ */
1033
+ pttl(key, _callback) {
1034
+ let retVal = -2;
1035
+ if (this._hasKey(key)) {
1036
+ if (!lodash.isNil(this.cache.get(key)?.timeout)) {
1037
+ retVal = this.cache.get(key).timeout - Date.now();
1038
+ // Prevent unexpected errors if the actual ttl just happens to be -2 or -1
1039
+ if (retVal < 0 && retVal > -3) {
1040
+ retVal = -3;
1041
+ }
1042
+ }
1043
+ else {
1044
+ retVal = -1;
1045
+ }
1046
+ }
1047
+ return retVal;
1048
+ }
1049
+ /**
1050
+ *
1051
+ *
1052
+ * @private
1053
+ * @param {string} key
1054
+ * @param {Function} [callback]
1055
+ * @returns {*}
1056
+ * @memberof MemoryCache
1057
+ */
1058
+ persist(key, callback) {
1059
+ let retVal = 0;
1060
+ if (this._hasKey(key)) {
1061
+ if (!lodash.isNil(this._key(key).timeout)) {
1062
+ this.cache.set(key, { ...this.cache.get(key), timeout: null });
1063
+ retVal = 1;
1064
+ }
1065
+ }
1066
+ return this._handleCallback(callback, retVal);
1067
+ }
1068
+ /**
1069
+ *
1070
+ *
1071
+ * @private
1072
+ * @param {string} key
1073
+ * @returns {*} {boolean}
1074
+ * @memberof MemoryCache
1075
+ */
1076
+ _hasKey(key) {
1077
+ return this.cache.has(key);
1078
+ }
1079
+ /**
1080
+ *
1081
+ *
1082
+ * @private
1083
+ * @param {*} value
1084
+ * @param {string} type
1085
+ * @param {number} timeout
1086
+ * @returns {*}
1087
+ * @memberof MemoryCache
1088
+ */
1089
+ _makeKey(value, type, timeout) {
1090
+ return { value: value, type: type, timeout: timeout || null, lastAccess: Date.now() };
1091
+ }
1092
+ /**
1093
+ *
1094
+ *
1095
+ * @private
1096
+ * @param {string} key
1097
+ * @returns {*}
1098
+ * @memberof MemoryCache
1099
+ */
1100
+ _key(key) {
1101
+ this.cache.get(key).lastAccess = Date.now();
1102
+ return this.cache.get(key);
1103
+ }
1104
+ /**
1105
+ *
1106
+ *
1107
+ * @private
1108
+ * @param {string} key
1109
+ * @param {number} amount
1110
+ * @param {Function} [callback]
1111
+ * @returns {*}
1112
+ * @memberof MemoryCache
1113
+ */
1114
+ _addToKey(key, amount, callback) {
1115
+ let keyValue = 0;
1116
+ if (isNaN(amount) || lodash.isNil(amount)) {
1117
+ return this._handleCallback(callback, null, messages.noint);
1118
+ }
1119
+ if (this._hasKey(key)) {
1120
+ this._testType(key, 'string', true, callback);
1121
+ keyValue = parseInt(this._getKey(key));
1122
+ if (isNaN(keyValue) || lodash.isNil(keyValue)) {
1123
+ return this._handleCallback(callback, null, messages.noint);
1124
+ }
1125
+ }
1126
+ else {
1127
+ this.cache.set(key, this._makeKey('0', 'string'));
1128
+ }
1129
+ const val = keyValue + amount;
1130
+ this._setKey(key, val.toString());
1131
+ return val;
1132
+ }
1133
+ /**
1134
+ *
1135
+ *
1136
+ * @private
1137
+ * @param {string} key
1138
+ * @param {string} type
1139
+ * @param {boolean} [throwError]
1140
+ * @param {Function} [callback]
1141
+ * @returns {*}
1142
+ * @memberof MemoryCache
1143
+ */
1144
+ _testType(key, type, throwError, callback) {
1145
+ throwError = !!throwError;
1146
+ const keyType = this._key(key).type;
1147
+ if (keyType !== type) {
1148
+ if (throwError) {
1149
+ return this._handleCallback(callback, null, messages.wrongTypeOp);
1150
+ }
1151
+ return false;
1152
+ }
1153
+ return true;
1154
+ }
1155
+ /**
1156
+ *
1157
+ *
1158
+ * @private
1159
+ * @param {string} key
1160
+ * @returns {*}
1161
+ * @memberof MemoryCache
1162
+ */
1163
+ _getKey(key) {
1164
+ const _key = this._key(key) || {};
1165
+ if (_key.timeout && _key.timeout <= Date.now()) {
1166
+ this.del(key);
1167
+ return null;
1168
+ }
1169
+ return _key.value;
1170
+ }
1171
+ /**
1172
+ *
1173
+ *
1174
+ * @private
1175
+ * @param {string} key
1176
+ * @param {(number | string)} value
1177
+ * @memberof MemoryCache
1178
+ */
1179
+ _setKey(key, value) {
1180
+ this.cache.set(key, { ...this.cache.get(key), value: value, lastAccess: Date.now() });
1181
+ }
1182
+ /**
1183
+ *
1184
+ *
1185
+ * @private
1186
+ * @param {string} key
1187
+ * @param {string} field
1188
+ * @param {number} [amount]
1189
+ * @param {boolean} [useFloat]
1190
+ * @param {Function} [callback]
1191
+ * @returns {*}
1192
+ * @memberof MemoryCache
1193
+ */
1194
+ _addToField(key, field, amount, useFloat, callback) {
1195
+ useFloat = useFloat || false;
1196
+ let fieldValue = useFloat ? 0.0 : 0;
1197
+ let value = 0;
1198
+ if (isNaN(amount) || lodash.isNil(amount)) {
1199
+ return this._handleCallback(callback, null, useFloat ? messages.nofloat : messages.noint);
1200
+ }
1201
+ if (this._hasKey(key)) {
1202
+ this._testType(key, 'hash', true, callback);
1203
+ if (this._hasField(key, field)) {
1204
+ value = this._getField(key, field);
1205
+ }
1206
+ }
1207
+ else {
1208
+ this.cache.set(key, this._makeKey({}, 'hash'));
1209
+ }
1210
+ fieldValue = useFloat ? parseFloat(`${value}`) : parseInt(`${value}`);
1211
+ amount = useFloat ? parseFloat(`${amount}`) : parseInt(`${amount}`);
1212
+ if (isNaN(fieldValue) || lodash.isNil(fieldValue)) {
1213
+ return this._handleCallback(callback, null, useFloat ? messages.nofloat : messages.noint);
1214
+ }
1215
+ fieldValue += amount;
1216
+ this._setField(key, field, fieldValue.toString());
1217
+ return fieldValue;
1218
+ }
1219
+ /**
1220
+ *
1221
+ *
1222
+ * @private
1223
+ * @param {string} key
1224
+ * @param {string} field
1225
+ * @returns {*}
1226
+ * @memberof MemoryCache
1227
+ */
1228
+ _getField(key, field) {
1229
+ return this._getKey(key)[field];
1230
+ }
1231
+ /**
1232
+ *
1233
+ *
1234
+ * @private
1235
+ * @param {string} key
1236
+ * @param {string} field
1237
+ * @returns {*} {boolean}
1238
+ * @memberof MemoryCache
1239
+ */
1240
+ _hasField(key, field) {
1241
+ let retVal = false;
1242
+ if (key && field) {
1243
+ const ky = this._getKey(key);
1244
+ if (ky) {
1245
+ retVal = ky.hasOwnProperty(field);
1246
+ }
1247
+ }
1248
+ return retVal;
1249
+ }
1250
+ /**
1251
+ *
1252
+ *
1253
+ * @param {string} key
1254
+ * @param {string} field
1255
+ * @param {*} value
1256
+ * @memberof MemoryCache
1257
+ */
1258
+ _setField(key, field, value) {
1259
+ this._getKey(key)[field] = value;
1260
+ }
1261
+ /**
1262
+ *
1263
+ *
1264
+ * @private
1265
+ * @param {Function} [callback]
1266
+ * @param {(any)} [message]
1267
+ * @param {*} [error]
1268
+ * @param {boolean} [nolog]
1269
+ * @returns {*}
1270
+ * @memberof MemoryCache
1271
+ */
1272
+ _handleCallback(callback, message, error, nolog) {
1273
+ let err = error;
1274
+ let msg = message;
1275
+ nolog = lodash.isNil(nolog) ? true : nolog;
1276
+ if (nolog) {
1277
+ err = this._logReturn(error);
1278
+ msg = this._logReturn(message);
1279
+ }
1280
+ if (typeof callback === 'function') {
1281
+ callback(err, msg);
1282
+ return;
1283
+ }
1284
+ if (err) {
1285
+ throw new Error(err);
1286
+ }
1287
+ return msg;
1288
+ }
1289
+ _logReturn(message) {
1290
+ if (!lodash.isUndefined(message)) {
1291
+ if (this.multiMode) {
1292
+ if (!lodash.isNil(this.responseMessages)) {
1293
+ this.responseMessages.push(message);
1294
+ if (message === messages.ok) {
1295
+ message = messages.queued;
1296
+ }
1297
+ }
1298
+ }
1299
+ return message;
1300
+ }
1301
+ return;
1302
+ }
1303
+ /**
1304
+ *
1305
+ *
1306
+ * @private
1307
+ * @param {any[]} [params]
1308
+ * @returns {*}
1309
+ * @memberof MemoryCache
1310
+ */
1311
+ _retrieveCallback(params) {
1312
+ if (Array.isArray(params) && params.length > 0 && typeof params[params.length - 1] === 'function') {
1313
+ return params.pop();
1314
+ }
1315
+ return;
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
+ }
1174
1644
  }
1175
1645
 
1176
- /*
1177
- * @Description:
1178
- * @Usage:
1179
- * @Author: richen
1180
- * @Date: 2021-06-29 19:07:57
1181
- * @LastEditTime: 2023-02-18 23:52:47
1182
- */
1183
- class MemoryStore {
1184
- /**
1185
- * Creates an instance of MemoryStore.
1186
- * @param {MemoryStoreOpt} options
1187
- * @memberof MemoryStore
1188
- */
1189
- constructor(options) {
1190
- this.options = options;
1191
- this.client = null;
1192
- }
1193
- /**
1194
- * getConnection
1195
- *
1196
- * @returns {*}
1197
- * @memberof MemoryStore
1198
- */
1199
- getConnection() {
1200
- if (!this.pool) {
1201
- this.pool = new MemoryCache({
1202
- database: this.options.db
1203
- });
1204
- }
1205
- if (!this.client) {
1206
- this.client = this.pool.createClient();
1207
- this.client.status = "ready";
1208
- }
1209
- return this.client;
1210
- }
1211
- /**
1212
- * close
1213
- *
1214
- * @returns {*} {Promise<void>}
1215
- * @memberof MemoryStore
1216
- */
1217
- async close() {
1218
- this.client.end();
1219
- this.client = null;
1220
- }
1221
- /**
1222
- * release
1223
- *
1224
- * @param {*} conn
1225
- * @returns {*} {Promise<void>}
1226
- * @memberof MemoryStore
1227
- */
1228
- async release(conn) {
1229
- return;
1230
- }
1231
- /**
1232
- * defineCommand
1233
- *
1234
- * @param {string} name
1235
- * @param {*} scripts
1236
- * @memberof MemoryStore
1237
- */
1238
- async defineCommand(name, scripts) {
1239
- throw new Error(messages.unsupported);
1240
- }
1241
- /**
1242
- * get and compare value
1243
- *
1244
- * @param {string} name
1245
- * @param {(string | number)} value
1246
- * @returns {*} {Promise<any>}
1247
- * @memberof MemoryStore
1248
- */
1249
- async getCompare(name, value) {
1250
- const client = this.getConnection();
1251
- const val = client.get(`${this.options.keyPrefix}${name}`);
1252
- if (!val) {
1253
- return 0;
1254
- }
1255
- else if (val == value) {
1256
- return client.del(`${this.options.keyPrefix}${name}`);
1257
- }
1258
- else {
1259
- return -1;
1260
- }
1261
- }
1646
+ /*
1647
+ * @Description:
1648
+ * @Usage:
1649
+ * @Author: richen
1650
+ * @Date: 2021-06-29 19:07:57
1651
+ * @LastEditTime: 2023-02-18 23:52:47
1652
+ */
1653
+ class MemoryStore {
1654
+ client;
1655
+ pool;
1656
+ options;
1657
+ /**
1658
+ * Creates an instance of MemoryStore.
1659
+ * @param {MemoryStoreOpt} options
1660
+ * @memberof MemoryStore
1661
+ */
1662
+ constructor(options) {
1663
+ this.options = {
1664
+ maxKeys: 1000,
1665
+ evictionPolicy: 'lru',
1666
+ ttlCheckInterval: 60000, // 1分钟
1667
+ ...options
1668
+ };
1669
+ this.client = null;
1670
+ }
1671
+ /**
1672
+ * getConnection
1673
+ *
1674
+ * @returns {*}
1675
+ * @memberof MemoryStore
1676
+ */
1677
+ getConnection() {
1678
+ if (!this.pool) {
1679
+ this.pool = new MemoryCache({
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
1685
+ });
1686
+ }
1687
+ if (!this.client) {
1688
+ this.client = this.pool.createClient();
1689
+ this.client.status = "ready";
1690
+ }
1691
+ return this.client;
1692
+ }
1693
+ /**
1694
+ * close
1695
+ *
1696
+ * @returns {*} {Promise<void>}
1697
+ * @memberof MemoryStore
1698
+ */
1699
+ async close() {
1700
+ if (this.client) {
1701
+ this.client.end();
1702
+ this.client = null;
1703
+ }
1704
+ }
1705
+ /**
1706
+ * release
1707
+ *
1708
+ * @param {*} _conn
1709
+ * @returns {*} {Promise<void>}
1710
+ * @memberof MemoryStore
1711
+ */
1712
+ async release(_conn) {
1713
+ return;
1714
+ }
1715
+ /**
1716
+ * defineCommand
1717
+ *
1718
+ * @param {string} _name
1719
+ * @param {*} _scripts
1720
+ * @memberof MemoryStore
1721
+ */
1722
+ async defineCommand(_name, _scripts) {
1723
+ throw new Error(messages.unsupported);
1724
+ }
1725
+ /**
1726
+ * get and compare value
1727
+ *
1728
+ * @param {string} name
1729
+ * @param {(string | number)} value
1730
+ * @returns {*} {Promise<any>}
1731
+ * @memberof MemoryStore
1732
+ */
1733
+ async getCompare(name, value) {
1734
+ const client = this.getConnection();
1735
+ const val = client.get(`${this.options.keyPrefix}${name}`);
1736
+ if (!val) {
1737
+ return 0;
1738
+ }
1739
+ else if (val == value) {
1740
+ return client.del(`${this.options.keyPrefix}${name}`);
1741
+ }
1742
+ else {
1743
+ return -1;
1744
+ }
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
+ }
1262
1760
  }
1263
1761
 
1264
- /*
1265
- * @Author: richen
1266
- * @Date: 2020-11-30 15:56:08
1267
- * @LastEditors: Please set LastEditors
1268
- * @LastEditTime: 2023-02-19 00:02:09
1269
- * @License: BSD (3-Clause)
1270
- * @Copyright (c) - <richenlin(at)gmail.com>
1271
- */
1272
- /**
1273
- *
1274
- *
1275
- * @export
1276
- * @class RedisStore
1277
- */
1278
- class RedisStore {
1279
- /**
1280
- * Creates an instance of RedisStore.
1281
- * @param {RedisStoreOpt} options
1282
- * @memberof RedisStore
1283
- */
1284
- constructor(options) {
1285
- this.options = this.parseOpt(options);
1286
- this.pool = null;
1287
- }
1288
- // parseOpt
1289
- parseOpt(options) {
1290
- const opt = {
1291
- type: options.type,
1292
- host: options.host || '127.0.0.1',
1293
- port: options.port || 3306,
1294
- username: options.username || "",
1295
- password: options.password || "",
1296
- db: options.db || 0,
1297
- timeout: options.timeout,
1298
- keyPrefix: options.keyPrefix || '',
1299
- poolSize: options.poolSize || 10,
1300
- connectTimeout: options.connectTimeout || 500,
1301
- };
1302
- if (helper__namespace.isArray(options.host)) {
1303
- const hosts = [];
1304
- for (let i = 0; i < options.host.length; i++) {
1305
- const h = options.host[i];
1306
- if (!helper__namespace.isEmpty(options.host[i])) {
1307
- let p;
1308
- if (helper__namespace.isArray(options.port)) {
1309
- p = options.port[i];
1310
- }
1311
- else {
1312
- p = options.port || 6379;
1313
- }
1314
- hosts.push({
1315
- host: h,
1316
- port: helper__namespace.toNumber(p),
1317
- });
1318
- }
1319
- }
1320
- // sentinel
1321
- if (!helper__namespace.isEmpty(options.name)) {
1322
- opt.host = "";
1323
- opt.port = null;
1324
- opt.sentinels = [...hosts];
1325
- opt.sentinelUsername = options.username;
1326
- opt.sentinelPassword = options.password;
1327
- }
1328
- else {
1329
- // cluster
1330
- opt.host = "";
1331
- opt.port = null;
1332
- opt.clusters = [...hosts];
1333
- }
1334
- }
1335
- return opt;
1336
- }
1337
- /**
1338
- * create connection by native
1339
- *
1340
- * @param {number} [connNum=0]
1341
- * @returns {*} {Promise<Redis | Cluster>}
1342
- * @memberof RedisStore
1343
- */
1344
- async connect(connNum = 0) {
1345
- if (this.client && this.client.status === 'ready') {
1346
- return this.client;
1347
- }
1348
- const defer = helper__namespace.getDefer();
1349
- let connection;
1350
- if (!helper__namespace.isEmpty(this.options.clusters)) {
1351
- connection = new ioredis.Cluster([...this.options.clusters], { redisOptions: this.options });
1352
- }
1353
- else {
1354
- connection = new ioredis.Redis(this.options);
1355
- }
1356
- // 去除prefix, 防止重复
1357
- this.options.keyPrefix = "";
1358
- connection.on('end', () => {
1359
- if (connNum < 3) {
1360
- connNum++;
1361
- defer.resolve(this.connect(connNum));
1362
- }
1363
- else {
1364
- this.close();
1365
- defer.reject('redis connection end');
1366
- }
1367
- });
1368
- connection.on('ready', () => {
1369
- this.client = connection;
1370
- defer.resolve(connection);
1371
- });
1372
- return defer.promise;
1373
- }
1374
- /**
1375
- * get connection from pool
1376
- *
1377
- * @returns {*}
1378
- * @memberof RedisStore
1379
- */
1380
- getConnection() {
1381
- if (!this.pool || !this.pool.acquire) {
1382
- const factory = {
1383
- create: () => {
1384
- return this.connect();
1385
- },
1386
- destroy: () => {
1387
- return this.close();
1388
- },
1389
- validate: (resource) => {
1390
- return Promise.resolve(resource.status === 'ready');
1391
- }
1392
- };
1393
- this.pool = genericPool.createPool(factory, {
1394
- max: this.options.poolSize || 10,
1395
- min: 2 // minimum size of the pool
1396
- });
1397
- this.pool.on('factoryCreateError', function (err) {
1398
- koatty_logger.DefaultLogger.Error(err);
1399
- });
1400
- this.pool.on('factoryDestroyError', function (err) {
1401
- koatty_logger.DefaultLogger.Error(err);
1402
- });
1403
- }
1404
- return this.pool.acquire();
1405
- }
1406
- /**
1407
- * close connection
1408
- *
1409
- * @returns {*}
1410
- * @memberof RedisStore
1411
- */
1412
- async close() {
1413
- this.client.disconnect();
1414
- this.client = null;
1415
- this.pool.destroy(this.client);
1416
- this.pool = null;
1417
- return;
1418
- }
1419
- /**
1420
- *
1421
- *
1422
- * @param {*} conn
1423
- * @returns {*}
1424
- * @memberof RedisStore
1425
- */
1426
- async release(conn) {
1427
- if (this.pool.isBorrowedResource(conn)) {
1428
- return this.pool.release(conn);
1429
- }
1430
- return Promise.resolve();
1431
- }
1432
- /**
1433
- * defineCommand
1434
- *
1435
- * @param {string} name
1436
- * @param {{ numberOfKeys?: number; lua?: string; }} scripts
1437
- * @returns {*}
1438
- * @memberof RedisStore
1439
- */
1440
- async defineCommand(name, scripts) {
1441
- const conn = await this.getConnection();
1442
- if (!conn[name]) {
1443
- conn.defineCommand(name, scripts);
1444
- }
1445
- return conn;
1446
- }
1447
- /**
1448
- * get and compare value
1449
- *
1450
- * @param {string} name
1451
- * @param {(string | number)} value
1452
- * @returns {*} {Promise<any>}
1453
- * @memberof RedisStore
1454
- */
1455
- async getCompare(name, value) {
1456
- let conn;
1457
- try {
1458
- conn = await this.defineCommand("getCompare", {
1459
- numberOfKeys: 1,
1762
+ /*
1763
+ * @Author: richen
1764
+ * @Date: 2020-11-30 15:56:08
1765
+ * @LastEditors: Please set LastEditors
1766
+ * @LastEditTime: 2023-02-19 00:02:09
1767
+ * @License: BSD (3-Clause)
1768
+ * @Copyright (c) - <richenlin(at)gmail.com>
1769
+ */
1770
+ /**
1771
+ *
1772
+ *
1773
+ * @export
1774
+ * @class RedisStore
1775
+ */
1776
+ class RedisStore {
1777
+ options;
1778
+ pool;
1779
+ client;
1780
+ reconnectAttempts = 0;
1781
+ maxReconnectAttempts = 5;
1782
+ reconnectDelay = 1000; // 初始重连延迟1秒
1783
+ /**
1784
+ * Creates an instance of RedisStore.
1785
+ * @param {RedisStoreOpt} options
1786
+ * @memberof RedisStore
1787
+ */
1788
+ constructor(options) {
1789
+ this.options = this.parseOpt(options);
1790
+ this.pool = null;
1791
+ }
1792
+ // parseOpt
1793
+ parseOpt(options) {
1794
+ const opt = {
1795
+ type: options.type,
1796
+ host: options.host || '127.0.0.1',
1797
+ port: options.port || 3306,
1798
+ username: options.username || "",
1799
+ password: options.password || "",
1800
+ db: options.db || 0,
1801
+ timeout: options.timeout,
1802
+ keyPrefix: options.keyPrefix || '',
1803
+ poolSize: options.poolSize || 10,
1804
+ connectTimeout: options.connectTimeout || 500,
1805
+ };
1806
+ if (helper__namespace.isArray(options.host)) {
1807
+ const hosts = [];
1808
+ for (let i = 0; i < options.host.length; i++) {
1809
+ const h = options.host[i];
1810
+ if (!helper__namespace.isEmpty(options.host[i])) {
1811
+ let p;
1812
+ if (helper__namespace.isArray(options.port)) {
1813
+ p = options.port[i];
1814
+ }
1815
+ else {
1816
+ p = options.port || 6379;
1817
+ }
1818
+ hosts.push({
1819
+ host: h,
1820
+ port: helper__namespace.toNumber(p),
1821
+ });
1822
+ }
1823
+ }
1824
+ // sentinel
1825
+ if (!helper__namespace.isEmpty(options.name)) {
1826
+ opt.host = "";
1827
+ opt.port = null;
1828
+ opt.sentinels = [...hosts];
1829
+ opt.sentinelUsername = options.username;
1830
+ opt.sentinelPassword = options.password;
1831
+ }
1832
+ else {
1833
+ // cluster
1834
+ opt.host = "";
1835
+ opt.port = null;
1836
+ opt.clusters = [...hosts];
1837
+ }
1838
+ }
1839
+ return opt;
1840
+ }
1841
+ /**
1842
+ * create connection by native with improved error handling
1843
+ *
1844
+ * @param {number} [connNum=0]
1845
+ * @returns {*} {Promise<Redis | Cluster>}
1846
+ * @memberof RedisStore
1847
+ */
1848
+ async connect(connNum = 0) {
1849
+ if (this.client && this.client.status === 'ready') {
1850
+ return this.client;
1851
+ }
1852
+ const defer = helper__namespace.getDefer();
1853
+ let connection;
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
+ });
1861
+ }
1862
+ else {
1863
+ connection = new ioredis.Redis({
1864
+ ...this.options,
1865
+ enableOfflineQueue: false,
1866
+ retryDelayOnFailover: 100,
1867
+ lazyConnect: true
1868
+ });
1869
+ }
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
+ }
1906
+ return defer.promise;
1907
+ }
1908
+ /**
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
1925
+ *
1926
+ * @returns {*}
1927
+ * @memberof RedisStore
1928
+ */
1929
+ getConnection() {
1930
+ if (!this.pool || !this.pool.acquire) {
1931
+ const factory = {
1932
+ create: () => {
1933
+ return this.connect();
1934
+ },
1935
+ destroy: (resource) => {
1936
+ if (resource && typeof resource.disconnect === 'function') {
1937
+ resource.disconnect();
1938
+ }
1939
+ return Promise.resolve();
1940
+ },
1941
+ validate: (resource) => {
1942
+ return Promise.resolve(resource && resource.status === 'ready');
1943
+ }
1944
+ };
1945
+ this.pool = genericPool.createPool(factory, {
1946
+ max: this.options.poolSize || 10, // maximum 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分钟软空闲超时
1953
+ });
1954
+ this.pool.on('factoryCreateError', function (err) {
1955
+ koatty_logger.DefaultLogger.Error(`Redis pool create error: ${err.message}`);
1956
+ });
1957
+ this.pool.on('factoryDestroyError', function (err) {
1958
+ koatty_logger.DefaultLogger.Error(`Redis pool destroy error: ${err.message}`);
1959
+ });
1960
+ }
1961
+ return this.pool.acquire();
1962
+ }
1963
+ /**
1964
+ * close connection with proper cleanup
1965
+ *
1966
+ * @returns {*}
1967
+ * @memberof RedisStore
1968
+ */
1969
+ async close() {
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
+ }
1986
+ }
1987
+ /**
1988
+ *
1989
+ *
1990
+ * @param {*} conn
1991
+ * @returns {*}
1992
+ * @memberof RedisStore
1993
+ */
1994
+ async release(conn) {
1995
+ if (this.pool.isBorrowedResource(conn)) {
1996
+ return this.pool.release(conn);
1997
+ }
1998
+ return Promise.resolve();
1999
+ }
2000
+ /**
2001
+ * defineCommand
2002
+ *
2003
+ * @param {string} name
2004
+ * @param {{ numberOfKeys?: number; lua?: string; }} scripts
2005
+ * @returns {*}
2006
+ * @memberof RedisStore
2007
+ */
2008
+ async defineCommand(name, scripts) {
2009
+ const conn = await this.getConnection();
2010
+ if (!conn[name]) {
2011
+ conn.defineCommand(name, scripts);
2012
+ }
2013
+ return conn;
2014
+ }
2015
+ /**
2016
+ * get and compare value
2017
+ *
2018
+ * @param {string} name
2019
+ * @param {(string | number)} value
2020
+ * @returns {*} {Promise<any>}
2021
+ * @memberof RedisStore
2022
+ */
2023
+ async getCompare(name, value) {
2024
+ let conn;
2025
+ try {
2026
+ conn = await this.defineCommand("getCompare", {
2027
+ numberOfKeys: 1,
1460
2028
  lua: `
1461
2029
  local remote_value = redis.call("get",KEYS[1])
1462
2030
 
@@ -1467,427 +2035,481 @@ class RedisStore {
1467
2035
  else
1468
2036
  return -1
1469
2037
  end
1470
- `
1471
- });
1472
- return conn.getCompare(name, value);
1473
- }
1474
- catch (error) {
1475
- throw error;
1476
- }
1477
- finally {
1478
- this.release(conn);
1479
- }
1480
- }
2038
+ `
2039
+ });
2040
+ return conn.getCompare(name, value);
2041
+ }
2042
+ catch (error) {
2043
+ throw error;
2044
+ }
2045
+ finally {
2046
+ this.release(conn);
2047
+ }
2048
+ }
1481
2049
  }
1482
2050
 
1483
- /*
1484
- * @Description:
1485
- * @Usage:
1486
- * @Author: richen
1487
- * @Date: 2021-12-02 15:26:55
1488
- * @LastEditTime: 2023-02-19 01:03:59
1489
- */
1490
- const defaultOptions = {
1491
- type: 'memory',
1492
- host: '',
1493
- port: 0,
1494
- keyPrefix: 'Koatty',
1495
- timeout: 600,
1496
- poolSize: 10,
1497
- connectTimeout: 500,
1498
- db: 0
1499
- };
1500
- /**
1501
- *
1502
- *
1503
- * @export
1504
- * @class Store
1505
- */
1506
- class CacheStore {
1507
- /**
1508
- * Creates an instance of CacheStore.
1509
- * @param {StoreOptions} options
1510
- * @memberof CacheStore
1511
- */
1512
- constructor(options) {
1513
- this.options = { ...defaultOptions, ...options };
1514
- this.client = null;
1515
- switch (options.type) {
1516
- case "redis":
1517
- this.client = new RedisStore(options);
1518
- break;
1519
- case "memory":
1520
- default:
1521
- this.client = new MemoryStore(options);
1522
- break;
1523
- }
1524
- }
1525
- /**
1526
- *
1527
- *
1528
- * @static
1529
- * @returns
1530
- */
1531
- static getInstance(options) {
1532
- if (this.instance) {
1533
- return this.instance;
1534
- }
1535
- this.instance = new CacheStore(options);
1536
- return this.instance;
1537
- }
1538
- getConnection() {
1539
- return this.client.getConnection();
1540
- }
1541
- close() {
1542
- return this.client.close();
1543
- }
1544
- release(conn) {
1545
- return this.client.release(conn);
1546
- }
1547
- defineCommand(name, scripts) {
1548
- return this.client.defineCommand(name, scripts);
1549
- }
1550
- getCompare(name, value) {
1551
- return this.client.getCompare(name, value);
1552
- }
1553
- /**
1554
- * handler for native client
1555
- *
1556
- * @param {string} name
1557
- * @param {any[]} data
1558
- * @returns {*}
1559
- * @memberof RedisStore
1560
- */
1561
- async wrap(name, data) {
1562
- let conn;
1563
- try {
1564
- conn = await this.getConnection();
1565
- const res = await conn[name](...data);
1566
- return res;
1567
- }
1568
- catch (err) {
1569
- throw err;
1570
- }
1571
- finally {
1572
- this.release(conn);
1573
- }
1574
- }
1575
- /**
1576
- * 字符串获取
1577
- * @param name
1578
- */
1579
- get(name) {
1580
- return this.wrap('get', [`${this.options.keyPrefix || ""}${name}`]);
1581
- }
1582
- /**
1583
- * 字符串写入
1584
- * @param name
1585
- * @param value
1586
- * @param timeout
1587
- * @returns {Promise}
1588
- */
1589
- set(name, value, timeout) {
1590
- if (typeof timeout !== 'number') {
1591
- timeout = this.options.timeout;
1592
- }
1593
- return this.wrap('set', [`${this.options.keyPrefix || ""}${name}`, value, 'ex', timeout]);
1594
- }
1595
- /**
1596
- * 以秒为单位,返回给定 key 的剩余生存时间
1597
- * @param name
1598
- * @returns {*}
1599
- */
1600
- ttl(name) {
1601
- return this.wrap('ttl', [`${this.options.keyPrefix || ""}${name}`]);
1602
- }
1603
- /**
1604
- * 设置key超时属性
1605
- * @param name
1606
- * @param timeout
1607
- */
1608
- expire(name, timeout) {
1609
- return this.wrap('expire', [`${this.options.keyPrefix || ""}${name}`, timeout]);
1610
- }
1611
- /**
1612
- * 删除key
1613
- * @param name
1614
- */
1615
- rm(name) {
1616
- return this.wrap('del', [`${this.options.keyPrefix || ""}${name}`]);
1617
- }
1618
- /**
1619
- *
1620
- *
1621
- * @param {*} name
1622
- * @returns
1623
- */
1624
- del(name) {
1625
- return this.wrap('del', [`${this.options.keyPrefix || ""}${name}`]);
1626
- }
1627
- /**
1628
- * 判断key是否存在
1629
- * @param name
1630
- */
1631
- exists(name) {
1632
- return this.wrap('exists', [`${this.options.keyPrefix || ""}${name}`]);
1633
- }
1634
- /**
1635
- * 自增
1636
- * @param name
1637
- */
1638
- incr(name) {
1639
- return this.wrap('incr', [`${this.options.keyPrefix || ""}${name}`]);
1640
- }
1641
- /**
1642
- * 自减
1643
- * @param name
1644
- * @returns {*}
1645
- */
1646
- decr(name) {
1647
- return this.wrap('decr', [`${this.options.keyPrefix || ""}${name}`]);
1648
- }
1649
- /**
1650
- * 将 key 所储存的值增加增量
1651
- * @param name
1652
- * @param incr
1653
- * @returns {*}
1654
- */
1655
- incrby(name, incr = 1) {
1656
- return this.wrap('incrby', [`${this.options.keyPrefix || ""}${name}`, incr]);
1657
- }
1658
- /**
1659
- * key 所储存的值减去减量
1660
- *
1661
- * @param {any} name
1662
- * @param {any} decr
1663
- */
1664
- decrby(name, decr = 1) {
1665
- return this.wrap('decrby', [`${this.options.keyPrefix || ""}${name}`, decr]);
1666
- }
1667
- /**
1668
- * 哈希写入
1669
- * @param name
1670
- * @param key
1671
- * @param value
1672
- * @param timeout
1673
- */
1674
- hset(name, key, value, timeout) {
1675
- const setP = [this.wrap('hset', [`${this.options.keyPrefix || ""}${name}`, key, value])];
1676
- if (typeof timeout !== 'number') {
1677
- timeout = this.options.timeout;
1678
- }
1679
- setP.push(this.set(`${name}:${key}_ex`, 1, timeout));
1680
- return Promise.all(setP);
1681
- }
1682
- /**
1683
- * 哈希获取
1684
- * @param name
1685
- * @param key
1686
- * @returns {*}
1687
- */
1688
- hget(name, key) {
1689
- const setP = [this.get(`${name}:${key}_ex`)];
1690
- setP.push(this.wrap('hget', [`${this.options.keyPrefix || ""}${name}`, key]));
1691
- return Promise.all(setP).then(dataArr => {
1692
- if (dataArr[0] === null) {
1693
- this.hdel(name, key);
1694
- return null;
1695
- }
1696
- return dataArr[1] || null;
1697
- });
1698
- }
1699
- /**
1700
- * 查看哈希表 hashKey 中,给定域 key 是否存在
1701
- * @param name
1702
- * @param key
1703
- * @returns {*}
1704
- */
1705
- hexists(name, key) {
1706
- const setP = [this.get(`${name}:${key}_ex`)];
1707
- setP.push(this.wrap('hexists', [`${this.options.keyPrefix || ""}${name}`, key]));
1708
- return Promise.all(setP).then(dataArr => {
1709
- if (dataArr[0] === null) {
1710
- this.hdel(name, key);
1711
- return 0;
1712
- }
1713
- return dataArr[1] || 0;
1714
- });
1715
- }
1716
- /**
1717
- * 哈希删除
1718
- * @param name
1719
- * @param key
1720
- * @returns {*}
1721
- */
1722
- hdel(name, key) {
1723
- const setP = [this.del(`${name}:${key}_ex`)];
1724
- setP.push(this.wrap('hdel', [`${this.options.keyPrefix || ""}${name}`, key]));
1725
- return Promise.all(setP);
1726
- }
1727
- /**
1728
- * 返回哈希表 key 中域的数量
1729
- * @param name
1730
- * @returns {*}
1731
- */
1732
- hlen(name) {
1733
- return this.wrap('hlen', [`${this.options.keyPrefix || ""}${name}`]);
1734
- }
1735
- /**
1736
- * 给哈希表指定key,增加increment
1737
- * @param name
1738
- * @param key
1739
- * @param incr
1740
- * @returns {*}
1741
- */
1742
- hincrby(name, key, incr = 1) {
1743
- return this.wrap('hincrby', [`${this.options.keyPrefix || ""}${name}`, key, incr]);
1744
- }
1745
- /**
1746
- * 返回哈希表所有key-value
1747
- * @param name
1748
- * @returns {*}
1749
- */
1750
- hgetall(name) {
1751
- return this.wrap('hgetall', [`${this.options.keyPrefix || ""}${name}`]);
1752
- }
1753
- /**
1754
- * 返回哈希表所有key
1755
- * @param name
1756
- * @returns {*}
1757
- */
1758
- hkeys(name) {
1759
- return this.wrap('hkeys', [`${this.options.keyPrefix || ""}${name}`]);
1760
- }
1761
- /**
1762
- * 返回哈希表所有value
1763
- * @param name
1764
- * @returns {*}
1765
- */
1766
- hvals(name) {
1767
- return this.wrap('hvals', [`${this.options.keyPrefix || ""}${name}`]);
1768
- }
1769
- /**
1770
- * 判断列表长度,若不存在则表示为空
1771
- * @param name
1772
- * @returns {*}
1773
- */
1774
- llen(name) {
1775
- return this.wrap('llen', [`${this.options.keyPrefix || ""}${name}`]);
1776
- }
1777
- /**
1778
- * 将值插入列表表尾
1779
- * @param name
1780
- * @param value
1781
- * @returns {*}
1782
- */
1783
- rpush(name, value) {
1784
- return this.wrap('rpush', [`${this.options.keyPrefix || ""}${name}`, value]);
1785
- }
1786
- /**
1787
- *
1788
- *
1789
- * @param {string} name
1790
- * @param {(string | number)} value
1791
- * @returns {*}
1792
- * @memberof RedisStore
1793
- */
1794
- lpush(name, value) {
1795
- return this.wrap('lpush', [`${this.options.keyPrefix || ""}${name}`, value]);
1796
- }
1797
- /**
1798
- * 将列表表头取出,并去除
1799
- * @param name
1800
- * @returns {*}
1801
- */
1802
- lpop(name) {
1803
- return this.wrap('lpop', [`${this.options.keyPrefix || ""}${name}`]);
1804
- }
1805
- /**
1806
- *
1807
- *
1808
- * @param {string} name
1809
- * @returns {*}
1810
- * @memberof RedisStore
1811
- */
1812
- rpop(name) {
1813
- return this.wrap('rpop', [`${this.options.keyPrefix || ""}${name}`]);
1814
- }
1815
- /**
1816
- * 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定
1817
- * @param name
1818
- * @param start
1819
- * @param stop
1820
- * @returns {*}
1821
- */
1822
- lrange(name, start, stop) {
1823
- return this.wrap('lrange', [`${this.options.keyPrefix || ""}${name}`, start, stop]);
1824
- }
1825
- /**
1826
- * 集合新增
1827
- * @param name
1828
- * @param value
1829
- * @param timeout
1830
- * @returns {*}
1831
- */
1832
- sadd(name, value, timeout) {
1833
- const setP = [this.wrap('sadd', [`${this.options.keyPrefix || ""}${name}`, value])];
1834
- if (typeof timeout !== 'number') {
1835
- setP.push(this.wrap('expire', [`${this.options.keyPrefix || ""}${name}`, timeout]));
1836
- }
1837
- return Promise.all(setP);
1838
- }
1839
- /**
1840
- * 返回集合的基数(集合中元素的数量)
1841
- * @param name
1842
- * @returns {*}
1843
- */
1844
- scard(name) {
1845
- return this.wrap('scard', [`${this.options.keyPrefix || ""}${name}`]);
1846
- }
1847
- /**
1848
- * 判断 member 元素是否集合的成员
1849
- * @param name
1850
- * @param key
1851
- * @returns {*}
1852
- */
1853
- sismember(name, key) {
1854
- return this.wrap('sismember', [`${this.options.keyPrefix || ""}${name}`, key]);
1855
- }
1856
- /**
1857
- * 返回集合中的所有成员
1858
- * @param name
1859
- * @returns {*}
1860
- */
1861
- smembers(name) {
1862
- return this.wrap('smembers', [`${this.options.keyPrefix || ""}${name}`]);
1863
- }
1864
- /**
1865
- * 移除并返回集合中的一个随机元素
1866
- * @param name
1867
- * @returns {*}
1868
- */
1869
- spop(name) {
1870
- return this.wrap('spop', [`${this.options.keyPrefix || ""}${name}`]);
1871
- }
1872
- /**
1873
- * 移除集合 key 中的一个 member 元素
1874
- * @param name
1875
- * @param key
1876
- * @returns {*}
1877
- */
1878
- srem(name, key) {
1879
- return this.wrap('srem', [`${this.options.keyPrefix || ""}${name}`, key]);
1880
- }
1881
- /**
1882
- * member 元素从 source 集合移动到 destination 集合
1883
- * @param source
1884
- * @param destination
1885
- * @param member
1886
- * @returns {*}
1887
- */
1888
- smove(source, destination, member) {
1889
- return this.wrap('smove', [`${this.options.keyPrefix || ""}${source}`, `${this.options.keyPrefix}${destination}`, member]);
1890
- }
2051
+ /*
2052
+ * @Description:
2053
+ * @Usage:
2054
+ * @Author: richen
2055
+ * @Date: 2021-12-02 15:26:55
2056
+ * @LastEditTime: 2024-11-07 14:27:25
2057
+ */
2058
+ const defaultOptions = {
2059
+ type: 'memory', // memory | redis
2060
+ host: '',
2061
+ port: 0,
2062
+ keyPrefix: 'Koatty',
2063
+ timeout: 600,
2064
+ poolSize: 10,
2065
+ connectTimeout: 500,
2066
+ db: 0
2067
+ };
2068
+ /**
2069
+ *
2070
+ *
2071
+ * @export
2072
+ * @class Store
2073
+ */
2074
+ class CacheStore {
2075
+ client;
2076
+ options;
2077
+ static instances = new Map();
2078
+ /**
2079
+ * Creates an instance of CacheStore.
2080
+ * @param {StoreOptions} options
2081
+ * @memberof CacheStore
2082
+ */
2083
+ constructor(options) {
2084
+ this.options = options ? { ...defaultOptions, ...options } : defaultOptions;
2085
+ this.client = null;
2086
+ switch (this.options.type) {
2087
+ case "redis":
2088
+ this.client = new RedisStore(this.options);
2089
+ break;
2090
+ case "memory":
2091
+ default:
2092
+ this.client = new MemoryStore(this.options);
2093
+ break;
2094
+ }
2095
+ }
2096
+ /**
2097
+ * 获取单例实例,支持多配置实例管理
2098
+ * @static
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}
2120
+ */
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位整数
2135
+ }
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();
2161
+ }
2162
+ getConnection() {
2163
+ return this.client.getConnection();
2164
+ }
2165
+ close() {
2166
+ return this.client.close();
2167
+ }
2168
+ release(conn) {
2169
+ return this.client.release(conn);
2170
+ }
2171
+ /**
2172
+ * 获取底层实现客户端,用于访问特定实现的功能
2173
+ * 例如:Redis的defineCommand, getCompare等
2174
+ * @returns {MemoryStore | RedisStore}
2175
+ */
2176
+ getRawClient() {
2177
+ return this.client;
2178
+ }
2179
+ /**
2180
+ * handler for native client
2181
+ *
2182
+ * @param {string} name
2183
+ * @param {any[]} data
2184
+ * @returns {*}
2185
+ * @memberof RedisStore
2186
+ */
2187
+ async wrap(name, data) {
2188
+ let conn;
2189
+ try {
2190
+ conn = await this.getConnection();
2191
+ const res = await conn[name](...data);
2192
+ return res;
2193
+ }
2194
+ catch (err) {
2195
+ throw err;
2196
+ }
2197
+ finally {
2198
+ this.release(conn);
2199
+ }
2200
+ }
2201
+ /**
2202
+ * 字符串获取
2203
+ * @param name
2204
+ */
2205
+ get(name) {
2206
+ return this.wrap('get', [`${this.options.keyPrefix || ""}${name}`]);
2207
+ }
2208
+ /**
2209
+ * 字符串写入
2210
+ * @param name
2211
+ * @param value
2212
+ * @param timeout
2213
+ * @returns {Promise}
2214
+ */
2215
+ set(name, value, timeout) {
2216
+ if (typeof timeout !== 'number') {
2217
+ timeout = this.options.timeout;
2218
+ }
2219
+ return this.wrap('set', [`${this.options.keyPrefix || ""}${name}`, value, 'ex', timeout]);
2220
+ }
2221
+ /**
2222
+ * 以秒为单位,返回给定 key 的剩余生存时间
2223
+ * @param name
2224
+ * @returns {*}
2225
+ */
2226
+ ttl(name) {
2227
+ return this.wrap('ttl', [`${this.options.keyPrefix || ""}${name}`]);
2228
+ }
2229
+ /**
2230
+ * 设置key超时属性
2231
+ * @param name
2232
+ * @param timeout
2233
+ */
2234
+ expire(name, timeout) {
2235
+ return this.wrap('expire', [`${this.options.keyPrefix || ""}${name}`, timeout]);
2236
+ }
2237
+ /**
2238
+ *
2239
+ *
2240
+ * @param {*} name
2241
+ * @returns
2242
+ */
2243
+ del(name) {
2244
+ return this.wrap('del', [`${this.options.keyPrefix || ""}${name}`]);
2245
+ }
2246
+ /**
2247
+ * 判断key是否存在
2248
+ * @param name
2249
+ */
2250
+ exists(name) {
2251
+ return this.wrap('exists', [`${this.options.keyPrefix || ""}${name}`]);
2252
+ }
2253
+ /**
2254
+ * 自增
2255
+ * @param name
2256
+ */
2257
+ incr(name) {
2258
+ return this.wrap('incr', [`${this.options.keyPrefix || ""}${name}`]);
2259
+ }
2260
+ /**
2261
+ * 自减
2262
+ * @param name
2263
+ * @returns {*}
2264
+ */
2265
+ decr(name) {
2266
+ return this.wrap('decr', [`${this.options.keyPrefix || ""}${name}`]);
2267
+ }
2268
+ /**
2269
+ * key 所储存的值增加增量
2270
+ * @param name
2271
+ * @param incr
2272
+ * @returns {*}
2273
+ */
2274
+ incrby(name, increment) {
2275
+ return this.wrap('incrby', [`${this.options.keyPrefix || ""}${name}`, increment]);
2276
+ }
2277
+ /**
2278
+ * key 所储存的值减去减量
2279
+ *
2280
+ * @param {any} name
2281
+ * @param {any} decr
2282
+ */
2283
+ decrby(name, decrement) {
2284
+ return this.wrap('decrby', [`${this.options.keyPrefix || ""}${name}`, decrement]);
2285
+ }
2286
+ /**
2287
+ * 哈希写入
2288
+ * @param name
2289
+ * @param key
2290
+ * @param value
2291
+ * @param timeout
2292
+ */
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);
2297
+ }
2298
+ else {
2299
+ // 如果没有指定timeout,设置一个永久标记,避免hget时误删
2300
+ await this.set(`${name}:${key}_ex`, 1);
2301
+ }
2302
+ return result;
2303
+ }
2304
+ /**
2305
+ * 哈希获取
2306
+ * @param name
2307
+ * @param key
2308
+ * @returns {*}
2309
+ */
2310
+ hget(name, key) {
2311
+ const setP = [this.get(`${name}:${key}_ex`)];
2312
+ setP.push(this.wrap('hget', [`${this.options.keyPrefix || ""}${name}`, key]));
2313
+ return Promise.all(setP).then(dataArr => {
2314
+ if (dataArr[0] === null) {
2315
+ this.hdel(name, key);
2316
+ return null;
2317
+ }
2318
+ return dataArr[1] || null;
2319
+ });
2320
+ }
2321
+ /**
2322
+ * 查看哈希表 hashKey 中,给定域 key 是否存在
2323
+ * @param name
2324
+ * @param key
2325
+ * @returns {*}
2326
+ */
2327
+ hexists(name, key) {
2328
+ const setP = [this.get(`${name}:${key}_ex`)];
2329
+ setP.push(this.wrap('hexists', [`${this.options.keyPrefix || ""}${name}`, key]));
2330
+ return Promise.all(setP).then(dataArr => {
2331
+ if (dataArr[0] === null) {
2332
+ this.hdel(name, key);
2333
+ return 0;
2334
+ }
2335
+ return Number(dataArr[1]) || 0;
2336
+ });
2337
+ }
2338
+ /**
2339
+ * 哈希删除
2340
+ * @param name
2341
+ * @param key
2342
+ * @returns {*}
2343
+ */
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;
2348
+ }
2349
+ /**
2350
+ * 返回哈希表 key 中域的数量
2351
+ * @param name
2352
+ * @returns {*}
2353
+ */
2354
+ hlen(name) {
2355
+ return this.wrap('hlen', [`${this.options.keyPrefix || ""}${name}`]);
2356
+ }
2357
+ /**
2358
+ * 给哈希表指定key,增加increment
2359
+ * @param name
2360
+ * @param key
2361
+ * @param increment
2362
+ * @returns {*}
2363
+ */
2364
+ hincrby(name, key, increment) {
2365
+ return this.wrap('hincrby', [`${this.options.keyPrefix || ""}${name}`, key, increment]);
2366
+ }
2367
+ /**
2368
+ * 返回哈希表所有key-value
2369
+ * @param name
2370
+ * @returns {*}
2371
+ */
2372
+ hgetall(name) {
2373
+ return this.wrap('hgetall', [`${this.options.keyPrefix || ""}${name}`]);
2374
+ }
2375
+ /**
2376
+ * 返回哈希表所有key
2377
+ * @param name
2378
+ * @returns {*}
2379
+ */
2380
+ hkeys(name) {
2381
+ return this.wrap('hkeys', [`${this.options.keyPrefix || ""}${name}`]);
2382
+ }
2383
+ /**
2384
+ * 返回哈希表所有value
2385
+ * @param name
2386
+ * @returns {*}
2387
+ */
2388
+ hvals(name) {
2389
+ return this.wrap('hvals', [`${this.options.keyPrefix || ""}${name}`]);
2390
+ }
2391
+ /**
2392
+ * 判断列表长度,若不存在则表示为空
2393
+ * @param name
2394
+ * @returns {*}
2395
+ */
2396
+ llen(name) {
2397
+ return this.wrap('llen', [`${this.options.keyPrefix || ""}${name}`]);
2398
+ }
2399
+ /**
2400
+ * 将值插入列表表尾
2401
+ * @param name
2402
+ * @param value
2403
+ * @returns {*}
2404
+ */
2405
+ rpush(name, value) {
2406
+ return this.wrap('rpush', [`${this.options.keyPrefix || ""}${name}`, value]);
2407
+ }
2408
+ /**
2409
+ *
2410
+ *
2411
+ * @param {string} name
2412
+ * @param {(string | number)} value
2413
+ * @returns {*}
2414
+ * @memberof RedisStore
2415
+ */
2416
+ lpush(name, value) {
2417
+ return this.wrap('lpush', [`${this.options.keyPrefix || ""}${name}`, value]);
2418
+ }
2419
+ /**
2420
+ * 将列表表头取出,并去除
2421
+ * @param name
2422
+ * @returns {*}
2423
+ */
2424
+ lpop(name) {
2425
+ return this.wrap('lpop', [`${this.options.keyPrefix || ""}${name}`]);
2426
+ }
2427
+ /**
2428
+ *
2429
+ *
2430
+ * @param {string} name
2431
+ * @returns {*}
2432
+ * @memberof RedisStore
2433
+ */
2434
+ rpop(name) {
2435
+ return this.wrap('rpop', [`${this.options.keyPrefix || ""}${name}`]);
2436
+ }
2437
+ /**
2438
+ * 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定
2439
+ * @param name
2440
+ * @param start
2441
+ * @param stop
2442
+ * @returns {*}
2443
+ */
2444
+ lrange(name, start, stop) {
2445
+ return this.wrap('lrange', [`${this.options.keyPrefix || ""}${name}`, start, stop]);
2446
+ }
2447
+ /**
2448
+ * 集合新增
2449
+ * @param name
2450
+ * @param value
2451
+ * @param timeout
2452
+ * @returns {*}
2453
+ */
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]);
2458
+ }
2459
+ return result;
2460
+ }
2461
+ /**
2462
+ * 返回集合的基数(集合中元素的数量)
2463
+ * @param name
2464
+ * @returns {*}
2465
+ */
2466
+ scard(name) {
2467
+ return this.wrap('scard', [`${this.options.keyPrefix || ""}${name}`]);
2468
+ }
2469
+ /**
2470
+ * 判断 member 元素是否集合的成员
2471
+ * @param name
2472
+ * @param key
2473
+ * @returns {*}
2474
+ */
2475
+ sismember(name, key) {
2476
+ return this.wrap('sismember', [`${this.options.keyPrefix || ""}${name}`, key]);
2477
+ }
2478
+ /**
2479
+ * 返回集合中的所有成员
2480
+ * @param name
2481
+ * @returns {*}
2482
+ */
2483
+ smembers(name) {
2484
+ return this.wrap('smembers', [`${this.options.keyPrefix || ""}${name}`]);
2485
+ }
2486
+ /**
2487
+ * 移除并返回集合中的一个随机元素
2488
+ * @param name
2489
+ * @returns {*}
2490
+ */
2491
+ spop(name) {
2492
+ return this.wrap('spop', [`${this.options.keyPrefix || ""}${name}`]);
2493
+ }
2494
+ /**
2495
+ * 移除集合 key 中的一个 member 元素
2496
+ * @param name
2497
+ * @param key
2498
+ * @returns {*}
2499
+ */
2500
+ srem(name, key) {
2501
+ return this.wrap('srem', [`${this.options.keyPrefix || ""}${name}`, key]);
2502
+ }
2503
+ /**
2504
+ * 将 member 元素从 source 集合移动到 destination 集合
2505
+ * @param source
2506
+ * @param destination
2507
+ * @param member
2508
+ * @returns {*}
2509
+ */
2510
+ smove(source, destination, member) {
2511
+ return this.wrap('smove', [`${this.options.keyPrefix || ""}${source}`, `${this.options.keyPrefix || ""}${destination}`, member]);
2512
+ }
1891
2513
  }
1892
2514
 
1893
2515
  exports.CacheStore = CacheStore;