bulltrackers-module 1.0.211 → 1.0.213
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 +199 -188
- package/functions/computation-system/helpers/computation_dispatcher.js +90 -90
- package/functions/computation-system/helpers/computation_manifest_builder.js +327 -283
- package/functions/computation-system/helpers/computation_pass_runner.js +168 -157
- package/functions/computation-system/helpers/computation_worker.js +85 -85
- package/functions/computation-system/helpers/orchestration_helpers.js +542 -558
- package/functions/computation-system/layers/extractors.js +279 -0
- package/functions/computation-system/layers/index.js +40 -0
- package/functions/computation-system/layers/math_primitives.js +743 -743
- package/functions/computation-system/layers/mathematics.js +397 -0
- package/functions/computation-system/layers/profiling.js +287 -0
- package/functions/computation-system/layers/validators.js +170 -0
- package/functions/computation-system/utils/schema_capture.js +63 -63
- package/functions/computation-system/utils/utils.js +22 -1
- package/functions/task-engine/helpers/update_helpers.js +17 -49
- package/package.json +1 -1
|
@@ -1,158 +1,169 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FILENAME: bulltrackers-module/functions/computation-system/helpers/computation_pass_runner.js
|
|
3
|
-
* FIXED: '
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
groupByPass,
|
|
8
|
-
checkRootDataAvailability,
|
|
9
|
-
fetchExistingResults,
|
|
10
|
-
fetchComputationStatus,
|
|
11
|
-
updateComputationStatus,
|
|
12
|
-
runStandardComputationPass,
|
|
13
|
-
runMetaComputationPass,
|
|
14
|
-
checkRootDependencies,
|
|
15
|
-
runBatchPriceComputation
|
|
16
|
-
} = require('./orchestration_helpers.js');
|
|
17
|
-
|
|
18
|
-
const { getExpectedDateStrings, normalizeName } = require('../utils/utils.js');
|
|
19
|
-
|
|
20
|
-
const PARALLEL_BATCH_SIZE = 7;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
1
|
+
/**
|
|
2
|
+
* FILENAME: bulltrackers-module/functions/computation-system/helpers/computation_pass_runner.js
|
|
3
|
+
* FIXED: 'storedStatus.substring' crash and 'missing dependency' log clarity.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
groupByPass,
|
|
8
|
+
checkRootDataAvailability,
|
|
9
|
+
fetchExistingResults,
|
|
10
|
+
fetchComputationStatus,
|
|
11
|
+
updateComputationStatus,
|
|
12
|
+
runStandardComputationPass,
|
|
13
|
+
runMetaComputationPass,
|
|
14
|
+
checkRootDependencies,
|
|
15
|
+
runBatchPriceComputation
|
|
16
|
+
} = require('./orchestration_helpers.js');
|
|
17
|
+
|
|
18
|
+
const { getExpectedDateStrings, normalizeName } = require('../utils/utils.js');
|
|
19
|
+
|
|
20
|
+
const PARALLEL_BATCH_SIZE = 7;
|
|
21
|
+
|
|
22
|
+
async function runComputationPass(config, dependencies, computationManifest) {
|
|
23
|
+
const { logger } = dependencies;
|
|
24
|
+
const passToRun = String(config.COMPUTATION_PASS_TO_RUN);
|
|
25
|
+
if (!passToRun) return logger.log('ERROR', '[PassRunner] No pass defined. Aborting.');
|
|
26
|
+
|
|
27
|
+
logger.log('INFO', `🚀 Starting PASS ${passToRun} (Legacy Mode)...`);
|
|
28
|
+
|
|
29
|
+
const earliestDates = {
|
|
30
|
+
portfolio: new Date('2025-09-25T00:00:00Z'),
|
|
31
|
+
history: new Date('2025-11-05T00:00:00Z'),
|
|
32
|
+
social: new Date('2025-10-30T00:00:00Z'),
|
|
33
|
+
insights: new Date('2025-08-26T00:00:00Z'),
|
|
34
|
+
price: new Date('2025-08-01T00:00:00Z')
|
|
35
|
+
};
|
|
36
|
+
earliestDates.absoluteEarliest = Object.values(earliestDates).reduce((a, b) => a < b ? a : b);
|
|
37
|
+
|
|
38
|
+
const passes = groupByPass(computationManifest);
|
|
39
|
+
const calcsInThisPass = passes[passToRun] || [];
|
|
40
|
+
|
|
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);
|
|
47
|
+
|
|
48
|
+
const priceBatchCalcs = calcsInThisPass.filter(c => c.type === 'meta' && c.rootDataDependencies?.includes('price'));
|
|
49
|
+
const standardAndOtherMetaCalcs = calcsInThisPass.filter(c => !priceBatchCalcs.includes(c));
|
|
50
|
+
|
|
51
|
+
if (priceBatchCalcs.length > 0) {
|
|
52
|
+
try {
|
|
53
|
+
await runBatchPriceComputation(config, dependencies, allExpectedDates, priceBatchCalcs);
|
|
54
|
+
} catch (e) { logger.log('ERROR', 'Legacy Batch Price failed', e); }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (standardAndOtherMetaCalcs.length === 0) return;
|
|
58
|
+
|
|
59
|
+
for (let i = 0; i < allExpectedDates.length; i += PARALLEL_BATCH_SIZE) {
|
|
60
|
+
const batch = allExpectedDates.slice(i, i + PARALLEL_BATCH_SIZE);
|
|
61
|
+
await Promise.all(batch.map(dateStr => runDateComputation(dateStr, passToRun, standardAndOtherMetaCalcs, config, dependencies, computationManifest)));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, dependencies, computationManifest) {
|
|
66
|
+
const { logger } = dependencies;
|
|
67
|
+
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
|
|
72
|
+
const calcsToAttempt = [];
|
|
73
|
+
|
|
74
|
+
for (const calc of calcsInThisPass) {
|
|
75
|
+
const cName = normalizeName(calc.name);
|
|
76
|
+
const storedStatus = dailyStatus[cName];
|
|
77
|
+
const currentHash = calc.hash;
|
|
78
|
+
|
|
79
|
+
// 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
|
+
}
|
|
89
|
+
|
|
90
|
+
// 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
|
+
}
|
|
96
|
+
|
|
97
|
+
// 3. Logic B: Hash Mismatch
|
|
98
|
+
// FIX: Ensure storedStatus is a string before calling substring
|
|
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
|
+
}
|
|
104
|
+
|
|
105
|
+
// 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
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!calcsToAttempt.length) return null;
|
|
114
|
+
|
|
115
|
+
const earliestDates = {
|
|
116
|
+
portfolio: new Date('2025-09-25T00:00:00Z'),
|
|
117
|
+
history: new Date('2025-11-05T00:00:00Z'),
|
|
118
|
+
social: new Date('2025-10-30T00:00:00Z'),
|
|
119
|
+
insights: new Date('2025-08-26T00:00:00Z'),
|
|
120
|
+
price: new Date('2025-08-01T00:00:00Z')
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
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
|
+
|
|
129
|
+
const runnableCalcs = calcsToAttempt.filter(c => checkRootDependencies(c, rootData.status).canRun);
|
|
130
|
+
|
|
131
|
+
if (!runnableCalcs.length) return null;
|
|
132
|
+
|
|
133
|
+
const standardToRun = runnableCalcs.filter(c => c.type === 'standard');
|
|
134
|
+
const metaToRun = runnableCalcs.filter(c => c.type === 'meta');
|
|
135
|
+
|
|
136
|
+
logger.log('INFO', `[DateRunner] Running ${dateStr}: ${standardToRun.length} std, ${metaToRun.length} meta`);
|
|
137
|
+
|
|
138
|
+
const dateUpdates = {};
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const calcsRunning = [...standardToRun, ...metaToRun];
|
|
142
|
+
|
|
143
|
+
const existingResults = await fetchExistingResults(dateStr, calcsRunning, computationManifest, config, dependencies, false);
|
|
144
|
+
const prevDate = new Date(dateToProcess); prevDate.setUTCDate(prevDate.getUTCDate() - 1);
|
|
145
|
+
const prevDateStr = prevDate.toISOString().slice(0, 10);
|
|
146
|
+
const previousResults = await fetchExistingResults(prevDateStr, calcsRunning, computationManifest, config, dependencies, true);
|
|
147
|
+
|
|
148
|
+
if (standardToRun.length) {
|
|
149
|
+
const updates = await runStandardComputationPass(dateToProcess, standardToRun, `Pass ${passToRun} (Std)`, config, dependencies, rootData, existingResults, previousResults, false);
|
|
150
|
+
Object.assign(dateUpdates, updates);
|
|
151
|
+
}
|
|
152
|
+
if (metaToRun.length) {
|
|
153
|
+
const updates = await runMetaComputationPass(dateToProcess, metaToRun, `Pass ${passToRun} (Meta)`, config, dependencies, existingResults, previousResults, rootData, false);
|
|
154
|
+
Object.assign(dateUpdates, updates);
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
logger.log('ERROR', `[DateRunner] FAILED Pass ${passToRun} for ${dateStr}`, { errorMessage: err.message });
|
|
158
|
+
[...standardToRun, ...metaToRun].forEach(c => dateUpdates[normalizeName(c.name)] = false);
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (Object.keys(dateUpdates).length > 0) {
|
|
163
|
+
await updateComputationStatus(dateStr, dateUpdates, config, dependencies);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { date: dateStr, updates: dateUpdates };
|
|
167
|
+
}
|
|
168
|
+
|
|
158
169
|
module.exports = { runComputationPass, runDateComputation };
|
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FILENAME: bulltrackers-module/functions/computation-system/helpers/computation_worker.js
|
|
3
|
-
* PURPOSE: Consumes computation tasks from Pub/Sub and executes them.
|
|
4
|
-
* FIXED: Added robust payload parsing to handle Cloud Functions Gen 2 (CloudEvents).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { runDateComputation } = require('./computation_pass_runner.js');
|
|
8
|
-
const { groupByPass } = require('./orchestration_helpers.js');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Handles a single Pub/Sub message for a computation task.
|
|
12
|
-
* Supports both Gen 1 (Message) and Gen 2 (CloudEvent) formats.
|
|
13
|
-
*/
|
|
14
|
-
async function handleComputationTask(message, config, dependencies, computationManifest) {
|
|
15
|
-
const { logger } = dependencies;
|
|
16
|
-
|
|
17
|
-
let data;
|
|
18
|
-
try {
|
|
19
|
-
// 1. Handle Cloud Functions Gen 2 (CloudEvent)
|
|
20
|
-
// Structure: event.data.message.data (base64)
|
|
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
|
-
}
|
|
25
|
-
// 2. Handle Cloud Functions Gen 1 / Legacy PubSub
|
|
26
|
-
// Structure: message.data (base64) or message.json
|
|
27
|
-
else if (message.data && typeof message.data === 'string') {
|
|
28
|
-
const buffer = Buffer.from(message.data, 'base64');
|
|
29
|
-
data = JSON.parse(buffer.toString());
|
|
30
|
-
}
|
|
31
|
-
// 3. Handle Direct JSON (Test harness or simulator)
|
|
32
|
-
else if (message.json) {
|
|
33
|
-
data = message.json;
|
|
34
|
-
}
|
|
35
|
-
// 4. Fallback: Assume message is the payload
|
|
36
|
-
else {
|
|
37
|
-
data = message;
|
|
38
|
-
}
|
|
39
|
-
} catch (parseError) {
|
|
40
|
-
logger.log('ERROR', `[Worker] Failed to parse Pub/Sub payload.`, { error: parseError.message });
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
// 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
|
-
|
|
52
|
-
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
|
-
|
|
59
|
-
logger.log('INFO', `[Worker] Received task: Date=${date}, Pass=${pass}`);
|
|
60
|
-
|
|
61
|
-
// Resolve calculations for this pass
|
|
62
|
-
const passes = groupByPass(computationManifest);
|
|
63
|
-
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.
|
|
72
|
-
const result = await runDateComputation(date, pass, calcsInThisPass, config, dependencies, computationManifest);
|
|
73
|
-
|
|
74
|
-
if (result) {
|
|
75
|
-
logger.log('INFO', `[Worker] Successfully processed ${date} (Pass ${pass}). Updates: ${Object.keys(result.updates || {}).length}`);
|
|
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
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
1
|
+
/**
|
|
2
|
+
* FILENAME: bulltrackers-module/functions/computation-system/helpers/computation_worker.js
|
|
3
|
+
* PURPOSE: Consumes computation tasks from Pub/Sub and executes them.
|
|
4
|
+
* FIXED: Added robust payload parsing to handle Cloud Functions Gen 2 (CloudEvents).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { runDateComputation } = require('./computation_pass_runner.js');
|
|
8
|
+
const { groupByPass } = require('./orchestration_helpers.js');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Handles a single Pub/Sub message for a computation task.
|
|
12
|
+
* Supports both Gen 1 (Message) and Gen 2 (CloudEvent) formats.
|
|
13
|
+
*/
|
|
14
|
+
async function handleComputationTask(message, config, dependencies, computationManifest) {
|
|
15
|
+
const { logger } = dependencies;
|
|
16
|
+
|
|
17
|
+
let data;
|
|
18
|
+
try {
|
|
19
|
+
// 1. Handle Cloud Functions Gen 2 (CloudEvent)
|
|
20
|
+
// Structure: event.data.message.data (base64)
|
|
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
|
+
}
|
|
25
|
+
// 2. Handle Cloud Functions Gen 1 / Legacy PubSub
|
|
26
|
+
// Structure: message.data (base64) or message.json
|
|
27
|
+
else if (message.data && typeof message.data === 'string') {
|
|
28
|
+
const buffer = Buffer.from(message.data, 'base64');
|
|
29
|
+
data = JSON.parse(buffer.toString());
|
|
30
|
+
}
|
|
31
|
+
// 3. Handle Direct JSON (Test harness or simulator)
|
|
32
|
+
else if (message.json) {
|
|
33
|
+
data = message.json;
|
|
34
|
+
}
|
|
35
|
+
// 4. Fallback: Assume message is the payload
|
|
36
|
+
else {
|
|
37
|
+
data = message;
|
|
38
|
+
}
|
|
39
|
+
} catch (parseError) {
|
|
40
|
+
logger.log('ERROR', `[Worker] Failed to parse Pub/Sub payload.`, { error: parseError.message });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// 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
|
+
|
|
52
|
+
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
|
+
|
|
59
|
+
logger.log('INFO', `[Worker] Received task: Date=${date}, Pass=${pass}`);
|
|
60
|
+
|
|
61
|
+
// Resolve calculations for this pass
|
|
62
|
+
const passes = groupByPass(computationManifest);
|
|
63
|
+
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.
|
|
72
|
+
const result = await runDateComputation(date, pass, calcsInThisPass, config, dependencies, computationManifest);
|
|
73
|
+
|
|
74
|
+
if (result) {
|
|
75
|
+
logger.log('INFO', `[Worker] Successfully processed ${date} (Pass ${pass}). Updates: ${Object.keys(result.updates || {}).length}`);
|
|
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
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
86
|
module.exports = { handleComputationTask };
|