bulltrackers-module 1.0.213 → 1.0.215
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/functions/computation-system/controllers/computation_controller.js +25 -82
- package/functions/computation-system/helpers/computation_dispatcher.js +11 -24
- package/functions/computation-system/helpers/computation_manifest_builder.js +52 -89
- package/functions/computation-system/helpers/computation_pass_runner.js +23 -63
- package/functions/computation-system/helpers/computation_worker.js +11 -53
- package/functions/computation-system/helpers/orchestration_helpers.js +89 -208
- package/functions/task-engine/helpers/update_helpers.js +34 -70
- package/package.json +1 -1
- package/functions/computation-system/layers/math_primitives.js +0 -744
|
@@ -33,24 +33,22 @@ async function runComputationPass(config, dependencies, computationManifest) {
|
|
|
33
33
|
insights: new Date('2025-08-26T00:00:00Z'),
|
|
34
34
|
price: new Date('2025-08-01T00:00:00Z')
|
|
35
35
|
};
|
|
36
|
+
|
|
36
37
|
earliestDates.absoluteEarliest = Object.values(earliestDates).reduce((a, b) => a < b ? a : b);
|
|
37
38
|
|
|
38
39
|
const passes = groupByPass(computationManifest);
|
|
39
40
|
const calcsInThisPass = passes[passToRun] || [];
|
|
40
41
|
|
|
41
|
-
if (!calcsInThisPass.length)
|
|
42
|
-
return logger.log('WARN', `[PassRunner] No calcs for Pass ${passToRun}. Exiting.`);
|
|
43
|
-
|
|
44
|
-
const passEarliestDate = earliestDates.absoluteEarliest;
|
|
45
|
-
const endDateUTC = new Date(Date.UTC(new Date().getUTCFullYear(), new Date().getUTCMonth(), new Date().getUTCDate() - 1));
|
|
46
|
-
const allExpectedDates = getExpectedDateStrings(passEarliestDate, endDateUTC);
|
|
42
|
+
if (!calcsInThisPass.length) return logger.log('WARN', `[PassRunner] No calcs for Pass ${passToRun}. Exiting.`);
|
|
47
43
|
|
|
48
|
-
const
|
|
44
|
+
const passEarliestDate = earliestDates.absoluteEarliest;
|
|
45
|
+
const endDateUTC = new Date(Date.UTC(new Date().getUTCFullYear(), new Date().getUTCMonth(), new Date().getUTCDate() - 1));
|
|
46
|
+
const allExpectedDates = getExpectedDateStrings(passEarliestDate, endDateUTC);
|
|
47
|
+
const priceBatchCalcs = calcsInThisPass.filter(c => c.type === 'meta' && c.rootDataDependencies?.includes('price'));
|
|
49
48
|
const standardAndOtherMetaCalcs = calcsInThisPass.filter(c => !priceBatchCalcs.includes(c));
|
|
50
49
|
|
|
51
50
|
if (priceBatchCalcs.length > 0) {
|
|
52
|
-
try {
|
|
53
|
-
await runBatchPriceComputation(config, dependencies, allExpectedDates, priceBatchCalcs);
|
|
51
|
+
try { await runBatchPriceComputation(config, dependencies, allExpectedDates, priceBatchCalcs);
|
|
54
52
|
} catch (e) { logger.log('ERROR', 'Legacy Batch Price failed', e); }
|
|
55
53
|
}
|
|
56
54
|
|
|
@@ -63,51 +61,26 @@ async function runComputationPass(config, dependencies, computationManifest) {
|
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, dependencies, computationManifest) {
|
|
66
|
-
const { logger }
|
|
64
|
+
const { logger } = dependencies;
|
|
67
65
|
const dateToProcess = new Date(dateStr + 'T00:00:00Z');
|
|
68
|
-
|
|
69
|
-
const dailyStatus = await fetchComputationStatus(dateStr, config, dependencies);
|
|
70
|
-
|
|
71
|
-
// Filter AND Log reason for skipping
|
|
66
|
+
const dailyStatus = await fetchComputationStatus(dateStr, config, dependencies);
|
|
72
67
|
const calcsToAttempt = [];
|
|
73
|
-
|
|
74
68
|
for (const calc of calcsInThisPass) {
|
|
75
|
-
const cName
|
|
69
|
+
const cName = normalizeName(calc.name);
|
|
76
70
|
const storedStatus = dailyStatus[cName];
|
|
77
|
-
const currentHash
|
|
71
|
+
const currentHash = calc.hash;
|
|
78
72
|
|
|
79
73
|
// 1. Dependency Check
|
|
80
|
-
if (calc.dependencies && calc.dependencies.length > 0) {
|
|
81
|
-
const missing = calc.dependencies.filter(depName => !dailyStatus[normalizeName(depName)]);
|
|
82
|
-
if (missing.length > 0) {
|
|
83
|
-
// Too noisy to log every skip, but useful for debugging if needed.
|
|
84
|
-
// Only logging if it's NOT a bulk skip.
|
|
85
|
-
// logger.log('TRACE', `[Skip] ${cName} missing deps: ${missing.join(', ')}`);
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
74
|
+
if (calc.dependencies && calc.dependencies.length > 0) { const missing = calc.dependencies.filter(depName => !dailyStatus[normalizeName(depName)]); if (missing.length > 0) { logger.log('TRACE', `[Skip] ${cName} missing deps: ${missing.join(', ')}`); continue; } }
|
|
89
75
|
|
|
90
76
|
// 2. Logic A: No previous run
|
|
91
|
-
if (!storedStatus) {
|
|
92
|
-
logger.log('INFO', `[Versioning] ${cName}: New run needed (No prior status).`);
|
|
93
|
-
calcsToAttempt.push(calc);
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
77
|
+
if (!storedStatus) { logger.log('INFO', `[Versioning] ${cName}: New run needed (No prior status).`); calcsToAttempt.push(calc); continue; }
|
|
96
78
|
|
|
97
79
|
// 3. Logic B: Hash Mismatch
|
|
98
|
-
|
|
99
|
-
if (typeof storedStatus === 'string' && currentHash && storedStatus !== currentHash) {
|
|
100
|
-
logger.log('INFO', `[Versioning] ${cName}: Code Changed. (Old: ${storedStatus.substring(0,6)}... New: ${currentHash.substring(0,6)}...)`);
|
|
101
|
-
calcsToAttempt.push(calc);
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
80
|
+
if (typeof storedStatus === 'string' && currentHash && storedStatus !== currentHash) { logger.log('INFO', `[Versioning] ${cName}: Code Changed. (Old: ${storedStatus.substring(0,6)}... New: ${currentHash.substring(0,6)}...)`); calcsToAttempt.push(calc); continue; }
|
|
104
81
|
|
|
105
82
|
// 4. Logic C: Upgrade Legacy Boolean -> Hash
|
|
106
|
-
if (storedStatus === true && currentHash) {
|
|
107
|
-
logger.log('INFO', `[Versioning] ${cName}: Upgrading legacy status to Hash.`);
|
|
108
|
-
calcsToAttempt.push(calc);
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
83
|
+
if (storedStatus === true && currentHash) { logger.log('INFO', `[Versioning] ${cName}: Upgrading legacy status to Hash.`); calcsToAttempt.push(calc); continue; }
|
|
111
84
|
}
|
|
112
85
|
|
|
113
86
|
if (!calcsToAttempt.length) return null;
|
|
@@ -121,47 +94,34 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
|
|
|
121
94
|
};
|
|
122
95
|
|
|
123
96
|
const rootData = await checkRootDataAvailability(dateStr, config, dependencies, earliestDates);
|
|
124
|
-
if (!rootData) {
|
|
125
|
-
logger.log('INFO', `[DateRunner] Root data missing for ${dateStr}. Skipping.`);
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
|
|
97
|
+
if (!rootData) { logger.log('INFO', `[DateRunner] Root data missing for ${dateStr}. Skipping.`); return null; }
|
|
129
98
|
const runnableCalcs = calcsToAttempt.filter(c => checkRootDependencies(c, rootData.status).canRun);
|
|
130
|
-
|
|
131
99
|
if (!runnableCalcs.length) return null;
|
|
132
|
-
|
|
133
100
|
const standardToRun = runnableCalcs.filter(c => c.type === 'standard');
|
|
134
101
|
const metaToRun = runnableCalcs.filter(c => c.type === 'meta');
|
|
135
|
-
|
|
136
102
|
logger.log('INFO', `[DateRunner] Running ${dateStr}: ${standardToRun.length} std, ${metaToRun.length} meta`);
|
|
137
|
-
|
|
138
103
|
const dateUpdates = {};
|
|
139
104
|
|
|
140
105
|
try {
|
|
141
106
|
const calcsRunning = [...standardToRun, ...metaToRun];
|
|
142
|
-
|
|
143
107
|
const existingResults = await fetchExistingResults(dateStr, calcsRunning, computationManifest, config, dependencies, false);
|
|
144
108
|
const prevDate = new Date(dateToProcess); prevDate.setUTCDate(prevDate.getUTCDate() - 1);
|
|
145
109
|
const prevDateStr = prevDate.toISOString().slice(0, 10);
|
|
146
110
|
const previousResults = await fetchExistingResults(prevDateStr, calcsRunning, computationManifest, config, dependencies, true);
|
|
147
111
|
|
|
148
|
-
if (standardToRun.length) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Object.assign(dateUpdates, updates);
|
|
155
|
-
}
|
|
112
|
+
if (standardToRun.length) { const updates = await runStandardComputationPass(dateToProcess, standardToRun, `Pass ${passToRun} (Std)`, config, dependencies, rootData, existingResults, previousResults, false);
|
|
113
|
+
Object.assign(dateUpdates, updates); }
|
|
114
|
+
|
|
115
|
+
if (metaToRun.length) { const updates = await runMetaComputationPass(dateToProcess, metaToRun, `Pass ${passToRun} (Meta)`, config, dependencies, existingResults, previousResults, rootData, false);
|
|
116
|
+
Object.assign(dateUpdates, updates); }
|
|
117
|
+
|
|
156
118
|
} catch (err) {
|
|
157
119
|
logger.log('ERROR', `[DateRunner] FAILED Pass ${passToRun} for ${dateStr}`, { errorMessage: err.message });
|
|
158
120
|
[...standardToRun, ...metaToRun].forEach(c => dateUpdates[normalizeName(c.name)] = false);
|
|
159
121
|
throw err;
|
|
160
122
|
}
|
|
161
123
|
|
|
162
|
-
if (Object.keys(dateUpdates).length > 0) {
|
|
163
|
-
await updateComputationStatus(dateStr, dateUpdates, config, dependencies);
|
|
164
|
-
}
|
|
124
|
+
if (Object.keys(dateUpdates).length > 0) { await updateComputationStatus(dateStr, dateUpdates, config, dependencies); }
|
|
165
125
|
|
|
166
126
|
return { date: dateStr, updates: dateUpdates };
|
|
167
127
|
}
|
|
@@ -17,70 +17,28 @@ async function handleComputationTask(message, config, dependencies, computationM
|
|
|
17
17
|
let data;
|
|
18
18
|
try {
|
|
19
19
|
// 1. Handle Cloud Functions Gen 2 (CloudEvent)
|
|
20
|
-
|
|
21
|
-
if (message.data && message.data.message && message.data.message.data) {
|
|
22
|
-
const buffer = Buffer.from(message.data.message.data, 'base64');
|
|
23
|
-
data = JSON.parse(buffer.toString());
|
|
24
|
-
}
|
|
20
|
+
if (message.data && message.data.message && message.data.message.data) { const buffer = Buffer.from(message.data.message.data, 'base64'); data = JSON.parse(buffer.toString()); }
|
|
25
21
|
// 2. Handle Cloud Functions Gen 1 / Legacy PubSub
|
|
26
|
-
|
|
27
|
-
else if (message.data && typeof message.data === 'string') {
|
|
28
|
-
const buffer = Buffer.from(message.data, 'base64');
|
|
29
|
-
data = JSON.parse(buffer.toString());
|
|
30
|
-
}
|
|
22
|
+
else if (message.data && typeof message.data === 'string') { const buffer = Buffer.from(message.data, 'base64'); data = JSON.parse(buffer.toString()); }
|
|
31
23
|
// 3. Handle Direct JSON (Test harness or simulator)
|
|
32
|
-
else if (message.json) {
|
|
33
|
-
data = message.json;
|
|
34
|
-
}
|
|
24
|
+
else if (message.json) { data = message.json; }
|
|
35
25
|
// 4. Fallback: Assume message is the payload
|
|
36
|
-
else {
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
} catch (parseError) {
|
|
40
|
-
logger.log('ERROR', `[Worker] Failed to parse Pub/Sub payload.`, { error: parseError.message });
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
26
|
+
else { data = message; }
|
|
27
|
+
} catch (parseError) { logger.log('ERROR', `[Worker] Failed to parse Pub/Sub payload.`, { error: parseError.message }); return; }
|
|
43
28
|
|
|
44
29
|
try {
|
|
45
30
|
// Validate Action
|
|
46
|
-
if (!data || data.action !== 'RUN_COMPUTATION_DATE') {
|
|
47
|
-
// Only log if data exists but action is wrong, prevents log spam on empty messages
|
|
48
|
-
if (data) logger.log('WARN', `[Worker] Unknown or missing action: ${data?.action}. Ignoring.`);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
31
|
+
if (!data || data.action !== 'RUN_COMPUTATION_DATE') { if (data) logger.log('WARN', `[Worker] Unknown or missing action: ${data?.action}. Ignoring.`); return; }
|
|
52
32
|
const { date, pass } = data;
|
|
53
|
-
|
|
54
|
-
if (!date || !pass) {
|
|
55
|
-
logger.log('ERROR', `[Worker] Missing date or pass in payload: ${JSON.stringify(data)}`);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
33
|
+
if (!date || !pass) { logger.log('ERROR', `[Worker] Missing date or pass in payload: ${JSON.stringify(data)}`); return; }
|
|
59
34
|
logger.log('INFO', `[Worker] Received task: Date=${date}, Pass=${pass}`);
|
|
60
|
-
|
|
61
|
-
// Resolve calculations for this pass
|
|
62
35
|
const passes = groupByPass(computationManifest);
|
|
63
36
|
const calcsInThisPass = passes[pass] || [];
|
|
64
|
-
|
|
65
|
-
if (!calcsInThisPass.length) {
|
|
66
|
-
logger.log('WARN', `[Worker] No calculations found for Pass ${pass}.`);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Execute the computation for this specific date
|
|
71
|
-
// The runner internally checks dependencies (Pass 1, 2, 3 status) and skips if not ready.
|
|
37
|
+
if (!calcsInThisPass.length) { logger.log('WARN', `[Worker] No calculations found for Pass ${pass}.`); return; }
|
|
72
38
|
const result = await runDateComputation(date, pass, calcsInThisPass, config, dependencies, computationManifest);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
} else {
|
|
77
|
-
logger.log('INFO', `[Worker] Processed ${date} (Pass ${pass}) - Skipped (Dependencies missing or already done).`);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
} catch (err) {
|
|
81
|
-
logger.log('ERROR', `[Worker] Fatal error processing task: ${err.message}`, { stack: err.stack });
|
|
82
|
-
throw err; // Re-throw to trigger Pub/Sub retry
|
|
83
|
-
}
|
|
39
|
+
if (result) { logger.log('INFO', `[Worker] Successfully processed ${date} (Pass ${pass}). Updates: ${Object.keys(result.updates || {}).length}`);
|
|
40
|
+
} else { logger.log('INFO', `[Worker] Processed ${date} (Pass ${pass}) - Skipped (Dependencies missing or already done).`); }
|
|
41
|
+
} catch (err) { logger.log('ERROR', `[Worker] Fatal error processing task: ${err.message}`, { stack: err.stack }); throw err; }
|
|
84
42
|
}
|
|
85
43
|
|
|
86
44
|
module.exports = { handleComputationTask };
|