bulltrackers-module 1.0.809 → 1.0.810

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.
@@ -6,6 +6,7 @@
6
6
  * - FIX: Enforces UTC date arithmetic.
7
7
  * - FIX: T-1 Scheduling Logic.
8
8
  * - FIX: Handles 'blocked' status properly.
9
+ * - FIX: Deduplication Key Mismatch (SOLVED ALREADY_EXISTS SPAM).
9
10
  */
10
11
 
11
12
  const { ScheduleValidator } = require('./ScheduleValidator');
@@ -43,7 +44,6 @@ class TaskScheduler {
43
44
 
44
45
  this.logger.log(`[Planner] Reconciling: ${windowStart.toISOString()} to ${windowEnd.toISOString()}`);
45
46
 
46
- // [FIX] Use Date Generator that respects MAX_DATE_OFFSET
47
47
  const targetDates = this._generateDateRange(windowStart, windowEnd);
48
48
  const tasksToSchedule = new Map();
49
49
  const stats = { checked: 0, missing: 0, mismatched: 0, scheduled: 0, exists: 0, deleted: 0 };
@@ -65,9 +65,8 @@ class TaskScheduler {
65
65
 
66
66
  let reason = null;
67
67
 
68
- // [FIX] Logic to handle 'blocked' status.
69
- // If it is 'blocked', we generally treat it as handled (do nothing).
70
- // It will only be retried if it is truly missing or failed.
68
+ // [FIX] Handle 'blocked' status (Treat as pending/retry only if configured, otherwise ignore)
69
+ // Assuming we treat blocked as "handled" to prevent spam, unless it's genuinely missing.
71
70
 
72
71
  if (!statusEntry || status === 'pending') {
73
72
  reason = 'MISSING_RUN';
@@ -77,23 +76,21 @@ class TaskScheduler {
77
76
  reason = 'RETRY_FAILED';
78
77
  stats.missing++;
79
78
  }
80
- // Check mismatch only if not running and not blocked (blocked means we are waiting for something else)
81
79
  else if (lastRunHash !== entry.hash && status !== 'running' && status !== 'blocked') {
82
80
  reason = 'HASH_MISMATCH';
83
81
  stats.mismatched++;
84
82
  }
85
83
 
86
84
  if (reason) {
87
- const taskKey = `root-${entry.name}-${dateStr}-${entry.hash}`;
85
+ // [CRITICAL FIX] Normalize Key to match CloudTasks Adapter logic
86
+ // CloudTasks uses: root-{normalizedName}-{date}-{shortHash}
87
+ const normalizedName = entry.name.toLowerCase().replace(/[^a-z0-9]/g, '');
88
+ const hashSuffix = entry.hash ? entry.hash.substring(0, 8) : 'noHash';
89
+ const taskKey = `root-${normalizedName}-${dateStr}-${hashSuffix}`;
88
90
 
89
91
  if (!tasksToSchedule.has(taskKey)) {
90
- // 1. Calculate Base Window
91
92
  const baseRunAt = this._getNextRunWindow(effectiveSchedule, dateObj);
92
-
93
- // 2. Calculate Topological Delay
94
93
  const passDelay = (entry.pass - 1) * this.PASS_DELAY_SECONDS;
95
-
96
- // 3. Determine Final Execution Time
97
94
  const nowSeconds = Math.floor(Date.now() / 1000);
98
95
  const effectiveBase = baseRunAt === 0 ? nowSeconds : baseRunAt;
99
96
  const runAtSeconds = effectiveBase + passDelay;
@@ -123,6 +120,7 @@ class TaskScheduler {
123
120
  // --- PHASE 3: DEDUPLICATION & DISPATCH ---
124
121
  const finalTasks = [];
125
122
  for (const [key, task] of tasksToSchedule) {
123
+ // [FIX] Now 'key' matches the format in 'activeTaskKeys' so this check works
126
124
  if (activeTaskKeys.has(key)) {
127
125
  stats.exists++;
128
126
  } else {
@@ -154,7 +152,6 @@ class TaskScheduler {
154
152
 
155
153
  this.logger.log(`[Watchdog] Found ${recoverable.length} zombies.`);
156
154
 
157
- // [ADD] Diagnostic Phase
158
155
  const diagnostics = await Promise.all(recoverable.map(async z => {
159
156
  try {
160
157
  const progress = await this.stateRepo.getCheckpointProgress(z.checkpointId);
@@ -190,7 +187,7 @@ class TaskScheduler {
190
187
  await Promise.all(recoverable.map(z => this.stateRepo.claimZombie(z.checkpointId)));
191
188
 
192
189
  const recoveryTasks = recoverable.map(z => {
193
- // [FIX] Enforce T-1 Rule on Zombies too to avoid resurrecting future tasks
190
+ // [FIX] Enforce T-1 Rule on Zombies too
194
191
  const zDate = new Date(z.date);
195
192
  const now = new Date();
196
193
  const maxAllowed = new Date(now);
@@ -226,7 +223,6 @@ class TaskScheduler {
226
223
  const dates = [];
227
224
  let cur = new Date(start);
228
225
 
229
- // [FIX] Cap the scheduling window based on config (Default T-1)
230
226
  const now = new Date();
231
227
  const maxAllowed = new Date(now);
232
228
  maxAllowed.setUTCDate(now.getUTCDate() + this.MAX_DATE_OFFSET);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.809",
3
+ "version": "1.0.810",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [