@@ -13,6 +13,8 @@ export class CacheAdaptor {
13
13
public tagsManifest : TagsManifest | undefined ;
14
14
/** The key used for the tags manifest in the cache. */
15
15
public tagsManifestKey = 'tags-manifest' ;
16
+ /** Promise that resolves when tags manifest is loaded */
17
+ public tagsManifestPromise : Promise < void > | undefined ;
16
18
17
19
/**
18
20
* @param ctx The incremental cache context from Next.js. NOTE: This is not currently utilised in NOP.
@@ -52,7 +54,7 @@ export class CacheAdaptor {
52
54
} ;
53
55
54
56
// Update the cache entry.
55
- await this . update ( key , JSON . stringify ( newEntry ) ) ;
57
+ const updateOp = this . update ( key , JSON . stringify ( newEntry ) ) ;
56
58
57
59
switch ( newEntry . value ?. kind ) {
58
60
case 'FETCH' : {
@@ -70,6 +72,9 @@ export class CacheAdaptor {
70
72
) ;
71
73
}
72
74
}
75
+
76
+ // Make sure the cache has been updated before returning
77
+ await updateOp ;
73
78
}
74
79
75
80
/**
@@ -84,7 +89,12 @@ export class CacheAdaptor {
84
89
{ softTags } : { softTags ?: string [ ] } ,
85
90
) : Promise < CacheHandlerValue | null > {
86
91
// Get entry from the cache.
87
- const entry = await this . retrieve ( key ) ;
92
+ const entryPromise = this . retrieve ( key ) ;
93
+
94
+ // Start loading the tags manifest.
95
+ const tagsManifestLoad = this . loadTagsManifest ( ) ;
96
+
97
+ const entry = await entryPromise ;
88
98
if ( ! entry ) return null ;
89
99
90
100
let data : CacheHandlerValue ;
@@ -97,8 +107,8 @@ export class CacheAdaptor {
97
107
98
108
switch ( data . value ?. kind ) {
99
109
case 'FETCH' : {
100
- // Load the tags manifest.
101
- await this . loadTagsManifest ( ) ;
110
+ // Await for the tags manifest to end loading .
111
+ await tagsManifestLoad ;
102
112
103
113
// Check if the cache entry is stale or fresh based on the tags.
104
114
const tags = getTagsFromEntry ( data ) ;
@@ -140,8 +150,29 @@ export class CacheAdaptor {
140
150
141
151
/**
142
152
* Loads the tags manifest from the suspense cache.
153
+ *
154
+ * @param force Whether to force a reload of the tags manifest.
155
+ */
156
+ public async loadTagsManifest ( force = false ) : Promise < void > {
157
+ // Load tags manifest if missing or refresh if forced.
158
+ const shouldLoad = force || ! this . tagsManifest ;
159
+
160
+ if ( ! shouldLoad ) {
161
+ return ;
162
+ }
163
+
164
+ // If the tags manifest is not already being loaded, kickstart the retrieval.
165
+ if ( ! this . tagsManifestPromise ) {
166
+ this . tagsManifestPromise = this . loadTagsManifestInternal ( ) ;
167
+ }
168
+
169
+ await this . tagsManifestPromise ;
170
+ }
171
+
172
+ /**
173
+ * Internal method to load the tags manifest from the suspense cache.
143
174
*/
144
- public async loadTagsManifest ( ) : Promise < void > {
175
+ private async loadTagsManifestInternal ( ) : Promise < void > {
145
176
try {
146
177
const rawManifest = await this . retrieve ( this . tagsManifestKey ) ;
147
178
if ( rawManifest ) {
@@ -152,6 +183,7 @@ export class CacheAdaptor {
152
183
}
153
184
154
185
this . tagsManifest ??= { version : 1 , items : { } } ;
186
+ this . tagsManifestPromise = undefined ;
155
187
}
156
188
157
189
/**
@@ -174,7 +206,7 @@ export class CacheAdaptor {
174
206
tags : string [ ] ,
175
207
{ cacheKey, revalidatedAt } : { cacheKey ?: string ; revalidatedAt ?: number } ,
176
208
) : Promise < void > {
177
- await this . loadTagsManifest ( ) ;
209
+ await this . loadTagsManifest ( true ) ;
178
210
179
211
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
180
212
const tagsManifest = this . tagsManifest ! ;
0 commit comments