bulltrackers-module 1.0.244 → 1.0.245
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: Implements State Simulation for accurate single-pass reporting.
|
|
4
4
|
*/
|
|
5
5
|
const { normalizeName, DEFINITIVE_EARLIEST_DATES } = require('./utils/utils');
|
|
6
6
|
const { checkRootDataAvailability } = require('./data/AvailabilityChecker');
|
|
@@ -21,6 +21,8 @@ function groupByPass(manifest) {
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Analyzes whether calculations should run, be skipped, or are blocked.
|
|
24
|
+
* NOW WITH SIMULATION: Updates a local status map as it progresses to ensure
|
|
25
|
+
* downstream dependencies 'see' the decisions made by upstream calculations.
|
|
24
26
|
*/
|
|
25
27
|
function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus, manifestMap, prevDailyStatus = null) {
|
|
26
28
|
const report = {
|
|
@@ -32,11 +34,16 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
32
34
|
skipped: []
|
|
33
35
|
};
|
|
34
36
|
|
|
37
|
+
// [SIMULATION STATE] Clone the initial DB status.
|
|
38
|
+
// We will update this locally as we make decisions, allowing 'future' calcs
|
|
39
|
+
// in this list to see the predicted state of their dependencies.
|
|
40
|
+
const simulationStatus = { ...dailyStatus };
|
|
41
|
+
|
|
35
42
|
const isTargetToday = (dateStr === new Date().toISOString().slice(0, 10));
|
|
36
43
|
|
|
37
|
-
const isDepSatisfied = (depName,
|
|
44
|
+
const isDepSatisfied = (depName, currentStatusMap, manifestMap) => {
|
|
38
45
|
const norm = normalizeName(depName);
|
|
39
|
-
const stored =
|
|
46
|
+
const stored = currentStatusMap[norm];
|
|
40
47
|
const depManifest = manifestMap.get(norm);
|
|
41
48
|
|
|
42
49
|
if (!stored) return false;
|
|
@@ -49,20 +56,37 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
49
56
|
|
|
50
57
|
for (const calc of calcsInPass) {
|
|
51
58
|
const cName = normalizeName(calc.name);
|
|
52
|
-
|
|
59
|
+
|
|
60
|
+
// Use simulationStatus instead of dailyStatus
|
|
61
|
+
const stored = simulationStatus[cName];
|
|
53
62
|
|
|
54
63
|
const storedHash = stored ? stored.hash : null;
|
|
55
64
|
const storedCategory = stored ? stored.category : null;
|
|
56
65
|
const currentHash = calc.hash;
|
|
57
66
|
|
|
67
|
+
// Decision Helpers
|
|
68
|
+
const markImpossible = (reason) => {
|
|
69
|
+
report.impossible.push({ name: cName, reason });
|
|
70
|
+
// UPDATE SIMULATION: Downstream deps will now see this as IMPOSSIBLE
|
|
71
|
+
simulationStatus[cName] = { hash: STATUS_IMPOSSIBLE, category: calc.category };
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const markRunnable = (isReRun = false, reRunDetails = null) => {
|
|
75
|
+
if (isReRun) report.reRuns.push(reRunDetails);
|
|
76
|
+
else report.runnable.push(calc);
|
|
77
|
+
// UPDATE SIMULATION: Downstream deps will see this as SUCCESS (matching hash)
|
|
78
|
+
simulationStatus[cName] = { hash: currentHash, category: calc.category };
|
|
79
|
+
};
|
|
80
|
+
|
|
58
81
|
let migrationOldCategory = null;
|
|
59
82
|
if (storedCategory && storedCategory !== calc.category) {
|
|
60
83
|
migrationOldCategory = storedCategory;
|
|
61
84
|
}
|
|
62
85
|
|
|
63
|
-
// 1. Check Impossible
|
|
86
|
+
// 1. Check Impossible (Previously recorded)
|
|
64
87
|
if (storedHash === STATUS_IMPOSSIBLE) {
|
|
65
88
|
report.skipped.push({ name: cName, reason: 'Permanently Impossible' });
|
|
89
|
+
// Simulation state remains IMPOSSIBLE
|
|
66
90
|
continue;
|
|
67
91
|
}
|
|
68
92
|
|
|
@@ -80,40 +104,46 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
80
104
|
|
|
81
105
|
if (missingRoots.length > 0) {
|
|
82
106
|
if (!isTargetToday) {
|
|
83
|
-
|
|
107
|
+
// If it's a past date and root data is missing, it's permanently impossible.
|
|
108
|
+
markImpossible(`Missing Root Data: ${missingRoots.join(', ')} (Historical)`);
|
|
84
109
|
} else {
|
|
110
|
+
// If it's today, we might just be early. Block, don't Impossible.
|
|
85
111
|
report.blocked.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')} (Waiting)` });
|
|
112
|
+
// We DO NOT update simulationStatus here because it's not permanently dead, just waiting.
|
|
86
113
|
}
|
|
87
114
|
continue;
|
|
88
115
|
}
|
|
89
116
|
|
|
90
|
-
// 3. Dependency Check
|
|
117
|
+
// 3. Dependency Check (Using Simulation Status)
|
|
91
118
|
let dependencyIsImpossible = false;
|
|
92
119
|
const missingDeps = [];
|
|
93
120
|
|
|
94
121
|
if (calc.dependencies) {
|
|
95
122
|
for (const dep of calc.dependencies) {
|
|
96
123
|
const normDep = normalizeName(dep);
|
|
97
|
-
|
|
124
|
+
|
|
125
|
+
// LOOK AT SIMULATION STATUS, NOT DB SNAPSHOT
|
|
126
|
+
const depStored = simulationStatus[normDep];
|
|
98
127
|
|
|
99
128
|
if (depStored && depStored.hash === STATUS_IMPOSSIBLE) {
|
|
100
129
|
dependencyIsImpossible = true;
|
|
101
130
|
break;
|
|
102
131
|
}
|
|
103
132
|
|
|
104
|
-
if (!isDepSatisfied(dep,
|
|
133
|
+
if (!isDepSatisfied(dep, simulationStatus, manifestMap)) {
|
|
105
134
|
missingDeps.push(dep);
|
|
106
135
|
}
|
|
107
136
|
}
|
|
108
137
|
}
|
|
109
138
|
|
|
110
139
|
if (dependencyIsImpossible) {
|
|
111
|
-
|
|
140
|
+
markImpossible('Dependency is Impossible');
|
|
112
141
|
continue;
|
|
113
142
|
}
|
|
114
143
|
|
|
115
144
|
if (missingDeps.length > 0) {
|
|
116
145
|
report.failedDependency.push({ name: cName, missing: missingDeps });
|
|
146
|
+
// Do not update simulation status; downstream will see this as 'missing' (Blocked)
|
|
117
147
|
continue;
|
|
118
148
|
}
|
|
119
149
|
|
|
@@ -137,16 +167,16 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
137
167
|
|
|
138
168
|
// 5. Runnable Decision
|
|
139
169
|
if (!storedHash) {
|
|
140
|
-
|
|
170
|
+
markRunnable();
|
|
141
171
|
} else if (storedHash !== currentHash) {
|
|
142
|
-
|
|
172
|
+
markRunnable(true, {
|
|
143
173
|
name: cName,
|
|
144
174
|
oldHash: storedHash,
|
|
145
175
|
newHash: currentHash,
|
|
146
176
|
previousCategory: migrationOldCategory
|
|
147
177
|
});
|
|
148
178
|
} else if (migrationOldCategory) {
|
|
149
|
-
|
|
179
|
+
markRunnable(true, {
|
|
150
180
|
name: cName,
|
|
151
181
|
reason: 'Category Migration',
|
|
152
182
|
previousCategory: migrationOldCategory,
|
|
@@ -154,6 +184,8 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
154
184
|
});
|
|
155
185
|
} else {
|
|
156
186
|
report.skipped.push({ name: cName });
|
|
187
|
+
// Even if skipped, ensure simulation status is fresh/set (it usually is from clone)
|
|
188
|
+
simulationStatus[cName] = { hash: currentHash, category: calc.category };
|
|
157
189
|
}
|
|
158
190
|
}
|
|
159
191
|
|
|
@@ -177,10 +209,8 @@ async function executeDispatchTask(dateStr, pass, targetComputation, config, dep
|
|
|
177
209
|
}
|
|
178
210
|
|
|
179
211
|
// 2. Fetch Root Data References (Required for execution streaming)
|
|
180
|
-
// Even though Dispatcher checked existence, we need the actual Refs/Data objects now.
|
|
181
212
|
const rootData = await checkRootDataAvailability(dateStr, config, dependencies, DEFINITIVE_EARLIEST_DATES);
|
|
182
213
|
|
|
183
|
-
// Safety Fallback (Should be impossible if Dispatcher is working)
|
|
184
214
|
if (!rootData) {
|
|
185
215
|
logger.log('ERROR', `[Executor] FATAL: Root data missing for ${targetComputation} on ${dateStr}. Dispatcher desync?`);
|
|
186
216
|
return;
|
|
@@ -239,18 +269,14 @@ async function executeDispatchTask(dateStr, pass, targetComputation, config, dep
|
|
|
239
269
|
|
|
240
270
|
/**
|
|
241
271
|
* Legacy/Orchestrator Mode execution (Performs analysis).
|
|
242
|
-
* Kept for manual runs or full-system validation.
|
|
243
272
|
*/
|
|
244
273
|
async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, dependencies, computationManifest) {
|
|
245
|
-
//
|
|
246
|
-
// ... or can be removed if the system is fully migrated. Keeping logic for "Dispatcher uses analyzeDateExecution"]
|
|
247
|
-
|
|
248
|
-
// Re-exporting executeDispatchTask as the primary worker entry point.
|
|
274
|
+
// Legacy support logic...
|
|
249
275
|
}
|
|
250
276
|
|
|
251
277
|
module.exports = {
|
|
252
278
|
runDateComputation,
|
|
253
|
-
executeDispatchTask,
|
|
279
|
+
executeDispatchTask,
|
|
254
280
|
groupByPass,
|
|
255
281
|
analyzeDateExecution
|
|
256
282
|
};
|