Skip to content

feat: Implement cache - #750

Merged
aXenDeveloper merged 2 commits into
canaryfrom
feat/cache
Aug 20, 2026
Merged

feat: Implement cache#750
aXenDeveloper merged 2 commits into
canaryfrom
feat/cache

Conversation

@aXenDeveloper

Copy link
Copy Markdown
Owner

Improving Documentation

Description

What?

Why?

@vercel

vercel Bot commented Aug 20, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
vitnode-docs Ready Ready Preview Aug 20, 2026 4:16pm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb48923de5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +121 to +122
const resolved = await loadStaffPermissions(c, { type, user });
await writeStaffPermissions(c, { type, userId: user.id }, resolved);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Prevent stale permission loads from repopulating the cache

When a cold permission lookup overlaps a role or staff mutation, loadStaffPermissions can resolve the old grants, the mutation can invalidate them, and this later write then repopulates the cache with that stale result. Per-user invalidation rewrites the deleted key, while global invalidation is even more vulnerable because writeStaffPermissions rereads the new epoch and writes the old result into the new generation; subsequent requests can therefore retain revoked privileges for the 60-second TTL. Carry the generation/version used by the initial read through the load and only write if it is still current, including a per-user generation for targeted invalidations.

Useful? React with 👍 / 👎.

// out its `cacheLife`. `updateTag` rather than `revalidateTag` because this is
// a Server Action: the admin who pressed the button sees the new feed on the
// refresh it triggers, not one navigation later.
updateTag(SEARCH_FEED_TAG);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Delay search-feed invalidation until the rebuild completes

The rebuild endpoint only inserts a database queue row, which is processed later by the process-queue cron job, so this invalidation happens before the index has changed. If the action-triggered refresh or any other feed request runs before the worker finishes, it repopulates the cache from the old or partially rebuilt index, and the worker never invalidates the tag again after completion; /search and /discover then remain stale until cacheLife("minutes") expires. Trigger the invalidation after the queued rebuild task successfully completes.

AGENTS.md reference: AGENTS.md:L28-L30

Useful? React with 👍 / 👎.

* Next's longest built-in profile (`max`), so an entry that outlives its marker
* has already been forced to refresh on age alone.
*/
const TAG_RETENTION_SECONDS = 60 * 60 * 24 * 30;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retain invalidation markers as long as tagged entries

For a valid custom cacheLife whose revalidate and expire exceed 30 days, a hard tag invalidation marker disappears here while the corresponding "use cache" entry is still retained in Redis. If that entry is not requested and replaced during the marker window, a later request no longer sees evidence of the invalidation and can serve the old value again; this can resurrect deleted or unpublished content. Marker retention needs to cover the maximum lifetime of every tagged entry, or expired entries must be deleted directly.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 384fc09f72

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +118 to +119
const cached = await readStaffPermissions(c, { type, userId: user.id });
if (cached) return cached;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Fail closed when permission invalidation misses Redis

If Redis is unavailable while a role or staff mutation runs, deleteSystem/setSystem silently drops the invalidation after the database mutation has succeeded. When Redis recovers within the 60-second TTL, this fast path resumes returning the old cached grants, so an administrator whose permissions were revoked can temporarily regain them. Authorization caching needs a durable generation/version or another mechanism that prevents a failed best-effort invalidation from making old grants valid again.

Useful? React with 👍 / 👎.

Comment on lines +28 to +32
export const getAnnouncements = async (locale: string) => {
'use cache';
cacheLife('hours');
cacheTag('announcements');
await connection();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Move connection outside the cached function

Developers copying this documented example will call connection() from inside a "use cache" scope, where Next.js runtime request APIs are not allowed, so the example fails instead of producing cached public data. The implementation in views/search/fetch-feed.ts already demonstrates the required ordering by awaiting connection() in the uncached wrapper before calling the cached function; the documentation should show the same split.

Useful? React with 👍 / 👎.

@aXenDeveloper
aXenDeveloper merged commit fc667d7 into canary Aug 20, 2026
4 checks passed
@aXenDeveloper
aXenDeveloper deleted the feat/cache branch August 20, 2026 16:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💡 Feature A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant