Skip to content

Conversation

@devin-ai-integration
Copy link
Contributor

Fixes FERN-DASHBOARD-EE, FERN-DASHBOARD-8R, FERN-DASHBOARD-8T, FERN-DASHBOARD-D1, FERN-DASHBOARD-93, FERN-DASHBOARD-E7, FERN-DASHBOARD-DY, FERN-DASHBOARD-BT, FERN-DASHBOARD-82

Deduplicate parallel getRoot() calls in editor route

Wraps the getRoot() and getNavigationNode() methods in the DocsLoader with React.cache() to prevent N+1 API calls when multiple parallel slot components independently fetch the same root resource.

What was the motivation & context behind this PR?

The editor route uses Next.js parallel routes with 6+ slot components (@Sidebar, @versionSelect, @headertabs, @productSelect, @logo, @devpanel) that each independently call loader.getRoot() during server-side rendering. While the underlying getRootCached() function was already cached, each parallel slot's call to loader.getRoot() created a new async function that independently awaited getAuthState() and authConfig, resulting in 7 simultaneous HTTP requests to /root?_rsc=* as observed in Sentry.

This caused the "N+1 API Call" error that has been occurring 9+ times recently according to Sentry logs.

How has this PR been tested?

⚠️ Not tested locally yet - This fix was implemented based on:

  • Sentry error analysis showing 7 simultaneous /root?_rsc=* requests
  • Code review identifying that getRoot() and getNavigationNode() were async functions without React.cache() wrapper
  • Confirmation that the parallel slots all use the same loader instance via getCachedEditableDocsLoader()

Recommended testing:

  1. Navigate to editor route in dev: /:orgName/editor/:docsUrl/:branch/:slug
  2. Monitor network requests - should see only 1 /root request instead of 7
  3. Verify editor functionality (sidebar, version selector, product selector, etc.) still works correctly
  4. Monitor Sentry after deployment to confirm error stops occurring

Key review points:

  • The change assumes all parallel slot components call getRoot() within the same React Server Component render pass (which the _rsc query parameters confirm)
  • React.cache() provides request-scoped memoization, so each editor page load will still make the underlying API call once
  • The loader is shared across parallel slots via getCachedEditableDocsLoader(), so the cache is properly shared

Link to Devin run: https://app.devin.ai/sessions/e4430584100a4e0d951e387b9b245f42
Requested by: [email protected] (@dannysheridan)

Wrap getRoot() and getNavigationNode() methods with React.cache() to prevent N+1 API calls when multiple parallel slot components (@Sidebar, @versionSelect, @headertabs, @productSelect, @logo, @devpanel) independently fetch the same root resource.

Each parallel slot was calling loader.getRoot(), creating separate promises that independently awaited getAuthState() and authConfig. While getRootCached was already cached, the outer async function was not, causing 7 simultaneous requests to /root?_rsc=*.

Fixes FERN-DASHBOARD-EE, FERN-DASHBOARD-8R, FERN-DASHBOARD-8T, FERN-DASHBOARD-D1, FERN-DASHBOARD-93, FERN-DASHBOARD-E7, FERN-DASHBOARD-DY, FERN-DASHBOARD-BT, FERN-DASHBOARD-82

Co-Authored-By: [email protected] <[email protected]>
@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@vercel
Copy link
Contributor

vercel bot commented Nov 6, 2025

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

Project Deployment Preview Updated (UTC)
dev.ferndocs.com Ready Ready Preview Nov 6, 2025 2:01pm
fern-dashboard Ready Ready Preview Nov 6, 2025 2:01pm
fern-dashboard-dev Ready Ready Preview Nov 6, 2025 2:01pm
ferndocs.com Ready Ready Preview Nov 6, 2025 2:01pm
preview.ferndocs.com Ready Ready Preview Nov 6, 2025 2:01pm
prod-assets.ferndocs.com Ready Ready Preview Nov 6, 2025 2:01pm
prod.ferndocs.com Ready Ready Preview Nov 6, 2025 2:01pm
1 Skipped Deployment
Project Deployment Preview Updated (UTC)
fern-platform Ignored Ignored Nov 6, 2025 2:01pm

Copy link
Collaborator

@sbawabe sbawabe left a comment

Choose a reason for hiding this comment

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

this response is too large to be react cached so it throws errors -- this is why we use upstash/redis caching

Reverted React.cache() wrappers on getRoot/getNavigationNode in readonly-docs-loader.ts
as the root response is too large (>2MB) for React's cache mechanism.

Instead, implemented promise memoization at the loader instance level in
getCachedEditableDocsLoader. This deduplicates parallel slot calls without
hitting React.cache size limits:

- Store in-flight promise for getRoot() on the loader instance
- Store in-flight promises for getNavigationNode(id) in a Map keyed by id
- All parallel slots share the same loader instance via getCachedEditableDocsLoader
- Each navigation only makes one underlying API call instead of 7

This approach avoids the 2MB React.cache limit mentioned in readonly-docs-loader.ts:1117-1122
while still eliminating the N+1 API calls observed in Sentry.

Co-Authored-By: [email protected] <[email protected]>
@devin-ai-integration
Copy link
Contributor Author

Thanks for catching this! You're absolutely right - the root response is too large for React.cache() (hits the 2MB limit mentioned in readonly-docs-loader.ts:1117-1122).

I've updated the fix to use promise memoization instead:

  • Reverted the React.cache() wrappers on getRoot/getNavigationNode in readonly-docs-loader.ts
  • Implemented promise memoization at the loader instance level in getCachedEditableDocsLoader
  • Now stores in-flight promises on the loader instance so all parallel slots await the same promise
  • This avoids the React.cache size limit while still eliminating the N+1 calls

The approach is simpler and more targeted - it only affects the dashboard editor route where the N+1 issue occurs, rather than changing the base docs-loader package.

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.

2 participants