Skip to content

Conversation

@gnoff
Copy link
Contributor

@gnoff gnoff commented Sep 26, 2025

Edge config is an IO library but it heavily caches the underlying IO to make reads incredibly fast. Next.js has a heuristic for prerenders that excludes IO. While we could argue that the kind of data edge-config provides is prerenderable because it has a long lifetime it is not clear that users expect this. To keep edge-config reads consistently treated as IO I've added a wrapper for the API methods which guarantees that any read will resolve in a future timeout task.

The choice of timeout is to align with React's IO semantics for what is and is not considered IO for purposes of Suspending. On edge runtimes this is unthrottled and should not add meaningful overhead. In Node.js this is at a minimum a 1ms delay if no other work takes time however since the edge config reads themselves may take longer than 1 ms often it won't be observed. Additionally as soon as one read is queued all subsequent reads that are initiated before the timeout executes will share the same boundary.

There is of course the possibility that previously scheduled events like immediates and OS IO can delay the unblocking of these APIs but if the workload is already CPU bound then reordering the resolution is not likely to meaningfully affect overall performance.

Best reviewed with whitespace ignored

@changeset-bot
Copy link

changeset-bot bot commented Sep 26, 2025

🦋 Changeset detected

Latest commit: 907e541

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@vercel/edge-config Patch
vercel-storage-integration-test-suite Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Sep 26, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
vercel-storage-next-integration-test-suite Ready Ready Preview Sep 29, 2025 9:00pm

Edge config is an IO library but it heavily caches the underlying IO to make reads incredibly fast. Next.js has a heuristic for prerenders that excludes IO. While we could argue that the kind of data edge-config provides is prerenderable because it has a long lifetime it is not clear that users expect this. To keep edge-config reads consistently treated as IO I've added a wrapper for the API methods which guarantees that any read will resolve in a future timeout task.

The choice of timeout is to align with React's IO semantics for what is and is not considered IO for purposes of Suspending. On edge runtimes this is unthrottled and should not add meaningful overhead. In Node.js this is at a minimum a 1ms delay if no other work takes time however since the edge config reads themselves may take longer than 1 ms often it won't be observed. Additionally as soon as one read is queued all subsequent reads that are initiated before the timeout executes will share the same boundary.

There is of course the possibility that previously scheduled events like immediates and OS IO can delay the unblocking of these APIs but if the workload is already CPU bound then reordering the resolution is not likely to meaningfully affect overall performance.
@gnoff gnoff changed the title [Edge Config] Enforce an IO boundary around client APIs feat(@vercel/edge-config) Enforce an IO boundary around client APIs Sep 26, 2025
@gnoff gnoff force-pushed the jstory/enforce-io-boundary branch from 4539964 to a14cdf5 Compare September 26, 2025 05:22
Copy link
Collaborator

@dferber90 dferber90 left a comment

Choose a reason for hiding this comment

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

Would love to explore alternatives, if there are any, as adding 1ms would double our p75 latency :dramatic-chipmunk:

// are tracked as IO by React in Server Component environments.
// TODO implement a Next.js specific version so we can skip this boundary checking
// except where needed.
setTimeout(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

however since the edge config reads themselves may take longer than 1 ms often it won't be observed

We worked very hard to make latency <1ms in the majority of Edge Config reads, as we don't go over the network. p75 is definitely under 1ms.

With this change we would always be over 1ms in observed latency if any user measures await get("foo"). That's quite the loss.

Is there any other way to signal an IO operation to Next.js that does not require artificially delaying the execution of the operation?

And if we absolutely must merge this I would prefer skipping the setTimeout for non-Next.js to avoid punishing other frameworks for what smells a bit like Next.js leaking its implementation details to this SDK.

Can we find another way of achieving this that does not rely on the heuristic?

@@ -0,0 +1,50 @@
let blockedBoundary: null | Promise<void> = null;

Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment is in random place so we can have a threaded discussion

While we could argue that the kind of data edge-config provides is prerenderable because it has a long lifetime it is not clear that users expect this.

I'm fully on board that the default expectation is for this to be IO and not part of the prerender. For, we have this explicit no-store configuration option, that is used by some users of this library to treat Edge Config more like a CMS. Would that still work with the proposed changes?

@dferber90 dferber90 changed the base branch from main to canary September 29, 2025 16:04
@gnoff gnoff force-pushed the jstory/enforce-io-boundary branch from a610430 to 5a72bfe Compare September 29, 2025 20:19
Copy link
Collaborator

@dferber90 dferber90 left a comment

Choose a reason for hiding this comment

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

Approving for the snapshot release as discussed. This is still missing a changeset to be release-able as a snapshot

In Vercel the local edge config is read from filesystem but it is cached. We need the boundary to be before this because the reading from the filesystem is where things like sync IO currently happen.
@dferber90
Copy link
Collaborator

closing as #883 got merged in favor of this

@dferber90 dferber90 closed this Oct 22, 2025
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