@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,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Unified Refresh Worker
|
|
4
|
+
|
|
5
|
+
Single worker that processes refresh jobs for ALL cache types (balance, price, etc.)
|
|
6
|
+
Replaces separate balance-refresh.worker.ts and price-refresh.worker.ts
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.RefreshWorker = void 0;
|
|
10
|
+
exports.startUnifiedWorker = startUnifiedWorker;
|
|
11
|
+
const log = require('@pioneer-platform/loggerdog')();
|
|
12
|
+
const TAG = ' | RefreshWorker | ';
|
|
13
|
+
/**
|
|
14
|
+
* Unified refresh worker that processes jobs for any cache type
|
|
15
|
+
*/
|
|
16
|
+
class RefreshWorker {
|
|
17
|
+
constructor(redis, config) {
|
|
18
|
+
this.cacheRegistry = new Map();
|
|
19
|
+
this.isRunning = false;
|
|
20
|
+
this.isProcessing = false;
|
|
21
|
+
this.pollTimeoutId = null;
|
|
22
|
+
this.redis = redis;
|
|
23
|
+
this.config = config;
|
|
24
|
+
try {
|
|
25
|
+
this.redisQueue = require('@pioneer-platform/redis-queue');
|
|
26
|
+
log.info(TAG, `✅ RefreshWorker initialized for queue: ${config.queueName}`);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
log.error(TAG, '❌ Failed to initialize redis-queue:', error);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Register a cache instance with this worker
|
|
35
|
+
* The worker will route jobs to the appropriate cache based on job type
|
|
36
|
+
*/
|
|
37
|
+
registerCache(cacheName, cache) {
|
|
38
|
+
this.cacheRegistry.set(cacheName.toLowerCase(), cache);
|
|
39
|
+
log.info(TAG, `Registered ${cacheName} cache with worker`);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Start processing jobs from the queue
|
|
43
|
+
*/
|
|
44
|
+
async start() {
|
|
45
|
+
const tag = TAG + 'start | ';
|
|
46
|
+
if (this.isRunning) {
|
|
47
|
+
log.warn(tag, 'Worker already running');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
log.info(tag, `🚀 Starting refresh worker for queue: ${this.config.queueName}`);
|
|
51
|
+
log.info(tag, `Registered caches: ${Array.from(this.cacheRegistry.keys()).join(', ')}`);
|
|
52
|
+
this.isRunning = true;
|
|
53
|
+
this.poll();
|
|
54
|
+
log.info(tag, '✅ Refresh worker started successfully');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Stop the worker gracefully
|
|
58
|
+
*/
|
|
59
|
+
async stop() {
|
|
60
|
+
const tag = TAG + 'stop | ';
|
|
61
|
+
log.info(tag, 'Stopping refresh worker...');
|
|
62
|
+
this.isRunning = false;
|
|
63
|
+
if (this.pollTimeoutId) {
|
|
64
|
+
clearTimeout(this.pollTimeoutId);
|
|
65
|
+
this.pollTimeoutId = null;
|
|
66
|
+
}
|
|
67
|
+
// Wait for current job to finish
|
|
68
|
+
while (this.isProcessing) {
|
|
69
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
70
|
+
}
|
|
71
|
+
log.info(tag, '✅ Refresh worker stopped');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Poll for next job from the queue
|
|
75
|
+
*/
|
|
76
|
+
async poll() {
|
|
77
|
+
const tag = TAG + 'poll | ';
|
|
78
|
+
if (!this.isRunning) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
// Don't poll if already processing
|
|
83
|
+
if (this.isProcessing) {
|
|
84
|
+
this.schedulePoll();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Get next job from queue
|
|
88
|
+
const work = await this.redisQueue.getWork(this.config.queueName, 1);
|
|
89
|
+
if (work) {
|
|
90
|
+
this.isProcessing = true;
|
|
91
|
+
await this.processJob(work);
|
|
92
|
+
this.isProcessing = false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
log.error(tag, 'Error in poll loop:', error.message);
|
|
97
|
+
this.isProcessing = false;
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
// Schedule next poll
|
|
101
|
+
this.schedulePoll();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Schedule next poll
|
|
106
|
+
*/
|
|
107
|
+
schedulePoll() {
|
|
108
|
+
if (this.pollTimeoutId) {
|
|
109
|
+
clearTimeout(this.pollTimeoutId);
|
|
110
|
+
}
|
|
111
|
+
const pollInterval = this.config.pollInterval || 100;
|
|
112
|
+
this.pollTimeoutId = setTimeout(() => {
|
|
113
|
+
this.poll();
|
|
114
|
+
}, pollInterval);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Process a single refresh job
|
|
118
|
+
*/
|
|
119
|
+
async processJob(job) {
|
|
120
|
+
const tag = TAG + 'processJob | ';
|
|
121
|
+
const startTime = Date.now();
|
|
122
|
+
try {
|
|
123
|
+
const { type, key, params, retryCount = 0 } = job;
|
|
124
|
+
log.info(tag, `Processing ${type} for ${key} (retry: ${retryCount})`);
|
|
125
|
+
// Extract cache name from job type (e.g., "REFRESH_BALANCE" -> "balance")
|
|
126
|
+
const cacheName = type.replace('REFRESH_', '').toLowerCase();
|
|
127
|
+
// Get the appropriate cache instance
|
|
128
|
+
const cache = this.cacheRegistry.get(cacheName);
|
|
129
|
+
if (!cache) {
|
|
130
|
+
log.error(tag, `No cache registered for type: ${type}`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Fetch fresh data using the cache's fetchFresh method
|
|
134
|
+
await cache.fetchFresh(params);
|
|
135
|
+
const processingTime = Date.now() - startTime;
|
|
136
|
+
log.info(tag, `✅ Processed ${type} in ${processingTime}ms: ${key}`);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
const processingTime = Date.now() - startTime;
|
|
140
|
+
log.error(tag, `❌ Failed to process ${job.type} after ${processingTime}ms:`, error);
|
|
141
|
+
// Retry logic
|
|
142
|
+
if ((job.retryCount || 0) < this.config.maxRetries) {
|
|
143
|
+
const newRetryCount = (job.retryCount || 0) + 1;
|
|
144
|
+
log.info(tag, `Retrying job (attempt ${newRetryCount}/${this.config.maxRetries})`);
|
|
145
|
+
// Re-queue with incremented retry count and delay
|
|
146
|
+
setTimeout(async () => {
|
|
147
|
+
try {
|
|
148
|
+
await this.redisQueue.createWork(this.config.queueName, {
|
|
149
|
+
...job,
|
|
150
|
+
retryCount: newRetryCount
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (requeueError) {
|
|
154
|
+
log.error(tag, 'Error re-queuing job:', requeueError);
|
|
155
|
+
}
|
|
156
|
+
}, this.config.retryDelay);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
log.error(tag, `Max retries reached for ${job.type}, giving up`);
|
|
160
|
+
}
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get worker statistics
|
|
166
|
+
*/
|
|
167
|
+
async getStats() {
|
|
168
|
+
const tag = TAG + 'getStats | ';
|
|
169
|
+
try {
|
|
170
|
+
const queueLength = await this.redisQueue.count(this.config.queueName);
|
|
171
|
+
return {
|
|
172
|
+
isRunning: this.isRunning,
|
|
173
|
+
isProcessing: this.isProcessing,
|
|
174
|
+
queueName: this.config.queueName,
|
|
175
|
+
queueLength,
|
|
176
|
+
registeredCaches: Array.from(this.cacheRegistry.keys()),
|
|
177
|
+
config: {
|
|
178
|
+
maxRetries: this.config.maxRetries,
|
|
179
|
+
retryDelay: this.config.retryDelay,
|
|
180
|
+
pollInterval: this.config.pollInterval || 100
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
log.error(tag, 'Error getting worker stats:', error);
|
|
186
|
+
return {
|
|
187
|
+
error: error instanceof Error ? error.message : String(error)
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.RefreshWorker = RefreshWorker;
|
|
193
|
+
/**
|
|
194
|
+
* Start a unified refresh worker for multiple cache types
|
|
195
|
+
* Convenience function for common usage
|
|
196
|
+
*/
|
|
197
|
+
async function startUnifiedWorker(redis, caches, queueName, config) {
|
|
198
|
+
const workerConfig = {
|
|
199
|
+
queueName,
|
|
200
|
+
maxRetries: config?.maxRetries || 3,
|
|
201
|
+
retryDelay: config?.retryDelay || 5000,
|
|
202
|
+
pollInterval: config?.pollInterval || 100
|
|
203
|
+
};
|
|
204
|
+
const worker = new RefreshWorker(redis, workerConfig);
|
|
205
|
+
// Register all caches
|
|
206
|
+
for (const [name, cache] of caches) {
|
|
207
|
+
worker.registerCache(name, cache);
|
|
208
|
+
}
|
|
209
|
+
// Start worker
|
|
210
|
+
await worker.start();
|
|
211
|
+
return worker;
|
|
212
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pioneer-platform/pioneer-cache",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Unified caching system for Pioneer platform with Redis backend",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch",
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"lint": "eslint src/**/*.ts"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"pioneer",
|
|
15
|
+
"cache",
|
|
16
|
+
"redis",
|
|
17
|
+
"balance",
|
|
18
|
+
"price"
|
|
19
|
+
],
|
|
20
|
+
"author": "Pioneer Platform",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@pioneer-platform/loggerdog": "workspace:*",
|
|
24
|
+
"@pioneer-platform/redis-queue": "workspace:*",
|
|
25
|
+
"@pioneer-platform/default-redis": "workspace:*"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"typescript": "^5.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|