bulltrackers-module 1.0.694 → 1.0.696
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.
|
@@ -11,6 +11,8 @@ const { PubSubUtils } = require('../../core/utils/pubsub_utils');
|
|
|
11
11
|
const { fetchComputationStatus } = require('../persistence/StatusRepository');
|
|
12
12
|
const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
|
|
13
13
|
const { runFinalSweepCheck } = require('../tools/FinalSweepReporter');
|
|
14
|
+
const { resolveDependencyChain } = require('./on_demand_helpers');
|
|
15
|
+
const { checkRootDependencies } = require('../data/AvailabilityChecker');
|
|
14
16
|
// 1. IMPORT SNAPSHOT SERVICE
|
|
15
17
|
const { generateDailySnapshots } = require('../services/SnapshotService');
|
|
16
18
|
const crypto = require('crypto');
|
|
@@ -261,8 +263,8 @@ async function handleSnapshot(config, dependencies, reqBody) {
|
|
|
261
263
|
}
|
|
262
264
|
|
|
263
265
|
const successful = results.filter(r => r.status === 'OK').length;
|
|
264
|
-
const skipped
|
|
265
|
-
const failed
|
|
266
|
+
const skipped = results.filter(r => r.status === 'SKIPPED').length;
|
|
267
|
+
const failed = results.filter(r => r.status === 'ERROR').length;
|
|
266
268
|
|
|
267
269
|
logger.log('INFO', `[Dispatcher] 📸 Snapshot batch complete: ${successful} processed, ${skipped} skipped, ${failed} failed out of ${results.length} total`);
|
|
268
270
|
|
|
@@ -318,6 +320,16 @@ async function handleForceRun(config, dependencies, computationManifest, reqBody
|
|
|
318
320
|
const manifestItem = computationManifest.find(c => normalizeName(c.name) === normalizeName(computationName));
|
|
319
321
|
if (!manifestItem) throw new Error(`Computation '${computationName}' not found.`);
|
|
320
322
|
|
|
323
|
+
// --- STEP 1: RESOLVE FULL ANCESTRY ---
|
|
324
|
+
// We get the full chain of dependencies (ancestors) for the target.
|
|
325
|
+
// This includes the target itself and all upstream computations.
|
|
326
|
+
const chainPasses = resolveDependencyChain(computationName, computationManifest);
|
|
327
|
+
const allAncestors = chainPasses.flatMap(p => p.computations); // Flat list of all required names
|
|
328
|
+
|
|
329
|
+
// Create a map for quick lookup of ancestor manifests
|
|
330
|
+
const manifestMap = new Map(computationManifest.map(c => [normalizeName(c.name), c]));
|
|
331
|
+
|
|
332
|
+
// --- STEP 2: DETERMINE CANDIDATE DATES ---
|
|
321
333
|
let candidateDates = [];
|
|
322
334
|
if (dateInput) {
|
|
323
335
|
candidateDates = [dateInput];
|
|
@@ -327,50 +339,85 @@ async function handleForceRun(config, dependencies, computationManifest, reqBody
|
|
|
327
339
|
candidateDates = getExpectedDateStrings(earliest.absoluteEarliest, new Date());
|
|
328
340
|
}
|
|
329
341
|
|
|
330
|
-
logger.log('INFO', `[ForceRun] Checking ${candidateDates.length} candidate dates for runnability...`);
|
|
342
|
+
logger.log('INFO', `[ForceRun] Checking ${candidateDates.length} candidate dates for runnability (Deep Check)...`);
|
|
331
343
|
|
|
332
344
|
const runnableDates = [];
|
|
333
345
|
const skippedDates = [];
|
|
334
|
-
const manifestMap = new Map(computationManifest.map(c => [normalizeName(c.name), c]));
|
|
335
|
-
|
|
336
|
-
const targetComp = { ...manifestItem, schedule: null };
|
|
337
346
|
const targetComputationNormalized = normalizeName(computationName);
|
|
347
|
+
// Remove schedule constraints for the force run assessment
|
|
348
|
+
const targetComp = { ...manifestItem, schedule: null };
|
|
338
349
|
|
|
339
350
|
for (const date of candidateDates) {
|
|
351
|
+
// --- STEP 3: DEEP ROOT CHECK ---
|
|
352
|
+
// Before even asking if the *target* is runnable, we ask:
|
|
353
|
+
// "Are the raw ingredients available for the ENTIRE chain?"
|
|
354
|
+
|
|
355
|
+
// Fetch Root Data Status for this date once
|
|
356
|
+
const availability = await checkRootDataAvailability(date, config, dependencies, DEFINITIVE_EARLIEST_DATES);
|
|
357
|
+
const rootStatus = availability ? availability.status : null;
|
|
358
|
+
|
|
359
|
+
if (!rootStatus) {
|
|
360
|
+
skippedDates.push({ date, reason: 'Availability Index Missing' });
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
let deepImpossibleReason = null;
|
|
365
|
+
|
|
366
|
+
// Check EVERY ancestor's root requirements
|
|
367
|
+
for (const ancName of allAncestors) {
|
|
368
|
+
const ancManifest = manifestMap.get(normalizeName(ancName));
|
|
369
|
+
if (!ancManifest) continue;
|
|
370
|
+
|
|
371
|
+
// Re-use the standard checker for each ancestor
|
|
372
|
+
const ancCheck = checkRootDependencies(ancManifest, rootStatus);
|
|
373
|
+
|
|
374
|
+
if (!ancCheck.canRun) {
|
|
375
|
+
// If an ancestor cannot exist, the target cannot exist.
|
|
376
|
+
deepImpossibleReason = `Ancestor '${ancName}' is missing roots: ${ancCheck.missing.join(', ')}`;
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (deepImpossibleReason) {
|
|
382
|
+
// Skip this date entirely - it is strictly impossible.
|
|
383
|
+
skippedDates.push({ date, reason: deepImpossibleReason });
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// --- STEP 4: STANDARD RUNNABILITY ---
|
|
388
|
+
// If deep roots are fine, we proceed to the standard check.
|
|
389
|
+
// This handles logic like "Waiting for yesterday" or "Already Complete"
|
|
340
390
|
const result = await assessDateRunnability(date, [targetComp], config, dependencies, manifestMap);
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
391
|
+
|
|
392
|
+
if (!result) {
|
|
393
|
+
skippedDates.push({ date, reason: 'Assessment Failed' });
|
|
394
|
+
continue;
|
|
344
395
|
}
|
|
345
396
|
|
|
346
397
|
const { report } = result;
|
|
347
398
|
const isRunnable = report.runnable.some(t => normalizeName(t.name) === targetComputationNormalized);
|
|
348
399
|
const needsReRun = report.reRuns.some(t => normalizeName(t.name) === targetComputationNormalized);
|
|
349
400
|
const hasFailedDep = report.failedDependency.some(t => normalizeName(t.name) === targetComputationNormalized);
|
|
350
|
-
const isImpossible = report.impossible.some(t => normalizeName(t.name) === targetComputationNormalized);
|
|
351
|
-
const isBlocked = report.blocked.some(t => normalizeName(t.name) === targetComputationNormalized);
|
|
352
401
|
const isSkipped = report.skipped.some(t => normalizeName(t.name) === targetComputationNormalized);
|
|
353
402
|
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
if (
|
|
358
|
-
skippedDates.push({ date, reason: report.impossible.find(t => normalizeName(t.name) === targetComputationNormalized)?.reason || 'Impossible' });
|
|
359
|
-
} else if (isRunnable || needsReRun || hasFailedDep || isSkipped) {
|
|
360
|
-
// Runnable, needs re-run, has failed deps (but not impossible), or skipped (already stored)
|
|
361
|
-
// All of these are runnable for force runs - will overwrite existing results if needed
|
|
403
|
+
// NOTE: hasFailedDep is ALLOWED here because we are "Forcing" it.
|
|
404
|
+
// We know the roots exist (checked above), so the missing dependency is likely just
|
|
405
|
+
// "Not Computed Yet", which is exactly what the user wants to fix manually.
|
|
406
|
+
if (isRunnable || needsReRun || hasFailedDep || isSkipped) {
|
|
362
407
|
runnableDates.push(date);
|
|
363
|
-
} else if (
|
|
364
|
-
// Blocked usually means
|
|
365
|
-
//
|
|
408
|
+
} else if (report.blocked.length > 0) {
|
|
409
|
+
// Blocked usually means "Waiting for yesterday"
|
|
410
|
+
// For force runs, we often want to override this, but if it's strictly blocked
|
|
411
|
+
// by logic, we might still count it. Usually, we treat it as runnable.
|
|
366
412
|
runnableDates.push(date);
|
|
367
413
|
} else {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
runnableDates.push(date);
|
|
414
|
+
const imp = report.impossible.find(t => normalizeName(t.name) === targetComputationNormalized);
|
|
415
|
+
skippedDates.push({ date, reason: imp ? imp.reason : 'Unknown State' });
|
|
371
416
|
}
|
|
372
417
|
}
|
|
373
418
|
|
|
419
|
+
// ... (Remainder of the function remains the same: dispatching tasks) ...
|
|
420
|
+
|
|
374
421
|
logger.log('INFO', `[ForceRun] ✅ Found ${runnableDates.length} runnable dates out of ${candidateDates.length} candidates`);
|
|
375
422
|
|
|
376
423
|
if (runnableDates.length === 0) {
|
|
@@ -384,13 +431,12 @@ async function handleForceRun(config, dependencies, computationManifest, reqBody
|
|
|
384
431
|
};
|
|
385
432
|
}
|
|
386
433
|
|
|
387
|
-
|
|
388
|
-
|
|
434
|
+
// Dispatch Logic
|
|
389
435
|
const topic = (reqBody.resources === 'high-mem')
|
|
390
436
|
? (config.computationTopicHighMem || 'computation-tasks-highmem')
|
|
391
437
|
: (config.computationTopicStandard || 'computation-tasks');
|
|
392
438
|
|
|
393
|
-
const dispatchId = crypto.randomUUID();
|
|
439
|
+
const dispatchId = require('crypto').randomUUID();
|
|
394
440
|
const tasks = runnableDates.map(date =>
|
|
395
441
|
createTaskPayload(manifestItem, date, manifestItem.pass || "1", dispatchId, reqBody.resources, 'MANUAL_FORCE_API')
|
|
396
442
|
);
|