bulltrackers-module 1.0.652 → 1.0.653
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.
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
* UPDATED: Enforces Strict One-Shot Policy (Standard -> HighMem -> Dead Letter).
|
|
6
6
|
* UPDATED: Generates Google Cloud Trace Context (traceId/spanId) for end-to-end monitoring.
|
|
7
7
|
* UPDATED: Added Schedule Awareness (Daily, Weekly, Monthly) to filter tasks by date.
|
|
8
|
+
* UPDATED: Force Run now validates Root Data Availability before dispatching.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
const { getExpectedDateStrings, getEarliestDataDates, normalizeName, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils.js');
|
|
11
12
|
const { groupByPass, analyzeDateExecution } = require('../WorkflowOrchestrator.js');
|
|
12
13
|
const { PubSubUtils } = require('../../core/utils/pubsub_utils');
|
|
13
14
|
const { fetchComputationStatus } = require('../persistence/StatusRepository');
|
|
14
|
-
|
|
15
|
-
const {
|
|
15
|
+
// [UPDATED] Imported getAvailabilityWindow and checkRootDependencies
|
|
16
|
+
const { checkRootDataAvailability, getAvailabilityWindow, checkRootDependencies } = require('../data/AvailabilityChecker');
|
|
17
|
+
const { runFinalSweepCheck } = require('../tools/FinalSweepReporter');
|
|
16
18
|
const crypto = require('crypto');
|
|
17
19
|
|
|
18
20
|
const OOM_THRESHOLD_MB = 1500; // Unused
|
|
@@ -233,7 +235,12 @@ async function handleForceRun(config, dependencies, computationManifest, reqBody
|
|
|
233
235
|
let targetDates = [];
|
|
234
236
|
if (dateInput) {
|
|
235
237
|
// Single Date Mode
|
|
236
|
-
|
|
238
|
+
// We still perform the check, but for a single item array
|
|
239
|
+
if (Array.isArray(dateInput)) {
|
|
240
|
+
targetDates = dateInput;
|
|
241
|
+
} else {
|
|
242
|
+
targetDates = [dateInput];
|
|
243
|
+
}
|
|
237
244
|
} else {
|
|
238
245
|
// All Dates Mode (Backfill)
|
|
239
246
|
logger.log('INFO', `[ForceRun] No date provided. Calculating date range for ${computationName}...`);
|
|
@@ -242,11 +249,54 @@ async function handleForceRun(config, dependencies, computationManifest, reqBody
|
|
|
242
249
|
targetDates = getExpectedDateStrings(earliestDates.absoluteEarliest, new Date());
|
|
243
250
|
}
|
|
244
251
|
|
|
245
|
-
|
|
252
|
+
// [NEW] 3. Filter Impossible Dates (Availability Check)
|
|
253
|
+
logger.log('INFO', `[ForceRun] 🔍 Validating data availability for ${targetDates.length} candidate dates...`);
|
|
254
|
+
|
|
255
|
+
// Sort dates to get efficient min/max for range query
|
|
256
|
+
targetDates.sort();
|
|
257
|
+
const startDate = targetDates[0];
|
|
258
|
+
const endDate = targetDates[targetDates.length - 1];
|
|
259
|
+
|
|
260
|
+
const availabilityMap = await getAvailabilityWindow(dependencies, startDate, endDate);
|
|
261
|
+
const validDates = [];
|
|
262
|
+
const skippedStats = {};
|
|
263
|
+
|
|
264
|
+
for (const date of targetDates) {
|
|
265
|
+
const status = availabilityMap.get(date);
|
|
266
|
+
|
|
267
|
+
// If no index exists, we pass an empty object (AvailabilityChecker will fail all checks)
|
|
268
|
+
// This effectively filters out dates where we have NO knowledge of data
|
|
269
|
+
const effectiveStatus = status || {};
|
|
270
|
+
|
|
271
|
+
const check = checkRootDependencies(manifestItem, effectiveStatus);
|
|
272
|
+
|
|
273
|
+
if (check.canRun) {
|
|
274
|
+
validDates.push(date);
|
|
275
|
+
} else {
|
|
276
|
+
const reason = check.missing.length > 0 ? `Missing: ${check.missing.join(', ')}` : 'Dependencies not met';
|
|
277
|
+
skippedStats[reason] = (skippedStats[reason] || 0) + 1;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (validDates.length === 0) {
|
|
282
|
+
logger.log('WARN', `[ForceRun] 🛑 ABORTING: No valid dates found for ${computationName} out of ${targetDates.length} requested.`);
|
|
283
|
+
return {
|
|
284
|
+
status: 'ABORTED',
|
|
285
|
+
computation: computationName,
|
|
286
|
+
reason: 'NO_DATA_AVAILABLE',
|
|
287
|
+
skippedDetails: skippedStats
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (validDates.length < targetDates.length) {
|
|
292
|
+
logger.log('INFO', `[ForceRun] ⚠️ Filtered impossible dates: ${targetDates.length} requested -> ${validDates.length} valid.`, { skippedStats });
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
logger.log('WARN', `[ForceRun] 🚨 MANUALLY Triggering ${computationName} for ${validDates.length} VALID dates. Pass: ${manifestItem.pass}`);
|
|
246
296
|
|
|
247
|
-
//
|
|
297
|
+
// 4. Construct Tasks
|
|
248
298
|
const dispatchId = crypto.randomUUID();
|
|
249
|
-
const tasks =
|
|
299
|
+
const tasks = validDates.map(date => {
|
|
250
300
|
const traceId = crypto.randomBytes(16).toString('hex');
|
|
251
301
|
const spanId = crypto.randomBytes(8).toString('hex');
|
|
252
302
|
return {
|
|
@@ -262,7 +312,7 @@ async function handleForceRun(config, dependencies, computationManifest, reqBody
|
|
|
262
312
|
};
|
|
263
313
|
});
|
|
264
314
|
|
|
265
|
-
//
|
|
315
|
+
// 5. Batch Publish (Chunked to stay under Pub/Sub limits)
|
|
266
316
|
const CHUNK_SIZE = 250; // Safe batch size
|
|
267
317
|
const topic = (reqBody.resources === 'high-mem')
|
|
268
318
|
? (config.computationTopicHighMem || 'computation-tasks-highmem')
|
|
@@ -287,8 +337,10 @@ async function handleForceRun(config, dependencies, computationManifest, reqBody
|
|
|
287
337
|
return {
|
|
288
338
|
status: 'FORCED',
|
|
289
339
|
computation: computationName,
|
|
290
|
-
mode: dateInput ? '
|
|
340
|
+
mode: dateInput ? 'SINGLE/ARRAY_DATE' : 'ALL_DATES',
|
|
341
|
+
datesRequested: targetDates.length,
|
|
291
342
|
datesTriggered: dispatchedCount,
|
|
343
|
+
skippedImpossible: targetDates.length - dispatchedCount,
|
|
292
344
|
targetTopic: topic
|
|
293
345
|
};
|
|
294
346
|
}
|