@pioneer-platform/pioneer-cache 1.0.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/.turbo/turbo-build.log +2 -0
- package/README.md +451 -0
- package/dist/core/base-cache.d.ts +75 -0
- package/dist/core/base-cache.js +493 -0
- package/dist/core/cache-manager.d.ts +62 -0
- package/dist/core/cache-manager.js +238 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +25 -0
- package/dist/stores/balance-cache.d.ts +47 -0
- package/dist/stores/balance-cache.js +158 -0
- package/dist/stores/price-cache.d.ts +39 -0
- package/dist/stores/price-cache.js +179 -0
- package/dist/stores/transaction-cache.d.ts +42 -0
- package/dist/stores/transaction-cache.js +148 -0
- package/dist/types/index.d.ts +98 -0
- package/dist/types/index.js +5 -0
- package/dist/workers/refresh-worker.d.ts +57 -0
- package/dist/workers/refresh-worker.js +212 -0
- package/package.json +31 -0
- package/src/core/base-cache.ts +595 -0
- package/src/core/cache-manager.ts +293 -0
- package/src/index.ts +36 -0
- package/src/stores/balance-cache.ts +196 -0
- package/src/stores/price-cache.ts +215 -0
- package/src/stores/transaction-cache.ts +172 -0
- package/src/types/index.ts +121 -0
- package/src/workers/refresh-worker.ts +267 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
PriceCache - Price-specific cache implementation
|
|
4
|
+
|
|
5
|
+
Extends BaseCache with price-specific logic.
|
|
6
|
+
All common logic is inherited from BaseCache (including all 5 fixes!)
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.PriceCache = void 0;
|
|
10
|
+
const base_cache_1 = require("../core/base-cache");
|
|
11
|
+
const log = require('@pioneer-platform/loggerdog')();
|
|
12
|
+
/**
|
|
13
|
+
* PriceCache - Caches USD prices for assets
|
|
14
|
+
*/
|
|
15
|
+
class PriceCache extends base_cache_1.BaseCache {
|
|
16
|
+
constructor(redis, markets, config) {
|
|
17
|
+
const defaultConfig = {
|
|
18
|
+
name: 'price',
|
|
19
|
+
keyPrefix: 'price_v2:',
|
|
20
|
+
ttl: 60 * 60 * 1000, // 1 hour
|
|
21
|
+
staleThreshold: 30 * 60 * 1000, // 30 minutes (refresh after 30min)
|
|
22
|
+
enableTTL: true,
|
|
23
|
+
queueName: 'price-refresh-v2',
|
|
24
|
+
enableQueue: true,
|
|
25
|
+
maxRetries: 3,
|
|
26
|
+
retryDelay: 5000,
|
|
27
|
+
blockOnMiss: false, // Return immediately with $0 (prices less critical)
|
|
28
|
+
enableLegacyFallback: true,
|
|
29
|
+
defaultValue: {
|
|
30
|
+
caip: '',
|
|
31
|
+
price: 0
|
|
32
|
+
},
|
|
33
|
+
maxConcurrentJobs: 5,
|
|
34
|
+
apiTimeout: 5000,
|
|
35
|
+
logCacheHits: false,
|
|
36
|
+
logCacheMisses: true,
|
|
37
|
+
logRefreshJobs: true
|
|
38
|
+
};
|
|
39
|
+
super(redis, { ...defaultConfig, ...config });
|
|
40
|
+
this.markets = markets;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build Redis key for price data
|
|
44
|
+
* Format: price_v2:caip
|
|
45
|
+
*/
|
|
46
|
+
buildKey(params) {
|
|
47
|
+
const { caip } = params;
|
|
48
|
+
if (!caip) {
|
|
49
|
+
throw new Error('PriceCache.buildKey: caip required');
|
|
50
|
+
}
|
|
51
|
+
const normalizedCaip = caip.toLowerCase();
|
|
52
|
+
return `${this.config.keyPrefix}${normalizedCaip}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Fetch price from markets API
|
|
56
|
+
*/
|
|
57
|
+
async fetchFromSource(params) {
|
|
58
|
+
const tag = this.TAG + 'fetchFromSource | ';
|
|
59
|
+
try {
|
|
60
|
+
const { caip } = params;
|
|
61
|
+
// Map CAIP to symbol for markets API
|
|
62
|
+
const assetData = require('@pioneer-platform/pioneer-discovery').assetData;
|
|
63
|
+
const asset = assetData[caip.toUpperCase()] || assetData[caip.toLowerCase()];
|
|
64
|
+
if (!asset || !asset.symbol) {
|
|
65
|
+
log.warn(tag, `No asset mapping found for ${caip}`);
|
|
66
|
+
return {
|
|
67
|
+
caip,
|
|
68
|
+
price: 0
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Get price from markets module
|
|
72
|
+
const symbol = asset.symbol.toLowerCase();
|
|
73
|
+
const priceResult = await this.markets.getPrice(symbol);
|
|
74
|
+
// Handle different response formats
|
|
75
|
+
let price = 0;
|
|
76
|
+
if (typeof priceResult === 'object' && priceResult.price) {
|
|
77
|
+
price = parseFloat(priceResult.price);
|
|
78
|
+
}
|
|
79
|
+
else if (typeof priceResult === 'number') {
|
|
80
|
+
price = priceResult;
|
|
81
|
+
}
|
|
82
|
+
if (isNaN(price) || price <= 0) {
|
|
83
|
+
log.warn(tag, `Invalid price for ${caip} (${symbol}): ${priceResult}`);
|
|
84
|
+
return {
|
|
85
|
+
caip,
|
|
86
|
+
price: 0
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
log.debug(tag, `Fetched price for ${caip} (${symbol}): $${price}`);
|
|
90
|
+
return {
|
|
91
|
+
caip,
|
|
92
|
+
price,
|
|
93
|
+
source: 'markets'
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
log.error(tag, 'Error fetching price:', error);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Try to get price from legacy cache formats
|
|
103
|
+
*/
|
|
104
|
+
async getLegacyCached(params) {
|
|
105
|
+
const tag = this.TAG + 'getLegacyCached | ';
|
|
106
|
+
try {
|
|
107
|
+
const { caip } = params;
|
|
108
|
+
// Try CoinGecko format first (more accurate)
|
|
109
|
+
const coingeckoKey = `coingecko:${caip}`;
|
|
110
|
+
const coingeckoData = await this.redis.get(coingeckoKey);
|
|
111
|
+
if (coingeckoData) {
|
|
112
|
+
const parsed = JSON.parse(coingeckoData);
|
|
113
|
+
if (parsed.current_price && typeof parsed.current_price === 'number') {
|
|
114
|
+
return {
|
|
115
|
+
caip,
|
|
116
|
+
price: parsed.current_price,
|
|
117
|
+
source: 'coingecko-legacy'
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Try CoinCap format
|
|
122
|
+
const coincapKey = `coincap:${caip}`;
|
|
123
|
+
const coincapData = await this.redis.get(coincapKey);
|
|
124
|
+
if (coincapData) {
|
|
125
|
+
const parsed = JSON.parse(coincapData);
|
|
126
|
+
if (parsed.priceUsd && typeof parsed.priceUsd === 'string') {
|
|
127
|
+
const price = parseFloat(parsed.priceUsd);
|
|
128
|
+
if (!isNaN(price)) {
|
|
129
|
+
return {
|
|
130
|
+
caip,
|
|
131
|
+
price,
|
|
132
|
+
source: 'coincap-legacy'
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
log.error(tag, 'Error getting legacy cached price:', error);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get price for a specific asset
|
|
146
|
+
* Convenience method that wraps base get()
|
|
147
|
+
*/
|
|
148
|
+
async getPrice(caip, waitForFresh) {
|
|
149
|
+
const result = await this.get({ caip }, waitForFresh);
|
|
150
|
+
return result.value?.price || 0;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get prices for multiple assets (batch operation)
|
|
154
|
+
*/
|
|
155
|
+
async getBatchPrices(caips, waitForFresh) {
|
|
156
|
+
const tag = this.TAG + 'getBatchPrices | ';
|
|
157
|
+
const startTime = Date.now();
|
|
158
|
+
try {
|
|
159
|
+
log.info(tag, `Batch request for ${caips.length} prices`);
|
|
160
|
+
// Get all prices in parallel
|
|
161
|
+
const promises = caips.map(caip => this.getPrice(caip, waitForFresh));
|
|
162
|
+
const results = await Promise.all(promises);
|
|
163
|
+
// Build map of caip -> price
|
|
164
|
+
const priceMap = new Map();
|
|
165
|
+
caips.forEach((caip, index) => {
|
|
166
|
+
priceMap.set(caip, results[index]);
|
|
167
|
+
});
|
|
168
|
+
const responseTime = Date.now() - startTime;
|
|
169
|
+
log.info(tag, `Batch completed: ${results.length} prices in ${responseTime}ms (${(responseTime / results.length).toFixed(1)}ms avg)`);
|
|
170
|
+
return priceMap;
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
log.error(tag, 'Error in batch price request:', error);
|
|
174
|
+
// Return empty map
|
|
175
|
+
return new Map();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.PriceCache = PriceCache;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TransactionCache - Caches immutable blockchain transaction data
|
|
3
|
+
*/
|
|
4
|
+
export declare class TransactionCache {
|
|
5
|
+
private redis;
|
|
6
|
+
private readonly keyPrefix;
|
|
7
|
+
private readonly TAG;
|
|
8
|
+
constructor(redis: any);
|
|
9
|
+
/**
|
|
10
|
+
* Get cached transaction data by txid
|
|
11
|
+
*/
|
|
12
|
+
get(txid: string): Promise<any | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Cache transaction data PERMANENTLY (no expiry)
|
|
15
|
+
* Transactions are immutable - once on the blockchain, they never change
|
|
16
|
+
*/
|
|
17
|
+
set(txid: string, txData: any): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Get transaction from cache or fetch from source and cache it
|
|
20
|
+
* Classic cache-aside pattern
|
|
21
|
+
*/
|
|
22
|
+
getOrFetch(txid: string, fetchFn: () => Promise<any>): Promise<any>;
|
|
23
|
+
/**
|
|
24
|
+
* Invalidate cached transaction (useful for unconfirmed transactions)
|
|
25
|
+
*/
|
|
26
|
+
invalidate(txid: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Get cache statistics
|
|
29
|
+
*/
|
|
30
|
+
getStats(): Promise<{
|
|
31
|
+
totalKeys: number;
|
|
32
|
+
memoryUsage: string;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Clear all cached transactions (use with caution)
|
|
36
|
+
*/
|
|
37
|
+
clearAll(): Promise<number>;
|
|
38
|
+
/**
|
|
39
|
+
* Build cache key for a transaction
|
|
40
|
+
*/
|
|
41
|
+
private buildKey;
|
|
42
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
TransactionCache - Transaction-specific cache implementation
|
|
4
|
+
|
|
5
|
+
Different pattern from Balance/Price caches:
|
|
6
|
+
- Caches transaction data PERMANENTLY (no TTL)
|
|
7
|
+
- No background refresh (blockchain data is immutable)
|
|
8
|
+
- Simple cache-aside pattern with getOrFetch()
|
|
9
|
+
- No workers needed
|
|
10
|
+
|
|
11
|
+
Does NOT extend BaseCache - intentionally different architecture.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.TransactionCache = void 0;
|
|
15
|
+
const log = require('@pioneer-platform/loggerdog')();
|
|
16
|
+
/**
|
|
17
|
+
* TransactionCache - Caches immutable blockchain transaction data
|
|
18
|
+
*/
|
|
19
|
+
class TransactionCache {
|
|
20
|
+
constructor(redis) {
|
|
21
|
+
this.keyPrefix = 'tx:';
|
|
22
|
+
this.TAG = ' | TransactionCache | ';
|
|
23
|
+
this.redis = redis;
|
|
24
|
+
log.info(this.TAG, 'TransactionCache initialized (permanent caching, no TTL)');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get cached transaction data by txid
|
|
28
|
+
*/
|
|
29
|
+
async get(txid) {
|
|
30
|
+
const tag = this.TAG + 'get | ';
|
|
31
|
+
try {
|
|
32
|
+
const key = this.buildKey(txid);
|
|
33
|
+
const cached = await this.redis.get(key);
|
|
34
|
+
if (cached) {
|
|
35
|
+
log.info(tag, `Cache HIT for txid: ${txid.substring(0, 16)}...`);
|
|
36
|
+
return JSON.parse(cached);
|
|
37
|
+
}
|
|
38
|
+
log.info(tag, `Cache MISS for txid: ${txid.substring(0, 16)}...`);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
log.error(tag, `Error getting cached tx ${txid}:`, error);
|
|
43
|
+
return null; // Fail gracefully
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Cache transaction data PERMANENTLY (no expiry)
|
|
48
|
+
* Transactions are immutable - once on the blockchain, they never change
|
|
49
|
+
*/
|
|
50
|
+
async set(txid, txData) {
|
|
51
|
+
const tag = this.TAG + 'set | ';
|
|
52
|
+
try {
|
|
53
|
+
const key = this.buildKey(txid);
|
|
54
|
+
// NO TTL - cache permanently
|
|
55
|
+
await this.redis.set(key, JSON.stringify(txData));
|
|
56
|
+
log.info(tag, `Cached txid: ${txid.substring(0, 16)}... (PERMANENT - no expiry)`);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
log.error(tag, `Error caching tx ${txid}:`, error);
|
|
60
|
+
// Don't throw - caching is optional
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get transaction from cache or fetch from source and cache it
|
|
65
|
+
* Classic cache-aside pattern
|
|
66
|
+
*/
|
|
67
|
+
async getOrFetch(txid, fetchFn) {
|
|
68
|
+
const tag = this.TAG + 'getOrFetch | ';
|
|
69
|
+
// Try cache first
|
|
70
|
+
const cached = await this.get(txid);
|
|
71
|
+
if (cached) {
|
|
72
|
+
return cached;
|
|
73
|
+
}
|
|
74
|
+
// Not in cache - fetch from source
|
|
75
|
+
log.info(tag, `Fetching from source: ${txid.substring(0, 16)}...`);
|
|
76
|
+
const txData = await fetchFn();
|
|
77
|
+
// Cache the result
|
|
78
|
+
await this.set(txid, txData);
|
|
79
|
+
return txData;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Invalidate cached transaction (useful for unconfirmed transactions)
|
|
83
|
+
*/
|
|
84
|
+
async invalidate(txid) {
|
|
85
|
+
const tag = this.TAG + 'invalidate | ';
|
|
86
|
+
try {
|
|
87
|
+
const key = this.buildKey(txid);
|
|
88
|
+
await this.redis.del(key);
|
|
89
|
+
log.info(tag, `Invalidated cache for txid: ${txid.substring(0, 16)}...`);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
log.error(tag, `Error invalidating cached tx ${txid}:`, error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get cache statistics
|
|
97
|
+
*/
|
|
98
|
+
async getStats() {
|
|
99
|
+
const tag = this.TAG + 'getStats | ';
|
|
100
|
+
try {
|
|
101
|
+
const keys = await this.redis.keys(`${this.keyPrefix}*`);
|
|
102
|
+
const totalKeys = keys.length;
|
|
103
|
+
// Get Redis memory info
|
|
104
|
+
const info = await this.redis.info('memory');
|
|
105
|
+
const memoryMatch = info.match(/used_memory_human:(.+)/);
|
|
106
|
+
const memoryUsage = memoryMatch ? memoryMatch[1].trim() : 'unknown';
|
|
107
|
+
log.info(tag, `Cache stats: ${totalKeys} keys, ${memoryUsage} memory`);
|
|
108
|
+
return {
|
|
109
|
+
totalKeys,
|
|
110
|
+
memoryUsage
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
log.error(tag, 'Error getting cache stats:', error);
|
|
115
|
+
return {
|
|
116
|
+
totalKeys: 0,
|
|
117
|
+
memoryUsage: 'unknown'
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Clear all cached transactions (use with caution)
|
|
123
|
+
*/
|
|
124
|
+
async clearAll() {
|
|
125
|
+
const tag = this.TAG + 'clearAll | ';
|
|
126
|
+
try {
|
|
127
|
+
const keys = await this.redis.keys(`${this.keyPrefix}*`);
|
|
128
|
+
if (keys.length === 0) {
|
|
129
|
+
log.info(tag, 'No cached transactions to clear');
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
await this.redis.del(...keys);
|
|
133
|
+
log.info(tag, `Cleared ${keys.length} cached transactions`);
|
|
134
|
+
return keys.length;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
log.error(tag, 'Error clearing cache:', error);
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Build cache key for a transaction
|
|
143
|
+
*/
|
|
144
|
+
buildKey(txid) {
|
|
145
|
+
return `${this.keyPrefix}${txid}`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.TransactionCache = TransactionCache;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache configuration for a specific cache store
|
|
3
|
+
*/
|
|
4
|
+
export interface CacheConfig {
|
|
5
|
+
name: string;
|
|
6
|
+
keyPrefix: string;
|
|
7
|
+
ttl: number;
|
|
8
|
+
staleThreshold?: number;
|
|
9
|
+
enableTTL: boolean;
|
|
10
|
+
queueName: string;
|
|
11
|
+
enableQueue: boolean;
|
|
12
|
+
maxRetries: number;
|
|
13
|
+
retryDelay: number;
|
|
14
|
+
blockOnMiss: boolean;
|
|
15
|
+
enableLegacyFallback: boolean;
|
|
16
|
+
defaultValue: any;
|
|
17
|
+
maxConcurrentJobs: number;
|
|
18
|
+
apiTimeout: number;
|
|
19
|
+
logCacheHits: boolean;
|
|
20
|
+
logCacheMisses: boolean;
|
|
21
|
+
logRefreshJobs: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generic cached value wrapper
|
|
25
|
+
*/
|
|
26
|
+
export interface CachedValue<T> {
|
|
27
|
+
value: T;
|
|
28
|
+
timestamp: number;
|
|
29
|
+
source: string;
|
|
30
|
+
lastUpdated: string;
|
|
31
|
+
metadata?: Record<string, any>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Cache operation result
|
|
35
|
+
*/
|
|
36
|
+
export interface CacheResult<T> {
|
|
37
|
+
success: boolean;
|
|
38
|
+
value?: T;
|
|
39
|
+
cached: boolean;
|
|
40
|
+
fresh: boolean;
|
|
41
|
+
age?: number;
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Health check result
|
|
46
|
+
*/
|
|
47
|
+
export interface HealthCheckResult {
|
|
48
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
49
|
+
queueInitialized: boolean;
|
|
50
|
+
redisConnected: boolean;
|
|
51
|
+
stats: CacheStats;
|
|
52
|
+
issues: string[];
|
|
53
|
+
warnings: string[];
|
|
54
|
+
timestamp: number;
|
|
55
|
+
timestampISO: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Cache statistics
|
|
59
|
+
*/
|
|
60
|
+
export interface CacheStats {
|
|
61
|
+
totalEntries: number;
|
|
62
|
+
staleEntries: number;
|
|
63
|
+
freshEntries: number;
|
|
64
|
+
stalenessRate: string;
|
|
65
|
+
sources?: Record<string, number>;
|
|
66
|
+
byNetwork?: Record<string, number>;
|
|
67
|
+
entriesWithoutTTL?: number;
|
|
68
|
+
ttl?: number;
|
|
69
|
+
staleThreshold?: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Refresh job for background workers
|
|
73
|
+
*/
|
|
74
|
+
export interface RefreshJob {
|
|
75
|
+
type: string;
|
|
76
|
+
key: string;
|
|
77
|
+
params: Record<string, any>;
|
|
78
|
+
priority?: 'high' | 'normal' | 'low';
|
|
79
|
+
retryCount?: number;
|
|
80
|
+
timestamp?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Source fetcher function signature
|
|
84
|
+
* Returns the data to be cached
|
|
85
|
+
*/
|
|
86
|
+
export type SourceFetcher<T> = (params: Record<string, any>) => Promise<T>;
|
|
87
|
+
/**
|
|
88
|
+
* Key builder function signature
|
|
89
|
+
* Builds Redis key from parameters
|
|
90
|
+
*/
|
|
91
|
+
export type KeyBuilder = (...args: any[]) => string;
|
|
92
|
+
/**
|
|
93
|
+
* Legacy key pattern for migration
|
|
94
|
+
*/
|
|
95
|
+
export interface LegacyKeyPattern {
|
|
96
|
+
prefix: string;
|
|
97
|
+
keyBuilder: KeyBuilder;
|
|
98
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { BaseCache } from '../core/base-cache';
|
|
2
|
+
/**
|
|
3
|
+
* Worker configuration
|
|
4
|
+
*/
|
|
5
|
+
export interface WorkerConfig {
|
|
6
|
+
queueName: string;
|
|
7
|
+
maxRetries: number;
|
|
8
|
+
retryDelay: number;
|
|
9
|
+
pollInterval?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Unified refresh worker that processes jobs for any cache type
|
|
13
|
+
*/
|
|
14
|
+
export declare class RefreshWorker {
|
|
15
|
+
private redis;
|
|
16
|
+
private redisQueue;
|
|
17
|
+
private cacheRegistry;
|
|
18
|
+
private config;
|
|
19
|
+
private isRunning;
|
|
20
|
+
private isProcessing;
|
|
21
|
+
private pollTimeoutId;
|
|
22
|
+
constructor(redis: any, config: WorkerConfig);
|
|
23
|
+
/**
|
|
24
|
+
* Register a cache instance with this worker
|
|
25
|
+
* The worker will route jobs to the appropriate cache based on job type
|
|
26
|
+
*/
|
|
27
|
+
registerCache(cacheName: string, cache: BaseCache<any>): void;
|
|
28
|
+
/**
|
|
29
|
+
* Start processing jobs from the queue
|
|
30
|
+
*/
|
|
31
|
+
start(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Stop the worker gracefully
|
|
34
|
+
*/
|
|
35
|
+
stop(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Poll for next job from the queue
|
|
38
|
+
*/
|
|
39
|
+
private poll;
|
|
40
|
+
/**
|
|
41
|
+
* Schedule next poll
|
|
42
|
+
*/
|
|
43
|
+
private schedulePoll;
|
|
44
|
+
/**
|
|
45
|
+
* Process a single refresh job
|
|
46
|
+
*/
|
|
47
|
+
private processJob;
|
|
48
|
+
/**
|
|
49
|
+
* Get worker statistics
|
|
50
|
+
*/
|
|
51
|
+
getStats(): Promise<any>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Start a unified refresh worker for multiple cache types
|
|
55
|
+
* Convenience function for common usage
|
|
56
|
+
*/
|
|
57
|
+
export declare function startUnifiedWorker(redis: any, caches: Map<string, BaseCache<any>>, queueName: string, config?: Partial<WorkerConfig>): Promise<RefreshWorker>;
|