1
1
import { createHash } from 'node:crypto' ;
2
-
3
- import { trace } from '@/lib/tracing' ;
4
2
import type {
5
3
CacheEntryType ,
6
4
CacheValue ,
@@ -34,41 +32,31 @@ class GitbookIncrementalCache implements IncrementalCache {
34
32
cacheType ?: CacheType
35
33
) : Promise < WithLastModified < CacheValue < CacheType > > | null > {
36
34
const cacheKey = this . getR2Key ( key , cacheType ) ;
37
- return trace (
38
- {
39
- operation : 'openNextIncrementalCacheGet' ,
40
- name : cacheKey ,
41
- } ,
42
- async ( span ) => {
43
- span . setAttribute ( 'cacheType' , cacheType ?? 'cache' ) ;
44
- const r2 = getCloudflareContext ( ) . env [ BINDING_NAME ] ;
45
- const localCache = await this . getCacheInstance ( ) ;
46
- if ( ! r2 ) throw new Error ( 'No R2 bucket' ) ;
47
- try {
48
- // Check local cache first if available
49
- const localCacheEntry = await localCache . match ( this . getCacheUrlKey ( cacheKey ) ) ;
50
- if ( localCacheEntry ) {
51
- span . setAttribute ( 'cacheHit' , 'local' ) ;
52
- const result = ( await localCacheEntry . json ( ) ) as WithLastModified <
53
- CacheValue < CacheType >
54
- > ;
55
- return this . returnNullOn404 ( result ) ;
56
- }
57
35
58
- const r2Object = await r2 . get ( cacheKey ) ;
59
- if ( ! r2Object ) return null ;
60
-
61
- span . setAttribute ( 'cacheHit' , 'r2' ) ;
62
- return this . returnNullOn404 ( {
63
- value : await r2Object . json ( ) ,
64
- lastModified : r2Object . uploaded . getTime ( ) ,
65
- } ) ;
66
- } catch ( e ) {
67
- console . error ( 'Failed to get from cache' , e ) ;
68
- return null ;
69
- }
36
+ const r2 = getCloudflareContext ( ) . env [ BINDING_NAME ] ;
37
+ const localCache = await this . getCacheInstance ( ) ;
38
+ if ( ! r2 ) throw new Error ( 'No R2 bucket' ) ;
39
+ try {
40
+ // Check local cache first if available
41
+ const localCacheEntry = await localCache . match ( this . getCacheUrlKey ( cacheKey ) ) ;
42
+ if ( localCacheEntry ) {
43
+ const result = ( await localCacheEntry . json ( ) ) as WithLastModified <
44
+ CacheValue < CacheType >
45
+ > ;
46
+ return this . returnNullOn404 ( result ) ;
70
47
}
71
- ) ;
48
+
49
+ const r2Object = await r2 . get ( cacheKey ) ;
50
+ if ( ! r2Object ) return null ;
51
+
52
+ return this . returnNullOn404 ( {
53
+ value : await r2Object . json ( ) ,
54
+ lastModified : r2Object . uploaded . getTime ( ) ,
55
+ } ) ;
56
+ } catch ( e ) {
57
+ console . error ( 'Failed to get from cache' , e ) ;
58
+ return null ;
59
+ }
72
60
}
73
61
74
62
//TODO: This is a workaround to handle 404 responses in the cache.
@@ -89,75 +77,60 @@ class GitbookIncrementalCache implements IncrementalCache {
89
77
cacheType ?: CacheType
90
78
) : Promise < void > {
91
79
const cacheKey = this . getR2Key ( key , cacheType ) ;
92
- return trace (
93
- {
94
- operation : 'openNextIncrementalCacheSet' ,
95
- name : cacheKey ,
96
- } ,
97
- async ( span ) => {
98
- span . setAttribute ( 'cacheType' , cacheType ?? 'cache' ) ;
99
- const localCache = await this . getCacheInstance ( ) ;
100
-
101
- try {
102
- await this . writeToR2 ( cacheKey , JSON . stringify ( value ) ) ;
103
-
104
- //TODO: Check if there is any places where we don't have tags
105
- // Ideally we should always have tags, but in case we don't, we need to decide how to handle it
106
- // For now we default to a build ID tag, which allow us to invalidate the cache in case something is wrong in this deployment
107
- const tags = this . getTagsFromCacheEntry ( value ) ?? [
108
- `build_id/${ process . env . NEXT_BUILD_ID } ` ,
109
- ] ;
110
-
111
- // We consider R2 as the source of truth, so we update the local cache
112
- // only after a successful R2 write
113
- await localCache . put (
114
- this . getCacheUrlKey ( cacheKey ) ,
115
- new Response (
116
- JSON . stringify ( {
117
- value,
118
- // Note: `Date.now()` returns the time of the last IO rather than the actual time.
119
- // See https://developers.cloudflare.com/workers/reference/security-model/
120
- lastModified : Date . now ( ) ,
121
- } ) ,
122
- {
123
- headers : {
124
- // Cache-Control default to 30 minutes, will be overridden by `revalidate`
125
- // In theory we should always get the `revalidate` value
126
- 'cache-control' : `max-age=${ value . revalidate ?? 60 * 30 } ` ,
127
- 'cache-tag' : tags . join ( ',' ) ,
128
- } ,
129
- }
130
- )
131
- ) ;
132
- } catch ( e ) {
133
- console . error ( 'Failed to set to cache' , e ) ;
134
- }
135
- }
136
- ) ;
80
+
81
+ const localCache = await this . getCacheInstance ( ) ;
82
+
83
+ try {
84
+ await this . writeToR2 ( cacheKey , JSON . stringify ( value ) ) ;
85
+
86
+ //TODO: Check if there is any places where we don't have tags
87
+ // Ideally we should always have tags, but in case we don't, we need to decide how to handle it
88
+ // For now we default to a build ID tag, which allow us to invalidate the cache in case something is wrong in this deployment
89
+ const tags = this . getTagsFromCacheEntry ( value ) ?? [
90
+ `build_id/${ process . env . NEXT_BUILD_ID } ` ,
91
+ ] ;
92
+
93
+ // We consider R2 as the source of truth, so we update the local cache
94
+ // only after a successful R2 write
95
+ await localCache . put (
96
+ this . getCacheUrlKey ( cacheKey ) ,
97
+ new Response (
98
+ JSON . stringify ( {
99
+ value,
100
+ // Note: `Date.now()` returns the time of the last IO rather than the actual time.
101
+ // See https://developers.cloudflare.com/workers/reference/security-model/
102
+ lastModified : Date . now ( ) ,
103
+ } ) ,
104
+ {
105
+ headers : {
106
+ // Cache-Control default to 30 minutes, will be overridden by `revalidate`
107
+ // In theory we should always get the `revalidate` value
108
+ 'cache-control' : `max-age=${ value . revalidate ?? 60 * 30 } ` ,
109
+ 'cache-tag' : tags . join ( ',' ) ,
110
+ } ,
111
+ }
112
+ )
113
+ ) ;
114
+ } catch ( e ) {
115
+ console . error ( 'Failed to set to cache' , e ) ;
116
+ }
137
117
}
138
118
139
119
async delete ( key : string ) : Promise < void > {
140
120
const cacheKey = this . getR2Key ( key ) ;
141
- return trace (
142
- {
143
- operation : 'openNextIncrementalCacheDelete' ,
144
- name : cacheKey ,
145
- } ,
146
- async ( ) => {
147
- const r2 = getCloudflareContext ( ) . env [ BINDING_NAME ] ;
148
- const localCache = await this . getCacheInstance ( ) ;
149
- if ( ! r2 ) throw new Error ( 'No R2 bucket' ) ;
150
-
151
- try {
152
- await r2 . delete ( cacheKey ) ;
153
-
154
- // Here again R2 is the source of truth, so we delete from local cache first
155
- await localCache . delete ( this . getCacheUrlKey ( cacheKey ) ) ;
156
- } catch ( e ) {
157
- console . error ( 'Failed to delete from cache' , e ) ;
158
- }
159
- }
160
- ) ;
121
+
122
+ const r2 = getCloudflareContext ( ) . env [ BINDING_NAME ] ;
123
+ const localCache = await this . getCacheInstance ( ) ;
124
+ if ( ! r2 ) throw new Error ( 'No R2 bucket' ) ;
125
+
126
+ try {
127
+ await r2 . delete ( cacheKey ) ;
128
+
129
+ // Here again R2 is the source of truth, so we delete from local cache first
130
+ await localCache . delete ( this . getCacheUrlKey ( cacheKey ) ) ;
131
+ } catch ( e ) {
132
+ console . error ( 'Failed to delete from cache' , e ) ;
133
+ }
161
134
}
162
135
163
136
async writeToR2 ( key : string , value : string ) : Promise < void > {
0 commit comments