Skip to content

Conversation

@mcollina
Copy link
Member

@mcollina mcollina commented Nov 28, 2025

Summary

Adds request deduplication to the cache interceptor. When enabled via the deduplication: true option, concurrent identical requests are deduplicated so only one request is sent to the origin server, and all waiting handlers receive the same response.

This feature is opt-in because creating the deduplication cache key has some overhead.

Changes

  • New file: lib/handler/cache-deduplication-handler.js - Handler that buffers responses for multiple waiting handlers
  • New file: test/interceptors/cache-deduplication.js - Comprehensive test suite (14 tests)
  • Modified: lib/util/cache.js - Added makeDeduplicationKey() function
  • Modified: lib/interceptor/cache.js - Added deduplication logic with pending requests map
  • Modified: types/cache-interceptor.d.ts - Added deduplication option type
  • Modified: docs/docs/api/Dispatcher.md - Documented the feature and option
  • Modified: docs/docs/api/DiagnosticsChannel.md - Documented undici:cache:pending-requests channel

Usage

const { Client, interceptors } = require("undici");
const { cache } = interceptors;

const client = new Client("http://example.com").compose(
  cache({ deduplication: true })
);

Observability

A diagnostic channel undici:cache:pending-requests is available for monitoring:

const diagnosticsChannel = require('node:diagnostics_channel');

diagnosticsChannel.channel('undici:cache:pending-requests').subscribe(({ type, size, key }) => {
  console.log(type);  // 'added' or 'removed'
  console.log(size);  // current number of pending requests
  console.log(key);   // the deduplication key
});

Test plan

  • All existing cache tests pass (39 tests)
  • New deduplication tests pass (14 tests)
  • Tests cover: basic deduplication, vary headers, error propagation, cleanup verification, chunked bodies, diagnostic channel events
  • Lint passes

🤖 Generated with Claude Code

Implements request deduplication for the cache interceptor to prevent
multiple identical concurrent requests from being sent to the origin
server. When multiple requests for the same resource are made while
one is already in-flight, subsequent requests wait for the original
request to complete and share its response.

This optimization reduces unnecessary network traffic and server load
when handling concurrent identical requests, particularly useful in
scenarios where the same resource is requested multiple times before
the cache is populated.

Changes:
- Add CacheDeduplicationHandler to manage multiple waiting handlers
- Add makeDeduplicationKey utility for generating request keys
- Integrate deduplication logic into cache interceptor
- Add comprehensive tests for deduplication scenarios

Signed-off-by: Matteo Collina <[email protected]>
- Add 'undici:cache:pending-requests' diagnostic channel
- Publish events when pending requests are added/removed
- Add tests for error cleanup and map state verification
- Replace callback approach with Node.js diagnostics channel

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Matteo Collina <[email protected]>
- Document request deduplication feature in cache interceptor section
- Add undici:cache:pending-requests diagnostic channel documentation
- Include examples for monitoring deduplication behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Matteo Collina <[email protected]>
@mcollina mcollina marked this pull request as ready for review November 28, 2025 22:09
@mcollina
Copy link
Member Author

I had claude opus 4.5 prepare this. Looks good enough but a second opionion would be interesting.

I opted to not put the dedupe burden on the cache storage. This means that in a distributed cache scenario, you’ll
see n invocations, one per event loop.

Creating the deduplication cache key is relatively expensive, so this
feature is now disabled by default and can be enabled with the
`deduplication: true` option.

- Add `deduplication` option to cache interceptor (default: false)
- Update TypeScript types with new option
- Update documentation to reflect opt-in behavior
- Update all tests to explicitly enable deduplication

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Matteo Collina <[email protected]>
@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 96.01329% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.87%. Comparing base (fdafc2a) to head (3c4ec39).

Files with missing lines Patch % Lines
lib/handler/cache-deduplication-handler.js 95.37% 10 Missing ⚠️
lib/interceptor/cache.js 96.72% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4679      +/-   ##
==========================================
+ Coverage   92.86%   92.87%   +0.01%     
==========================================
  Files         107      108       +1     
  Lines       33499    33791     +292     
==========================================
+ Hits        31108    31385     +277     
- Misses       2391     2406      +15     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

3 participants