bulltrackers-module 1.0.201 → 1.0.203
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,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FILENAME: bulltrackers-module/functions/computation-system/helpers/computation_dispatcher.js
|
|
3
3
|
* PURPOSE: Dispatches computation tasks to Pub/Sub for scalable execution.
|
|
4
|
+
* FIXED: Instantiates PubSubUtils locally to ensure valid logger/dependencies are used.
|
|
5
|
+
* IMPROVED: Logging now explicitly lists the calculations being scheduled.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
const { getExpectedDateStrings } = require('../utils/utils.js');
|
|
7
9
|
const { groupByPass } = require('./orchestration_helpers.js');
|
|
10
|
+
const { PubSubUtils } = require('../../core/utils/pubsub_utils');
|
|
8
11
|
|
|
9
12
|
const TOPIC_NAME = 'computation-tasks';
|
|
10
13
|
|
|
@@ -13,16 +16,30 @@ const TOPIC_NAME = 'computation-tasks';
|
|
|
13
16
|
* Instead of running them, it queues them in Pub/Sub.
|
|
14
17
|
*/
|
|
15
18
|
async function dispatchComputationPass(config, dependencies, computationManifest) {
|
|
16
|
-
const { logger
|
|
19
|
+
const { logger } = dependencies;
|
|
20
|
+
|
|
21
|
+
// Create fresh PubSubUtils instance
|
|
22
|
+
const pubsubUtils = new PubSubUtils(dependencies);
|
|
23
|
+
|
|
17
24
|
const passToRun = String(config.COMPUTATION_PASS_TO_RUN);
|
|
18
25
|
|
|
19
26
|
if (!passToRun) {
|
|
20
27
|
return logger.log('ERROR', '[Dispatcher] No pass defined (COMPUTATION_PASS_TO_RUN). Aborting.');
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
// 1. Validate Pass Existence
|
|
31
|
+
const passes = groupByPass(computationManifest);
|
|
32
|
+
const calcsInThisPass = passes[passToRun] || [];
|
|
33
|
+
|
|
34
|
+
if (!calcsInThisPass.length) {
|
|
35
|
+
return logger.log('WARN', `[Dispatcher] No calcs for Pass ${passToRun}. Exiting.`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const calcNames = calcsInThisPass.map(c => c.name).join(', ');
|
|
39
|
+
logger.log('INFO', `🚀 [Dispatcher] Preparing PASS ${passToRun}.`);
|
|
40
|
+
logger.log('INFO', `[Dispatcher] Included Calculations: [${calcNames}]`);
|
|
24
41
|
|
|
25
|
-
//
|
|
42
|
+
// 2. Determine Date Range
|
|
26
43
|
// Hardcoded earliest dates - keep synced with PassRunner for now
|
|
27
44
|
const earliestDates = {
|
|
28
45
|
portfolio: new Date('2025-09-25T00:00:00Z'),
|
|
@@ -36,19 +53,11 @@ async function dispatchComputationPass(config, dependencies, computationManifest
|
|
|
36
53
|
|
|
37
54
|
const allExpectedDates = getExpectedDateStrings(passEarliestDate, endDateUTC);
|
|
38
55
|
|
|
39
|
-
|
|
40
|
-
const passes = groupByPass(computationManifest);
|
|
41
|
-
const calcsInThisPass = passes[passToRun] || [];
|
|
42
|
-
|
|
43
|
-
if (!calcsInThisPass.length) {
|
|
44
|
-
return logger.log('WARN', `[Dispatcher] No calcs for Pass ${passToRun}. Exiting.`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
logger.log('INFO', `[Dispatcher] Found ${calcsInThisPass.length} calcs for Pass ${passToRun}. Target dates: ${allExpectedDates.length}`);
|
|
56
|
+
logger.log('INFO', `[Dispatcher] Dispatches checks for ${allExpectedDates.length} dates (${allExpectedDates[0]} to ${allExpectedDates[allExpectedDates.length - 1]}). Workers will validate dependencies.`);
|
|
48
57
|
|
|
49
58
|
// 3. Dispatch Messages
|
|
50
59
|
let dispatchedCount = 0;
|
|
51
|
-
const BATCH_SIZE = 50;
|
|
60
|
+
const BATCH_SIZE = 50;
|
|
52
61
|
|
|
53
62
|
// We can publish in parallel batches
|
|
54
63
|
const chunks = [];
|
|
@@ -75,8 +84,8 @@ async function dispatchComputationPass(config, dependencies, computationManifest
|
|
|
75
84
|
}
|
|
76
85
|
}
|
|
77
86
|
|
|
78
|
-
logger.log('INFO', `[Dispatcher] Finished
|
|
87
|
+
logger.log('INFO', `[Dispatcher] Finished. Dispatched ${dispatchedCount} checks for Pass ${passToRun}.`);
|
|
79
88
|
return { dispatched: dispatchedCount };
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
module.exports = { dispatchComputationPass };
|
|
91
|
+
module.exports = { dispatchComputationPass };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FILENAME: bulltrackers-module/functions/computation-system/helpers/computation_worker.js
|
|
3
3
|
* PURPOSE: Consumes computation tasks from Pub/Sub and executes them.
|
|
4
|
+
* FIXED: Added robust payload parsing to handle Cloud Functions Gen 2 (CloudEvents).
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
const { runDateComputation } = require('./computation_pass_runner.js');
|
|
@@ -8,15 +9,43 @@ const { groupByPass } = require('./orchestration_helpers.js');
|
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Handles a single Pub/Sub message for a computation task.
|
|
12
|
+
* Supports both Gen 1 (Message) and Gen 2 (CloudEvent) formats.
|
|
11
13
|
*/
|
|
12
14
|
async function handleComputationTask(message, config, dependencies, computationManifest) {
|
|
13
15
|
const { logger } = dependencies;
|
|
14
16
|
|
|
17
|
+
let data;
|
|
15
18
|
try {
|
|
16
|
-
|
|
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
|
+
}
|
|
17
43
|
|
|
18
|
-
|
|
19
|
-
|
|
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.`);
|
|
20
49
|
return;
|
|
21
50
|
}
|
|
22
51
|
|
|
@@ -39,12 +68,13 @@ async function handleComputationTask(message, config, dependencies, computationM
|
|
|
39
68
|
}
|
|
40
69
|
|
|
41
70
|
// Execute the computation for this specific date
|
|
71
|
+
// The runner internally checks dependencies (Pass 1, 2, 3 status) and skips if not ready.
|
|
42
72
|
const result = await runDateComputation(date, pass, calcsInThisPass, config, dependencies, computationManifest);
|
|
43
73
|
|
|
44
74
|
if (result) {
|
|
45
75
|
logger.log('INFO', `[Worker] Successfully processed ${date} (Pass ${pass}). Updates: ${Object.keys(result.updates || {}).length}`);
|
|
46
76
|
} else {
|
|
47
|
-
logger.log('INFO', `[Worker] Processed ${date} (Pass ${pass}) -
|
|
77
|
+
logger.log('INFO', `[Worker] Processed ${date} (Pass ${pass}) - Skipped (Dependencies missing or already done).`);
|
|
48
78
|
}
|
|
49
79
|
|
|
50
80
|
} catch (err) {
|
|
@@ -53,4 +83,4 @@ async function handleComputationTask(message, config, dependencies, computationM
|
|
|
53
83
|
}
|
|
54
84
|
}
|
|
55
85
|
|
|
56
|
-
module.exports = { handleComputationTask };
|
|
86
|
+
module.exports = { handleComputationTask };
|