From c099bc468d752fb357ac6ad786701c12728ef6c4 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 7 Feb 2023 12:22:32 -0600 Subject: [PATCH] Add "debug" config and "allowStale" configs --- config/config.example.yml | 12 ++++++++++++ server.ts | 15 +++++++++------ src/config/cache-config.interface.ts | 6 ++++++ src/config/default-app-config.ts | 3 +++ src/environments/environment.test.ts | 3 +++ 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 52b06b1b08..500c2c476a 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -49,6 +49,8 @@ cache: # NOTE: To control the cache size, use the "max" setting. Keep in mind, individual cached pages are usually small (<100KB). # Enabling *both* caches will mean that a page may be cached twice, once in each cache (but may expire at different times via timeToLive). serverSide: + # Set to true to see all cache hits/misses/refreshes in your console logs. Useful for debugging SSR caching issues. + debug: false # When enabled (i.e. max > 0), known bots will be sent pages from a server side cache specific for bots. # (Keep in mind, bot detection cannot be guarranteed. It is possible some bots will bypass this cache.) botCache: @@ -62,6 +64,11 @@ cache: # NOTE: For the bot cache, this setting may impact how quickly search engine bots will index new content on your site. # For example, setting this to one week may mean that search engine bots may not find all new content for one week. timeToLive: 86400000 # 1 day + # When set to true, after timeToLive expires, the next request will receive the *cached* page & then re-render the page + # behind the scenes to update the cache. This ensures users primarily interact with the cache, but may receive stale pages (older than timeToLive). + # When set to false, after timeToLive expires, the next request will wait on SSR to complete & receive a fresh page (which is then saved to cache). + # This ensures stale pages (older than timeToLive) are never returned from the cache, but some users will wait on SSR. + allowStale: true # When enabled (i.e. max > 0), all anonymous users will be sent pages from a server side cache. # This allows anonymous users to interact more quickly with the site, but also means they may see slightly # outdated content (based on timeToLive) @@ -74,6 +81,11 @@ cache: # copy is automatically refreshed on the next request. # NOTE: For the anonymous cache, it is recommended to keep this value low to avoid anonymous users seeing outdated content. timeToLive: 10000 # 10 seconds + # When set to true, after timeToLive expires, the next request will receive the *cached* page & then re-render the page + # behind the scenes to update the cache. This ensures users primarily interact with the cache, but may receive stale pages (older than timeToLive). + # When set to false, after timeToLive expires, the next request will wait on SSR to complete & receive a fresh page (which is then saved to cache). + # This ensures stale pages (older than timeToLive) are never returned from the cache, but some users will wait on SSR. + allowStale: true # Authentication settings auth: diff --git a/server.ts b/server.ts index 8c9835cf16..478a4c0ec1 100644 --- a/server.ts +++ b/server.ts @@ -324,7 +324,7 @@ function initCache() { botCache = new LRU( { max: environment.cache.serverSide.botCache.max, ttl: environment.cache.serverSide.botCache.timeToLive || 24 * 60 * 60 * 1000, // 1 day - allowStale: true // If object is found to be stale, return stale value before deleting + allowStale: environment.cache.serverSide.botCache.allowStale || true // if object is stale, return stale value before deleting }); } @@ -335,7 +335,7 @@ function initCache() { anonymousCache = new LRU( { max: environment.cache.serverSide.anonymousCache.max, ttl: environment.cache.serverSide.anonymousCache.timeToLive || 10 * 1000, // 10 seconds - allowStale: true // If object is found to be stale, return stale value before deleting + allowStale: environment.cache.serverSide.anonymousCache.allowStale || true // if object is stale, return stale value before deleting }); } } @@ -399,24 +399,25 @@ function cacheCheck(req, res, next) { * @returns cached copy (if found) or undefined (if not found) */ function checkCacheForRequest(cacheName: string, cache: LRU, req, res): any { - let debug = false; // Enable to see cache hits & re-rendering in logs - // Get the cache key for this request const key = getCacheKey(req); // Check if this page is in our cache let cachedCopy = cache.get(key); if (cachedCopy) { - if (debug) { console.log(`CACHE HIT FOR ${key} in ${cacheName} cache`); } + if (environment.cache.serverSide.debug) { console.log(`CACHE HIT FOR ${key} in ${cacheName} cache`); } // Check if cached copy is expired (If expired, the key will now be gone from cache) + // NOTE: This will only occur when "allowStale=true", as it means the "get(key)" above returned a stale value. if (!cache.has(key)) { - if (debug) { console.log(`CACHE EXPIRED FOR ${key} in ${cacheName} cache. Re-rendering...`); } + if (environment.cache.serverSide.debug) { console.log(`CACHE EXPIRED FOR ${key} in ${cacheName} cache. Re-rendering...`); } // Update cached copy by rerendering server-side // NOTE: In this scenario the currently cached copy will be returned to the current user. // This re-render is peformed behind the scenes to update cached copy for next user. serverSideRender(req, res, false); } + } else { + if (environment.cache.serverSide.debug) { console.log(`CACHE MISS FOR ${key} in ${cacheName} cache.`); } } // return page from cache @@ -455,11 +456,13 @@ function saveToCache(req, page: any) { // (NOTE: has() will return false if page is expired in cache) if (botCacheEnabled() && !botCache.has(key)) { botCache.set(key, page); + if (environment.cache.serverSide.debug) { console.log(`CACHE SAVE FOR ${key} in bot cache.`); } } // If anonymous cache is enabled, save it to that cache if it doesn't exist or is expired if (anonymousCacheEnabled() && !anonymousCache.has(key)) { anonymousCache.set(key, page); + if (environment.cache.serverSide.debug) { console.log(`CACHE SAVE FOR ${key} in anonymous cache.`); } } } } diff --git a/src/config/cache-config.interface.ts b/src/config/cache-config.interface.ts index 1826bd0d30..9560fe46a5 100644 --- a/src/config/cache-config.interface.ts +++ b/src/config/cache-config.interface.ts @@ -11,12 +11,16 @@ export interface CacheConfig extends Config { // In-memory caches of server-side rendered (SSR) content. These caches can be used to limit the frequency // of re-generating SSR pages to improve performance. serverSide: { + // Debug server-side caching. Set to true to see cache hits/misses/refreshes in console logs. + debug: boolean, // Cache specific to known bots. Allows you to serve cached contents to bots only. botCache: { // Maximum number of pages (rendered via SSR) to cache. Setting max=0 disables the cache. max: number; // Amount of time after which cached pages are considered stale (in ms) timeToLive: number; + // true = return page from cache after timeToLive expires. false = return a fresh page after timeToLive expires + allowStale: boolean; }, // Cache specific to anonymous users. Allows you to serve cached content to non-authenticated users. anonymousCache: { @@ -24,6 +28,8 @@ export interface CacheConfig extends Config { max: number; // Amount of time after which cached pages are considered stale (in ms) timeToLive: number; + // true = return page from cache after timeToLive expires. false = return a fresh page after timeToLive expires + allowStale: boolean; } } } diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 9e5b535872..e7851d4b34 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -76,6 +76,7 @@ export class DefaultAppConfig implements AppConfig { }, // In-memory cache of server-side rendered content serverSide: { + debug: false, // Cache specific to known bots. Allows you to serve cached contents to bots only. // Defaults to caching 1,000 pages. Each page expires after 1 day botCache: { @@ -83,6 +84,7 @@ export class DefaultAppConfig implements AppConfig { max: 1000, // Amount of time after which cached pages are considered stale (in ms) timeToLive: 24 * 60 * 60 * 1000, // 1 day + allowStale: true, }, // Cache specific to anonymous users. Allows you to serve cached content to non-authenticated users. // Defaults to caching 0 pages. But, when enabled, each page expires after 10 seconds (to minimize anonymous users seeing out-of-date content) @@ -91,6 +93,7 @@ export class DefaultAppConfig implements AppConfig { max: 0, // disabled by default // Amount of time after which cached pages are considered stale (in ms) timeToLive: 10 * 1000, // 10 seconds + allowStale: true, } } }; diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 613024f798..0bb36da61f 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -58,13 +58,16 @@ export const environment: BuildConfig = { }, // In-memory cache of server-side rendered pages. Disabled in test environment (max=0) serverSide: { + debug: false, botCache: { max: 0, timeToLive: 24 * 60 * 60 * 1000, // 1 day + allowStale: true, }, anonymousCache: { max: 0, timeToLive: 10 * 1000, // 10 seconds + allowStale: true, } } },