Skip to content

Refresh buffered files when source changes#111

Open
vibhor-aggr wants to merge 3 commits into
koajs:masterfrom
vibhor-aggr:fix/refresh-buffered-files
Open

Refresh buffered files when source changes#111
vibhor-aggr wants to merge 3 commits into
koajs:masterfrom
vibhor-aggr:fix/refresh-buffered-files

Conversation

@vibhor-aggr

@vibhor-aggr vibhor-aggr commented Jun 17, 2026

Copy link
Copy Markdown

Closes #96.

Buffered files now use the same freshness check as streamed files. When the source file mtime or size changes, the cached metadata is refreshed; buffered responses also reload the file contents and recalculate the MD5/ETag before serving.

This also clears any cached gzip buffer when the source changes so compressed responses are regenerated from the current file contents.

Verification:

  • ./node_modules/.bin/mocha --require should --require should-http --harmony --grep "refresh buffered files"
  • npm test
  • git diff --check

Summary by Sourcery

Refresh cached static file metadata and contents when the source file changes, ensuring buffered responses stay in sync with the underlying files.

New Features:

  • Support automatic refresh of buffered static file contents and metadata when the source file mtime or size changes.

Bug Fixes:

  • Ensure ETag/MD5 and content for buffered static files are updated when the underlying file changes instead of serving stale responses.
  • Clear cached gzip buffers when the source file changes so compressed responses are regenerated from current contents.

Tests:

  • Add an integration test verifying buffered files are reloaded and ETags change when the source file is modified.

Summary by CodeRabbit

  • Bug Fixes

    • Improved static cached file freshness checks to detect changes using both modification time and file size, including scenarios where content is already buffered, so updates and ETags refresh reliably.
  • Tests

    • Added coverage for buffered + dynamic refresh behavior, verifying that cached buffered content updates after source changes and that the ETag reflects the update.

@sourcery-ai

sourcery-ai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Reviewer's Guide

Aligns buffered file caching with streamed file freshness checks by always stat-ing the source file, refreshing metadata and buffer (including MD5/ETag) when mtime or size changes, and clearing any cached gzip buffer; adds a regression test to verify buffered responses update when the underlying file changes.

Sequence diagram for refreshed buffered file caching on source change

sequenceDiagram
  actor Client
  participant KoaApp
  participant staticCacheMiddleware
  participant fs
  participant crypto

  Client->>KoaApp: HTTP GET /asset
  KoaApp->>staticCacheMiddleware: handle request
  staticCacheMiddleware->>fs: stat(file.path)
  fs-->>staticCacheMiddleware: stats
  alt [stats.mtime or stats.size changed]
    staticCacheMiddleware->>staticCacheMiddleware: file.mtime = stats.mtime
    staticCacheMiddleware->>staticCacheMiddleware: file.length = stats.size
    staticCacheMiddleware->>staticCacheMiddleware: file.md5 = null
    staticCacheMiddleware->>staticCacheMiddleware: file.zipBuffer = null
    alt [file.buffer exists]
      staticCacheMiddleware->>fs: readFile(file.path)
      fs-->>staticCacheMiddleware: file.buffer
      staticCacheMiddleware->>crypto: createHash(md5).update(file.buffer).digest(base64)
      crypto-->>staticCacheMiddleware: md5
      staticCacheMiddleware->>staticCacheMiddleware: file.md5 = md5
    end
  end
  staticCacheMiddleware-->>Client: HTTP response (buffered or streamed)
Loading

File-Level Changes

Change Details Files
Unify freshness check for buffered and non-buffered files, refreshing metadata and content when the source file changes, and clearing gzip cache.
  • Always stat the file on each request instead of only when not buffered.
  • Compare both mtime and size to detect source changes and refresh cache metadata.
  • Reset MD5 hash and content length whenever the file changes.
  • Clear any cached gzip buffer when the source file is updated.
  • For buffered files, reload the file contents from disk and recompute the MD5/ETag before serving.
index.js
Add regression test to ensure buffered files are refreshed when the source file changes.
  • Create a temporary file, serve it with staticCache in buffered mode, and assert initial body and ETag.
  • Modify the file contents and mtime, then re-request and assert the response body updates and ETag changes.
  • Verify the in-memory cache entry's buffer matches the updated file contents and clean up the temporary file after the test.
test/index.js

Assessment against linked issues

Issue Objective Addressed Explanation
#96 Ensure that when options.buffer is enabled, the middleware correctly detects source file changes (freshness) and updates cached metadata and contents instead of serving stale buffered data.
#96 Add automated tests to verify that buffered files are refreshed when the underlying source file changes.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vibhor-aggr, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 24 minutes and 7 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b0e505b2-38f6-4275-8dd6-5fe012d594fe

📥 Commits

Reviewing files that changed from the base of the PR and between aad5a83 and c2a36b4.

📒 Files selected for processing (2)
  • index.js
  • test/index.js
📝 Walkthrough

Walkthrough

The per-request freshness check inside staticCache is updated to always execute fs.stat(file.path), removing the prior condition that skipped this check when file.buffer was already populated. The invalidation condition now triggers on either mtime or size differences (previously only mtime). When invalidated, file.mtime, file.length, file.md5, and file.zipBuffer are updated; if file.buffer is set, the file contents are reloaded and file.md5 is recomputed. A new test verifies this behavior by writing a file, populating the buffer via a request, modifying the file contents and timestamps, and asserting that a subsequent request returns the updated buffer content and a changed ETag.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: implementing file refresh logic for buffered files when source files are modified.
Linked Issues check ✅ Passed The PR implements freshness checks for buffered files using mtime and size detection, reloading contents and recalculating MD5/ETag when changes are detected, directly addressing issue #96's concern about stale buffered files.
Out of Scope Changes check ✅ Passed All changes are focused on implementing file freshness checks for buffered files and adding corresponding test coverage, with no unrelated modifications present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The freshness logic for updating file.mtime, file.length, file.md5, and file.zipBuffer is now embedded directly in the middleware; consider extracting this into a shared helper so it can’t drift from the streamed-file path and is easier to test in isolation.
  • In the new should refresh buffered files when the source changes test, the file cleanup relies on multiple fs.unlinkSync calls within nested callbacks; using a try/finally or Mocha afterEach hook to guarantee cleanup would make the test more robust against early failures.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The freshness logic for updating `file.mtime`, `file.length`, `file.md5`, and `file.zipBuffer` is now embedded directly in the middleware; consider extracting this into a shared helper so it can’t drift from the streamed-file path and is easier to test in isolation.
- In the new `should refresh buffered files when the source changes` test, the file cleanup relies on multiple `fs.unlinkSync` calls within nested callbacks; using a `try/finally` or Mocha `afterEach` hook to guarantee cleanup would make the test more robust against early failures.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/index.js (1)

153-190: ⚡ Quick win

Add a regression assertion for gzip-buffer invalidation on refresh.

This test validates buffer + ETag refresh, but it doesn’t cover the zipBuffer invalidation path described in the PR objective. Consider extending this case with a gzip request before/after mutation to ensure stale compressed bytes are not reused.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/index.js` around lines 153 - 190, The test case for buffer freshness
does not verify that the gzip-compressed buffer (zipBuffer) is properly
invalidated when the source file changes. Extend the test by adding gzip
encoding requests both before and after the file mutation to ensure that stale
compressed bytes are not reused. Specifically, make an additional request with
Accept-Encoding: gzip header before the fs.writeFileSync call that mutates the
file, capture the gzip buffer from the files object, then after the file
mutation and the second request, verify that the new gzip buffer is different
from the stale one to confirm the compressed cache was properly invalidated and
refreshed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@index.js`:
- Around line 84-93: The fs.stat and fs.readFile calls in the freshness check
block can throw errors when files are deleted or rotated between requests,
currently causing unhandled 500 errors. Wrap the entire stats checking and file
reading logic (starting from the fs.stat(file.path) call through the
crypto.createHash operations) in a try-catch block that specifically handles
ENOENT errors by treating them as cache invalidation and calling next() to allow
the request to proceed (resulting in a 404 downstream), while rethrowing any
other unexpected I/O errors to maintain proper error handling for genuine
issues.

---

Nitpick comments:
In `@test/index.js`:
- Around line 153-190: The test case for buffer freshness does not verify that
the gzip-compressed buffer (zipBuffer) is properly invalidated when the source
file changes. Extend the test by adding gzip encoding requests both before and
after the file mutation to ensure that stale compressed bytes are not reused.
Specifically, make an additional request with Accept-Encoding: gzip header
before the fs.writeFileSync call that mutates the file, capture the gzip buffer
from the files object, then after the file mutation and the second request,
verify that the new gzip buffer is different from the stale one to confirm the
compressed cache was properly invalidated and refreshed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 514d73c6-e12d-40f9-833c-e43a8ccd9423

📥 Commits

Reviewing files that changed from the base of the PR and between 182f260 and 27c5e88.

📒 Files selected for processing (2)
  • index.js
  • test/index.js

Comment thread index.js Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

options.buffer seems never judge whether the file is fresh

1 participant