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