Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/few-wombats-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opennextjs/aws": patch
---

add(dev-overrides): In memory tagCache with nextMode
2 changes: 1 addition & 1 deletion examples/app-router/open-next.config.local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default {
converter: "node",
incrementalCache: "fs-dev",
queue: "direct",
tagCache: "dummy",
tagCache: "fs-dev-nextMode",
},
},

Expand Down
88 changes: 88 additions & 0 deletions packages/open-next/src/overrides/tagCache/fs-dev-nextMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import fs from "node:fs";
import path from "node:path";

import type { NextModeTagCache } from "types/overrides";
import { getMonorepoRelativePath } from "utils/normalize-path";
import { debug } from "../../adapters/logger";

const tagFile = path.join(
getMonorepoRelativePath(),
"dynamodb-provider/dynamodb-cache.json",
);
const tagContent = fs.readFileSync(tagFile, "utf-8");

let tags = JSON.parse(tagContent) as {
tag: { S: string };
path: { S: string };
revalidatedAt: { N: string };
}[];

function buildKey(key: string) {
const { NEXT_BUILD_ID } = process.env;
return path.posix.join(NEXT_BUILD_ID ?? "", key);
}

function buildObject(tag: string, revalidatedAt?: number) {
return {
path: { S: buildKey(tag) },
tag: { S: buildKey(tag) },
revalidatedAt: { N: `${revalidatedAt ?? Date.now()}` },
};
}

export default {
name: "fs-dev-nextMode",
mode: "nextMode",
getLastRevalidated: async (tagsToCheck: string[]) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not familiar w/ the base spec for tag cache. I see writeTags, shouldn't there be a readTags?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is for the new tagCache mode called nextMode. It works a bit different, and also is necessary to make composable cache work when that becomes stable.

hasBeenRevalidated() will check if any tags have been revalidated. You can see it in action here:

if (globalThis.tagCache.mode === "nextMode") {
return await globalThis.tagCache.hasBeenRevalidated(tags, lastModified);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Which part of the code reads and load the tags that was written to the file system?

Copy link
Contributor Author

@sommeeeer sommeeeer Aug 28, 2025

Choose a reason for hiding this comment

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

https://github.com/opennextjs/opennextjs-aws/blob/31c3740/packages/open-next/src/build/createAssets.ts#L253-L269 - the dynamodb-cache.json gets written here during build. In our fs-dev-nextMode override we read that file on top level and put it in a let tags = ....

Copy link
Contributor

Choose a reason for hiding this comment

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

cool thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

Composable cache works with the original tag cache as well. And you don't need the original file

// Not supported for now
// TODO: Implement getLastRevalidated
return 0;
},
hasBeenRevalidated: async (tagsToCheck: string[], lastModified?: number) => {
if (globalThis.openNextConfig.dangerous?.disableTagCache) {
return false;
}
debug("hasBeenRevalidated", { tags: tagsToCheck, lastModified });

// Build the cache keys for the tags we're checking
const cacheKeys = tagsToCheck.map((tag) => buildKey(tag));

// Check if any tag has been revalidated after the lastModified time
const hasRevalidatedTag = tags.some((tagEntry) => {
const tagRevalidatedAt = Number.parseInt(tagEntry.revalidatedAt.N);
return (
cacheKeys.includes(tagEntry.tag.S) &&
tagRevalidatedAt > (lastModified ?? Date.now())
);
});

debug("hasBeenRevalidated result:", hasRevalidatedTag);
return hasRevalidatedTag;
},
writeTags: async (tagsToWrite: string[]) => {
if (
globalThis.openNextConfig.dangerous?.disableTagCache ||
tagsToWrite.length === 0
) {
return;
}

debug("writeTags", { tags: tagsToWrite });

// Create new tag objects to write
const newTagObjects = tagsToWrite.map((tag) =>
buildObject(tag, Date.now()),
);

// Remove any existing entries for these tags to avoid duplicates
const existingTagKeys = newTagObjects.map((obj) => obj.tag.S);
tags = tags.filter((tagEntry) => !existingTagKeys.includes(tagEntry.tag.S));

// Add the new tags
tags.push(...newTagObjects);

fs.writeFileSync(tagFile, JSON.stringify(tags));

debug("writeTags completed, written", newTagObjects.length, "tags");
},
} satisfies NextModeTagCache;
1 change: 1 addition & 0 deletions packages/open-next/src/types/open-next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ export type IncludedTagCache =
| "dynamodb-lite"
| "dynamodb-nextMode"
| "fs-dev"
| "fs-dev-nextMode"
| "dummy";

export type IncludedImageLoader =
Expand Down
Loading