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