bulltrackers-module 1.0.186 → 1.0.187
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FIXED: computation_controller.js
|
|
3
|
-
*
|
|
3
|
+
* V4.0: Implements Batch/Sharded Execution for Price Dependencies to prevent OOM.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { DataExtractor,
|
|
@@ -43,36 +43,54 @@ class DataLoader {
|
|
|
43
43
|
this.cache.social.set(dateStr, social);
|
|
44
44
|
return social;
|
|
45
45
|
}
|
|
46
|
+
|
|
46
47
|
/**
|
|
47
|
-
* NEW:
|
|
48
|
+
* NEW: Get references to all price shards without loading data.
|
|
49
|
+
*/
|
|
50
|
+
async getPriceShardReferences() {
|
|
51
|
+
const { db, logger } = this.deps;
|
|
52
|
+
const collection = this.config.priceCollection || 'asset_prices';
|
|
53
|
+
try {
|
|
54
|
+
const refs = await db.collection(collection).listDocuments();
|
|
55
|
+
logger.log('INFO', `[DataLoader] Found ${refs.length} price shards to process.`);
|
|
56
|
+
return refs;
|
|
57
|
+
} catch (e) {
|
|
58
|
+
logger.log('ERROR', `[DataLoader] Failed to list price shards: ${e.message}`);
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* NEW: Load a single price shard.
|
|
65
|
+
*/
|
|
66
|
+
async loadPriceShard(docRef) {
|
|
67
|
+
try {
|
|
68
|
+
const snap = await docRef.get();
|
|
69
|
+
if (!snap.exists) return {};
|
|
70
|
+
return snap.data();
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.error(`Error loading shard ${docRef.path}:`, e);
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* LEGACY: Kept for non-batched calls if ever needed, but effectively replaced by batching.
|
|
48
79
|
*/
|
|
49
80
|
async loadPrices() {
|
|
50
|
-
if (this.cache.prices) return this.cache.prices;
|
|
51
81
|
const { db, logger } = this.deps;
|
|
52
82
|
const collection = this.config.priceCollection || 'asset_prices';
|
|
53
|
-
|
|
54
|
-
logger.log('INFO', `[DataLoader] Loading all price shards from ${collection}...`);
|
|
55
|
-
|
|
83
|
+
logger.log('WARN', `[DataLoader] loadPrices() called. This is memory intensive!`);
|
|
56
84
|
try {
|
|
57
85
|
const snapshot = await db.collection(collection).get();
|
|
58
86
|
if (snapshot.empty) return { history: {} };
|
|
59
|
-
|
|
60
87
|
const historyMap = {};
|
|
61
|
-
|
|
62
88
|
snapshot.forEach(doc => {
|
|
63
89
|
const shardData = doc.data();
|
|
64
|
-
// Merge shard keys (instrumentIds) into main map
|
|
65
90
|
if (shardData) Object.assign(historyMap, shardData);
|
|
66
91
|
});
|
|
67
|
-
|
|
68
|
-
logger.log('INFO', `[DataLoader] Loaded prices for ${Object.keys(historyMap).length} instruments.`);
|
|
69
|
-
|
|
70
|
-
// Cache as an object with 'history' map to match priceExtractor expectations
|
|
71
|
-
this.cache.prices = { history: historyMap };
|
|
72
|
-
return this.cache.prices;
|
|
73
|
-
|
|
92
|
+
return { history: historyMap };
|
|
74
93
|
} catch (e) {
|
|
75
|
-
logger.log('ERROR', `[DataLoader] Failed to load prices: ${e.message}`);
|
|
76
94
|
return { history: {} };
|
|
77
95
|
}
|
|
78
96
|
}
|
|
@@ -81,21 +99,9 @@ class DataLoader {
|
|
|
81
99
|
class ContextBuilder {
|
|
82
100
|
static buildPerUserContext(options) {
|
|
83
101
|
const {
|
|
84
|
-
todayPortfolio,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
yesterdayHistory,
|
|
88
|
-
userId,
|
|
89
|
-
userType,
|
|
90
|
-
dateStr,
|
|
91
|
-
metadata,
|
|
92
|
-
mappings,
|
|
93
|
-
insights,
|
|
94
|
-
socialData,
|
|
95
|
-
computedDependencies,
|
|
96
|
-
previousComputedDependencies,
|
|
97
|
-
config,
|
|
98
|
-
deps
|
|
102
|
+
todayPortfolio, yesterdayPortfolio, todayHistory, yesterdayHistory,
|
|
103
|
+
userId, userType, dateStr, metadata, mappings, insights, socialData,
|
|
104
|
+
computedDependencies, previousComputedDependencies, config, deps
|
|
99
105
|
} = options;
|
|
100
106
|
|
|
101
107
|
return {
|
|
@@ -131,23 +137,16 @@ class ContextBuilder {
|
|
|
131
137
|
|
|
132
138
|
static buildMetaContext(options) {
|
|
133
139
|
const {
|
|
134
|
-
dateStr,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
insights,
|
|
138
|
-
socialData,
|
|
139
|
-
prices, // <--- ADDED THIS
|
|
140
|
-
computedDependencies,
|
|
141
|
-
previousComputedDependencies,
|
|
142
|
-
config,
|
|
143
|
-
deps
|
|
140
|
+
dateStr, metadata, mappings, insights, socialData,
|
|
141
|
+
prices,
|
|
142
|
+
computedDependencies, previousComputedDependencies, config, deps
|
|
144
143
|
} = options;
|
|
145
144
|
|
|
146
145
|
return {
|
|
147
146
|
date: { today: dateStr },
|
|
148
147
|
insights: { today: insights?.today, yesterday: insights?.yesterday },
|
|
149
148
|
social: { today: socialData?.today, yesterday: socialData?.yesterday },
|
|
150
|
-
prices: prices || {},
|
|
149
|
+
prices: prices || {},
|
|
151
150
|
mappings: mappings || {},
|
|
152
151
|
math: {
|
|
153
152
|
extract: DataExtractor,
|
|
@@ -207,27 +206,70 @@ class ComputationExecutor {
|
|
|
207
206
|
}
|
|
208
207
|
}
|
|
209
208
|
|
|
209
|
+
/**
|
|
210
|
+
* REFACTORED: Batched execution for Price dependencies
|
|
211
|
+
*/
|
|
210
212
|
async executeOncePerDay(calcInstance, metadata, dateStr, computedDeps, prevDeps) {
|
|
211
213
|
const mappings = await this.loader.loadMappings();
|
|
214
|
+
const { logger } = this.deps;
|
|
212
215
|
|
|
213
|
-
// Load standard dependencies
|
|
214
216
|
const insights = metadata.rootDataDependencies?.includes('insights') ? { today: await this.loader.loadInsights(dateStr) } : null;
|
|
215
217
|
const social = metadata.rootDataDependencies?.includes('social') ? { today: await this.loader.loadSocial(dateStr) } : null;
|
|
216
218
|
|
|
217
|
-
//
|
|
218
|
-
let prices = null;
|
|
219
|
+
// CHECK: Does this calculation require price history?
|
|
219
220
|
if (metadata.rootDataDependencies?.includes('price')) {
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
logger.log('INFO', `[Executor] Running Batched/Sharded Execution for ${metadata.name}`);
|
|
222
|
+
|
|
223
|
+
// 1. Get Shard References (Low Memory)
|
|
224
|
+
const shardRefs = await this.loader.getPriceShardReferences();
|
|
225
|
+
|
|
226
|
+
if (shardRefs.length === 0) {
|
|
227
|
+
logger.log('WARN', '[Executor] No price shards found.');
|
|
228
|
+
return {};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 2. Iterate Shards (One at a time in memory)
|
|
232
|
+
let processedCount = 0;
|
|
233
|
+
for (const ref of shardRefs) {
|
|
234
|
+
// Load ONE shard
|
|
235
|
+
const shardData = await this.loader.loadPriceShard(ref);
|
|
236
|
+
|
|
237
|
+
// Construct a "Partial Context" containing only this shard's prices
|
|
238
|
+
const partialContext = ContextBuilder.buildMetaContext({
|
|
239
|
+
dateStr, metadata, mappings, insights, socialData: social,
|
|
240
|
+
prices: { history: shardData }, // Inject partial history
|
|
241
|
+
computedDependencies: computedDeps,
|
|
242
|
+
previousComputedDependencies: prevDeps,
|
|
243
|
+
config: this.config, deps: this.deps
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Process the chunk
|
|
247
|
+
// IMPORTANT: The calc instance must ACCUMULATE results, not overwrite.
|
|
248
|
+
await calcInstance.process(partialContext);
|
|
249
|
+
|
|
250
|
+
// Force dereference for GC
|
|
251
|
+
partialContext.prices = null;
|
|
252
|
+
processedCount++;
|
|
253
|
+
|
|
254
|
+
if (processedCount % 10 === 0) {
|
|
255
|
+
if (global.gc) { global.gc(); } // Optional: Hint GC if exposed
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
logger.log('INFO', `[Executor] Finished Batched Execution for ${metadata.name} (${processedCount} shards).`);
|
|
260
|
+
return calcInstance.getResult ? await calcInstance.getResult() : {};
|
|
222
261
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
262
|
+
} else {
|
|
263
|
+
// Standard execution for non-price calculations
|
|
264
|
+
const context = ContextBuilder.buildMetaContext({
|
|
265
|
+
dateStr, metadata, mappings, insights, socialData: social,
|
|
266
|
+
prices: {},
|
|
267
|
+
computedDependencies: computedDeps,
|
|
268
|
+
previousComputedDependencies: prevDeps,
|
|
269
|
+
config: this.config, deps: this.deps
|
|
270
|
+
});
|
|
271
|
+
return await calcInstance.process(context);
|
|
272
|
+
}
|
|
231
273
|
}
|
|
232
274
|
}
|
|
233
275
|
|