@@ -7,10 +7,17 @@ import { getCloudflareContext } from "./cloudflare-context.js";
7
7
/**
8
8
* An instance of the Tag Cache that uses a D1 binding (`NEXT_CACHE_D1`) as it's underlying data store.
9
9
*
10
- * The table used defaults to `tags`, but can be configured with the `NEXT_CACHE_D1_TABLE`
10
+ * **Tag/path mappings**
11
+ *
12
+ * Information about the relation between tags and paths is stored in a `tags` table that contains
13
+ * two columns; `tag`, and `path`. The table name can be configured with `NEXT_CACHE_D1_TAGS_TABLE`
11
14
* environment variable.
12
15
*
13
- * There should be three columns created in the table; `tag`, `path`, and `revalidatedAt`.
16
+ * **Tag revalidations**
17
+ *
18
+ * Revalidation times for tags are stored in a `revalidations` table that contains two columns; `tags`,
19
+ * and `revalidatedAt`. The table name can be configured with `NEXT_CACHE_D1_REVALIDATIONS_TABLE`
20
+ * environment variable.
14
21
*/
15
22
class D1TagCache implements TagCache {
16
23
public readonly name = "d1-tag-cache" ;
@@ -23,7 +30,7 @@ class D1TagCache implements TagCache {
23
30
24
31
try {
25
32
const { success, results } = await db
26
- . prepare ( `SELECT tag FROM ${ table } WHERE path = ?` )
33
+ . prepare ( `SELECT tag FROM ${ table . tags } WHERE path = ?` )
27
34
. bind ( path )
28
35
. all < { tag : string } > ( ) ;
29
36
@@ -47,7 +54,7 @@ class D1TagCache implements TagCache {
47
54
48
55
try {
49
56
const { success, results } = await db
50
- . prepare ( `SELECT path FROM ${ table } WHERE tag = ?` )
57
+ . prepare ( `SELECT path FROM ${ table . tags } WHERE tag = ?` )
51
58
. bind ( tag )
52
59
. all < { path : string } > ( ) ;
53
60
@@ -69,7 +76,11 @@ class D1TagCache implements TagCache {
69
76
70
77
try {
71
78
const { success, results } = await db
72
- . prepare ( `SELECT tag FROM ${ table } WHERE path = ? AND revalidatedAt > ?` )
79
+ . prepare (
80
+ `SELECT ${ table . revalidations } .tag FROM ${ table . revalidations }
81
+ INNER JOIN ${ table . tags } ON ${ table . revalidations } .tag = ${ table . tags } .tag
82
+ WHERE ${ table . tags } .path = ? AND ${ table . revalidations } .revalidatedAt > ?;`
83
+ )
73
84
. bind ( this . getCacheKey ( path ) , lastModified ?? 0 )
74
85
. all < { tag : string } > ( ) ;
75
86
@@ -89,11 +100,19 @@ class D1TagCache implements TagCache {
89
100
90
101
try {
91
102
const results = await db . batch (
92
- tags . map ( ( { tag, path, revalidatedAt } ) =>
93
- db
94
- . prepare ( `INSERT INTO ${ table } (tag, path, revalidatedAt) VALUES(?, ?, ?)` )
95
- . bind ( this . getCacheKey ( tag ) , this . getCacheKey ( path ) , revalidatedAt ?? Date . now ( ) )
96
- )
103
+ tags . map ( ( { tag, path, revalidatedAt } ) => {
104
+ if ( revalidatedAt === 1 ) {
105
+ // new tag/path mapping from set
106
+ return db
107
+ . prepare ( `INSERT INTO ${ table . tags } (tag, path) VALUES (?, ?)` )
108
+ . bind ( this . getCacheKey ( tag ) , this . getCacheKey ( path ) ) ;
109
+ }
110
+
111
+ // tag was revalidated
112
+ return db
113
+ . prepare ( `INSERT INTO ${ table . revalidations } (tag, revalidatedAt) VALUES (?, ?)` )
114
+ . bind ( this . getCacheKey ( tag ) , revalidatedAt ?? Date . now ( ) ) ;
115
+ } )
97
116
) ;
98
117
99
118
const failedResults = results . filter ( ( res ) => ! res . success ) ;
@@ -109,7 +128,6 @@ class D1TagCache implements TagCache {
109
128
private getConfig ( ) {
110
129
const cfEnv = getCloudflareContext ( ) . env ;
111
130
const db = cfEnv . NEXT_CACHE_D1 ;
112
- const table = cfEnv . NEXT_CACHE_D1_TABLE ?? "tags" ;
113
131
114
132
if ( ! db ) debug ( "No D1 database found" ) ;
115
133
@@ -120,7 +138,14 @@ class D1TagCache implements TagCache {
120
138
return { isDisabled : true as const } ;
121
139
}
122
140
123
- return { isDisabled : false as const , db, table } ;
141
+ return {
142
+ isDisabled : false as const ,
143
+ db,
144
+ table : {
145
+ tags : cfEnv . NEXT_CACHE_D1_TAGS_TABLE ?? "tags" ,
146
+ revalidations : cfEnv . NEXT_CACHE_D1_REVALIDATIONS_TABLE ?? "revalidations" ,
147
+ } ,
148
+ } ;
124
149
}
125
150
126
151
protected removeBuildId ( key : string ) {
0 commit comments