Skip to content

Commit 7671845

Browse files
committed
自前実装のinmemorycacheではなく、nextjsのcacheを使う
1 parent 1252137 commit 7671845

4 files changed

Lines changed: 58 additions & 64 deletions

File tree

app/[docs_id]/page.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { join } from "node:path";
66
import { splitMarkdown } from "./splitMarkdown";
77
import { PageContent } from "./pageContent";
88
import { ChatHistoryProvider } from "./chatHistory";
9-
import { getChatFromCache } from "@/lib/chatHistory";
9+
import { getChatFromCache, initContext } from "@/lib/chatHistory";
1010
import { getLanguageName, pagesList } from "@/pagesList";
1111
import { isCloudflare } from "@/lib/detectCloudflare";
1212

@@ -72,7 +72,8 @@ export default async function Page({
7272

7373
const mdContent = getMarkdownContent(docs_id);
7474
const splitMdContent = mdContent.then((text) => splitMarkdown(text));
75-
const initialChatHistories = getChatFromCache(docs_id);
75+
const context = await initContext();
76+
const initialChatHistories = getChatFromCache(docs_id, context);
7677

7778
return (
7879
<ChatHistoryProvider

app/lib/cache.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

app/lib/chatHistory.ts

Lines changed: 50 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import { getDrizzle } from "./drizzle";
66
import { chat, message } from "@/schema/chat";
77
import { and, asc, eq } from "drizzle-orm";
88
import { Auth } from "better-auth";
9-
import { inMemoryCache } from "./cache";
9+
import { revalidateTag, unstable_cacheLife } from "next/cache";
10+
import { isCloudflare } from "./detectCloudflare";
11+
import { unstable_cacheTag } from "next/cache";
1012

1113
export interface CreateChatMessage {
1214
role: "user" | "ai" | "error";
@@ -26,7 +28,7 @@ interface Context {
2628
* authが初期化されてなければ初期化し、
2729
* userIdがなければセッションから取得してセットする。
2830
*/
29-
async function initAll(ctx?: Partial<Context>): Promise<Context> {
31+
export async function initContext(ctx?: Partial<Context>): Promise<Context> {
3032
if (!ctx) {
3133
ctx = {};
3234
}
@@ -46,23 +48,14 @@ async function initAll(ctx?: Partial<Context>): Promise<Context> {
4648
}
4749
return ctx as Context;
4850
}
49-
async function getCache() {
50-
if ("caches" in globalThis) {
51-
// worker
52-
return await caches.open("chatHistory");
53-
} else {
54-
// nodejs
55-
return inMemoryCache;
56-
}
57-
}
5851

5952
export async function addChat(
6053
docsId: string,
6154
sectionId: string,
6255
messages: CreateChatMessage[],
6356
context?: Partial<Context>
6457
) {
65-
const { drizzle, userId } = await initAll(context);
58+
const { drizzle, userId } = await initContext(context);
6659
if (!userId) {
6760
throw new Error("Not authenticated");
6861
}
@@ -86,13 +79,16 @@ export async function addChat(
8679
)
8780
.returning();
8881

89-
console.log(
90-
`deleting cache for chatHistory/getChat for user ${userId} and docs ${docsId}`
91-
);
92-
const cache = await getCache();
93-
await cache.delete(
94-
`${CACHE_KEY_BASE}/getChat?docsId=${docsId}&userId=${userId}`
95-
);
82+
revalidateTag(`${CACHE_KEY_BASE}/getChat?docsId=${docsId}&userId=${userId}`);
83+
if (isCloudflare()) {
84+
const cache = await caches.open("chatHistory");
85+
console.log(
86+
`deleting cache for chatHistory/getChat for user ${userId} and docs ${docsId}`
87+
);
88+
await cache.delete(
89+
`${CACHE_KEY_BASE}/getChat?docsId=${docsId}&userId=${userId}`
90+
);
91+
}
9692

9793
return {
9894
...newChat,
@@ -106,7 +102,7 @@ export async function getChat(
106102
docsId: string,
107103
context?: Partial<Context>
108104
): Promise<ChatWithMessages[]> {
109-
const { drizzle, userId } = await initAll(context);
105+
const { drizzle, userId } = await initContext(context);
110106
if (!userId) {
111107
return [];
112108
}
@@ -121,35 +117,47 @@ export async function getChat(
121117
orderBy: [asc(chat.createdAt)],
122118
});
123119

124-
const cache = await getCache();
125-
await cache.put(
126-
`${CACHE_KEY_BASE}/getChat?docsId=${docsId}&userId=${userId}`,
127-
new Response(JSON.stringify(chats), {
128-
headers: { "Cache-Control": "max-age=86400, s-maxage=86400" },
129-
})
130-
);
120+
if (isCloudflare()) {
121+
const cache = await caches.open("chatHistory");
122+
await cache.put(
123+
`${CACHE_KEY_BASE}/getChat?docsId=${docsId}&userId=${userId}`,
124+
new Response(JSON.stringify(chats), {
125+
headers: { "Cache-Control": "max-age=86400, s-maxage=86400" },
126+
})
127+
);
128+
}
131129
return chats;
132130
}
133-
export async function getChatFromCache(
134-
docsId: string,
135-
context?: Partial<Context>
136-
) {
137-
const { drizzle, auth, userId } = await initAll(context);
131+
export async function getChatFromCache(docsId: string, context: Context) {
132+
"use cache";
133+
unstable_cacheLife("days");
134+
135+
// cacheされる関数の中でheader()にはアクセスできない。
136+
// なので外でinitContext()を呼んだものを引数に渡す必要がある。
137+
// しかし、drizzleオブジェクトは外から渡せないのでgetChatの中で改めてinitContext()を呼んでdrizzleだけ再初期化している
138+
const { auth, userId } = context;
139+
unstable_cacheTag(
140+
`${CACHE_KEY_BASE}/getChat?docsId=${docsId}&userId=${userId}`
141+
);
142+
138143
if (!userId) {
139144
return [];
140145
}
141146

142-
const cache = await getCache();
143-
const cachedResponse = await cache.match(
144-
`${CACHE_KEY_BASE}/getChat?docsId=${docsId}&userId=${userId}`
145-
);
146-
if (cachedResponse) {
147-
console.log("Cache hit for chatHistory/getChat");
148-
const data = (await cachedResponse.json()) as ChatWithMessages[];
149-
return data;
147+
if (isCloudflare()) {
148+
const cache = await caches.open("chatHistory");
149+
const cachedResponse = await cache.match(
150+
`${CACHE_KEY_BASE}/getChat?docsId=${docsId}&userId=${userId}`
151+
);
152+
if (cachedResponse) {
153+
console.log("Cache hit for chatHistory/getChat");
154+
const data = (await cachedResponse.json()) as ChatWithMessages[];
155+
return data;
156+
} else {
157+
console.log("Cache miss for chatHistory/getChat");
158+
}
150159
}
151-
console.log("Cache miss for chatHistory/getChat");
152-
return await getChat(docsId, { drizzle, auth, userId });
160+
return await getChat(docsId, { auth, userId });
153161
}
154162

155163
export async function migrateChatUser(oldUserId: string, newUserId: string) {

next.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ initOpenNextCloudflareForDev();
66
const nextConfig: NextConfig = {
77
/* config options here */
88
output: "standalone",
9+
experimental: {
10+
useCache: true,
11+
// @ts-expect-error なぜかエラーになるがドキュメントによればこれであってるはず
12+
cacheComponents: true,
13+
},
914
eslint: {
1015
ignoreDuringBuilds: true,
1116
},

0 commit comments

Comments
 (0)