bulltrackers-module 1.0.770 → 1.0.772

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.
@@ -168,46 +168,67 @@ async function runWatchdog(req, res) {
168
168
  return res.status(500).json({ error: error.message });
169
169
  }
170
170
  }
171
-
172
171
  // =============================================================================
173
172
  // ACTIVE GARBAGE COLLECTION LOGIC
174
173
  // =============================================================================
175
174
 
176
175
  async function cleanupOrphanedTasks() {
177
176
  const parent = getQueuePath();
178
- const validKebabNames = new Set(manifest.map(m => toKebab(m.originalName)));
177
+
178
+ // Create a map of { kebabName: activeHash } for O(1) lookups
179
+ const activeComputations = new Map(
180
+ manifest.map(m => [toKebab(m.originalName), m.hash])
181
+ );
182
+
179
183
  const limit = pLimit(CLOUD_TASKS_CONCURRENCY);
180
184
  let deletedCount = 0;
181
185
 
182
186
  try {
183
- // Iterate over ALL tasks in the queue
184
- // Note: listTasksAsync handles pagination automatically
185
187
  const tasksToDelete = [];
186
188
 
187
- for await (const task of tasksClient.listTasksAsync({ parent, responseView: 'BASIC' })) {
189
+ // Note: listTasksAsync handles pagination, but if you have thousands of tasks,
190
+ // you might eventually need to handle page tokens explicitly if the library version is old.
191
+ for await (const task of tasksClient.listTasksAsync({
192
+ parent,
193
+ responseView: 'BASIC',
194
+ pageSize: 1000 // Increase page size to capture more per request
195
+ })){
188
196
  const taskNameFull = task.name;
189
- const taskNameShort = taskNameFull.split('/').pop(); // e.g., root-my-comp-2023-01-01-abcdef
197
+ const taskNameShort = taskNameFull.split('/').pop();
190
198
 
191
- // 1. Regex Match: Capture the computation name part
192
- // Pattern: (root|recovery)-{kebabName}-{date}-{hash}
193
- // Date is YYYY-MM-DD (10 chars)
194
- // Hash is 8 chars (or more)
195
- const match = taskNameShort.match(/^(?:root|recovery)-(.+)-\d{4}-\d{2}-\d{2}-/);
199
+ // 1. Handle ROOT Tasks: root-{kebabName}-{date}-{hash}
200
+ // We capture the name AND the hash at the end
201
+ const rootMatch = taskNameShort.match(/^root-(.+)-\d{4}-\d{2}-\d{2}-(.+)$/);
196
202
 
197
- if (!match) continue; // Skip tasks that don't match our naming convention
203
+ if (rootMatch) {
204
+ const [_, kebabName, taskHash] = rootMatch;
205
+ const activeHash = activeComputations.get(kebabName);
198
206
 
199
- const extractedKebabName = match[1];
207
+ // DELETE IF:
208
+ // A) Computation removed from manifest (!activeHash)
209
+ // B) Hash mismatch (Old deployment/Stale) (activeHash !== taskHash)
210
+ if (!activeHash || activeHash !== taskHash) {
211
+ tasksToDelete.push(taskNameFull);
212
+ }
213
+ continue;
214
+ }
200
215
 
201
- // 2. Check Validity
202
- if (!validKebabNames.has(extractedKebabName)) {
203
- // ORPHAN DETECTED!
204
- tasksToDelete.push(taskNameFull);
216
+ // 2. Handle RECOVERY Tasks: recovery-{kebabName}-{date}-{timestamp}
217
+ // We only delete these if the computation is completely gone.
218
+ // (Timestamps won't match a config hash, so we just check existence)
219
+ const recoveryMatch = taskNameShort.match(/^recovery-(.+)-\d{4}-\d{2}-\d{2}-/);
220
+
221
+ if (recoveryMatch) {
222
+ const [_, kebabName] = recoveryMatch;
223
+ if (!activeComputations.has(kebabName)) {
224
+ tasksToDelete.push(taskNameFull);
225
+ }
205
226
  }
206
227
  }
207
228
 
208
229
  if (tasksToDelete.length === 0) return 0;
209
230
 
210
- console.log(`[Planner] 🗑️ Found ${tasksToDelete.length} orphaned tasks. Deleting...`);
231
+ console.log(`[Planner] 🗑️ Found ${tasksToDelete.length} stale/orphaned tasks. Deleting...`);
211
232
 
212
233
  // 3. Delete in parallel
213
234
  await Promise.all(tasksToDelete.map(name => limit(async () => {
@@ -215,7 +236,10 @@ async function cleanupOrphanedTasks() {
215
236
  await tasksClient.deleteTask({ name });
216
237
  deletedCount++;
217
238
  } catch (e) {
218
- console.warn(`[Planner] Failed to delete orphan ${name}: ${e.message}`);
239
+ // Ignore "NOT_FOUND" errors in case of race conditions
240
+ if (e.code !== 5) {
241
+ console.warn(`[Planner] Failed to delete ${name}: ${e.message}`);
242
+ }
219
243
  }
220
244
  })));
221
245
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.770",
3
+ "version": "1.0.772",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [