@hazeljs/cache 0.2.0-beta.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/README.md +523 -0
- package/dist/cache.module.d.ts +81 -0
- package/dist/cache.module.d.ts.map +1 -0
- package/dist/cache.module.js +111 -0
- package/dist/cache.service.d.ts +109 -0
- package/dist/cache.service.d.ts.map +1 -0
- package/dist/cache.service.js +263 -0
- package/dist/cache.types.d.ts +180 -0
- package/dist/cache.types.d.ts.map +1 -0
- package/dist/cache.types.js +2 -0
- package/dist/decorators/cache.decorator.d.ts +93 -0
- package/dist/decorators/cache.decorator.d.ts.map +1 -0
- package/dist/decorators/cache.decorator.js +155 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/src/cache.module.d.ts +81 -0
- package/dist/src/cache.module.d.ts.map +1 -0
- package/dist/src/cache.module.js +111 -0
- package/dist/src/cache.service.d.ts +109 -0
- package/dist/src/cache.service.d.ts.map +1 -0
- package/dist/src/cache.service.js +263 -0
- package/dist/src/cache.types.d.ts +180 -0
- package/dist/src/cache.types.d.ts.map +1 -0
- package/dist/src/cache.types.js +2 -0
- package/dist/src/decorators/cache.decorator.d.ts +93 -0
- package/dist/src/decorators/cache.decorator.d.ts.map +1 -0
- package/dist/src/decorators/cache.decorator.js +155 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +26 -0
- package/dist/src/strategies/memory.strategy.d.ts +73 -0
- package/dist/src/strategies/memory.strategy.d.ts.map +1 -0
- package/dist/src/strategies/memory.strategy.js +236 -0
- package/dist/src/strategies/multi-tier.strategy.d.ts +69 -0
- package/dist/src/strategies/multi-tier.strategy.d.ts.map +1 -0
- package/dist/src/strategies/multi-tier.strategy.js +154 -0
- package/dist/src/strategies/redis.strategy.d.ts +61 -0
- package/dist/src/strategies/redis.strategy.d.ts.map +1 -0
- package/dist/src/strategies/redis.strategy.js +185 -0
- package/dist/strategies/memory.strategy.d.ts +73 -0
- package/dist/strategies/memory.strategy.d.ts.map +1 -0
- package/dist/strategies/memory.strategy.js +236 -0
- package/dist/strategies/multi-tier.strategy.d.ts +69 -0
- package/dist/strategies/multi-tier.strategy.d.ts.map +1 -0
- package/dist/strategies/multi-tier.strategy.js +154 -0
- package/dist/strategies/redis.strategy.d.ts +61 -0
- package/dist/strategies/redis.strategy.d.ts.map +1 -0
- package/dist/strategies/redis.strategy.js +185 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MultiTierCacheStore = void 0;
|
|
7
|
+
const memory_strategy_1 = require("./memory.strategy");
|
|
8
|
+
const redis_strategy_1 = require("./redis.strategy");
|
|
9
|
+
const core_1 = __importDefault(require("@hazeljs/core"));
|
|
10
|
+
/**
|
|
11
|
+
* Multi-tier cache store implementation
|
|
12
|
+
* Uses memory cache as L1 and Redis as L2
|
|
13
|
+
*/
|
|
14
|
+
class MultiTierCacheStore {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.l1Cache = new memory_strategy_1.MemoryCacheStore();
|
|
17
|
+
this.l2Cache = new redis_strategy_1.RedisCacheStore(options?.redis);
|
|
18
|
+
core_1.default.info('Multi-tier cache store initialized (L1: Memory, L2: Redis)');
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get a value from cache (L1 first, then L2)
|
|
22
|
+
*/
|
|
23
|
+
async get(key) {
|
|
24
|
+
// Try L1 cache first
|
|
25
|
+
let value = await this.l1Cache.get(key);
|
|
26
|
+
if (value !== null) {
|
|
27
|
+
core_1.default.debug(`Multi-tier cache hit (L1): ${key}`);
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
// Try L2 cache
|
|
31
|
+
value = await this.l2Cache.get(key);
|
|
32
|
+
if (value !== null) {
|
|
33
|
+
core_1.default.debug(`Multi-tier cache hit (L2): ${key}`);
|
|
34
|
+
// Promote to L1
|
|
35
|
+
await this.l1Cache.set(key, value, 300); // 5 minutes in L1
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
core_1.default.debug(`Multi-tier cache miss: ${key}`);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Set a value in cache (both L1 and L2)
|
|
43
|
+
*/
|
|
44
|
+
async set(key, value, ttl = 3600) {
|
|
45
|
+
// Set in both caches
|
|
46
|
+
await Promise.all([
|
|
47
|
+
this.l1Cache.set(key, value, Math.min(ttl, 300)), // Max 5 minutes in L1
|
|
48
|
+
this.l2Cache.set(key, value, ttl),
|
|
49
|
+
]);
|
|
50
|
+
core_1.default.debug(`Multi-tier cache set: ${key} (TTL: ${ttl}s)`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Set a value with tags
|
|
54
|
+
*/
|
|
55
|
+
async setWithTags(key, value, ttl, tags) {
|
|
56
|
+
await Promise.all([
|
|
57
|
+
this.l1Cache.setWithTags(key, value, Math.min(ttl, 300), tags),
|
|
58
|
+
this.l2Cache.setWithTags(key, value, ttl, tags),
|
|
59
|
+
]);
|
|
60
|
+
core_1.default.debug(`Multi-tier cache set with tags: ${key} (TTL: ${ttl}s, Tags: ${tags?.join(', ')})`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Delete a value from cache (both L1 and L2)
|
|
64
|
+
*/
|
|
65
|
+
async delete(key) {
|
|
66
|
+
await Promise.all([this.l1Cache.delete(key), this.l2Cache.delete(key)]);
|
|
67
|
+
core_1.default.debug(`Multi-tier cache deleted: ${key}`);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Delete entries by tag
|
|
71
|
+
*/
|
|
72
|
+
async deleteByTag(tag) {
|
|
73
|
+
await Promise.all([this.l1Cache.deleteByTag(tag), this.l2Cache.deleteByTag(tag)]);
|
|
74
|
+
core_1.default.debug(`Multi-tier cache invalidated by tag: ${tag}`);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if key exists in cache
|
|
78
|
+
*/
|
|
79
|
+
async has(key) {
|
|
80
|
+
const inL1 = await this.l1Cache.has(key);
|
|
81
|
+
if (inL1) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
return await this.l2Cache.has(key);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Clear all cache entries
|
|
88
|
+
*/
|
|
89
|
+
async clear() {
|
|
90
|
+
await Promise.all([this.l1Cache.clear(), this.l2Cache.clear()]);
|
|
91
|
+
core_1.default.info('Multi-tier cache cleared');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get all keys matching a pattern
|
|
95
|
+
*/
|
|
96
|
+
async keys(pattern) {
|
|
97
|
+
// Get keys from both caches and merge
|
|
98
|
+
const [l1Keys, l2Keys] = await Promise.all([
|
|
99
|
+
this.l1Cache.keys(pattern),
|
|
100
|
+
this.l2Cache.keys(pattern),
|
|
101
|
+
]);
|
|
102
|
+
// Merge and deduplicate
|
|
103
|
+
return Array.from(new Set([...l1Keys, ...l2Keys]));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get cache statistics
|
|
107
|
+
*/
|
|
108
|
+
async getStats() {
|
|
109
|
+
const [l1Stats, l2Stats] = await Promise.all([
|
|
110
|
+
this.l1Cache.getStats(),
|
|
111
|
+
this.l2Cache.getStats(),
|
|
112
|
+
]);
|
|
113
|
+
// Combine stats
|
|
114
|
+
const totalHits = l1Stats.hits + l2Stats.hits;
|
|
115
|
+
const totalMisses = l1Stats.misses + l2Stats.misses;
|
|
116
|
+
const total = totalHits + totalMisses;
|
|
117
|
+
const hitRate = total > 0 ? (totalHits / total) * 100 : 0;
|
|
118
|
+
return {
|
|
119
|
+
hits: totalHits,
|
|
120
|
+
misses: totalMisses,
|
|
121
|
+
hitRate: Math.round(hitRate * 100) / 100,
|
|
122
|
+
size: l1Stats.size + l2Stats.size,
|
|
123
|
+
memoryUsage: l1Stats.memoryUsage,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get L1 cache statistics
|
|
128
|
+
*/
|
|
129
|
+
async getL1Stats() {
|
|
130
|
+
return await this.l1Cache.getStats();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get L2 cache statistics
|
|
134
|
+
*/
|
|
135
|
+
async getL2Stats() {
|
|
136
|
+
return await this.l2Cache.getStats();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Reset statistics
|
|
140
|
+
*/
|
|
141
|
+
resetStats() {
|
|
142
|
+
this.l1Cache.resetStats();
|
|
143
|
+
this.l2Cache.resetStats();
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Destroy the cache store
|
|
147
|
+
*/
|
|
148
|
+
async destroy() {
|
|
149
|
+
this.l1Cache.destroy();
|
|
150
|
+
await this.l2Cache.disconnect();
|
|
151
|
+
core_1.default.info('Multi-tier cache destroyed');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.MultiTierCacheStore = MultiTierCacheStore;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ICacheStore, CacheStats } from '../cache.types';
|
|
2
|
+
/**
|
|
3
|
+
* Redis cache store implementation
|
|
4
|
+
* Note: This is a mock implementation. In production, you would use a real Redis client like ioredis
|
|
5
|
+
*/
|
|
6
|
+
export declare class RedisCacheStore implements ICacheStore {
|
|
7
|
+
private options?;
|
|
8
|
+
private mockStore;
|
|
9
|
+
private tagIndex;
|
|
10
|
+
private stats;
|
|
11
|
+
constructor(options?: {
|
|
12
|
+
host?: string;
|
|
13
|
+
port?: number;
|
|
14
|
+
password?: string;
|
|
15
|
+
} | undefined);
|
|
16
|
+
/**
|
|
17
|
+
* Get a value from cache
|
|
18
|
+
*/
|
|
19
|
+
get<T>(key: string): Promise<T | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Set a value in cache
|
|
22
|
+
*/
|
|
23
|
+
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Set a value with tags
|
|
26
|
+
*/
|
|
27
|
+
setWithTags<T>(key: string, value: T, ttl: number, tags?: string[]): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Delete a value from cache
|
|
30
|
+
*/
|
|
31
|
+
delete(key: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Delete entries by tag
|
|
34
|
+
*/
|
|
35
|
+
deleteByTag(tag: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if key exists in cache
|
|
38
|
+
*/
|
|
39
|
+
has(key: string): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Clear all cache entries
|
|
42
|
+
*/
|
|
43
|
+
clear(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Get all keys matching a pattern
|
|
46
|
+
*/
|
|
47
|
+
keys(pattern?: string): Promise<string[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Get cache statistics
|
|
50
|
+
*/
|
|
51
|
+
getStats(): Promise<CacheStats>;
|
|
52
|
+
/**
|
|
53
|
+
* Reset statistics
|
|
54
|
+
*/
|
|
55
|
+
resetStats(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Disconnect from Redis
|
|
58
|
+
*/
|
|
59
|
+
disconnect(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=redis.strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.strategy.d.ts","sourceRoot":"","sources":["../../src/strategies/redis.strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGzD;;;GAGG;AACH,qBAAa,eAAgB,YAAW,WAAW;IAQrC,OAAO,CAAC,OAAO,CAAC;IAP5B,OAAO,CAAC,SAAS,CAAgE;IACjF,OAAO,CAAC,QAAQ,CAAuC;IACvD,OAAO,CAAC,KAAK,CAGX;gBAEkB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,YAAA;IAMjF;;OAEG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IA4B5C;;OAEG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtE;;OAEG;IACG,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBxF;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxC;;OAEG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB7C;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAe/C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC;IAgBrC;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAQlC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.RedisCacheStore = void 0;
|
|
7
|
+
const core_1 = __importDefault(require("@hazeljs/core"));
|
|
8
|
+
/**
|
|
9
|
+
* Redis cache store implementation
|
|
10
|
+
* Note: This is a mock implementation. In production, you would use a real Redis client like ioredis
|
|
11
|
+
*/
|
|
12
|
+
class RedisCacheStore {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.options = options;
|
|
15
|
+
this.mockStore = new Map();
|
|
16
|
+
this.tagIndex = new Map();
|
|
17
|
+
this.stats = {
|
|
18
|
+
hits: 0,
|
|
19
|
+
misses: 0,
|
|
20
|
+
};
|
|
21
|
+
core_1.default.info('Redis cache store initialized (mock mode)');
|
|
22
|
+
// In production, initialize real Redis client here
|
|
23
|
+
// this.client = new Redis(options);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get a value from cache
|
|
27
|
+
*/
|
|
28
|
+
async get(key) {
|
|
29
|
+
const entry = this.mockStore.get(key);
|
|
30
|
+
if (!entry) {
|
|
31
|
+
this.stats.misses++;
|
|
32
|
+
core_1.default.debug(`Redis cache miss: ${key}`);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
// Check if expired
|
|
36
|
+
if (Date.now() > entry.expiresAt) {
|
|
37
|
+
this.mockStore.delete(key);
|
|
38
|
+
this.stats.misses++;
|
|
39
|
+
core_1.default.debug(`Redis cache expired: ${key}`);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
this.stats.hits++;
|
|
43
|
+
core_1.default.debug(`Redis cache hit: ${key}`);
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(entry.value);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
core_1.default.error(`Failed to parse cached value for key ${key}:`, error);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Set a value in cache
|
|
54
|
+
*/
|
|
55
|
+
async set(key, value, ttl = 3600) {
|
|
56
|
+
const serialized = JSON.stringify(value);
|
|
57
|
+
const expiresAt = Date.now() + ttl * 1000;
|
|
58
|
+
this.mockStore.set(key, { value: serialized, expiresAt });
|
|
59
|
+
core_1.default.debug(`Redis cache set: ${key} (TTL: ${ttl}s)`);
|
|
60
|
+
// In production:
|
|
61
|
+
// await this.client.setex(key, ttl, serialized);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Set a value with tags
|
|
65
|
+
*/
|
|
66
|
+
async setWithTags(key, value, ttl, tags) {
|
|
67
|
+
await this.set(key, value, ttl);
|
|
68
|
+
// Update tag index
|
|
69
|
+
if (tags) {
|
|
70
|
+
for (const tag of tags) {
|
|
71
|
+
if (!this.tagIndex.has(tag)) {
|
|
72
|
+
this.tagIndex.set(tag, new Set());
|
|
73
|
+
}
|
|
74
|
+
this.tagIndex.get(tag).add(key);
|
|
75
|
+
// In production, use Redis sets:
|
|
76
|
+
// await this.client.sadd(`tag:${tag}`, key);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
core_1.default.debug(`Redis cache set with tags: ${key} (TTL: ${ttl}s, Tags: ${tags?.join(', ')})`);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Delete a value from cache
|
|
83
|
+
*/
|
|
84
|
+
async delete(key) {
|
|
85
|
+
this.mockStore.delete(key);
|
|
86
|
+
core_1.default.debug(`Redis cache deleted: ${key}`);
|
|
87
|
+
// In production:
|
|
88
|
+
// await this.client.del(key);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Delete entries by tag
|
|
92
|
+
*/
|
|
93
|
+
async deleteByTag(tag) {
|
|
94
|
+
const keys = this.tagIndex.get(tag);
|
|
95
|
+
if (!keys) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (const key of keys) {
|
|
99
|
+
await this.delete(key);
|
|
100
|
+
}
|
|
101
|
+
this.tagIndex.delete(tag);
|
|
102
|
+
core_1.default.debug(`Redis cache invalidated by tag: ${tag} (${keys.size} entries)`);
|
|
103
|
+
// In production:
|
|
104
|
+
// const keys = await this.client.smembers(`tag:${tag}`);
|
|
105
|
+
// if (keys.length > 0) {
|
|
106
|
+
// await this.client.del(...keys);
|
|
107
|
+
// await this.client.del(`tag:${tag}`);
|
|
108
|
+
// }
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check if key exists in cache
|
|
112
|
+
*/
|
|
113
|
+
async has(key) {
|
|
114
|
+
const entry = this.mockStore.get(key);
|
|
115
|
+
if (!entry) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
// Check if expired
|
|
119
|
+
if (Date.now() > entry.expiresAt) {
|
|
120
|
+
this.mockStore.delete(key);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
// In production:
|
|
125
|
+
// return (await this.client.exists(key)) === 1;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Clear all cache entries
|
|
129
|
+
*/
|
|
130
|
+
async clear() {
|
|
131
|
+
this.mockStore.clear();
|
|
132
|
+
this.tagIndex.clear();
|
|
133
|
+
core_1.default.info('Redis cache cleared');
|
|
134
|
+
// In production:
|
|
135
|
+
// await this.client.flushdb();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get all keys matching a pattern
|
|
139
|
+
*/
|
|
140
|
+
async keys(pattern) {
|
|
141
|
+
const allKeys = Array.from(this.mockStore.keys());
|
|
142
|
+
if (!pattern) {
|
|
143
|
+
return allKeys;
|
|
144
|
+
}
|
|
145
|
+
// Simple pattern matching
|
|
146
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
147
|
+
return allKeys.filter((key) => regex.test(key));
|
|
148
|
+
// In production:
|
|
149
|
+
// return await this.client.keys(pattern || '*');
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get cache statistics
|
|
153
|
+
*/
|
|
154
|
+
async getStats() {
|
|
155
|
+
const total = this.stats.hits + this.stats.misses;
|
|
156
|
+
const hitRate = total > 0 ? (this.stats.hits / total) * 100 : 0;
|
|
157
|
+
return {
|
|
158
|
+
hits: this.stats.hits,
|
|
159
|
+
misses: this.stats.misses,
|
|
160
|
+
hitRate: Math.round(hitRate * 100) / 100,
|
|
161
|
+
size: this.mockStore.size,
|
|
162
|
+
};
|
|
163
|
+
// In production:
|
|
164
|
+
// const info = await this.client.info('stats');
|
|
165
|
+
// Parse info and return stats
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Reset statistics
|
|
169
|
+
*/
|
|
170
|
+
resetStats() {
|
|
171
|
+
this.stats.hits = 0;
|
|
172
|
+
this.stats.misses = 0;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Disconnect from Redis
|
|
176
|
+
*/
|
|
177
|
+
async disconnect() {
|
|
178
|
+
this.mockStore.clear();
|
|
179
|
+
this.tagIndex.clear();
|
|
180
|
+
core_1.default.info('Redis cache disconnected');
|
|
181
|
+
// In production:
|
|
182
|
+
// await this.client.quit();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.RedisCacheStore = RedisCacheStore;
|