From 091df8b5efb86dc765a7bb2ad2165a71ad579a04 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 00:33:00 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20optimize=20cache=20eviction=20loop?= =?UTF-8?q?=20in=20server.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - leverage Map insertion order for O(K) eviction - use setImmediate to move eviction out of critical path - ensure chronological order by deleting before re-setting keys Co-authored-by: praxstack <73683289+praxstack@users.noreply.github.com> --- .jules/bolt.md | 6 +++++- server.js | 19 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 68b59a4..6e9cdbd 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,3 +1,7 @@ ## 2024-05-18 - Optimize asynchronous file operations and child processes in `services/ReviewGenerator.js` **Learning:** `generateSplitReviews` and `generateUnifiedReview` previously executed Git operations (e.g. `GitService.getDiffForFile`) and file write operations (`fs.writeFileSync`) sequentially within a `for...of` loop. For large PRs or repos with many changed files, this sequential execution significantly bottlenecks review generation time. -**Action:** Replace `for...of` loops over `includedFiles` with an array of Promises processed via `Promise.all`. This allows Git diffs to be requested and files to be written in parallel, substantially cutting down end-to-end execution time for large payloads. It is critical to use asynchronous I/O (`fs.promises.writeFile`) instead of synchronous alternatives. \ No newline at end of file +**Action:** Replace `for...of` loops over `includedFiles` with an array of Promises processed via `Promise.all`. This allows Git diffs to be requested and files to be written in parallel, substantially cutting down end-to-end execution time for large payloads. It is critical to use asynchronous I/O (`fs.promises.writeFile`) instead of synchronous alternatives. + +## 2025-05-14 - Optimize cache eviction in server.js +**Learning:** JavaScript Map maintains insertion order. By deleting a key before setting it, we ensure it's at the "end" of the Map (most recent). This allows the eviction loop to break early when it hits the first non-expired entry, reducing complexity from O(N) to O(K) where K is number of expired items. +**Action:** Use `Map.delete(key)` before `Map.set(key, val)` for updates to maintain chronological order, and use `break` in the eviction loop once an unexpired entry is encountered. Additionally, offload the eviction logic to `setImmediate` to keep it out of the critical request/response path. diff --git a/server.js b/server.js index c60c8c9..e5218d7 100644 --- a/server.js +++ b/server.js @@ -451,19 +451,26 @@ function cacheMiddleware(ttlSeconds = 30) { const originalJson = res.json; res.json = function (data) { if (res.statusCode === 200) { + // Ensure the key is moved to the end of the Map to maintain insertion order by timestamp + requestCache.delete(key); requestCache.set(key, { data, timestamp: Date.now() }); - // Clean old cache entries periodically + // Clean old cache entries periodically using setImmediate to avoid blocking the response if (requestCache.size > 100) { - const now = Date.now(); - for (const [k, v] of requestCache.entries()) { - if (now - v.timestamp > 300000) { // 5 minutes - requestCache.delete(k); + setImmediate(() => { + const now = Date.now(); + for (const [k, v] of requestCache.entries()) { + if (now - v.timestamp > 300000) { // 5 minutes + requestCache.delete(k); + } else { + // Since Map maintains insertion order, we can stop at the first non-expired entry + break; + } } - } + }); } } return originalJson.call(this, data);