Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: http caching #3562

Merged
merged 65 commits into from
Oct 14, 2024
Merged

feat: http caching #3562

merged 65 commits into from
Oct 14, 2024

Conversation

flakey5
Copy link
Member

@flakey5 flakey5 commented Sep 7, 2024

Implements bare-bones opt-in http caching as per rfc9111. Bare-bones in this case means what's required by the spec and a few extra bits, with more coming in future prs.

Opening as a draft since there's still some more work to be done (mostly tests, but a bit more functionality-wise)

No request cache directives are supported at this time, this will come later.

Response caching directives supported:

  • public
  • private
  • s-maxage
  • max-age
  • Expires header
  • no-cache
  • no-store
  • stale-while-revalidate

This relates to...

Closes #3231
Closes #2760
Closes #2256
Closes #1146

Changes

Features

  • Opt-in http caching
  • In-memory default cache store

Bug Fixes

n/a

Breaking Changes and Deprecations

n/a

Status

lib/cache/lru-cache-store.js Outdated Show resolved Hide resolved
lib/cache/lru-cache-store.js Outdated Show resolved Hide resolved
lib/cache/lru-cache-store.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
@ronag
Copy link
Member

ronag commented Sep 7, 2024

Would it maybe be better to use jsdoc types rather than separate type definition files? @mcollina

index.js Outdated Show resolved Hide resolved
lib/cache/lru-cache-store.js Outdated Show resolved Hide resolved
lib/cache/lru-cache-store.js Outdated Show resolved Hide resolved
lib/cache/lru-cache-store.js Outdated Show resolved Hide resolved
lib/cache/lru-cache-store.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Show resolved Hide resolved
lib/interceptor/cache.js Outdated Show resolved Hide resolved
lib/interceptor/cache.js Outdated Show resolved Hide resolved
@mcollina
Copy link
Member

mcollina commented Sep 9, 2024

Would it maybe be better to use jsdoc types rather than separate type definition files? @mcollina

I think using type definitions are in line with the rest of the codebase, and therefore the correct implementation.

If we want to migrate to a jsdoc-generated world, let's discuss in another issue!

lib/handler/cache-handler.js Outdated Show resolved Hide resolved
lib/cache/lru-cache-store.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
@ronag
Copy link
Member

ronag commented Sep 11, 2024

@flakey5 please make sure to add me and @IsakT as co-authors (assuming you took some of our code in nxt-undici).

lib/cache/lru-cache-store.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
@flakey5 flakey5 force-pushed the flakey5/3231 branch 2 times, most recently from e07e3ec to 938fa7a Compare September 12, 2024 03:58
Implements bare-bones http caching as per rfc9111

Closes nodejs#3231
Closes nodejs#2760
Closes nodejs#2256
Closes nodejs#1146

Co-authored-by: Carlos Fuentes <[email protected]>

Co-authored-by: Robert Nagy <[email protected]>

Co-authored-by: Isak Törnros <[email protected]>

Signed-off-by: flakey5 <[email protected]>
@flakey5 flakey5 marked this pull request as ready for review September 14, 2024 05:29
types/interceptors.d.ts Show resolved Hide resolved
test/cache-interceptor/cache-stores.js Outdated Show resolved Hide resolved
test/cache-interceptor/interceptor.js Outdated Show resolved Hide resolved
lib/cache/memory-cache-store.js Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
lib/cache/memory-cache-store.js Outdated Show resolved Hide resolved
lib/cache/memory-cache-store.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
lib/handler/cache-handler.js Outdated Show resolved Hide resolved
lib/handler/cache-revalidation-handler.js Outdated Show resolved Hide resolved
lib/interceptor/cache.js Outdated Show resolved Hide resolved
test/cache-interceptor/interceptor.js Show resolved Hide resolved
lib/cache/memory-cache-store.js Outdated Show resolved Hide resolved
lib/cache/memory-cache-store.js Outdated Show resolved Hide resolved
Copy link
Contributor

@Uzlopak Uzlopak left a comment

Choose a reason for hiding this comment

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

headers could be null or undefined

lib/cache/memory-cache-store.js Outdated Show resolved Hide resolved
Copy link
Contributor

@Uzlopak Uzlopak left a comment

Choose a reason for hiding this comment

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

@ronag
Should we use fastTimers?

lib/cache/memory-cache-store.js Show resolved Hide resolved
}

Promise.resolve(stream).then(handleStream).catch(err => handler.onError(err))

Copy link
Contributor

Choose a reason for hiding this comment

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

why wrap it?

Suggested change
Promise.resolve(stream).then(handleStream).catch(handler.onError)

Copy link
Member Author

Choose a reason for hiding this comment

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

The result of createReadStream can be a promise

Copy link
Member

Choose a reason for hiding this comment

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

I think we should try to avoid that. Promises and streams do not play well together, and there are a few horrid edge cases to be aware of when building such APIs, specifically related to error handling. I would prefer for this to not be a promise.

Copy link
Contributor

@SukkaW SukkaW Oct 14, 2024

Choose a reason for hiding this comment

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

I would prefer for this to not be a promise.

From what I see, stream here is coming from CacheStore's createReadStream. People may bring their cache store implementation, and depending on the implementation the returned value could be a Promise<ReadableStream>, and IMHO undici should accept that.

Let's say, what happens if someone is using redis as their cache backend and all ioredis APIs are async.

Copy link
Contributor

Choose a reason for hiding this comment

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

actually this was not about promise or not. it was about the unnecessary wrapping in an anonymous function.

Copy link
Contributor

@Uzlopak Uzlopak left a comment

Choose a reason for hiding this comment

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

found some issue, which need some love ;)

@Uzlopak
Copy link
Contributor

Uzlopak commented Oct 14, 2024

I will review it shortly ;)

// https://www.rfc-editor.org/rfc/rfc9111.html#name-age
const age = Math.round((Date.now() - value.cachedAt) / 1000)

value.rawHeaders.push(AGE_HEADER, Buffer.from(`${age}`))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
value.rawHeaders.push(AGE_HEADER, Buffer.from(`${age}`))
value.rawHeaders.push(AGE_HEADER, Buffer.from(age))

const CacheRevalidationHandler = require('../handler/cache-revalidation-handler')
const { UNSAFE_METHODS } = require('../util/cache.js')

const AGE_HEADER = Buffer.from('age')
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not entirely sure why age here is lowercased. If it is Age, we can re-use getWellKnownHeaderBuffer.

@ronag
Copy link
Member

ronag commented Oct 14, 2024

Guys. Please stop with the nits for now and open follow up PRs.we need to land this.

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

lgtm, we can do the rest in issue.

@Uzlopak can you take another look?

lib/util/cache.js Outdated Show resolved Hide resolved
lib/util/cache.js Outdated Show resolved Hide resolved
Copy link
Contributor

@Uzlopak Uzlopak left a comment

Choose a reason for hiding this comment

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

I dont see any obvious code smell any more. :)

LGTM

@Uzlopak
Copy link
Contributor

Uzlopak commented Oct 14, 2024

Unrelated Test is flaky. see #3731

@mcollina mcollina merged commit 2d40ade into nodejs:main Oct 14, 2024
31 of 32 checks passed
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.

Implement HTTP caching fetch: caching Caching Question: Does undici.fetch support RFC 7234?
7 participants