bulltrackers-module 1.0.226 → 1.0.228
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
|
* @fileoverview Main Orchestrator. Coordinates the topological execution.
|
|
3
|
-
* UPDATED:
|
|
3
|
+
* UPDATED: Includes defensive checks for Logger compatibility.
|
|
4
4
|
*/
|
|
5
5
|
const { normalizeName } = require('./utils/utils');
|
|
6
6
|
const { checkRootDataAvailability } = require('./data/AvailabilityChecker');
|
|
@@ -31,26 +31,17 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
31
31
|
runnable: [],
|
|
32
32
|
blocked: [], // Missing Root Data
|
|
33
33
|
failedDependency: [], // Missing Dependency OR Stale Dependency
|
|
34
|
-
reRuns: [], // Hash Mismatch
|
|
34
|
+
reRuns: [], // Hash Mismatch
|
|
35
35
|
skipped: [] // Already done & valid
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
// Helper: Is a dependency satisfied AND valid (matching hash)?
|
|
39
38
|
const isDepSatisfied = (depName, dailyStatus, manifestMap) => {
|
|
40
39
|
const norm = normalizeName(depName);
|
|
41
40
|
const storedDepHash = dailyStatus[norm];
|
|
42
41
|
const depManifest = manifestMap.get(norm);
|
|
43
42
|
|
|
44
|
-
// 1. Must exist in DB. If missing, we cannot run.
|
|
45
43
|
if (!storedDepHash) return false;
|
|
46
|
-
|
|
47
|
-
// 2. Must exist in Manifest (Sanity check)
|
|
48
44
|
if (!depManifest) return false;
|
|
49
|
-
|
|
50
|
-
// 3. STRICT: The dependency's stored hash must match its current manifest hash.
|
|
51
|
-
// If 'A' changed code, 'A' has a new hash. If we are running 'B' (Pass 2),
|
|
52
|
-
// we expect 'A' (Pass 1) to have already run and updated the DB with the NEW hash.
|
|
53
|
-
// If DB still has OLD hash, 'A' failed or didn't run. 'B' is unsafe to run.
|
|
54
45
|
if (storedDepHash !== depManifest.hash) return false;
|
|
55
46
|
|
|
56
47
|
return true;
|
|
@@ -61,7 +52,7 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
61
52
|
const storedHash = dailyStatus[cName];
|
|
62
53
|
const currentHash = calc.hash;
|
|
63
54
|
|
|
64
|
-
// 1. Root Data Check
|
|
55
|
+
// 1. Root Data Check
|
|
65
56
|
const missingRoots = [];
|
|
66
57
|
if (calc.rootDataDependencies) {
|
|
67
58
|
for (const dep of calc.rootDataDependencies) {
|
|
@@ -75,12 +66,10 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
75
66
|
|
|
76
67
|
if (missingRoots.length > 0) {
|
|
77
68
|
report.blocked.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')}` });
|
|
78
|
-
continue;
|
|
69
|
+
continue;
|
|
79
70
|
}
|
|
80
71
|
|
|
81
|
-
// 2. Dependency Check
|
|
82
|
-
// Since we are in a topological pass system, all dependencies SHOULD be satisfied
|
|
83
|
-
// by previous passes. If not, it is a fatal error for this calculation.
|
|
72
|
+
// 2. Dependency Check
|
|
84
73
|
const missingDeps = [];
|
|
85
74
|
if (calc.dependencies) {
|
|
86
75
|
for (const dep of calc.dependencies) {
|
|
@@ -92,23 +81,17 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
92
81
|
|
|
93
82
|
if (missingDeps.length > 0) {
|
|
94
83
|
report.failedDependency.push({ name: cName, missing: missingDeps });
|
|
95
|
-
continue;
|
|
84
|
+
continue;
|
|
96
85
|
}
|
|
97
86
|
|
|
98
87
|
// 3. Hash / State Check
|
|
99
88
|
if (!storedHash) {
|
|
100
|
-
// Case A: No result exists (or status is 'false' from previous failure) -> RUN
|
|
101
|
-
// Note: If storedHash is boolean false, !storedHash is true, so we retry.
|
|
102
89
|
report.runnable.push(calc);
|
|
103
90
|
} else if (storedHash !== currentHash) {
|
|
104
|
-
// Case B: Result exists, but hash mismatch -> RE-RUN
|
|
105
|
-
// This covers code changes in THIS calc, AND cascading changes from dependencies
|
|
106
91
|
report.reRuns.push({ name: cName, oldHash: storedHash, newHash: currentHash });
|
|
107
92
|
} else if (storedHash === true) {
|
|
108
|
-
// Case C: Legacy boolean status -> RE-RUN (Upgrade to hash)
|
|
109
93
|
report.reRuns.push({ name: cName, reason: 'Legacy Upgrade' });
|
|
110
94
|
} else {
|
|
111
|
-
// Case D: Result exists, Hash Matches -> SKIP
|
|
112
95
|
report.skipped.push({ name: cName });
|
|
113
96
|
}
|
|
114
97
|
}
|
|
@@ -119,13 +102,12 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
119
102
|
async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, dependencies, computationManifest) {
|
|
120
103
|
const { logger } = dependencies;
|
|
121
104
|
const orchestratorPid = generateProcessId(PROCESS_TYPES.ORCHESTRATOR, passToRun, dateStr);
|
|
122
|
-
|
|
123
105
|
const dateToProcess = new Date(dateStr + 'T00:00:00Z');
|
|
124
106
|
|
|
125
107
|
// 1. Fetch State
|
|
126
108
|
const dailyStatus = await fetchComputationStatus(dateStr, config, dependencies);
|
|
127
109
|
|
|
128
|
-
// 2. Check Data Availability
|
|
110
|
+
// 2. Check Data Availability
|
|
129
111
|
const earliestDates = {
|
|
130
112
|
portfolio: new Date('2025-09-25T00:00:00Z'),
|
|
131
113
|
history: new Date('2025-11-05T00:00:00Z'),
|
|
@@ -141,11 +123,17 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
|
|
|
141
123
|
const manifestMap = new Map(computationManifest.map(c => [normalizeName(c.name), c]));
|
|
142
124
|
const analysisReport = analyzeDateExecution(dateStr, calcsInThisPass, rootStatus, dailyStatus, manifestMap);
|
|
143
125
|
|
|
144
|
-
// 4. LOG ANALYSIS
|
|
145
|
-
logger.logDateAnalysis
|
|
126
|
+
// 4. LOG ANALYSIS (SAFEGUARDED)
|
|
127
|
+
if (logger && typeof logger.logDateAnalysis === 'function') {
|
|
128
|
+
logger.logDateAnalysis(dateStr, analysisReport);
|
|
129
|
+
} else {
|
|
130
|
+
// Fallback if injected logger is outdated
|
|
131
|
+
const logMsg = `[Analysis] Date: ${dateStr} | Runnable: ${analysisReport.runnable.length} | Blocked: ${analysisReport.blocked.length} | ReRuns: ${analysisReport.reRuns.length}`;
|
|
132
|
+
if (logger && logger.info) logger.info(logMsg);
|
|
133
|
+
else console.log(logMsg);
|
|
134
|
+
}
|
|
146
135
|
|
|
147
|
-
// 5. MARK FAILURES (
|
|
148
|
-
// This prevents UI/downstream consumers from waiting indefinitely.
|
|
136
|
+
// 5. MARK FAILURES (Write 'false' to DB for blocked items)
|
|
149
137
|
const failureUpdates = {};
|
|
150
138
|
analysisReport.blocked.forEach(item => failureUpdates[item.name] = false);
|
|
151
139
|
analysisReport.failedDependency.forEach(item => failureUpdates[item.name] = false);
|
|
@@ -163,11 +151,12 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
|
|
|
163
151
|
const finalRunList = calcsInThisPass.filter(c => calcsToRunNames.has(normalizeName(c.name)));
|
|
164
152
|
|
|
165
153
|
if (!finalRunList.length) {
|
|
166
|
-
// Nothing to run (everything either skipped, blocked, or failed)
|
|
167
154
|
return { date: dateStr, updates: {}, skipped: analysisReport.skipped.length };
|
|
168
155
|
}
|
|
169
156
|
|
|
170
|
-
logger
|
|
157
|
+
if (logger && logger.log) {
|
|
158
|
+
logger.log('INFO', `[Orchestrator] Executing ${finalRunList.length} calculations for ${dateStr}`, { processId: orchestratorPid });
|
|
159
|
+
}
|
|
171
160
|
|
|
172
161
|
const standardToRun = finalRunList.filter(c => c.type === 'standard');
|
|
173
162
|
const metaToRun = finalRunList.filter(c => c.type === 'meta');
|
|
@@ -177,11 +166,8 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
|
|
|
177
166
|
try {
|
|
178
167
|
const calcsRunning = [...standardToRun, ...metaToRun];
|
|
179
168
|
|
|
180
|
-
// Fetch dependencies
|
|
181
|
-
// includeSelf=false: We are re-running, so we ignore our own old results.
|
|
169
|
+
// Fetch dependencies
|
|
182
170
|
const existingResults = await fetchExistingResults(dateStr, calcsRunning, computationManifest, config, dependencies, false);
|
|
183
|
-
|
|
184
|
-
// Fetch Yesterday's results (if Historical)
|
|
185
171
|
const prevDate = new Date(dateToProcess); prevDate.setUTCDate(prevDate.getUTCDate() - 1);
|
|
186
172
|
const prevDateStr = prevDate.toISOString().slice(0, 10);
|
|
187
173
|
const previousResults = await fetchExistingResults(prevDateStr, calcsRunning, computationManifest, config, dependencies, true);
|
|
@@ -196,9 +182,11 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
|
|
|
196
182
|
}
|
|
197
183
|
|
|
198
184
|
} catch (err) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
185
|
+
if (logger && logger.log) {
|
|
186
|
+
logger.log('ERROR', `[Orchestrator] Failed execution for ${dateStr}`, { processId: orchestratorPid, error: err.message });
|
|
187
|
+
} else {
|
|
188
|
+
console.error(`[Orchestrator] Failed execution for ${dateStr}: ${err.message}`);
|
|
189
|
+
}
|
|
202
190
|
throw err;
|
|
203
191
|
}
|
|
204
192
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FILENAME: computation-system/helpers/computation_worker.js
|
|
3
3
|
* PURPOSE: Consumes computation tasks from Pub/Sub and executes them.
|
|
4
|
-
* UPDATED:
|
|
4
|
+
* UPDATED: Instantiates the internal StructuredLogger to replace the generic injected logger.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { runDateComputation, groupByPass } = require('../WorkflowOrchestrator.js');
|
|
8
8
|
const { getManifest } = require('../topology/ManifestLoader');
|
|
9
|
+
const { StructuredLogger } = require('../logger/logger'); // <--- CRITICAL IMPORT
|
|
9
10
|
|
|
10
|
-
// 1. IMPORT CALCULATIONS
|
|
11
|
+
// 1. IMPORT CALCULATIONS
|
|
11
12
|
// We import the specific package containing your strategies (gem, pyro, core, etc.)
|
|
12
|
-
// Adjust the require path if you are using a local relative path instead of node_modules.
|
|
13
13
|
let calculationPackage;
|
|
14
14
|
try {
|
|
15
15
|
// Primary: Try to load from the installed npm package
|
|
@@ -21,34 +21,50 @@ try {
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
// The package exports { calculations: { ... }, utils: { ... } }
|
|
24
|
-
// We only need the 'calculations' object (which contains the folder-grouped classes)
|
|
25
24
|
const calculations = calculationPackage.calculations;
|
|
26
25
|
|
|
27
26
|
/**
|
|
28
27
|
* Handles a single Pub/Sub message for a computation task.
|
|
29
28
|
* Supports both Gen 1 (Message) and Gen 2 (CloudEvent) formats.
|
|
30
29
|
* @param {object} message - The Pub/Sub message payload.
|
|
31
|
-
* @param {object} config - System configuration
|
|
32
|
-
* @param {object} dependencies -
|
|
30
|
+
* @param {object} config - System configuration.
|
|
31
|
+
* @param {object} dependencies - Injected dependencies (db, generic logger, etc.).
|
|
33
32
|
*/
|
|
34
33
|
async function handleComputationTask(message, config, dependencies) {
|
|
35
|
-
|
|
34
|
+
|
|
35
|
+
// 2. INITIALIZE SYSTEM LOGGER
|
|
36
|
+
// We ignore the generic 'dependencies.logger' and instantiate our specialized one.
|
|
37
|
+
// This ensures methods like 'logDateAnalysis' and 'logStorage' exist.
|
|
38
|
+
const systemLogger = new StructuredLogger({
|
|
39
|
+
minLevel: config.minLevel || 'INFO',
|
|
40
|
+
enableStructured: true,
|
|
41
|
+
...config // Apply any other relevant config overrides
|
|
42
|
+
});
|
|
36
43
|
|
|
37
|
-
//
|
|
38
|
-
// We
|
|
44
|
+
// 3. OVERRIDE DEPENDENCIES
|
|
45
|
+
// We create a new dependencies object to pass downstream.
|
|
46
|
+
// This fixes the "logger.logStorage is not a function" error in ResultCommitter.js
|
|
47
|
+
const runDependencies = {
|
|
48
|
+
...dependencies,
|
|
49
|
+
logger: systemLogger
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const { logger } = runDependencies; // Use this for local logging
|
|
53
|
+
|
|
54
|
+
// 4. LAZY LOAD MANIFEST
|
|
39
55
|
let computationManifest;
|
|
40
56
|
try {
|
|
41
57
|
computationManifest = getManifest(
|
|
42
58
|
config.activeProductLines || [],
|
|
43
59
|
calculations,
|
|
44
|
-
dependencies
|
|
60
|
+
runDependencies // Pass the updated dependencies
|
|
45
61
|
);
|
|
46
62
|
} catch (manifestError) {
|
|
47
63
|
logger.log('FATAL', `[Worker] Failed to load Manifest: ${manifestError.message}`);
|
|
48
64
|
return;
|
|
49
65
|
}
|
|
50
66
|
|
|
51
|
-
//
|
|
67
|
+
// 5. PARSE PUB/SUB MESSAGE
|
|
52
68
|
let data;
|
|
53
69
|
try {
|
|
54
70
|
if (message.data && message.data.message && message.data.message.data) {
|
|
@@ -67,7 +83,7 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
67
83
|
return;
|
|
68
84
|
}
|
|
69
85
|
|
|
70
|
-
//
|
|
86
|
+
// 6. EXECUTE TASK
|
|
71
87
|
try {
|
|
72
88
|
if (!data || data.action !== 'RUN_COMPUTATION_DATE') {
|
|
73
89
|
if (data) logger.log('WARN', `[Worker] Unknown or missing action: ${data?.action}. Ignoring.`);
|
|
@@ -95,7 +111,7 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
95
111
|
pass,
|
|
96
112
|
calcsInThisPass,
|
|
97
113
|
config,
|
|
98
|
-
|
|
114
|
+
runDependencies, // <--- IMPORTANT: Pass the dependencies with the System Logger
|
|
99
115
|
computationManifest
|
|
100
116
|
);
|
|
101
117
|
|