@@ -7,10 +7,17 @@ import { getCloudflareContext } from "./cloudflare-context.js";
77/**
88 * An instance of the Tag Cache that uses a D1 binding (`NEXT_CACHE_D1`) as it's underlying data store.
99 *
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`
1114 * environment variable.
1215 *
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.
1421 */
1522class D1TagCache implements TagCache {
1623 public readonly name = "d1-tag-cache" ;
@@ -23,7 +30,7 @@ class D1TagCache implements TagCache {
2330
2431 try {
2532 const { success, results } = await db
26- . prepare ( `SELECT tag FROM ${ table } WHERE path = ?` )
33+ . prepare ( `SELECT tag FROM ${ table . tags } WHERE path = ?` )
2734 . bind ( path )
2835 . all < { tag : string } > ( ) ;
2936
@@ -47,7 +54,7 @@ class D1TagCache implements TagCache {
4754
4855 try {
4956 const { success, results } = await db
50- . prepare ( `SELECT path FROM ${ table } WHERE tag = ?` )
57+ . prepare ( `SELECT path FROM ${ table . tags } WHERE tag = ?` )
5158 . bind ( tag )
5259 . all < { path : string } > ( ) ;
5360
@@ -69,7 +76,11 @@ class D1TagCache implements TagCache {
6976
7077 try {
7178 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+ )
7384 . bind ( this . getCacheKey ( path ) , lastModified ?? 0 )
7485 . all < { tag : string } > ( ) ;
7586
@@ -89,11 +100,19 @@ class D1TagCache implements TagCache {
89100
90101 try {
91102 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+ } )
97116 ) ;
98117
99118 const failedResults = results . filter ( ( res ) => ! res . success ) ;
@@ -109,7 +128,6 @@ class D1TagCache implements TagCache {
109128 private getConfig ( ) {
110129 const cfEnv = getCloudflareContext ( ) . env ;
111130 const db = cfEnv . NEXT_CACHE_D1 ;
112- const table = cfEnv . NEXT_CACHE_D1_TABLE ?? "tags" ;
113131
114132 if ( ! db ) debug ( "No D1 database found" ) ;
115133
@@ -120,7 +138,14 @@ class D1TagCache implements TagCache {
120138 return { isDisabled : true as const } ;
121139 }
122140
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+ } ;
124149 }
125150
126151 protected removeBuildId ( key : string ) {
0 commit comments