koatty_store 1.8.0 → 1.9.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/CHANGELOG.md +50 -0
- 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 +5 -3
- package/package.json +5 -3
- package/.vscode/launch.json +0 -25
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @Author: richen
|
|
3
|
-
* @Date: 2025-
|
|
3
|
+
* @Date: 2025-11-02 08:37:33
|
|
4
4
|
* @License: BSD (3-Clause)
|
|
5
5
|
* @Copyright (c) - <richenlin(at)gmail.com>
|
|
6
6
|
* @HomePage: https://koatty.org/
|
|
@@ -9,6 +9,7 @@ import { flatten, isNil, union, isUndefined } from 'lodash';
|
|
|
9
9
|
import * as helper from 'koatty_lib';
|
|
10
10
|
import { EventEmitter } from 'events';
|
|
11
11
|
import { LRUCache } from 'lru-cache';
|
|
12
|
+
import AsyncLock from 'async-lock';
|
|
12
13
|
import { DefaultLogger } from 'koatty_logger';
|
|
13
14
|
import { Cluster, Redis } from 'ioredis';
|
|
14
15
|
import genericPool from 'generic-pool';
|
|
@@ -50,15 +51,6 @@ var messages;
|
|
|
50
51
|
messages["mutuallyExclusiveNXXX"] = "ERR XX and NX options at the same time are not compatible";
|
|
51
52
|
})(messages || (messages = {}));
|
|
52
53
|
class MemoryCache extends EventEmitter {
|
|
53
|
-
databases = new Map();
|
|
54
|
-
options;
|
|
55
|
-
currentDBIndex;
|
|
56
|
-
connected;
|
|
57
|
-
lastSave;
|
|
58
|
-
multiMode;
|
|
59
|
-
cache;
|
|
60
|
-
responseMessages;
|
|
61
|
-
ttlCheckTimer = null;
|
|
62
54
|
/**
|
|
63
55
|
* Creates an instance of MemoryCache.
|
|
64
56
|
* @param {MemoryCacheOptions} options
|
|
@@ -66,6 +58,8 @@ class MemoryCache extends EventEmitter {
|
|
|
66
58
|
*/
|
|
67
59
|
constructor(options) {
|
|
68
60
|
super();
|
|
61
|
+
this.databases = new Map();
|
|
62
|
+
this.ttlCheckTimer = null;
|
|
69
63
|
this.options = {
|
|
70
64
|
database: 0,
|
|
71
65
|
maxKeys: 1000,
|
|
@@ -79,6 +73,7 @@ class MemoryCache extends EventEmitter {
|
|
|
79
73
|
this.lastSave = Date.now();
|
|
80
74
|
this.multiMode = false;
|
|
81
75
|
this.responseMessages = [];
|
|
76
|
+
this.lock = new AsyncLock();
|
|
82
77
|
// 初始化数据库和缓存
|
|
83
78
|
if (!this.databases.has(this.currentDBIndex)) {
|
|
84
79
|
this.databases.set(this.currentDBIndex, this.createLRUCache());
|
|
@@ -142,6 +137,15 @@ class MemoryCache extends EventEmitter {
|
|
|
142
137
|
this.ttlCheckTimer = null;
|
|
143
138
|
}
|
|
144
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* 清理所有资源
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
cleanup() {
|
|
145
|
+
this.stopTTLCheck();
|
|
146
|
+
this.databases.clear();
|
|
147
|
+
this.removeAllListeners();
|
|
148
|
+
}
|
|
145
149
|
/**
|
|
146
150
|
*
|
|
147
151
|
*
|
|
@@ -167,8 +171,8 @@ class MemoryCache extends EventEmitter {
|
|
|
167
171
|
* @memberof MemoryCache
|
|
168
172
|
*/
|
|
169
173
|
quit() {
|
|
174
|
+
this.cleanup(); // 调用清理方法
|
|
170
175
|
this.connected = false;
|
|
171
|
-
this.stopTTLCheck();
|
|
172
176
|
// exit multi mode if we are in it
|
|
173
177
|
this.discard(null, true);
|
|
174
178
|
this.emit('end');
|
|
@@ -431,14 +435,18 @@ class MemoryCache extends EventEmitter {
|
|
|
431
435
|
* @memberof MemoryCache
|
|
432
436
|
*/
|
|
433
437
|
incr(key, callback) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
438
|
+
// 使用锁保护原子操作
|
|
439
|
+
const lockKey = `incr:${key}`;
|
|
440
|
+
return this.lock.acquire(lockKey, () => {
|
|
441
|
+
let retVal = null;
|
|
442
|
+
try {
|
|
443
|
+
retVal = this._addToKey(key, 1);
|
|
444
|
+
}
|
|
445
|
+
catch (err) {
|
|
446
|
+
return this._handleCallback(callback, null, err);
|
|
447
|
+
}
|
|
448
|
+
return this._handleCallback(callback, retVal);
|
|
449
|
+
});
|
|
442
450
|
}
|
|
443
451
|
/**
|
|
444
452
|
*
|
|
@@ -450,14 +458,18 @@ class MemoryCache extends EventEmitter {
|
|
|
450
458
|
* @memberof MemoryCache
|
|
451
459
|
*/
|
|
452
460
|
incrby(key, amount, callback) {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
+
// 使用锁保护原子操作
|
|
462
|
+
const lockKey = `incrby:${key}`;
|
|
463
|
+
return this.lock.acquire(lockKey, () => {
|
|
464
|
+
let retVal = null;
|
|
465
|
+
try {
|
|
466
|
+
retVal = this._addToKey(key, amount);
|
|
467
|
+
}
|
|
468
|
+
catch (err) {
|
|
469
|
+
return this._handleCallback(callback, null, err);
|
|
470
|
+
}
|
|
471
|
+
return this._handleCallback(callback, retVal);
|
|
472
|
+
});
|
|
461
473
|
}
|
|
462
474
|
/**
|
|
463
475
|
*
|
|
@@ -468,14 +480,18 @@ class MemoryCache extends EventEmitter {
|
|
|
468
480
|
* @memberof MemoryCache
|
|
469
481
|
*/
|
|
470
482
|
decr(key, callback) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
483
|
+
// 使用锁保护原子操作
|
|
484
|
+
const lockKey = `decr:${key}`;
|
|
485
|
+
return this.lock.acquire(lockKey, () => {
|
|
486
|
+
let retVal = null;
|
|
487
|
+
try {
|
|
488
|
+
retVal = this._addToKey(key, -1);
|
|
489
|
+
}
|
|
490
|
+
catch (err) {
|
|
491
|
+
return this._handleCallback(callback, null, err);
|
|
492
|
+
}
|
|
493
|
+
return this._handleCallback(callback, retVal);
|
|
494
|
+
});
|
|
479
495
|
}
|
|
480
496
|
/**
|
|
481
497
|
*
|
|
@@ -487,19 +503,23 @@ class MemoryCache extends EventEmitter {
|
|
|
487
503
|
* @memberof MemoryCache
|
|
488
504
|
*/
|
|
489
505
|
decrby(key, amount, callback) {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
506
|
+
// 使用锁保护原子操作
|
|
507
|
+
const lockKey = `decrby:${key}`;
|
|
508
|
+
return this.lock.acquire(lockKey, () => {
|
|
509
|
+
let retVal = null;
|
|
510
|
+
try {
|
|
511
|
+
retVal = this._addToKey(key, 0 - amount);
|
|
512
|
+
}
|
|
513
|
+
catch (err) {
|
|
514
|
+
return this._handleCallback(callback, null, err);
|
|
515
|
+
}
|
|
516
|
+
return this._handleCallback(callback, retVal);
|
|
517
|
+
});
|
|
498
518
|
}
|
|
499
519
|
// ---------------------------------------
|
|
500
520
|
// ## Hash ##
|
|
501
521
|
// ---------------------------------------
|
|
502
|
-
hset(key, field, value, callback) {
|
|
522
|
+
hset(key, field, value, timeout, callback) {
|
|
503
523
|
let retVal = 0;
|
|
504
524
|
if (this._hasKey(key)) {
|
|
505
525
|
this._testType(key, 'hash', true, callback);
|
|
@@ -511,6 +531,14 @@ class MemoryCache extends EventEmitter {
|
|
|
511
531
|
retVal = 1;
|
|
512
532
|
}
|
|
513
533
|
this._setField(key, field, value.toString());
|
|
534
|
+
// 如果指定了 timeout,存储字段级别的过期时间
|
|
535
|
+
if (typeof timeout === 'number') {
|
|
536
|
+
const hashObj = this.cache.get(key).value;
|
|
537
|
+
if (!hashObj._fieldTTL) {
|
|
538
|
+
hashObj._fieldTTL = {};
|
|
539
|
+
}
|
|
540
|
+
hashObj._fieldTTL[field] = Date.now() + (timeout * 1000);
|
|
541
|
+
}
|
|
514
542
|
this.persist(key);
|
|
515
543
|
return this._handleCallback(callback, retVal);
|
|
516
544
|
}
|
|
@@ -527,8 +555,17 @@ class MemoryCache extends EventEmitter {
|
|
|
527
555
|
let retVal = null;
|
|
528
556
|
if (this._hasKey(key)) {
|
|
529
557
|
this._testType(key, 'hash', true, callback);
|
|
558
|
+
// 检查字段级别的过期时间
|
|
559
|
+
const hashObj = this._getKey(key);
|
|
560
|
+
if (hashObj && hashObj._fieldTTL && hashObj._fieldTTL[field]) {
|
|
561
|
+
if (hashObj._fieldTTL[field] <= Date.now()) {
|
|
562
|
+
// 过期,删除字段
|
|
563
|
+
this.hdel(key, field);
|
|
564
|
+
return this._handleCallback(callback, null);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
530
567
|
if (this._hasField(key, field)) {
|
|
531
|
-
retVal =
|
|
568
|
+
retVal = hashObj[field];
|
|
532
569
|
}
|
|
533
570
|
}
|
|
534
571
|
return this._handleCallback(callback, retVal);
|
|
@@ -546,6 +583,15 @@ class MemoryCache extends EventEmitter {
|
|
|
546
583
|
let retVal = 0;
|
|
547
584
|
if (this._hasKey(key)) {
|
|
548
585
|
this._testType(key, 'hash', true, callback);
|
|
586
|
+
// 检查字段级别的过期时间
|
|
587
|
+
const hashObj = this._getKey(key);
|
|
588
|
+
if (hashObj && hashObj._fieldTTL && hashObj._fieldTTL[field]) {
|
|
589
|
+
if (hashObj._fieldTTL[field] <= Date.now()) {
|
|
590
|
+
// 过期,删除字段
|
|
591
|
+
this.hdel(key, field);
|
|
592
|
+
return this._handleCallback(callback, 0);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
549
595
|
if (this._hasField(key, field)) {
|
|
550
596
|
retVal = 1;
|
|
551
597
|
}
|
|
@@ -565,10 +611,15 @@ class MemoryCache extends EventEmitter {
|
|
|
565
611
|
const callback = this._retrieveCallback(fields);
|
|
566
612
|
if (this._hasKey(key)) {
|
|
567
613
|
this._testType(key, 'hash', true, callback);
|
|
614
|
+
const hashObj = this.cache.get(key).value;
|
|
568
615
|
for (let itr = 0; itr < fields.length; itr++) {
|
|
569
616
|
const field = fields[itr];
|
|
570
617
|
if (this._hasField(key, field)) {
|
|
571
|
-
delete
|
|
618
|
+
delete hashObj[field];
|
|
619
|
+
// 清理过期时间记录
|
|
620
|
+
if (hashObj._fieldTTL && hashObj._fieldTTL[field]) {
|
|
621
|
+
delete hashObj._fieldTTL[field];
|
|
622
|
+
}
|
|
572
623
|
retVal++;
|
|
573
624
|
}
|
|
574
625
|
}
|
|
@@ -598,14 +649,18 @@ class MemoryCache extends EventEmitter {
|
|
|
598
649
|
* @memberof MemoryCache
|
|
599
650
|
*/
|
|
600
651
|
hincrby(key, field, value, callback) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
652
|
+
// 使用锁保护原子操作,使用组合 key
|
|
653
|
+
const lockKey = `hincrby:${key}:${field}`;
|
|
654
|
+
return this.lock.acquire(lockKey, () => {
|
|
655
|
+
let retVal;
|
|
656
|
+
try {
|
|
657
|
+
retVal = this._addToField(key, field, value, false);
|
|
658
|
+
}
|
|
659
|
+
catch (err) {
|
|
660
|
+
return this._handleCallback(callback, null, err);
|
|
661
|
+
}
|
|
662
|
+
return this._handleCallback(callback, retVal);
|
|
663
|
+
});
|
|
609
664
|
}
|
|
610
665
|
/**
|
|
611
666
|
*
|
|
@@ -619,7 +674,11 @@ class MemoryCache extends EventEmitter {
|
|
|
619
674
|
let retVals = {};
|
|
620
675
|
if (this._hasKey(key)) {
|
|
621
676
|
this._testType(key, 'hash', true, callback);
|
|
622
|
-
|
|
677
|
+
const hashObj = this._getKey(key);
|
|
678
|
+
// 排除内部属性 _fieldTTL
|
|
679
|
+
retVals = Object.entries(hashObj)
|
|
680
|
+
.filter(([k]) => k !== '_fieldTTL')
|
|
681
|
+
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
|
|
623
682
|
}
|
|
624
683
|
return this._handleCallback(callback, retVals);
|
|
625
684
|
}
|
|
@@ -635,7 +694,9 @@ class MemoryCache extends EventEmitter {
|
|
|
635
694
|
let retVals = [];
|
|
636
695
|
if (this._hasKey(key)) {
|
|
637
696
|
this._testType(key, 'hash', true, callback);
|
|
638
|
-
|
|
697
|
+
const hashObj = this._getKey(key);
|
|
698
|
+
// 排除内部属性 _fieldTTL
|
|
699
|
+
retVals = Object.keys(hashObj).filter(k => k !== '_fieldTTL');
|
|
639
700
|
}
|
|
640
701
|
return this._handleCallback(callback, retVals);
|
|
641
702
|
}
|
|
@@ -651,7 +712,11 @@ class MemoryCache extends EventEmitter {
|
|
|
651
712
|
let retVals = [];
|
|
652
713
|
if (this._hasKey(key)) {
|
|
653
714
|
this._testType(key, 'hash', true, callback);
|
|
654
|
-
|
|
715
|
+
const hashObj = this._getKey(key);
|
|
716
|
+
// 排除内部属性 _fieldTTL 的值
|
|
717
|
+
retVals = Object.entries(hashObj)
|
|
718
|
+
.filter(([k]) => k !== '_fieldTTL')
|
|
719
|
+
.map(([, v]) => v);
|
|
655
720
|
}
|
|
656
721
|
return this._handleCallback(callback, retVals);
|
|
657
722
|
}
|
|
@@ -802,8 +867,7 @@ class MemoryCache extends EventEmitter {
|
|
|
802
867
|
stop = length - 1;
|
|
803
868
|
}
|
|
804
869
|
if (stop >= 0 && stop >= start) {
|
|
805
|
-
|
|
806
|
-
for (let itr = start; itr < size; itr++) {
|
|
870
|
+
for (let itr = start; itr <= stop; itr++) {
|
|
807
871
|
retVal.push(list[itr]);
|
|
808
872
|
}
|
|
809
873
|
}
|
|
@@ -1010,9 +1074,10 @@ class MemoryCache extends EventEmitter {
|
|
|
1010
1074
|
* @memberof MemoryCache
|
|
1011
1075
|
*/
|
|
1012
1076
|
pttl(key, _callback) {
|
|
1077
|
+
var _a;
|
|
1013
1078
|
let retVal = -2;
|
|
1014
1079
|
if (this._hasKey(key)) {
|
|
1015
|
-
if (!isNil(this.cache.get(key)
|
|
1080
|
+
if (!isNil((_a = this.cache.get(key)) === null || _a === void 0 ? void 0 : _a.timeout)) {
|
|
1016
1081
|
retVal = this.cache.get(key).timeout - Date.now();
|
|
1017
1082
|
// Prevent unexpected errors if the actual ttl just happens to be -2 or -1
|
|
1018
1083
|
if (retVal < 0 && retVal > -3) {
|
|
@@ -1221,7 +1286,8 @@ class MemoryCache extends EventEmitter {
|
|
|
1221
1286
|
if (key && field) {
|
|
1222
1287
|
const ky = this._getKey(key);
|
|
1223
1288
|
if (ky) {
|
|
1224
|
-
|
|
1289
|
+
// 排除内部属性 _fieldTTL
|
|
1290
|
+
retVal = field !== '_fieldTTL' && ky.hasOwnProperty(field);
|
|
1225
1291
|
}
|
|
1226
1292
|
}
|
|
1227
1293
|
return retVal;
|
|
@@ -1630,9 +1696,6 @@ class MemoryCache extends EventEmitter {
|
|
|
1630
1696
|
* @LastEditTime: 2023-02-18 23:52:47
|
|
1631
1697
|
*/
|
|
1632
1698
|
class MemoryStore {
|
|
1633
|
-
client;
|
|
1634
|
-
pool;
|
|
1635
|
-
options;
|
|
1636
1699
|
/**
|
|
1637
1700
|
* Creates an instance of MemoryStore.
|
|
1638
1701
|
* @param {MemoryStoreOpt} options
|
|
@@ -1645,7 +1708,15 @@ class MemoryStore {
|
|
|
1645
1708
|
ttlCheckInterval: 60000, // 1分钟
|
|
1646
1709
|
...options
|
|
1647
1710
|
};
|
|
1648
|
-
|
|
1711
|
+
// 直接创建 MemoryCache 实例
|
|
1712
|
+
this.client = new MemoryCache({
|
|
1713
|
+
database: this.options.db || 0,
|
|
1714
|
+
maxKeys: this.options.maxKeys,
|
|
1715
|
+
maxMemory: this.options.maxMemory,
|
|
1716
|
+
evictionPolicy: this.options.evictionPolicy,
|
|
1717
|
+
ttlCheckInterval: this.options.ttlCheckInterval
|
|
1718
|
+
});
|
|
1719
|
+
this.client.createClient();
|
|
1649
1720
|
}
|
|
1650
1721
|
/**
|
|
1651
1722
|
* getConnection
|
|
@@ -1654,19 +1725,7 @@ class MemoryStore {
|
|
|
1654
1725
|
* @memberof MemoryStore
|
|
1655
1726
|
*/
|
|
1656
1727
|
getConnection() {
|
|
1657
|
-
|
|
1658
|
-
this.pool = new MemoryCache({
|
|
1659
|
-
database: this.options.db || 0,
|
|
1660
|
-
maxKeys: this.options.maxKeys,
|
|
1661
|
-
maxMemory: this.options.maxMemory,
|
|
1662
|
-
evictionPolicy: this.options.evictionPolicy,
|
|
1663
|
-
ttlCheckInterval: this.options.ttlCheckInterval
|
|
1664
|
-
});
|
|
1665
|
-
}
|
|
1666
|
-
if (!this.client) {
|
|
1667
|
-
this.client = this.pool.createClient();
|
|
1668
|
-
this.client.status = "ready";
|
|
1669
|
-
}
|
|
1728
|
+
// 直接返回 MemoryCache 实例
|
|
1670
1729
|
return this.client;
|
|
1671
1730
|
}
|
|
1672
1731
|
/**
|
|
@@ -1689,7 +1748,8 @@ class MemoryStore {
|
|
|
1689
1748
|
* @memberof MemoryStore
|
|
1690
1749
|
*/
|
|
1691
1750
|
async release(_conn) {
|
|
1692
|
-
|
|
1751
|
+
// 对于内存存储,不需要释放连接
|
|
1752
|
+
return Promise.resolve();
|
|
1693
1753
|
}
|
|
1694
1754
|
/**
|
|
1695
1755
|
* defineCommand
|
|
@@ -1753,18 +1813,15 @@ class MemoryStore {
|
|
|
1753
1813
|
* @class RedisStore
|
|
1754
1814
|
*/
|
|
1755
1815
|
class RedisStore {
|
|
1756
|
-
options;
|
|
1757
|
-
pool;
|
|
1758
|
-
client;
|
|
1759
|
-
reconnectAttempts = 0;
|
|
1760
|
-
maxReconnectAttempts = 5;
|
|
1761
|
-
reconnectDelay = 1000; // 初始重连延迟1秒
|
|
1762
1816
|
/**
|
|
1763
1817
|
* Creates an instance of RedisStore.
|
|
1764
1818
|
* @param {RedisStoreOpt} options
|
|
1765
1819
|
* @memberof RedisStore
|
|
1766
1820
|
*/
|
|
1767
1821
|
constructor(options) {
|
|
1822
|
+
this.reconnectAttempts = 0;
|
|
1823
|
+
this.maxReconnectAttempts = 5;
|
|
1824
|
+
this.reconnectDelay = 1000; // 初始重连延迟1秒
|
|
1768
1825
|
this.options = this.parseOpt(options);
|
|
1769
1826
|
this.pool = null;
|
|
1770
1827
|
}
|
|
@@ -2051,9 +2108,6 @@ const defaultOptions = {
|
|
|
2051
2108
|
* @class Store
|
|
2052
2109
|
*/
|
|
2053
2110
|
class CacheStore {
|
|
2054
|
-
client;
|
|
2055
|
-
options;
|
|
2056
|
-
static instances = new Map();
|
|
2057
2111
|
/**
|
|
2058
2112
|
* Creates an instance of CacheStore.
|
|
2059
2113
|
* @param {StoreOptions} options
|
|
@@ -2171,10 +2225,22 @@ class CacheStore {
|
|
|
2171
2225
|
return res;
|
|
2172
2226
|
}
|
|
2173
2227
|
catch (err) {
|
|
2174
|
-
|
|
2228
|
+
// 添加详细的错误信息
|
|
2229
|
+
const error = new Error(`Cache operation failed: ${name}(${data.slice(0, 2).join(', ')}${data.length > 2 ? '...' : ''}) - ${err.message}`);
|
|
2230
|
+
error.stack = err.stack;
|
|
2231
|
+
throw error;
|
|
2175
2232
|
}
|
|
2176
2233
|
finally {
|
|
2177
|
-
|
|
2234
|
+
// 安全地释放连接
|
|
2235
|
+
if (conn) {
|
|
2236
|
+
try {
|
|
2237
|
+
await this.release(conn);
|
|
2238
|
+
}
|
|
2239
|
+
catch (releaseErr) {
|
|
2240
|
+
// 记录但不抛出,避免掩盖原始错误
|
|
2241
|
+
DefaultLogger.Error(`Failed to release connection: ${releaseErr.message}`);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2178
2244
|
}
|
|
2179
2245
|
}
|
|
2180
2246
|
/**
|
|
@@ -2270,15 +2336,8 @@ class CacheStore {
|
|
|
2270
2336
|
* @param timeout
|
|
2271
2337
|
*/
|
|
2272
2338
|
async hset(name, key, value, timeout) {
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
await this.set(`${name}:${key}_ex`, 1, timeout);
|
|
2276
|
-
}
|
|
2277
|
-
else {
|
|
2278
|
-
// 如果没有指定timeout,设置一个永久标记,避免hget时误删
|
|
2279
|
-
await this.set(`${name}:${key}_ex`, 1);
|
|
2280
|
-
}
|
|
2281
|
-
return result;
|
|
2339
|
+
// MemoryStore 直接支持字段级 TTL,传递 timeout 参数
|
|
2340
|
+
return this.wrap('hset', [`${this.options.keyPrefix || ""}${name}`, key, value, timeout]);
|
|
2282
2341
|
}
|
|
2283
2342
|
/**
|
|
2284
2343
|
* 哈希获取
|
|
@@ -2287,15 +2346,8 @@ class CacheStore {
|
|
|
2287
2346
|
* @returns {*}
|
|
2288
2347
|
*/
|
|
2289
2348
|
hget(name, key) {
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
return Promise.all(setP).then(dataArr => {
|
|
2293
|
-
if (dataArr[0] === null) {
|
|
2294
|
-
this.hdel(name, key);
|
|
2295
|
-
return null;
|
|
2296
|
-
}
|
|
2297
|
-
return dataArr[1] || null;
|
|
2298
|
-
});
|
|
2349
|
+
// 直接调用底层实现,TTL 检查在 MemoryCache 中处理
|
|
2350
|
+
return this.wrap('hget', [`${this.options.keyPrefix || ""}${name}`, key]);
|
|
2299
2351
|
}
|
|
2300
2352
|
/**
|
|
2301
2353
|
* 查看哈希表 hashKey 中,给定域 key 是否存在
|
|
@@ -2304,15 +2356,8 @@ class CacheStore {
|
|
|
2304
2356
|
* @returns {*}
|
|
2305
2357
|
*/
|
|
2306
2358
|
hexists(name, key) {
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
return Promise.all(setP).then(dataArr => {
|
|
2310
|
-
if (dataArr[0] === null) {
|
|
2311
|
-
this.hdel(name, key);
|
|
2312
|
-
return 0;
|
|
2313
|
-
}
|
|
2314
|
-
return Number(dataArr[1]) || 0;
|
|
2315
|
-
});
|
|
2359
|
+
// 直接调用底层实现,TTL 检查在 MemoryCache 中处理
|
|
2360
|
+
return this.wrap('hexists', [`${this.options.keyPrefix || ""}${name}`, key]);
|
|
2316
2361
|
}
|
|
2317
2362
|
/**
|
|
2318
2363
|
* 哈希删除
|
|
@@ -2321,9 +2366,8 @@ class CacheStore {
|
|
|
2321
2366
|
* @returns {*}
|
|
2322
2367
|
*/
|
|
2323
2368
|
async hdel(name, key) {
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
return result;
|
|
2369
|
+
// 直接调用底层实现,TTL 清理在 MemoryCache 中处理
|
|
2370
|
+
return this.wrap('hdel', [`${this.options.keyPrefix || ""}${name}`, key]);
|
|
2327
2371
|
}
|
|
2328
2372
|
/**
|
|
2329
2373
|
* 返回哈希表 key 中域的数量
|
|
@@ -2490,5 +2534,6 @@ class CacheStore {
|
|
|
2490
2534
|
return this.wrap('smove', [`${this.options.keyPrefix || ""}${source}`, `${this.options.keyPrefix || ""}${destination}`, member]);
|
|
2491
2535
|
}
|
|
2492
2536
|
}
|
|
2537
|
+
CacheStore.instances = new Map();
|
|
2493
2538
|
|
|
2494
2539
|
export { CacheStore };
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koatty_store",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Cache store for koatty.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npm run build:js && npm run build:dts && npm run build:doc && npm run build:cp",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"@rollup/plugin-commonjs": "^28.x.x",
|
|
66
66
|
"@rollup/plugin-json": "^6.x.x",
|
|
67
67
|
"@rollup/plugin-node-resolve": "^15.x.x",
|
|
68
|
+
"@types/async-lock": "^1.4.2",
|
|
68
69
|
"@types/jest": "^29.x.x",
|
|
69
70
|
"@types/koa": "^2.x.x",
|
|
70
71
|
"@types/lodash": "^4.x.x",
|
|
@@ -91,12 +92,13 @@
|
|
|
91
92
|
"typescript": "^5.x.x"
|
|
92
93
|
},
|
|
93
94
|
"dependencies": {
|
|
95
|
+
"async-lock": "^1.4.1",
|
|
94
96
|
"generic-pool": "^3.9.0",
|
|
95
|
-
"ioredis": "^5.
|
|
97
|
+
"ioredis": "^5.8.2",
|
|
96
98
|
"koatty_lib": "^1.x.x",
|
|
97
99
|
"koatty_logger": "^2.x.x",
|
|
98
100
|
"lodash": "^4.17.21",
|
|
99
|
-
"lru-cache": "^11.
|
|
101
|
+
"lru-cache": "^11.2.2"
|
|
100
102
|
},
|
|
101
103
|
"peerDependencies": {
|
|
102
104
|
"koatty_lib": "^1.x.x",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koatty_store",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Cache store for koatty.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npm run build:js && npm run build:dts && npm run build:doc && npm run build:cp",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"@rollup/plugin-commonjs": "^28.x.x",
|
|
66
66
|
"@rollup/plugin-json": "^6.x.x",
|
|
67
67
|
"@rollup/plugin-node-resolve": "^15.x.x",
|
|
68
|
+
"@types/async-lock": "^1.4.2",
|
|
68
69
|
"@types/jest": "^29.x.x",
|
|
69
70
|
"@types/koa": "^2.x.x",
|
|
70
71
|
"@types/lodash": "^4.x.x",
|
|
@@ -91,12 +92,13 @@
|
|
|
91
92
|
"typescript": "^5.x.x"
|
|
92
93
|
},
|
|
93
94
|
"dependencies": {
|
|
95
|
+
"async-lock": "^1.4.1",
|
|
94
96
|
"generic-pool": "^3.9.0",
|
|
95
|
-
"ioredis": "^5.
|
|
97
|
+
"ioredis": "^5.8.2",
|
|
96
98
|
"koatty_lib": "^1.x.x",
|
|
97
99
|
"koatty_logger": "^2.x.x",
|
|
98
100
|
"lodash": "^4.17.21",
|
|
99
|
-
"lru-cache": "^11.
|
|
101
|
+
"lru-cache": "^11.2.2"
|
|
100
102
|
},
|
|
101
103
|
"peerDependencies": {
|
|
102
104
|
"koatty_lib": "^1.x.x",
|
package/.vscode/launch.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
// 使用 IntelliSense 了解相关属性。
|
|
3
|
-
// 悬停以查看现有属性的描述。
|
|
4
|
-
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
-
"version": "0.2.0",
|
|
6
|
-
"configurations": [
|
|
7
|
-
{
|
|
8
|
-
"name": "Launch Typescript Project",
|
|
9
|
-
"type": "node",
|
|
10
|
-
"request": "launch",
|
|
11
|
-
"args": [
|
|
12
|
-
"${workspaceRoot}/test/test.ts"
|
|
13
|
-
],
|
|
14
|
-
"runtimeArgs": [
|
|
15
|
-
"--nolazy",
|
|
16
|
-
"-r",
|
|
17
|
-
"ts-node/register"
|
|
18
|
-
],
|
|
19
|
-
"sourceMaps": true,
|
|
20
|
-
"cwd": "${workspaceRoot}",
|
|
21
|
-
"protocol": "inspector",
|
|
22
|
-
"internalConsoleOptions": "neverOpen"
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
}
|