@hazeljs/cache 0.2.0-alpha.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/LICENSE +192 -0
- package/README.md +524 -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.test.d.ts +2 -0
- package/dist/cache.test.d.ts.map +1 -0
- package/dist/cache.test.js +374 -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/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/package.json +54 -0
|
@@ -0,0 +1,155 @@
|
|
|
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.Cache = Cache;
|
|
7
|
+
exports.getCacheMetadata = getCacheMetadata;
|
|
8
|
+
exports.hasCacheMetadata = hasCacheMetadata;
|
|
9
|
+
exports.CacheKey = CacheKey;
|
|
10
|
+
exports.CacheTTL = CacheTTL;
|
|
11
|
+
exports.CacheTags = CacheTags;
|
|
12
|
+
exports.CacheEvict = CacheEvict;
|
|
13
|
+
exports.getCacheEvictMetadata = getCacheEvictMetadata;
|
|
14
|
+
require("reflect-metadata");
|
|
15
|
+
const core_1 = __importDefault(require("@hazeljs/core"));
|
|
16
|
+
const CACHE_METADATA_KEY = 'hazel:cache';
|
|
17
|
+
/**
|
|
18
|
+
* Cache decorator for methods
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* @Cache({
|
|
23
|
+
* strategy: 'memory',
|
|
24
|
+
* ttl: 3600,
|
|
25
|
+
* key: 'user-{id}',
|
|
26
|
+
* tags: ['users']
|
|
27
|
+
* })
|
|
28
|
+
* @Get('/users/:id')
|
|
29
|
+
* async getUser(@Param('id') id: string) {
|
|
30
|
+
* return this.userService.findById(id);
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function Cache(options = {}) {
|
|
35
|
+
return (target, propertyKey, descriptor) => {
|
|
36
|
+
const defaults = {
|
|
37
|
+
strategy: 'memory',
|
|
38
|
+
ttl: 3600,
|
|
39
|
+
ttlStrategy: 'absolute',
|
|
40
|
+
cacheNull: false,
|
|
41
|
+
...options,
|
|
42
|
+
};
|
|
43
|
+
// Store metadata
|
|
44
|
+
Reflect.defineMetadata(CACHE_METADATA_KEY, defaults, target, propertyKey);
|
|
45
|
+
core_1.default.debug(`Cache decorator applied to ${target.constructor.name}.${String(propertyKey)}`);
|
|
46
|
+
// Note: The actual caching logic will be implemented by the CacheInterceptor
|
|
47
|
+
// This decorator just marks the method and stores configuration
|
|
48
|
+
return descriptor;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get cache metadata from a method
|
|
53
|
+
*/
|
|
54
|
+
function getCacheMetadata(target, propertyKey) {
|
|
55
|
+
return Reflect.getMetadata(CACHE_METADATA_KEY, target, propertyKey);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if a method has cache metadata
|
|
59
|
+
*/
|
|
60
|
+
function hasCacheMetadata(target, propertyKey) {
|
|
61
|
+
return Reflect.hasMetadata(CACHE_METADATA_KEY, target, propertyKey);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* CacheKey decorator to specify custom cache key generation
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* @CacheKey('user-{id}-{role}')
|
|
69
|
+
* @Get('/users/:id')
|
|
70
|
+
* async getUser(@Param('id') id: string, @Query('role') role: string) {
|
|
71
|
+
* return this.userService.findById(id);
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
function CacheKey(keyPattern) {
|
|
76
|
+
return (target, propertyKey, descriptor) => {
|
|
77
|
+
const existingMetadata = getCacheMetadata(target, propertyKey) || {};
|
|
78
|
+
const updatedMetadata = {
|
|
79
|
+
...existingMetadata,
|
|
80
|
+
key: keyPattern,
|
|
81
|
+
};
|
|
82
|
+
Reflect.defineMetadata(CACHE_METADATA_KEY, updatedMetadata, target, propertyKey);
|
|
83
|
+
return descriptor;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* CacheTTL decorator to specify cache TTL
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* @CacheTTL(7200) // 2 hours
|
|
92
|
+
* @Get('/users')
|
|
93
|
+
* async getUsers() {
|
|
94
|
+
* return this.userService.findAll();
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
function CacheTTL(ttl) {
|
|
99
|
+
return (target, propertyKey, descriptor) => {
|
|
100
|
+
const existingMetadata = getCacheMetadata(target, propertyKey) || {};
|
|
101
|
+
const updatedMetadata = {
|
|
102
|
+
...existingMetadata,
|
|
103
|
+
ttl,
|
|
104
|
+
};
|
|
105
|
+
Reflect.defineMetadata(CACHE_METADATA_KEY, updatedMetadata, target, propertyKey);
|
|
106
|
+
return descriptor;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* CacheTags decorator to specify cache tags
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* @CacheTags(['users', 'profiles'])
|
|
115
|
+
* @Get('/users/:id/profile')
|
|
116
|
+
* async getUserProfile(@Param('id') id: string) {
|
|
117
|
+
* return this.userService.getProfile(id);
|
|
118
|
+
* }
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
function CacheTags(tags) {
|
|
122
|
+
return (target, propertyKey, descriptor) => {
|
|
123
|
+
const existingMetadata = getCacheMetadata(target, propertyKey) || {};
|
|
124
|
+
const updatedMetadata = {
|
|
125
|
+
...existingMetadata,
|
|
126
|
+
tags,
|
|
127
|
+
};
|
|
128
|
+
Reflect.defineMetadata(CACHE_METADATA_KEY, updatedMetadata, target, propertyKey);
|
|
129
|
+
return descriptor;
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* CacheEvict decorator to evict cache entries
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* @CacheEvict({ tags: ['users'] })
|
|
138
|
+
* @Post('/users')
|
|
139
|
+
* async createUser(@Body() user: CreateUserDto) {
|
|
140
|
+
* return this.userService.create(user);
|
|
141
|
+
* }
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
function CacheEvict(options) {
|
|
145
|
+
return (target, propertyKey, descriptor) => {
|
|
146
|
+
Reflect.defineMetadata('hazel:cache:evict', options, target, propertyKey);
|
|
147
|
+
return descriptor;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get cache evict metadata
|
|
152
|
+
*/
|
|
153
|
+
function getCacheEvictMetadata(target, propertyKey) {
|
|
154
|
+
return Reflect.getMetadata('hazel:cache:evict', target, propertyKey);
|
|
155
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hazeljs/cache - Caching module for HazelJS
|
|
3
|
+
*/
|
|
4
|
+
export { CacheModule, type CacheModuleOptions } from './cache.module';
|
|
5
|
+
export { CacheService, CacheManager } from './cache.service';
|
|
6
|
+
export { Cache, CacheKey, CacheTTL, CacheTags, CacheEvict, getCacheMetadata, hasCacheMetadata, getCacheEvictMetadata, } from './decorators/cache.decorator';
|
|
7
|
+
export { type CacheOptions, type CacheStrategy, type TTLStrategy, type CacheEntry, type CacheStats, type ICacheStore, type CacheWarmingOptions, type CacheInvalidationEvent, } from './cache.types';
|
|
8
|
+
export { MemoryCacheStore } from './strategies/memory.strategy';
|
|
9
|
+
export { RedisCacheStore } from './strategies/redis.strategy';
|
|
10
|
+
export { MultiTierCacheStore } from './strategies/multi-tier.strategy';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,GAC5B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @hazeljs/cache - Caching module for HazelJS
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MultiTierCacheStore = exports.RedisCacheStore = exports.MemoryCacheStore = exports.getCacheEvictMetadata = exports.hasCacheMetadata = exports.getCacheMetadata = exports.CacheEvict = exports.CacheTags = exports.CacheTTL = exports.CacheKey = exports.Cache = exports.CacheManager = exports.CacheService = exports.CacheModule = void 0;
|
|
7
|
+
var cache_module_1 = require("./cache.module");
|
|
8
|
+
Object.defineProperty(exports, "CacheModule", { enumerable: true, get: function () { return cache_module_1.CacheModule; } });
|
|
9
|
+
var cache_service_1 = require("./cache.service");
|
|
10
|
+
Object.defineProperty(exports, "CacheService", { enumerable: true, get: function () { return cache_service_1.CacheService; } });
|
|
11
|
+
Object.defineProperty(exports, "CacheManager", { enumerable: true, get: function () { return cache_service_1.CacheManager; } });
|
|
12
|
+
var cache_decorator_1 = require("./decorators/cache.decorator");
|
|
13
|
+
Object.defineProperty(exports, "Cache", { enumerable: true, get: function () { return cache_decorator_1.Cache; } });
|
|
14
|
+
Object.defineProperty(exports, "CacheKey", { enumerable: true, get: function () { return cache_decorator_1.CacheKey; } });
|
|
15
|
+
Object.defineProperty(exports, "CacheTTL", { enumerable: true, get: function () { return cache_decorator_1.CacheTTL; } });
|
|
16
|
+
Object.defineProperty(exports, "CacheTags", { enumerable: true, get: function () { return cache_decorator_1.CacheTags; } });
|
|
17
|
+
Object.defineProperty(exports, "CacheEvict", { enumerable: true, get: function () { return cache_decorator_1.CacheEvict; } });
|
|
18
|
+
Object.defineProperty(exports, "getCacheMetadata", { enumerable: true, get: function () { return cache_decorator_1.getCacheMetadata; } });
|
|
19
|
+
Object.defineProperty(exports, "hasCacheMetadata", { enumerable: true, get: function () { return cache_decorator_1.hasCacheMetadata; } });
|
|
20
|
+
Object.defineProperty(exports, "getCacheEvictMetadata", { enumerable: true, get: function () { return cache_decorator_1.getCacheEvictMetadata; } });
|
|
21
|
+
var memory_strategy_1 = require("./strategies/memory.strategy");
|
|
22
|
+
Object.defineProperty(exports, "MemoryCacheStore", { enumerable: true, get: function () { return memory_strategy_1.MemoryCacheStore; } });
|
|
23
|
+
var redis_strategy_1 = require("./strategies/redis.strategy");
|
|
24
|
+
Object.defineProperty(exports, "RedisCacheStore", { enumerable: true, get: function () { return redis_strategy_1.RedisCacheStore; } });
|
|
25
|
+
var multi_tier_strategy_1 = require("./strategies/multi-tier.strategy");
|
|
26
|
+
Object.defineProperty(exports, "MultiTierCacheStore", { enumerable: true, get: function () { return multi_tier_strategy_1.MultiTierCacheStore; } });
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ICacheStore, CacheStats } from '../cache.types';
|
|
2
|
+
/**
|
|
3
|
+
* In-memory cache store implementation
|
|
4
|
+
*/
|
|
5
|
+
export declare class MemoryCacheStore implements ICacheStore {
|
|
6
|
+
private cleanupIntervalMs;
|
|
7
|
+
private cache;
|
|
8
|
+
private tagIndex;
|
|
9
|
+
private stats;
|
|
10
|
+
private cleanupInterval?;
|
|
11
|
+
constructor(cleanupIntervalMs?: number);
|
|
12
|
+
/**
|
|
13
|
+
* Get a value from cache
|
|
14
|
+
*/
|
|
15
|
+
get<T>(key: string): Promise<T | null>;
|
|
16
|
+
/**
|
|
17
|
+
* Set a value in cache
|
|
18
|
+
*/
|
|
19
|
+
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Set a value with tags
|
|
22
|
+
*/
|
|
23
|
+
setWithTags<T>(key: string, value: T, ttl: number, tags?: string[]): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Delete a value from cache
|
|
26
|
+
*/
|
|
27
|
+
delete(key: string): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Delete entries by tag
|
|
30
|
+
*/
|
|
31
|
+
deleteByTag(tag: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Check if key exists in cache
|
|
34
|
+
*/
|
|
35
|
+
has(key: string): Promise<boolean>;
|
|
36
|
+
/**
|
|
37
|
+
* Clear all cache entries
|
|
38
|
+
*/
|
|
39
|
+
clear(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Get all keys matching a pattern
|
|
42
|
+
*/
|
|
43
|
+
keys(pattern?: string): Promise<string[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Get cache statistics
|
|
46
|
+
*/
|
|
47
|
+
getStats(): Promise<CacheStats>;
|
|
48
|
+
/**
|
|
49
|
+
* Reset statistics
|
|
50
|
+
*/
|
|
51
|
+
resetStats(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Start cleanup interval
|
|
54
|
+
*/
|
|
55
|
+
private startCleanup;
|
|
56
|
+
/**
|
|
57
|
+
* Stop cleanup interval
|
|
58
|
+
*/
|
|
59
|
+
stopCleanup(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Cleanup expired entries
|
|
62
|
+
*/
|
|
63
|
+
private cleanup;
|
|
64
|
+
/**
|
|
65
|
+
* Remove key from tag index
|
|
66
|
+
*/
|
|
67
|
+
private removeFromTagIndex;
|
|
68
|
+
/**
|
|
69
|
+
* Destroy the cache store
|
|
70
|
+
*/
|
|
71
|
+
destroy(): void;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=memory.strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.strategy.d.ts","sourceRoot":"","sources":["../../src/strategies/memory.strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGrE;;GAEG;AACH,qBAAa,gBAAiB,YAAW,WAAW;IAStC,OAAO,CAAC,iBAAiB;IARrC,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,QAAQ,CAAuC;IACvD,OAAO,CAAC,KAAK,CAGX;IACF,OAAO,CAAC,eAAe,CAAC,CAAiB;gBAErB,iBAAiB,GAAE,MAAc;IAIrD;;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;IActE;;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;IA0BxF;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASxC;;OAEG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc7C;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAY/C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC;IAoBrC;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;OAEG;IACH,OAAO,CAAC,YAAY;IAMpB;;OAEG;IACH,WAAW,IAAI,IAAI;IAOnB;;OAEG;IACH,OAAO,CAAC,OAAO;IAiBf;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;OAEG;IACH,OAAO,IAAI,IAAI;CAKhB"}
|
|
@@ -0,0 +1,236 @@
|
|
|
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.MemoryCacheStore = void 0;
|
|
7
|
+
const core_1 = __importDefault(require("@hazeljs/core"));
|
|
8
|
+
/**
|
|
9
|
+
* In-memory cache store implementation
|
|
10
|
+
*/
|
|
11
|
+
class MemoryCacheStore {
|
|
12
|
+
constructor(cleanupIntervalMs = 60000) {
|
|
13
|
+
this.cleanupIntervalMs = cleanupIntervalMs;
|
|
14
|
+
this.cache = new Map();
|
|
15
|
+
this.tagIndex = new Map();
|
|
16
|
+
this.stats = {
|
|
17
|
+
hits: 0,
|
|
18
|
+
misses: 0,
|
|
19
|
+
};
|
|
20
|
+
this.startCleanup();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get a value from cache
|
|
24
|
+
*/
|
|
25
|
+
async get(key) {
|
|
26
|
+
const entry = this.cache.get(key);
|
|
27
|
+
if (!entry) {
|
|
28
|
+
this.stats.misses++;
|
|
29
|
+
core_1.default.debug(`Cache miss: ${key}`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// Check if expired
|
|
33
|
+
if (Date.now() > entry.expiresAt) {
|
|
34
|
+
this.cache.delete(key);
|
|
35
|
+
this.removeFromTagIndex(key, entry.tags);
|
|
36
|
+
this.stats.misses++;
|
|
37
|
+
core_1.default.debug(`Cache expired: ${key}`);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
// Update last accessed for sliding TTL
|
|
41
|
+
if (entry.lastAccessedAt !== undefined) {
|
|
42
|
+
entry.lastAccessedAt = Date.now();
|
|
43
|
+
}
|
|
44
|
+
this.stats.hits++;
|
|
45
|
+
core_1.default.debug(`Cache hit: ${key}`);
|
|
46
|
+
return entry.value;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Set a value in cache
|
|
50
|
+
*/
|
|
51
|
+
async set(key, value, ttl = 3600) {
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const entry = {
|
|
54
|
+
value,
|
|
55
|
+
key,
|
|
56
|
+
cachedAt: now,
|
|
57
|
+
expiresAt: now + ttl * 1000,
|
|
58
|
+
lastAccessedAt: now,
|
|
59
|
+
};
|
|
60
|
+
this.cache.set(key, entry);
|
|
61
|
+
core_1.default.debug(`Cache set: ${key} (TTL: ${ttl}s)`);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Set a value with tags
|
|
65
|
+
*/
|
|
66
|
+
async setWithTags(key, value, ttl, tags) {
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
const entry = {
|
|
69
|
+
value,
|
|
70
|
+
key,
|
|
71
|
+
cachedAt: now,
|
|
72
|
+
expiresAt: now + ttl * 1000,
|
|
73
|
+
lastAccessedAt: now,
|
|
74
|
+
tags,
|
|
75
|
+
};
|
|
76
|
+
this.cache.set(key, entry);
|
|
77
|
+
// Update tag index
|
|
78
|
+
if (tags) {
|
|
79
|
+
tags.forEach((tag) => {
|
|
80
|
+
if (!this.tagIndex.has(tag)) {
|
|
81
|
+
this.tagIndex.set(tag, new Set());
|
|
82
|
+
}
|
|
83
|
+
this.tagIndex.get(tag).add(key);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
core_1.default.debug(`Cache set with tags: ${key} (TTL: ${ttl}s, Tags: ${tags?.join(', ')})`);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Delete a value from cache
|
|
90
|
+
*/
|
|
91
|
+
async delete(key) {
|
|
92
|
+
const entry = this.cache.get(key);
|
|
93
|
+
if (entry) {
|
|
94
|
+
this.removeFromTagIndex(key, entry.tags);
|
|
95
|
+
}
|
|
96
|
+
this.cache.delete(key);
|
|
97
|
+
core_1.default.debug(`Cache deleted: ${key}`);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Delete entries by tag
|
|
101
|
+
*/
|
|
102
|
+
async deleteByTag(tag) {
|
|
103
|
+
const keys = this.tagIndex.get(tag);
|
|
104
|
+
if (!keys) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (const key of keys) {
|
|
108
|
+
await this.delete(key);
|
|
109
|
+
}
|
|
110
|
+
this.tagIndex.delete(tag);
|
|
111
|
+
core_1.default.debug(`Cache invalidated by tag: ${tag} (${keys.size} entries)`);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if key exists in cache
|
|
115
|
+
*/
|
|
116
|
+
async has(key) {
|
|
117
|
+
const entry = this.cache.get(key);
|
|
118
|
+
if (!entry) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
// Check if expired
|
|
122
|
+
if (Date.now() > entry.expiresAt) {
|
|
123
|
+
this.cache.delete(key);
|
|
124
|
+
this.removeFromTagIndex(key, entry.tags);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Clear all cache entries
|
|
131
|
+
*/
|
|
132
|
+
async clear() {
|
|
133
|
+
this.cache.clear();
|
|
134
|
+
this.tagIndex.clear();
|
|
135
|
+
core_1.default.info('Cache cleared');
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get all keys matching a pattern
|
|
139
|
+
*/
|
|
140
|
+
async keys(pattern) {
|
|
141
|
+
const allKeys = Array.from(this.cache.keys());
|
|
142
|
+
if (!pattern) {
|
|
143
|
+
return allKeys;
|
|
144
|
+
}
|
|
145
|
+
// Simple pattern matching (supports * wildcard)
|
|
146
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
147
|
+
return allKeys.filter((key) => regex.test(key));
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get cache statistics
|
|
151
|
+
*/
|
|
152
|
+
async getStats() {
|
|
153
|
+
const total = this.stats.hits + this.stats.misses;
|
|
154
|
+
const hitRate = total > 0 ? (this.stats.hits / total) * 100 : 0;
|
|
155
|
+
// Calculate approximate memory usage
|
|
156
|
+
let memoryUsage = 0;
|
|
157
|
+
for (const entry of this.cache.values()) {
|
|
158
|
+
const entrySize = JSON.stringify(entry.value).length;
|
|
159
|
+
memoryUsage += entrySize;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
hits: this.stats.hits,
|
|
163
|
+
misses: this.stats.misses,
|
|
164
|
+
hitRate: Math.round(hitRate * 100) / 100,
|
|
165
|
+
size: this.cache.size,
|
|
166
|
+
memoryUsage,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Reset statistics
|
|
171
|
+
*/
|
|
172
|
+
resetStats() {
|
|
173
|
+
this.stats.hits = 0;
|
|
174
|
+
this.stats.misses = 0;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Start cleanup interval
|
|
178
|
+
*/
|
|
179
|
+
startCleanup() {
|
|
180
|
+
this.cleanupInterval = setInterval(() => {
|
|
181
|
+
this.cleanup();
|
|
182
|
+
}, this.cleanupIntervalMs);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Stop cleanup interval
|
|
186
|
+
*/
|
|
187
|
+
stopCleanup() {
|
|
188
|
+
if (this.cleanupInterval) {
|
|
189
|
+
clearInterval(this.cleanupInterval);
|
|
190
|
+
this.cleanupInterval = undefined;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Cleanup expired entries
|
|
195
|
+
*/
|
|
196
|
+
cleanup() {
|
|
197
|
+
const now = Date.now();
|
|
198
|
+
let expiredCount = 0;
|
|
199
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
200
|
+
if (now > entry.expiresAt) {
|
|
201
|
+
this.cache.delete(key);
|
|
202
|
+
this.removeFromTagIndex(key, entry.tags);
|
|
203
|
+
expiredCount++;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (expiredCount > 0) {
|
|
207
|
+
core_1.default.debug(`Cleaned up ${expiredCount} expired cache entries`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Remove key from tag index
|
|
212
|
+
*/
|
|
213
|
+
removeFromTagIndex(key, tags) {
|
|
214
|
+
if (!tags) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
tags.forEach((tag) => {
|
|
218
|
+
const keys = this.tagIndex.get(tag);
|
|
219
|
+
if (keys) {
|
|
220
|
+
keys.delete(key);
|
|
221
|
+
if (keys.size === 0) {
|
|
222
|
+
this.tagIndex.delete(tag);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Destroy the cache store
|
|
229
|
+
*/
|
|
230
|
+
destroy() {
|
|
231
|
+
this.stopCleanup();
|
|
232
|
+
this.cache.clear();
|
|
233
|
+
this.tagIndex.clear();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
exports.MemoryCacheStore = MemoryCacheStore;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ICacheStore, CacheStats } from '../cache.types';
|
|
2
|
+
/**
|
|
3
|
+
* Multi-tier cache store implementation
|
|
4
|
+
* Uses memory cache as L1 and Redis as L2
|
|
5
|
+
*/
|
|
6
|
+
export declare class MultiTierCacheStore implements ICacheStore {
|
|
7
|
+
private l1Cache;
|
|
8
|
+
private l2Cache;
|
|
9
|
+
constructor(options?: {
|
|
10
|
+
redis?: {
|
|
11
|
+
host?: string;
|
|
12
|
+
port?: number;
|
|
13
|
+
password?: string;
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Get a value from cache (L1 first, then L2)
|
|
18
|
+
*/
|
|
19
|
+
get<T>(key: string): Promise<T | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Set a value in cache (both L1 and L2)
|
|
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 (both L1 and L2)
|
|
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
|
+
* Get L1 cache statistics
|
|
54
|
+
*/
|
|
55
|
+
getL1Stats(): Promise<CacheStats>;
|
|
56
|
+
/**
|
|
57
|
+
* Get L2 cache statistics
|
|
58
|
+
*/
|
|
59
|
+
getL2Stats(): Promise<CacheStats>;
|
|
60
|
+
/**
|
|
61
|
+
* Reset statistics
|
|
62
|
+
*/
|
|
63
|
+
resetStats(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Destroy the cache store
|
|
66
|
+
*/
|
|
67
|
+
destroy(): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=multi-tier.strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-tier.strategy.d.ts","sourceRoot":"","sources":["../../src/strategies/multi-tier.strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAKzD;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,OAAO,CAAkB;gBAErB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;IAMrF;;OAEG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAqB5C;;OAEG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtE;;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;IAWxF;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxC;;OAEG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAW/C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC;IAqBrC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAIvC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAIvC;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAK/B"}
|