bulltrackers-module 1.0.229 → 1.0.230
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 'IMPOSSIBLE' state logic for missing root data on historical dates.
|
|
4
4
|
*/
|
|
5
5
|
const { normalizeName } = require('./utils/utils');
|
|
6
6
|
const { checkRootDataAvailability } = require('./data/AvailabilityChecker');
|
|
@@ -10,6 +10,9 @@ const { StandardExecutor } = require('./executor
|
|
|
10
10
|
const { MetaExecutor } = require('./executors/MetaExecutor');
|
|
11
11
|
const { generateProcessId, PROCESS_TYPES } = require('./logger/logger');
|
|
12
12
|
|
|
13
|
+
// New Status Constant
|
|
14
|
+
const STATUS_IMPOSSIBLE = 'IMPOSSIBLE';
|
|
15
|
+
|
|
13
16
|
function groupByPass(manifest) {
|
|
14
17
|
return manifest.reduce((acc, calc) => {
|
|
15
18
|
(acc[calc.pass] = acc[calc.pass] || []).push(calc);
|
|
@@ -18,28 +21,32 @@ function groupByPass(manifest) {
|
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
* Performs strict analysis of what can run
|
|
22
|
-
*
|
|
23
|
-
* 1.
|
|
24
|
-
* 2.
|
|
25
|
-
* 3.
|
|
26
|
-
* 4. No Result -> Run
|
|
27
|
-
* 5. Result Exists & Hash Match -> Skip
|
|
24
|
+
* Performs strict analysis of what can run.
|
|
25
|
+
* IMPOSSIBLE LOGIC:
|
|
26
|
+
* 1. If Root Data is missing AND Date != Today -> IMPOSSIBLE.
|
|
27
|
+
* 2. If Dependency is IMPOSSIBLE -> IMPOSSIBLE.
|
|
28
|
+
* 3. IMPOSSIBLE items are written to DB to prevent future retries.
|
|
28
29
|
*/
|
|
29
30
|
function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus, manifestMap) {
|
|
30
31
|
const report = {
|
|
31
32
|
runnable: [],
|
|
32
|
-
blocked: [], // Missing Root Data
|
|
33
|
-
|
|
33
|
+
blocked: [], // Missing Root Data (Today - Retriable)
|
|
34
|
+
impossible: [], // Missing Root Data (Historical) or Dependency Impossible
|
|
35
|
+
failedDependency: [], // Missing/Stale Dependency (Transient)
|
|
34
36
|
reRuns: [], // Hash Mismatch
|
|
35
37
|
skipped: [] // Already done & valid
|
|
36
38
|
};
|
|
37
39
|
|
|
40
|
+
const isTargetToday = (dateStr === new Date().toISOString().slice(0, 10));
|
|
41
|
+
|
|
38
42
|
const isDepSatisfied = (depName, dailyStatus, manifestMap) => {
|
|
39
43
|
const norm = normalizeName(depName);
|
|
40
44
|
const storedDepHash = dailyStatus[norm];
|
|
41
45
|
const depManifest = manifestMap.get(norm);
|
|
42
46
|
|
|
47
|
+
// Check 1: Is dependency IMPOSSIBLE? (Logic handled in main loop, but safe to check here)
|
|
48
|
+
if (storedDepHash === STATUS_IMPOSSIBLE) return false;
|
|
49
|
+
|
|
43
50
|
if (!storedDepHash) return false;
|
|
44
51
|
if (!depManifest) return false;
|
|
45
52
|
if (storedDepHash !== depManifest.hash) return false;
|
|
@@ -52,7 +59,13 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
52
59
|
const storedHash = dailyStatus[cName];
|
|
53
60
|
const currentHash = calc.hash;
|
|
54
61
|
|
|
55
|
-
// 1.
|
|
62
|
+
// 1. Check if ALREADY marked IMPOSSIBLE
|
|
63
|
+
if (storedHash === STATUS_IMPOSSIBLE) {
|
|
64
|
+
report.skipped.push({ name: cName, reason: 'Permanently Impossible' });
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 2. Root Data Check
|
|
56
69
|
const missingRoots = [];
|
|
57
70
|
if (calc.rootDataDependencies) {
|
|
58
71
|
for (const dep of calc.rootDataDependencies) {
|
|
@@ -65,27 +78,49 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
65
78
|
}
|
|
66
79
|
|
|
67
80
|
if (missingRoots.length > 0) {
|
|
68
|
-
|
|
81
|
+
// LOGIC: If date is NOT today, missing root data is fatal and permanent.
|
|
82
|
+
if (!isTargetToday) {
|
|
83
|
+
report.impossible.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')} (Historical)` });
|
|
84
|
+
} else {
|
|
85
|
+
report.blocked.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')} (Waiting)` });
|
|
86
|
+
}
|
|
69
87
|
continue;
|
|
70
88
|
}
|
|
71
89
|
|
|
72
|
-
//
|
|
90
|
+
// 3. Dependency Check
|
|
91
|
+
let dependencyIsImpossible = false;
|
|
73
92
|
const missingDeps = [];
|
|
93
|
+
|
|
74
94
|
if (calc.dependencies) {
|
|
75
95
|
for (const dep of calc.dependencies) {
|
|
96
|
+
const normDep = normalizeName(dep);
|
|
97
|
+
|
|
98
|
+
// Check if the dependency is marked IMPOSSIBLE in the DB
|
|
99
|
+
if (dailyStatus[normDep] === STATUS_IMPOSSIBLE) {
|
|
100
|
+
dependencyIsImpossible = true;
|
|
101
|
+
// We can break early, if one input is impossible, the result is impossible.
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
|
|
76
105
|
if (!isDepSatisfied(dep, dailyStatus, manifestMap)) {
|
|
77
106
|
missingDeps.push(dep);
|
|
78
107
|
}
|
|
79
108
|
}
|
|
80
109
|
}
|
|
81
110
|
|
|
111
|
+
if (dependencyIsImpossible) {
|
|
112
|
+
// Propagate the Impossible Status
|
|
113
|
+
report.impossible.push({ name: cName, reason: 'Dependency is Impossible' });
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
82
117
|
if (missingDeps.length > 0) {
|
|
83
118
|
report.failedDependency.push({ name: cName, missing: missingDeps });
|
|
84
119
|
continue;
|
|
85
120
|
}
|
|
86
121
|
|
|
87
|
-
//
|
|
88
|
-
if (!storedHash) {
|
|
122
|
+
// 4. Hash / State Check
|
|
123
|
+
if (!storedHash || storedHash === false) { // false indicates previous transient failure
|
|
89
124
|
report.runnable.push(calc);
|
|
90
125
|
} else if (storedHash !== currentHash) {
|
|
91
126
|
report.reRuns.push({ name: cName, oldHash: storedHash, newHash: currentHash });
|
|
@@ -123,23 +158,28 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
|
|
|
123
158
|
const manifestMap = new Map(computationManifest.map(c => [normalizeName(c.name), c]));
|
|
124
159
|
const analysisReport = analyzeDateExecution(dateStr, calcsInThisPass, rootStatus, dailyStatus, manifestMap);
|
|
125
160
|
|
|
126
|
-
// 4. LOG ANALYSIS
|
|
161
|
+
// 4. LOG ANALYSIS
|
|
127
162
|
if (logger && typeof logger.logDateAnalysis === 'function') {
|
|
128
163
|
logger.logDateAnalysis(dateStr, analysisReport);
|
|
129
164
|
} else {
|
|
130
|
-
//
|
|
131
|
-
const logMsg = `[Analysis] Date: ${dateStr} | Runnable: ${analysisReport.runnable.length} | Blocked: ${analysisReport.blocked.length} |
|
|
165
|
+
// Safe fallback
|
|
166
|
+
const logMsg = `[Analysis] Date: ${dateStr} | Runnable: ${analysisReport.runnable.length} | Blocked: ${analysisReport.blocked.length} | Impossible: ${analysisReport.impossible.length}`;
|
|
132
167
|
if (logger && logger.info) logger.info(logMsg);
|
|
133
168
|
else console.log(logMsg);
|
|
134
169
|
}
|
|
135
170
|
|
|
136
|
-
// 5.
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
171
|
+
// 5. UPDATE STATUS FOR NON-RUNNABLE ITEMS
|
|
172
|
+
const statusUpdates = {};
|
|
173
|
+
|
|
174
|
+
// A. Mark BLOCKED as 'false' (Transient Failure)
|
|
175
|
+
analysisReport.blocked.forEach(item => statusUpdates[item.name] = false);
|
|
176
|
+
analysisReport.failedDependency.forEach(item => statusUpdates[item.name] = false);
|
|
177
|
+
|
|
178
|
+
// B. Mark IMPOSSIBLE as 'IMPOSSIBLE' (Permanent Failure - Overwrites existing status)
|
|
179
|
+
analysisReport.impossible.forEach(item => statusUpdates[item.name] = STATUS_IMPOSSIBLE);
|
|
140
180
|
|
|
141
|
-
if (Object.keys(
|
|
142
|
-
await updateComputationStatus(dateStr,
|
|
181
|
+
if (Object.keys(statusUpdates).length > 0) {
|
|
182
|
+
await updateComputationStatus(dateStr, statusUpdates, config, dependencies);
|
|
143
183
|
}
|
|
144
184
|
|
|
145
185
|
// 6. EXECUTE RUNNABLES
|
|
@@ -151,7 +191,12 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
|
|
|
151
191
|
const finalRunList = calcsInThisPass.filter(c => calcsToRunNames.has(normalizeName(c.name)));
|
|
152
192
|
|
|
153
193
|
if (!finalRunList.length) {
|
|
154
|
-
return {
|
|
194
|
+
return {
|
|
195
|
+
date: dateStr,
|
|
196
|
+
updates: {},
|
|
197
|
+
skipped: analysisReport.skipped.length,
|
|
198
|
+
impossible: analysisReport.impossible.length
|
|
199
|
+
};
|
|
155
200
|
}
|
|
156
201
|
|
|
157
202
|
if (logger && logger.log) {
|
|
@@ -1,26 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Structured Logging System for Computation Engine
|
|
3
|
-
*
|
|
4
|
-
* UPDATED: Fixed Date Analysis keys to match WorkflowOrchestrator (failedDependency vs resolving).
|
|
3
|
+
* UPDATED: Added 'impossible' category handling to Date Analysis.
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
const crypto = require('crypto');
|
|
8
7
|
|
|
9
|
-
/**
|
|
10
|
-
* Log Levels (Ordered by Severity)
|
|
11
|
-
*/
|
|
12
8
|
const LOG_LEVELS = {
|
|
13
|
-
TRACE: 0,
|
|
14
|
-
DEBUG: 1,
|
|
15
|
-
INFO: 2,
|
|
16
|
-
WARN: 3,
|
|
17
|
-
ERROR: 4,
|
|
18
|
-
FATAL: 5
|
|
9
|
+
TRACE: 0, DEBUG: 1, INFO: 2, WARN: 3, ERROR: 4, FATAL: 5
|
|
19
10
|
};
|
|
20
11
|
|
|
21
|
-
/**
|
|
22
|
-
* Process Types for Tracking
|
|
23
|
-
*/
|
|
24
12
|
const PROCESS_TYPES = {
|
|
25
13
|
MANIFEST: 'manifest',
|
|
26
14
|
ORCHESTRATOR: 'orchestrator',
|
|
@@ -31,17 +19,11 @@ const PROCESS_TYPES = {
|
|
|
31
19
|
DISPATCH: 'dispatch'
|
|
32
20
|
};
|
|
33
21
|
|
|
34
|
-
/**
|
|
35
|
-
* Generates a deterministic process ID.
|
|
36
|
-
*/
|
|
37
22
|
function generateProcessId(type, identifier, date = '') {
|
|
38
23
|
const input = `${type}|${identifier}|${date}`;
|
|
39
24
|
return crypto.createHash('sha256').update(input).digest('hex').substring(0, 16);
|
|
40
25
|
}
|
|
41
26
|
|
|
42
|
-
/**
|
|
43
|
-
* Formats a log entry into structured JSON
|
|
44
|
-
*/
|
|
45
27
|
function formatLogEntry(entry) {
|
|
46
28
|
return JSON.stringify({
|
|
47
29
|
timestamp: entry.timestamp,
|
|
@@ -68,7 +50,6 @@ class StructuredLogger {
|
|
|
68
50
|
includeStackTrace: config.includeStackTrace !== false,
|
|
69
51
|
...config
|
|
70
52
|
};
|
|
71
|
-
|
|
72
53
|
this.activeProcesses = new Map();
|
|
73
54
|
}
|
|
74
55
|
|
|
@@ -78,7 +59,6 @@ class StructuredLogger {
|
|
|
78
59
|
: crypto.randomBytes(8).toString('hex');
|
|
79
60
|
|
|
80
61
|
const processLogger = new ProcessLogger(this, processType, processId, computationName, date);
|
|
81
|
-
|
|
82
62
|
this.activeProcesses.set(processId, {
|
|
83
63
|
type: processType,
|
|
84
64
|
computationName,
|
|
@@ -86,7 +66,6 @@ class StructuredLogger {
|
|
|
86
66
|
startTime: Date.now(),
|
|
87
67
|
logger: processLogger
|
|
88
68
|
});
|
|
89
|
-
|
|
90
69
|
return processLogger;
|
|
91
70
|
}
|
|
92
71
|
|
|
@@ -101,17 +80,17 @@ class StructuredLogger {
|
|
|
101
80
|
}
|
|
102
81
|
|
|
103
82
|
/**
|
|
104
|
-
* The Main Date Analysis Logger (
|
|
105
|
-
* Handles '
|
|
83
|
+
* The Main Date Analysis Logger (UPDATED)
|
|
84
|
+
* Handles 'impossible' category for permanent failures.
|
|
106
85
|
*/
|
|
107
86
|
logDateAnalysis(dateStr, analysisReport) {
|
|
108
|
-
// Destructure with SAFETY DEFAULTS to prevent crashes
|
|
109
87
|
const {
|
|
110
88
|
runnable = [],
|
|
111
89
|
blocked = [],
|
|
90
|
+
impossible = [], // New Category
|
|
112
91
|
reRuns = [],
|
|
113
|
-
failedDependency = [],
|
|
114
|
-
skipped = []
|
|
92
|
+
failedDependency = [],
|
|
93
|
+
skipped = []
|
|
115
94
|
} = analysisReport;
|
|
116
95
|
|
|
117
96
|
// 1. Structured Output
|
|
@@ -125,6 +104,7 @@ class StructuredLogger {
|
|
|
125
104
|
stats: {
|
|
126
105
|
runnable: runnable.length,
|
|
127
106
|
blocked: blocked.length,
|
|
107
|
+
impossible: impossible.length,
|
|
128
108
|
reRuns: reRuns.length,
|
|
129
109
|
failedDependency: failedDependency.length,
|
|
130
110
|
skipped: skipped.length
|
|
@@ -135,7 +115,10 @@ class StructuredLogger {
|
|
|
135
115
|
|
|
136
116
|
// 2. Human Readable Output
|
|
137
117
|
if (this.config.enableConsole) {
|
|
138
|
-
const symbols = {
|
|
118
|
+
const symbols = {
|
|
119
|
+
info: 'ℹ️', warn: '⚠️', check: '✅', block: '⛔',
|
|
120
|
+
cycle: '🔄', skip: '⏭️', dead: '💀'
|
|
121
|
+
};
|
|
139
122
|
|
|
140
123
|
console.log(`\n🔍 === DATE ANALYSIS REPORT: ${dateStr} ===`);
|
|
141
124
|
|
|
@@ -146,29 +129,34 @@ class StructuredLogger {
|
|
|
146
129
|
});
|
|
147
130
|
}
|
|
148
131
|
|
|
149
|
-
// Updated to handle failedDependency instead of resolving
|
|
150
132
|
if (failedDependency.length) {
|
|
151
|
-
console.log(`\n${symbols.block} [FAILED DEPENDENCIES] (
|
|
133
|
+
console.log(`\n${symbols.block} [FAILED DEPENDENCIES] (Upstream failure)`);
|
|
152
134
|
failedDependency.forEach(item => {
|
|
153
|
-
// item.missing comes from Orchestrator (not item.missingDeps)
|
|
154
135
|
console.log(` • ${item.name}: Missing ${item.missing ? item.missing.join(', ') : 'dependencies'}`);
|
|
155
136
|
});
|
|
156
137
|
}
|
|
157
138
|
|
|
139
|
+
// NEW: Impossible items
|
|
140
|
+
if (impossible.length) {
|
|
141
|
+
console.log(`\n${symbols.dead} [IMPOSSIBLE] (Permanent Failure - Will not retry)`);
|
|
142
|
+
impossible.forEach(item => {
|
|
143
|
+
console.log(` • ${item.name}: ${item.reason}`);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
158
147
|
if (runnable.length) {
|
|
159
148
|
console.log(`\n${symbols.check} [READY TO RUN]`);
|
|
160
149
|
runnable.forEach(item => console.log(` • ${item.name}`));
|
|
161
150
|
}
|
|
162
151
|
|
|
163
152
|
if (blocked.length) {
|
|
164
|
-
console.log(`\n${symbols.warn} [BLOCKED] (
|
|
153
|
+
console.log(`\n${symbols.warn} [BLOCKED] (Waiting for data - Retriable)`);
|
|
165
154
|
blocked.forEach(item => {
|
|
166
155
|
console.log(` • ${item.name}: ${item.reason}`);
|
|
167
156
|
});
|
|
168
157
|
}
|
|
169
158
|
|
|
170
159
|
if (skipped.length) {
|
|
171
|
-
// Optional: Only show count if list is huge
|
|
172
160
|
if (skipped.length > 10) {
|
|
173
161
|
console.log(`\n${symbols.skip} [SKIPPED] (${skipped.length} calculations up-to-date)`);
|
|
174
162
|
} else {
|
|
@@ -264,7 +252,6 @@ class StructuredLogger {
|
|
|
264
252
|
}
|
|
265
253
|
}
|
|
266
254
|
|
|
267
|
-
// ... ProcessLogger and Templates (unchanged) ...
|
|
268
255
|
class ProcessLogger {
|
|
269
256
|
constructor(parent, processType, processId, computationName, date) {
|
|
270
257
|
this.parent = parent;
|