koatty_store 1.8.0 → 1.9.1
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/.turbo/turbo-build.log +540 -0
- package/CHANGELOG.md +64 -12
- package/README.md +372 -1
- package/dist/README.md +372 -1
- package/dist/index.d.ts +48 -45
- package/dist/index.js +165 -120
- package/dist/index.mjs +165 -120
- package/dist/package.json +10 -12
- package/package.json +21 -24
- package/.vscode/launch.json +0 -25
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @Author: richen
|
|
3
|
-
* @Date:
|
|
3
|
+
* @Date: 2026-01-28 11:06:43
|
|
4
4
|
* @License: BSD (3-Clause)
|
|
5
5
|
* @Copyright (c) - <richenlin(at)gmail.com>
|
|
6
6
|
* @HomePage: https://koatty.org/
|
|
@@ -11,6 +11,7 @@ var lodash = require('lodash');
|
|
|
11
11
|
var helper = require('koatty_lib');
|
|
12
12
|
var events = require('events');
|
|
13
13
|
var lruCache = require('lru-cache');
|
|
14
|
+
var AsyncLock = require('async-lock');
|
|
14
15
|
var koatty_logger = require('koatty_logger');
|
|
15
16
|
var ioredis = require('ioredis');
|
|
16
17
|
var genericPool = require('generic-pool');
|
|
@@ -71,15 +72,6 @@ var messages;
|
|
|
71
72
|
messages["mutuallyExclusiveNXXX"] = "ERR XX and NX options at the same time are not compatible";
|
|
72
73
|
})(messages || (messages = {}));
|
|
73
74
|
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
75
|
/**
|
|
84
76
|
* Creates an instance of MemoryCache.
|
|
85
77
|
* @param {MemoryCacheOptions} options
|
|
@@ -87,6 +79,8 @@ class MemoryCache extends events.EventEmitter {
|
|
|
87
79
|
*/
|
|
88
80
|
constructor(options) {
|
|
89
81
|
super();
|
|
82
|
+
this.databases = new Map();
|
|
83
|
+
this.ttlCheckTimer = null;
|
|
90
84
|
this.options = {
|
|
91
85
|
database: 0,
|
|
92
86
|
maxKeys: 1000,
|
|
@@ -100,6 +94,7 @@ class MemoryCache extends events.EventEmitter {
|
|
|
100
94
|
this.lastSave = Date.now();
|
|
101
95
|
this.multiMode = false;
|
|
102
96
|
this.responseMessages = [];
|
|
97
|
+
this.lock = new AsyncLock();
|
|
103
98
|
// 初始化数据库和缓存
|
|
104
99
|
if (!this.databases.has(this.currentDBIndex)) {
|
|
105
100
|
this.databases.set(this.currentDBIndex, this.createLRUCache());
|
|
@@ -163,6 +158,15 @@ class MemoryCache extends events.EventEmitter {
|
|
|
163
158
|
this.ttlCheckTimer = null;
|
|
164
159
|
}
|
|
165
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* 清理所有资源
|
|
163
|
+
* @private
|
|
164
|
+
*/
|
|
165
|
+
cleanup() {
|
|
166
|
+
this.stopTTLCheck();
|
|
167
|
+
this.databases.clear();
|
|
168
|
+
this.removeAllListeners();
|
|
169
|
+
}
|
|
166
170
|
/**
|
|
167
171
|
*
|
|
168
172
|
*
|
|
@@ -188,8 +192,8 @@ class MemoryCache extends events.EventEmitter {
|
|
|
188
192
|
* @memberof MemoryCache
|
|
189
193
|
*/
|
|
190
194
|
quit() {
|
|
195
|
+
this.cleanup(); // 调用清理方法
|
|
191
196
|
this.connected = false;
|
|
192
|
-
this.stopTTLCheck();
|
|
193
197
|
// exit multi mode if we are in it
|
|
194
198
|
this.discard(null, true);
|
|
195
199
|
this.emit('end');
|
|
@@ -452,14 +456,18 @@ class MemoryCache extends events.EventEmitter {
|
|
|
452
456
|
* @memberof MemoryCache
|
|
453
457
|
*/
|
|
454
458
|
incr(key, callback) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
459
|
+
// 使用锁保护原子操作
|
|
460
|
+
const lockKey = `incr:${key}`;
|
|
461
|
+
return this.lock.acquire(lockKey, () => {
|
|
462
|
+
let retVal = null;
|
|
463
|
+
try {
|
|
464
|
+
retVal = this._addToKey(key, 1);
|
|
465
|
+
}
|
|
466
|
+
catch (err) {
|
|
467
|
+
return this._handleCallback(callback, null, err);
|
|
468
|
+
}
|
|
469
|
+
return this._handleCallback(callback, retVal);
|
|
470
|
+
});
|
|
463
471
|
}
|
|
464
472
|
/**
|
|
465
473
|
*
|
|
@@ -471,14 +479,18 @@ class MemoryCache extends events.EventEmitter {
|
|
|
471
479
|
* @memberof MemoryCache
|
|
472
480
|
*/
|
|
473
481
|
incrby(key, amount, callback) {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
+
// 使用锁保护原子操作
|
|
483
|
+
const lockKey = `incrby:${key}`;
|
|
484
|
+
return this.lock.acquire(lockKey, () => {
|
|
485
|
+
let retVal = null;
|
|
486
|
+
try {
|
|
487
|
+
retVal = this._addToKey(key, amount);
|
|
488
|
+
}
|
|
489
|
+
catch (err) {
|
|
490
|
+
return this._handleCallback(callback, null, err);
|
|
491
|
+
}
|
|
492
|
+
return this._handleCallback(callback, retVal);
|
|
493
|
+
});
|
|
482
494
|
}
|
|
483
495
|
/**
|
|
484
496
|
*
|
|
@@ -489,14 +501,18 @@ class MemoryCache extends events.EventEmitter {
|
|
|
489
501
|
* @memberof MemoryCache
|
|
490
502
|
*/
|
|
491
503
|
decr(key, callback) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
504
|
+
// 使用锁保护原子操作
|
|
505
|
+
const lockKey = `decr:${key}`;
|
|
506
|
+
return this.lock.acquire(lockKey, () => {
|
|
507
|
+
let retVal = null;
|
|
508
|
+
try {
|
|
509
|
+
retVal = this._addToKey(key, -1);
|
|
510
|
+
}
|
|
511
|
+
catch (err) {
|
|
512
|
+
return this._handleCallback(callback, null, err);
|
|
513
|
+
}
|
|
514
|
+
return this._handleCallback(callback, retVal);
|
|
515
|
+
});
|
|
500
516
|
}
|
|
501
517
|
/**
|
|
502
518
|
*
|
|
@@ -508,19 +524,23 @@ class MemoryCache extends events.EventEmitter {
|
|
|
508
524
|
* @memberof MemoryCache
|
|
509
525
|
*/
|
|
510
526
|
decrby(key, amount, callback) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
527
|
+
// 使用锁保护原子操作
|
|
528
|
+
const lockKey = `decrby:${key}`;
|
|
529
|
+
return this.lock.acquire(lockKey, () => {
|
|
530
|
+
let retVal = null;
|
|
531
|
+
try {
|
|
532
|
+
retVal = this._addToKey(key, 0 - amount);
|
|
533
|
+
}
|
|
534
|
+
catch (err) {
|
|
535
|
+
return this._handleCallback(callback, null, err);
|
|
536
|
+
}
|
|
537
|
+
return this._handleCallback(callback, retVal);
|
|
538
|
+
});
|
|
519
539
|
}
|
|
520
540
|
// ---------------------------------------
|
|
521
541
|
// ## Hash ##
|
|
522
542
|
// ---------------------------------------
|
|
523
|
-
hset(key, field, value, callback) {
|
|
543
|
+
hset(key, field, value, timeout, callback) {
|
|
524
544
|
let retVal = 0;
|
|
525
545
|
if (this._hasKey(key)) {
|
|
526
546
|
this._testType(key, 'hash', true, callback);
|
|
@@ -532,6 +552,14 @@ class MemoryCache extends events.EventEmitter {
|
|
|
532
552
|
retVal = 1;
|
|
533
553
|
}
|
|
534
554
|
this._setField(key, field, value.toString());
|
|
555
|
+
// 如果指定了 timeout,存储字段级别的过期时间
|
|
556
|
+
if (typeof timeout === 'number') {
|
|
557
|
+
const hashObj = this.cache.get(key).value;
|
|
558
|
+
if (!hashObj._fieldTTL) {
|
|
559
|
+
hashObj._fieldTTL = {};
|
|
560
|
+
}
|
|
561
|
+
hashObj._fieldTTL[field] = Date.now() + (timeout * 1000);
|
|
562
|
+
}
|
|
535
563
|
this.persist(key);
|
|
536
564
|
return this._handleCallback(callback, retVal);
|
|
537
565
|
}
|
|
@@ -548,8 +576,17 @@ class MemoryCache extends events.EventEmitter {
|
|
|
548
576
|
let retVal = null;
|
|
549
577
|
if (this._hasKey(key)) {
|
|
550
578
|
this._testType(key, 'hash', true, callback);
|
|
579
|
+
// 检查字段级别的过期时间
|
|
580
|
+
const hashObj = this._getKey(key);
|
|
581
|
+
if (hashObj && hashObj._fieldTTL && hashObj._fieldTTL[field]) {
|
|
582
|
+
if (hashObj._fieldTTL[field] <= Date.now()) {
|
|
583
|
+
// 过期,删除字段
|
|
584
|
+
this.hdel(key, field);
|
|
585
|
+
return this._handleCallback(callback, null);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
551
588
|
if (this._hasField(key, field)) {
|
|
552
|
-
retVal =
|
|
589
|
+
retVal = hashObj[field];
|
|
553
590
|
}
|
|
554
591
|
}
|
|
555
592
|
return this._handleCallback(callback, retVal);
|
|
@@ -567,6 +604,15 @@ class MemoryCache extends events.EventEmitter {
|
|
|
567
604
|
let retVal = 0;
|
|
568
605
|
if (this._hasKey(key)) {
|
|
569
606
|
this._testType(key, 'hash', true, callback);
|
|
607
|
+
// 检查字段级别的过期时间
|
|
608
|
+
const hashObj = this._getKey(key);
|
|
609
|
+
if (hashObj && hashObj._fieldTTL && hashObj._fieldTTL[field]) {
|
|
610
|
+
if (hashObj._fieldTTL[field] <= Date.now()) {
|
|
611
|
+
// 过期,删除字段
|
|
612
|
+
this.hdel(key, field);
|
|
613
|
+
return this._handleCallback(callback, 0);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
570
616
|
if (this._hasField(key, field)) {
|
|
571
617
|
retVal = 1;
|
|
572
618
|
}
|
|
@@ -586,10 +632,15 @@ class MemoryCache extends events.EventEmitter {
|
|
|
586
632
|
const callback = this._retrieveCallback(fields);
|
|
587
633
|
if (this._hasKey(key)) {
|
|
588
634
|
this._testType(key, 'hash', true, callback);
|
|
635
|
+
const hashObj = this.cache.get(key).value;
|
|
589
636
|
for (let itr = 0; itr < fields.length; itr++) {
|
|
590
637
|
const field = fields[itr];
|
|
591
638
|
if (this._hasField(key, field)) {
|
|
592
|
-
delete
|
|
639
|
+
delete hashObj[field];
|
|
640
|
+
// 清理过期时间记录
|
|
641
|
+
if (hashObj._fieldTTL && hashObj._fieldTTL[field]) {
|
|
642
|
+
delete hashObj._fieldTTL[field];
|
|
643
|
+
}
|
|
593
644
|
retVal++;
|
|
594
645
|
}
|
|
595
646
|
}
|
|
@@ -619,14 +670,18 @@ class MemoryCache extends events.EventEmitter {
|
|
|
619
670
|
* @memberof MemoryCache
|
|
620
671
|
*/
|
|
621
672
|
hincrby(key, field, value, callback) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
673
|
+
// 使用锁保护原子操作,使用组合 key
|
|
674
|
+
const lockKey = `hincrby:${key}:${field}`;
|
|
675
|
+
return this.lock.acquire(lockKey, () => {
|
|
676
|
+
let retVal;
|
|
677
|
+
try {
|
|
678
|
+
retVal = this._addToField(key, field, value, false);
|
|
679
|
+
}
|
|
680
|
+
catch (err) {
|
|
681
|
+
return this._handleCallback(callback, null, err);
|
|
682
|
+
}
|
|
683
|
+
return this._handleCallback(callback, retVal);
|
|
684
|
+
});
|
|
630
685
|
}
|
|
631
686
|
/**
|
|
632
687
|
*
|
|
@@ -640,7 +695,11 @@ class MemoryCache extends events.EventEmitter {
|
|
|
640
695
|
let retVals = {};
|
|
641
696
|
if (this._hasKey(key)) {
|
|
642
697
|
this._testType(key, 'hash', true, callback);
|
|
643
|
-
|
|
698
|
+
const hashObj = this._getKey(key);
|
|
699
|
+
// 排除内部属性 _fieldTTL
|
|
700
|
+
retVals = Object.entries(hashObj)
|
|
701
|
+
.filter(([k]) => k !== '_fieldTTL')
|
|
702
|
+
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
|
|
644
703
|
}
|
|
645
704
|
return this._handleCallback(callback, retVals);
|
|
646
705
|
}
|
|
@@ -656,7 +715,9 @@ class MemoryCache extends events.EventEmitter {
|
|
|
656
715
|
let retVals = [];
|
|
657
716
|
if (this._hasKey(key)) {
|
|
658
717
|
this._testType(key, 'hash', true, callback);
|
|
659
|
-
|
|
718
|
+
const hashObj = this._getKey(key);
|
|
719
|
+
// 排除内部属性 _fieldTTL
|
|
720
|
+
retVals = Object.keys(hashObj).filter(k => k !== '_fieldTTL');
|
|
660
721
|
}
|
|
661
722
|
return this._handleCallback(callback, retVals);
|
|
662
723
|
}
|
|
@@ -672,7 +733,11 @@ class MemoryCache extends events.EventEmitter {
|
|
|
672
733
|
let retVals = [];
|
|
673
734
|
if (this._hasKey(key)) {
|
|
674
735
|
this._testType(key, 'hash', true, callback);
|
|
675
|
-
|
|
736
|
+
const hashObj = this._getKey(key);
|
|
737
|
+
// 排除内部属性 _fieldTTL 的值
|
|
738
|
+
retVals = Object.entries(hashObj)
|
|
739
|
+
.filter(([k]) => k !== '_fieldTTL')
|
|
740
|
+
.map(([, v]) => v);
|
|
676
741
|
}
|
|
677
742
|
return this._handleCallback(callback, retVals);
|
|
678
743
|
}
|
|
@@ -823,8 +888,7 @@ class MemoryCache extends events.EventEmitter {
|
|
|
823
888
|
stop = length - 1;
|
|
824
889
|
}
|
|
825
890
|
if (stop >= 0 && stop >= start) {
|
|
826
|
-
|
|
827
|
-
for (let itr = start; itr < size; itr++) {
|
|
891
|
+
for (let itr = start; itr <= stop; itr++) {
|
|
828
892
|
retVal.push(list[itr]);
|
|
829
893
|
}
|
|
830
894
|
}
|
|
@@ -1031,9 +1095,10 @@ class MemoryCache extends events.EventEmitter {
|
|
|
1031
1095
|
* @memberof MemoryCache
|
|
1032
1096
|
*/
|
|
1033
1097
|
pttl(key, _callback) {
|
|
1098
|
+
var _a;
|
|
1034
1099
|
let retVal = -2;
|
|
1035
1100
|
if (this._hasKey(key)) {
|
|
1036
|
-
if (!lodash.isNil(this.cache.get(key)
|
|
1101
|
+
if (!lodash.isNil((_a = this.cache.get(key)) === null || _a === void 0 ? void 0 : _a.timeout)) {
|
|
1037
1102
|
retVal = this.cache.get(key).timeout - Date.now();
|
|
1038
1103
|
// Prevent unexpected errors if the actual ttl just happens to be -2 or -1
|
|
1039
1104
|
if (retVal < 0 && retVal > -3) {
|
|
@@ -1242,7 +1307,8 @@ class MemoryCache extends events.EventEmitter {
|
|
|
1242
1307
|
if (key && field) {
|
|
1243
1308
|
const ky = this._getKey(key);
|
|
1244
1309
|
if (ky) {
|
|
1245
|
-
|
|
1310
|
+
// 排除内部属性 _fieldTTL
|
|
1311
|
+
retVal = field !== '_fieldTTL' && ky.hasOwnProperty(field);
|
|
1246
1312
|
}
|
|
1247
1313
|
}
|
|
1248
1314
|
return retVal;
|
|
@@ -1651,9 +1717,6 @@ class MemoryCache extends events.EventEmitter {
|
|
|
1651
1717
|
* @LastEditTime: 2023-02-18 23:52:47
|
|
1652
1718
|
*/
|
|
1653
1719
|
class MemoryStore {
|
|
1654
|
-
client;
|
|
1655
|
-
pool;
|
|
1656
|
-
options;
|
|
1657
1720
|
/**
|
|
1658
1721
|
* Creates an instance of MemoryStore.
|
|
1659
1722
|
* @param {MemoryStoreOpt} options
|
|
@@ -1666,7 +1729,15 @@ class MemoryStore {
|
|
|
1666
1729
|
ttlCheckInterval: 60000, // 1分钟
|
|
1667
1730
|
...options
|
|
1668
1731
|
};
|
|
1669
|
-
|
|
1732
|
+
// 直接创建 MemoryCache 实例
|
|
1733
|
+
this.client = new MemoryCache({
|
|
1734
|
+
database: this.options.db || 0,
|
|
1735
|
+
maxKeys: this.options.maxKeys,
|
|
1736
|
+
maxMemory: this.options.maxMemory,
|
|
1737
|
+
evictionPolicy: this.options.evictionPolicy,
|
|
1738
|
+
ttlCheckInterval: this.options.ttlCheckInterval
|
|
1739
|
+
});
|
|
1740
|
+
this.client.createClient();
|
|
1670
1741
|
}
|
|
1671
1742
|
/**
|
|
1672
1743
|
* getConnection
|
|
@@ -1675,19 +1746,7 @@ class MemoryStore {
|
|
|
1675
1746
|
* @memberof MemoryStore
|
|
1676
1747
|
*/
|
|
1677
1748
|
getConnection() {
|
|
1678
|
-
|
|
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
|
-
}
|
|
1749
|
+
// 直接返回 MemoryCache 实例
|
|
1691
1750
|
return this.client;
|
|
1692
1751
|
}
|
|
1693
1752
|
/**
|
|
@@ -1710,7 +1769,8 @@ class MemoryStore {
|
|
|
1710
1769
|
* @memberof MemoryStore
|
|
1711
1770
|
*/
|
|
1712
1771
|
async release(_conn) {
|
|
1713
|
-
|
|
1772
|
+
// 对于内存存储,不需要释放连接
|
|
1773
|
+
return Promise.resolve();
|
|
1714
1774
|
}
|
|
1715
1775
|
/**
|
|
1716
1776
|
* defineCommand
|
|
@@ -1774,18 +1834,15 @@ class MemoryStore {
|
|
|
1774
1834
|
* @class RedisStore
|
|
1775
1835
|
*/
|
|
1776
1836
|
class RedisStore {
|
|
1777
|
-
options;
|
|
1778
|
-
pool;
|
|
1779
|
-
client;
|
|
1780
|
-
reconnectAttempts = 0;
|
|
1781
|
-
maxReconnectAttempts = 5;
|
|
1782
|
-
reconnectDelay = 1000; // 初始重连延迟1秒
|
|
1783
1837
|
/**
|
|
1784
1838
|
* Creates an instance of RedisStore.
|
|
1785
1839
|
* @param {RedisStoreOpt} options
|
|
1786
1840
|
* @memberof RedisStore
|
|
1787
1841
|
*/
|
|
1788
1842
|
constructor(options) {
|
|
1843
|
+
this.reconnectAttempts = 0;
|
|
1844
|
+
this.maxReconnectAttempts = 5;
|
|
1845
|
+
this.reconnectDelay = 1000; // 初始重连延迟1秒
|
|
1789
1846
|
this.options = this.parseOpt(options);
|
|
1790
1847
|
this.pool = null;
|
|
1791
1848
|
}
|
|
@@ -2072,9 +2129,6 @@ const defaultOptions = {
|
|
|
2072
2129
|
* @class Store
|
|
2073
2130
|
*/
|
|
2074
2131
|
class CacheStore {
|
|
2075
|
-
client;
|
|
2076
|
-
options;
|
|
2077
|
-
static instances = new Map();
|
|
2078
2132
|
/**
|
|
2079
2133
|
* Creates an instance of CacheStore.
|
|
2080
2134
|
* @param {StoreOptions} options
|
|
@@ -2192,10 +2246,22 @@ class CacheStore {
|
|
|
2192
2246
|
return res;
|
|
2193
2247
|
}
|
|
2194
2248
|
catch (err) {
|
|
2195
|
-
|
|
2249
|
+
// 添加详细的错误信息
|
|
2250
|
+
const error = new Error(`Cache operation failed: ${name}(${data.slice(0, 2).join(', ')}${data.length > 2 ? '...' : ''}) - ${err.message}`);
|
|
2251
|
+
error.stack = err.stack;
|
|
2252
|
+
throw error;
|
|
2196
2253
|
}
|
|
2197
2254
|
finally {
|
|
2198
|
-
|
|
2255
|
+
// 安全地释放连接
|
|
2256
|
+
if (conn) {
|
|
2257
|
+
try {
|
|
2258
|
+
await this.release(conn);
|
|
2259
|
+
}
|
|
2260
|
+
catch (releaseErr) {
|
|
2261
|
+
// 记录但不抛出,避免掩盖原始错误
|
|
2262
|
+
koatty_logger.DefaultLogger.Error(`Failed to release connection: ${releaseErr.message}`);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2199
2265
|
}
|
|
2200
2266
|
}
|
|
2201
2267
|
/**
|
|
@@ -2291,15 +2357,8 @@ class CacheStore {
|
|
|
2291
2357
|
* @param timeout
|
|
2292
2358
|
*/
|
|
2293
2359
|
async hset(name, key, value, timeout) {
|
|
2294
|
-
|
|
2295
|
-
|
|
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;
|
|
2360
|
+
// MemoryStore 直接支持字段级 TTL,传递 timeout 参数
|
|
2361
|
+
return this.wrap('hset', [`${this.options.keyPrefix || ""}${name}`, key, value, timeout]);
|
|
2303
2362
|
}
|
|
2304
2363
|
/**
|
|
2305
2364
|
* 哈希获取
|
|
@@ -2308,15 +2367,8 @@ class CacheStore {
|
|
|
2308
2367
|
* @returns {*}
|
|
2309
2368
|
*/
|
|
2310
2369
|
hget(name, key) {
|
|
2311
|
-
|
|
2312
|
-
|
|
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
|
-
});
|
|
2370
|
+
// 直接调用底层实现,TTL 检查在 MemoryCache 中处理
|
|
2371
|
+
return this.wrap('hget', [`${this.options.keyPrefix || ""}${name}`, key]);
|
|
2320
2372
|
}
|
|
2321
2373
|
/**
|
|
2322
2374
|
* 查看哈希表 hashKey 中,给定域 key 是否存在
|
|
@@ -2325,15 +2377,8 @@ class CacheStore {
|
|
|
2325
2377
|
* @returns {*}
|
|
2326
2378
|
*/
|
|
2327
2379
|
hexists(name, key) {
|
|
2328
|
-
|
|
2329
|
-
|
|
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
|
-
});
|
|
2380
|
+
// 直接调用底层实现,TTL 检查在 MemoryCache 中处理
|
|
2381
|
+
return this.wrap('hexists', [`${this.options.keyPrefix || ""}${name}`, key]);
|
|
2337
2382
|
}
|
|
2338
2383
|
/**
|
|
2339
2384
|
* 哈希删除
|
|
@@ -2342,9 +2387,8 @@ class CacheStore {
|
|
|
2342
2387
|
* @returns {*}
|
|
2343
2388
|
*/
|
|
2344
2389
|
async hdel(name, key) {
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
return result;
|
|
2390
|
+
// 直接调用底层实现,TTL 清理在 MemoryCache 中处理
|
|
2391
|
+
return this.wrap('hdel', [`${this.options.keyPrefix || ""}${name}`, key]);
|
|
2348
2392
|
}
|
|
2349
2393
|
/**
|
|
2350
2394
|
* 返回哈希表 key 中域的数量
|
|
@@ -2511,5 +2555,6 @@ class CacheStore {
|
|
|
2511
2555
|
return this.wrap('smove', [`${this.options.keyPrefix || ""}${source}`, `${this.options.keyPrefix || ""}${destination}`, member]);
|
|
2512
2556
|
}
|
|
2513
2557
|
}
|
|
2558
|
+
CacheStore.instances = new Map();
|
|
2514
2559
|
|
|
2515
2560
|
exports.CacheStore = CacheStore;
|