Skip to content

feat(boards): cache named board data with tag-based invalidation#5737

Open
ajnart wants to merge 4 commits into
devfrom
feat/board-cache
Open

feat(boards): cache named board data with tag-based invalidation#5737
ajnart wants to merge 4 commits into
devfrom
feat/board-cache

Conversation

@ajnart
Copy link
Copy Markdown
Member

@ajnart ajnart commented May 18, 2026

Summary

  • Add getSharedBoardWithWhereAsync — fetches board structure without per-user data (permissions, collapse states), safe to cache across requests
  • Wrap named board fetches in unstable_cache with a 1h TTL and board-{name} tag
  • Add invalidateBoardCacheAsync server action that busts cache on board save, layout save, and partial settings mutations
  • Home board path is unchanged (no caching, still uses tRPC getHomeBoard)

Depends on #5736.

Test plan

  • Named board loads from cache on repeat visits (check server logs for fewer DB queries)
  • Editing a board (save, settings, layout) invalidates cache — next load reflects changes
  • Home board still works without caching
  • Anonymous users see the correct board data

ajnart added 2 commits May 18, 2026 14:49
Board fetch + auth() now run concurrently via Promise.all instead of
sequentially. Widget prefetches for all kinds also run in parallel
alongside integration permission loading.
Named boards are now served from Next.js unstable_cache (1h TTL,
tag `board-{name}`). A new `getSharedBoardWithWhereAsync` helper
fetches the board without per-user data so the result is safe to
share across requests.

Cache is busted via `invalidateBoardCacheAsync` (revalidateTag +
revalidatePath) from board save, layout save, and partial settings
mutations.
@ajnart ajnart requested a review from a team as a code owner May 18, 2026 12:51
- Run prettier on all modified files
- Add required cache profile arg to revalidateTag
- Suppress require-await for server action (must be async)
@ajnart ajnart added the needs-demo This PR needs a demo deployment label May 18, 2026
@dokploy-homarr-labs
Copy link
Copy Markdown

dokploy-homarr-labs Bot commented May 18, 2026

Dokploy Preview Deployment

Name Status Preview Updated (UTC)
homarr ✅ Done Preview URL 2026-05-18T13:35:22.356Z

manuel-rw
manuel-rw previously approved these changes May 18, 2026
Copy link
Copy Markdown
Member

@manuel-rw manuel-rw left a comment

Choose a reason for hiding this comment

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

this looks good to me, but wait for @Meierschlumpf 's approval.
What consequences does this have, when the cache is outdated? Does it cause race conditions when saving?

Copy link
Copy Markdown
Member

@Meierschlumpf Meierschlumpf left a comment

Choose a reason for hiding this comment

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

Based on my understanding these changes will result in a few problems:

  1. User specific data is no longer delivered, so groups / permissions of the user and collapse states will no longer be available.
  2. ⚠️ Anybody can access even private boards as there no longer is a throwIfActionForbiddenAsync with view permission - at least if the _layout-creator would not check for it additionally. You could consider caching the values from getInitialBoard for the specific request so it does not send 3 calls to that function (layout, page and metadata) instead and maybe also improve the throwIfActionForbiddenAsync which is currently also sending db requests for info that could be available through the requested board

The unstable_cache approach bypassed throwIfActionForbiddenAsync and
dropped user-specific data (permissions, collapse states). Use React
cache() to deduplicate getInitialBoard within a single request instead,
preserving auth checks and all user data while reducing DB calls from
9 to 3 per board page load.
@ajnart
Copy link
Copy Markdown
Member Author

ajnart commented May 25, 2026

Based on my understanding these changes will result in a few problems:

  1. User specific data is no longer delivered, so groups / permissions of the user and collapse states will no longer be available.
  2. ⚠️ Anybody can access even private boards as there no longer is a throwIfActionForbiddenAsync with view permission - at least if the _layout-creator would not check for it additionally. You could consider caching the values from getInitialBoard for the specific request so it does not send 3 calls to that function (layout, page and metadata) instead and maybe also improve the throwIfActionForbiddenAsync which is currently also sending db requests for info that could be available through the requested board

I replaced the unstable_cache approach with React cache() for per-request deduplication. Layout, page, and metadata now share one tRPC call (3 DB queries instead of 9), while keeping the full auth check and all user-specific data intact

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-demo This PR needs a demo deployment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants