bulltrackers-module 1.0.272 → 1.0.274

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,9 +1,8 @@
1
1
  /**
2
2
  * FILENAME: computation-system/helpers/computation_worker.js
3
3
  * PURPOSE: Consumes computation tasks from Pub/Sub and executes them.
4
+ * UPDATED: Added Deterministic Error Short-Circuit to prevent infinite retry storms on data limits.
4
5
  * UPDATED: Integrated Run Ledger for per-run/per-date success/failure tracking.
5
- * UPDATED: Added Dead Letter Queue logic for Poison Pills.
6
- * UPDATED: Now logs the trigger reason.
7
6
  */
8
7
 
9
8
  const { executeDispatchTask } = require('../WorkflowOrchestrator.js');
@@ -89,6 +88,35 @@ async function handleComputationTask(message, config, dependencies) {
89
88
  await recordRunAttempt(db, { date, computation, pass }, 'SUCCESS', { message: 'Empty Result' }, { durationMs: duration }, triggerReason);
90
89
  }
91
90
  } catch (err) {
91
+ // ----------------------------------- ERROR HANDLING & RETRY LOGIC -----------------------------------
92
+
93
+ // 1. DETERMINISTIC ERROR CHECK (Short-Circuit)
94
+ // If the error is permanent (like "Too Big" or "Validation Failed"), DO NOT RETRY.
95
+ // This stops the "Retry Storm" where we pay for 3-4 retries of a task that will never succeed.
96
+ const isDeterministicError = err.stage === 'SHARDING_LIMIT_EXCEEDED' ||
97
+ err.stage === 'QUALITY_CIRCUIT_BREAKER' ||
98
+ (err.message && (err.message.includes('INVALID_ARGUMENT') || err.message.includes('Transaction too big')));
99
+
100
+ if (isDeterministicError) {
101
+ logger.log('ERROR', `[Worker] 🛑 Permanent Failure (Data/Limit Issue). Sending to DLQ immediately: ${computation} ${date}`);
102
+ try {
103
+ await db.collection('computation_dead_letter_queue').add({
104
+ originalData: data,
105
+ error: { message: err.message, stack: err.stack, stage: err.stage || 'UNKNOWN' },
106
+ finalAttemptAt: new Date(),
107
+ failureReason: 'PERMANENT_DETERMINISTIC_ERROR'
108
+ });
109
+
110
+ // CRITICAL: We record the failure but return successfully to Pub/Sub to ACK the message and stop retries.
111
+ // This ensures the task is marked as Failed in run history, but does NOT block the queue.
112
+ await recordRunAttempt(db, { date, computation, pass }, 'FAILURE', { message: err.message, stage: err.stage || 'PERMANENT_FAIL' }, { durationMs: 0 }, triggerReason);
113
+ return;
114
+ } catch (dlqErr) {
115
+ logger.log('FATAL', `[Worker] Failed to write to DLQ for deterministic error`, dlqErr);
116
+ }
117
+ }
118
+
119
+ // 2. STANDARD RETRY LOGIC (Timeout / Crash)
92
120
  const retryCount = message.deliveryAttempt || 0;
93
121
  if (retryCount >= MAX_RETRIES) {
94
122
  logger.log('ERROR', `[Worker] ☠️ Task POISONED. Moved to DLQ: ${computation} ${date} (Attempt ${retryCount})`);
@@ -102,6 +130,8 @@ async function handleComputationTask(message, config, dependencies) {
102
130
  return;
103
131
  } catch (dlqErr) { logger.log('FATAL', `[Worker] Failed to write to DLQ`, dlqErr); }
104
132
  }
133
+
134
+ // If it's not deterministic and not max retries, we throw to let Pub/Sub retry it.
105
135
  logger.log('ERROR', `[Worker] ❌ Crash: ${computation} for ${date}: ${err.message}`);
106
136
  await recordRunAttempt(db, { date, computation, pass }, 'CRASH', { message: err.message, stack: err.stack, stage: 'SYSTEM_CRASH' }, { durationMs: 0 }, triggerReason);
107
137
  throw err;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.272",
3
+ "version": "1.0.274",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [