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]
|
|
69
|
-
//
|
|
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
|
-
|
|
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
|
|
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);
|