Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f41d38f

Browse files
maxisameneajaho
authored andcommitted
feat(isr): added background revalidation and non-blocking render
1 parent 0bb7443 commit f41d38f

File tree

5 files changed

+67
-30
lines changed

5 files changed

+67
-30
lines changed

apps/ssr-isr/server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ export function app(): express.Express {
3131
browserDistFolder,
3232
bootstrap,
3333
commonEngine,
34+
backgroundRevalidation: true, // will revalidate in background and serve the cache page first
35+
nonBlockingRender: true, // will serve page first and store in cache in background
3436
modifyGeneratedHtml: defaultModifyGeneratedHtml,
35-
3637
// cache: fsCacheHandler,
3738
});
3839

libs/isr/models/src/isr-handler-config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,17 @@ export interface ISRHandlerConfig {
113113
* which only add commented text to the html to indicate when it was generated.
114114
*/
115115
modifyGeneratedHtml?: ModifyHtmlCallbackFn;
116+
117+
/**
118+
* If set to true, the server will not wait for storing the rendered page to the cache storage first and will return the rendered HTML as soon as possible.
119+
* This can avoid client-side waiting times if the remote cache storage is down.
120+
*/
121+
nonBlockingRender?: boolean;
122+
123+
/**
124+
* If set to true, the server will provide the cached HTML as soon as possible and will revalidate the cache in the background.
125+
*/
126+
backgroundRevalidation?: boolean;
116127
}
117128

118129
export interface ServeFromCacheConfig {

libs/isr/server/src/cache-generation.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,17 @@ export class CacheGeneration {
104104
return { html: finalHtml };
105105
}
106106
// add the regenerated page to cache
107-
await this.cache.add(cacheKey, finalHtml, {
108-
revalidate,
109-
buildId: this.isrConfig.buildId,
110-
});
107+
if (this.isrConfig.nonBlockingRender) {
108+
this.cache.add(cacheKey, finalHtml, {
109+
revalidate,
110+
buildId: this.isrConfig.buildId,
111+
});
112+
} else {
113+
await this.cache.add(cacheKey, finalHtml, {
114+
revalidate,
115+
buildId: this.isrConfig.buildId,
116+
});
117+
}
111118
if (mode === 'regenerate') {
112119
// remove from urlsOnHold because we are done
113120
this.urlsOnHold = this.urlsOnHold.filter((x) => x !== cacheKey);

libs/isr/server/src/cache-handlers/filesystem-cache-handler.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,20 @@ export class FileSystemCacheHandler extends CacheHandler {
7676

7777
if (cachedRoute) {
7878
// on html field we have saved path to file
79-
this.readFromFile(cachedRoute.htmlFilePath).then((html) => {
80-
const cacheData: CacheData = {
81-
html,
82-
options: cachedRoute.options,
83-
createdAt: cachedRoute.createdAt,
84-
};
85-
resolve(cacheData);
86-
});
79+
this.readFromFile(cachedRoute.htmlFilePath)
80+
.then((html) => {
81+
const cacheData: CacheData = {
82+
html,
83+
options: cachedRoute.options,
84+
createdAt: cachedRoute.createdAt,
85+
};
86+
resolve(cacheData);
87+
})
88+
.catch((err) => {
89+
reject(
90+
`Error: 💥 Cannot read cache file for route ${route}: ${cachedRoute.htmlFilePath}, ${err}`,
91+
);
92+
});
8793
} else {
8894
reject('Error: 💥 Url is not cached.');
8995
}

libs/isr/server/src/isr-handler.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -188,34 +188,46 @@ export class ISRHandler {
188188
return;
189189
}
190190

191-
// Apply the callback if given
192-
let finalHtml = html;
193-
if (config?.modifyCachedHtml) {
194-
const timeStart = performance.now();
195-
finalHtml = config.modifyCachedHtml(req, html);
196-
const totalTime = (performance.now() - timeStart).toFixed(2);
197-
finalHtml += `<!--\nℹ️ ISR: This cachedHtml has been modified with modifyCachedHtml()\n❗️
198-
This resulted into more ${totalTime}ms of processing time.\n-->`;
199-
}
200-
201191
// Cache exists. Send it.
202192
this.logger.log(`Page was retrieved from cache: `, cacheKey);
193+
let finalHtml = html;
203194

204195
// if the cache is expired, we will regenerate it
205196
if (cacheConfig.revalidate && cacheConfig.revalidate > 0) {
206197
const lastCacheDateDiff = (Date.now() - createdAt) / 1000; // in seconds
207198

208199
if (lastCacheDateDiff > cacheConfig.revalidate) {
209200
// regenerate the page without awaiting, so the user gets the cached page immediately
210-
this.cacheGeneration.generateWithCacheKey(
211-
req,
212-
res,
213-
cacheKey,
214-
config?.providers,
215-
'regenerate',
216-
);
201+
if (this.isrConfig.backgroundRevalidation) {
202+
this.cacheGeneration.generateWithCacheKey(
203+
req,
204+
res,
205+
cacheKey,
206+
config?.providers,
207+
'regenerate',
208+
);
209+
} else {
210+
const result = await this.cacheGeneration.generateWithCacheKey(
211+
req,
212+
res,
213+
cacheKey,
214+
config?.providers,
215+
'regenerate',
216+
);
217+
if (result?.html) {
218+
finalHtml = result.html;
219+
}
220+
}
217221
}
218222
}
223+
// Apply the callback if given
224+
if (config?.modifyCachedHtml) {
225+
const timeStart = performance.now();
226+
finalHtml = config.modifyCachedHtml(req, finalHtml);
227+
const totalTime = (performance.now() - timeStart).toFixed(2);
228+
finalHtml += `<!--\nℹ️ ISR: This cachedHtml has been modified with modifyCachedHtml()\n❗️
229+
This resulted into more ${totalTime}ms of processing time.\n-->`;
230+
}
219231
return res.send(finalHtml);
220232
} catch (error) {
221233
// Cache does not exist. Serve user using SSR

0 commit comments

Comments
 (0)