|
@@ -32,6 +32,11 @@ export class JobQueue {
|
|
|
// Track recently processed issue events to prevent duplicates
|
|
// Track recently processed issue events to prevent duplicates
|
|
|
this.recentlyProcessed = new Map(); // key: "issues-15", value: timestamp
|
|
this.recentlyProcessed = new Map(); // key: "issues-15", value: timestamp
|
|
|
|
|
|
|
|
|
|
+ // Start periodic cleanup of old deduplication entries
|
|
|
|
|
+ this.cleanupTimer = setInterval(() => {
|
|
|
|
|
+ this._cleanupRecentlyProcessed();
|
|
|
|
|
+ }, 600000); // Run cleanup every 10 minutes
|
|
|
|
|
+
|
|
|
// Load existing queue from file
|
|
// Load existing queue from file
|
|
|
this.loadQueue();
|
|
this.loadQueue();
|
|
|
}
|
|
}
|
|
@@ -163,6 +168,27 @@ export class JobQueue {
|
|
|
return this.queues.get(repoName);
|
|
return this.queues.get(repoName);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Clean up old entries from recentlyProcessed Map
|
|
|
|
|
+ * This prevents unbounded memory growth
|
|
|
|
|
+ */
|
|
|
|
|
+ _cleanupRecentlyProcessed() {
|
|
|
|
|
+ const now = Date.now();
|
|
|
|
|
+ let removedCount = 0;
|
|
|
|
|
+
|
|
|
|
|
+ for (const [key, timestamp] of this.recentlyProcessed.entries()) {
|
|
|
|
|
+ // Remove entries older than 10 minutes
|
|
|
|
|
+ if (now - timestamp > 600000) {
|
|
|
|
|
+ this.recentlyProcessed.delete(key);
|
|
|
|
|
+ removedCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (removedCount > 0) {
|
|
|
|
|
+ logger.deduplicationCleanup(removedCount);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Add a job to the queue
|
|
* Add a job to the queue
|
|
|
* @param {string} eventType - The webhook event type
|
|
* @param {string} eventType - The webhook event type
|
|
@@ -192,13 +218,6 @@ export class JobQueue {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
this.recentlyProcessed.set(key, now);
|
|
this.recentlyProcessed.set(key, now);
|
|
|
-
|
|
|
|
|
- // Cleanup old entries (older than 10 minutes)
|
|
|
|
|
- for (const [k, ts] of this.recentlyProcessed.entries()) {
|
|
|
|
|
- if (now - ts > 600000) {
|
|
|
|
|
- this.recentlyProcessed.delete(k);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -551,6 +570,15 @@ export class JobQueue {
|
|
|
* Graceful shutdown - save queue state
|
|
* Graceful shutdown - save queue state
|
|
|
*/
|
|
*/
|
|
|
async shutdown() {
|
|
async shutdown() {
|
|
|
|
|
+ // Clear cleanup timer to prevent memory leak
|
|
|
|
|
+ if (this.cleanupTimer) {
|
|
|
|
|
+ clearInterval(this.cleanupTimer);
|
|
|
|
|
+ this.cleanupTimer = null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Clear recentlyProcessed Map to free memory
|
|
|
|
|
+ this.recentlyProcessed.clear();
|
|
|
|
|
+
|
|
|
if (this.parallelPerRepository) {
|
|
if (this.parallelPerRepository) {
|
|
|
// Multi-queue mode: save all queues
|
|
// Multi-queue mode: save all queues
|
|
|
let totalJobs = 0;
|
|
let totalJobs = 0;
|