bulltrackers-module 1.0.651 → 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.
@@ -205,7 +205,7 @@ async function executeDispatchTask(dateStr, pass, targetComputation, config, dep
205
205
  const execDate = new Date(dateStr + 'T00:00:00Z');
206
206
  const updates = (calcManifest.type === 'standard')
207
207
  ? await StandardExecutor.run(execDate, calcsToRun, `Pass ${pass}`, config, dependencies, rootData, existingResults, previousResults)
208
- : await MetaExecutor.run(execDate, calcsToRun, `Pass ${pass}`, config, dependencies, existingResults, previousResults, rootData);
208
+ : await MetaExecutor.run(execDate, calcsToRun, `Pass ${pass}`, config, dependencies, rootData, existingResults, previousResults);
209
209
 
210
210
  return { date: dateStr, updates };
211
211
  }
@@ -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
- const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
15
- const { runFinalSweepCheck } = require('../tools/FinalSweepReporter'); // [NEW]
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
- targetDates = [dateInput];
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
- logger.log('WARN', `[ForceRun] 🚨 MANUALLY Triggering ${computationName} for ${targetDates.length} dates. Pass: ${manifestItem.pass}`);
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
- // 3. Construct Tasks
297
+ // 4. Construct Tasks
248
298
  const dispatchId = crypto.randomUUID();
249
- const tasks = targetDates.map(date => {
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
- // 4. Batch Publish (Chunked to stay under Pub/Sub limits)
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 ? 'SINGLE_DATE' : 'ALL_DATES',
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.651",
3
+ "version": "1.0.653",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [