bulltrackers-module 1.0.337 → 1.0.338
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.
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* PURPOSE: Sequential Cursor-Based Dispatcher.
|
|
4
4
|
* BEHAVIOR: Dispatch -> Wait ETA -> Next Date.
|
|
5
5
|
* UPDATED: Added "Sweep" Protocol for OOM recovery & High-Mem Verification.
|
|
6
|
-
* UPDATED: Added
|
|
6
|
+
* UPDATED: Added Safety Checks to permanently skip Deterministic Failures.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const { getExpectedDateStrings, getEarliestDataDates, normalizeName, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils.js');
|
|
@@ -23,7 +23,7 @@ const STALE_LOCK_THRESHOLD_MS = 1000 * 60 * 15;
|
|
|
23
23
|
// =============================================================================
|
|
24
24
|
async function filterActiveTasks(db, date, pass, tasks, logger, forceRun = false) {
|
|
25
25
|
if (!tasks || tasks.length === 0) return [];
|
|
26
|
-
if (forceRun) return tasks; // Bypass check for Sweep Mode (Handled separately
|
|
26
|
+
if (forceRun) return tasks; // Bypass check for Sweep Mode (Handled separately)
|
|
27
27
|
|
|
28
28
|
const checkPromises = tasks.map(async (t) => {
|
|
29
29
|
const taskName = normalizeName(t.name);
|
|
@@ -184,15 +184,12 @@ async function handlePassVerification(config, dependencies, computationManifest,
|
|
|
184
184
|
|
|
185
185
|
const missingTasks = [];
|
|
186
186
|
|
|
187
|
-
// Optimize: Batch fetch statuses if possible, but for now loop is safer for memory
|
|
188
|
-
// In production, we might want p-limit here.
|
|
189
187
|
for (const date of sessionDates) {
|
|
190
188
|
const [dailyStatus, availability] = await Promise.all([
|
|
191
189
|
fetchComputationStatus(date, config, dependencies),
|
|
192
190
|
checkRootDataAvailability(date, config, dependencies, DEFINITIVE_EARLIEST_DATES)
|
|
193
191
|
]);
|
|
194
192
|
|
|
195
|
-
// Need previous status for historical calcs
|
|
196
193
|
let prevDailyStatus = null;
|
|
197
194
|
if (calcsInPass.some(c => c.isHistorical)) {
|
|
198
195
|
const prevD = new Date(date + 'T00:00:00Z');
|
|
@@ -202,8 +199,6 @@ async function handlePassVerification(config, dependencies, computationManifest,
|
|
|
202
199
|
|
|
203
200
|
const report = analyzeDateExecution(date, calcsInPass, availability ? availability.status : {}, dailyStatus, manifestMap, prevDailyStatus);
|
|
204
201
|
|
|
205
|
-
// We only care about Runnable (New) or ReRuns (Changed/Failed)
|
|
206
|
-
// We ignore Blocked (impossible to run) and Impossible (permanent fail)
|
|
207
202
|
const pending = [...report.runnable, ...report.reRuns];
|
|
208
203
|
|
|
209
204
|
if (pending.length > 0) {
|
|
@@ -244,7 +239,6 @@ async function handleSweepDispatch(config, dependencies, computationManifest, re
|
|
|
244
239
|
checkRootDataAvailability(date, config, dependencies, DEFINITIVE_EARLIEST_DATES)
|
|
245
240
|
]);
|
|
246
241
|
|
|
247
|
-
// Previous Status Fetch (simplified for brevity, assume historical dependency check works or fails safe)
|
|
248
242
|
let prevDailyStatus = null;
|
|
249
243
|
if (calcsInPass.some(c => c.isHistorical)) {
|
|
250
244
|
const prevD = new Date(date + 'T00:00:00Z');
|
|
@@ -260,8 +254,8 @@ async function handleSweepDispatch(config, dependencies, computationManifest, re
|
|
|
260
254
|
return { dispatched: 0 };
|
|
261
255
|
}
|
|
262
256
|
|
|
263
|
-
// [FIX] Filter out deterministic failures from Sweep
|
|
264
|
-
//
|
|
257
|
+
// [FIX] Filter out deterministic failures from Sweep.
|
|
258
|
+
// If it failed due to 'QUALITY_CIRCUIT_BREAKER', High-Mem won't fix it.
|
|
265
259
|
const validTasks = [];
|
|
266
260
|
for (const task of pending) {
|
|
267
261
|
const name = normalizeName(task.name);
|
|
@@ -284,7 +278,6 @@ async function handleSweepDispatch(config, dependencies, computationManifest, re
|
|
|
284
278
|
}
|
|
285
279
|
|
|
286
280
|
// 2. FORCE High Mem & Skip Zombie Check
|
|
287
|
-
// We use validTasks now
|
|
288
281
|
const currentDispatchId = crypto.randomUUID();
|
|
289
282
|
|
|
290
283
|
const tasksPayload = validTasks.map(t => ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FILENAME: computation-system/helpers/computation_worker.js
|
|
3
|
+
* UPDATED: Fixed Error Propagation Bug. Preserves 'stage' property when re-throwing logic errors.
|
|
3
4
|
* UPDATED: Fixed Firestore 'undefined' field error for dispatchId.
|
|
4
|
-
* UPDATED: Writes structured Error objects (with stage) to Ledger to prevent retry loops.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { executeDispatchTask } = require('../WorkflowOrchestrator.js');
|
|
@@ -47,7 +47,7 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
47
47
|
|
|
48
48
|
logger.log('INFO', `[Worker] 📥 Task: ${computation} (${date}) [Tier: ${resourceTier}]`);
|
|
49
49
|
|
|
50
|
-
// [FIX] Build document object and only add dispatchId if it is defined
|
|
50
|
+
// [FIX] Build document object and only add dispatchId if it is defined (prevents Firestore "undefined" error)
|
|
51
51
|
const leaseData = {
|
|
52
52
|
status: 'IN_PROGRESS',
|
|
53
53
|
workerId: process.env.K_REVISION || os.hostname(),
|
|
@@ -72,7 +72,16 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
72
72
|
const failureReport = result?.updates?.failureReport || [];
|
|
73
73
|
const successUpdates = result?.updates?.successUpdates || {};
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
// [CRITICAL FIX] Correctly propagate the Error Stage.
|
|
76
|
+
// Previously, 'throw new Error(msg)' stripped the 'stage' property, causing the
|
|
77
|
+
// catch block to treat Deterministic errors (Quality/Logic) as System errors (Transient),
|
|
78
|
+
// triggering infinite Pub/Sub retries.
|
|
79
|
+
if (failureReport.length > 0) {
|
|
80
|
+
const reportedError = failureReport[0].error;
|
|
81
|
+
const errorObj = new Error(reportedError.message);
|
|
82
|
+
errorObj.stage = reportedError.stage; // Preserve stage (e.g. 'QUALITY_CIRCUIT_BREAKER')
|
|
83
|
+
throw errorObj;
|
|
84
|
+
}
|
|
76
85
|
|
|
77
86
|
const calcUpdate = successUpdates[normalizeName(computation)] || {};
|
|
78
87
|
const metrics = {
|
|
@@ -92,9 +101,11 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
92
101
|
clearInterval(heartbeat.timer);
|
|
93
102
|
const isDeterministic = ['SHARDING_LIMIT_EXCEEDED', 'QUALITY_CIRCUIT_BREAKER', 'SEMANTIC_GATE'].includes(err.stage);
|
|
94
103
|
|
|
104
|
+
// If error is deterministic (Logic/Quality), we record FAILURE and RETURN.
|
|
105
|
+
// This ACKs the message and stops the retry loop.
|
|
95
106
|
if (isDeterministic || (message.deliveryAttempt || 1) >= MAX_RETRIES) {
|
|
96
|
-
|
|
97
|
-
//
|
|
107
|
+
|
|
108
|
+
// Write structured error to Ledger so Dispatcher can see the 'stage' later
|
|
98
109
|
const errorPayload = {
|
|
99
110
|
message: err.message,
|
|
100
111
|
stage: err.stage || 'FATAL'
|
|
@@ -109,6 +120,8 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
109
120
|
await recordRunAttempt(db, { date, computation, pass }, 'FAILURE', { message: err.message, stage: err.stage || 'FATAL' }, { peakMemoryMB: heartbeat.getPeak() }, triggerReason, resourceTier);
|
|
110
121
|
return;
|
|
111
122
|
}
|
|
123
|
+
|
|
124
|
+
// If non-deterministic (Network/System), throw to trigger Pub/Sub Retry
|
|
112
125
|
throw err;
|
|
113
126
|
}
|
|
114
127
|
}
|