diff --git a/app/lib/docs/doc.server.ts b/app/lib/docs/doc.server.ts index acfb1df..175e7f7 100644 --- a/app/lib/docs/doc.server.ts +++ b/app/lib/docs/doc.server.ts @@ -3,6 +3,7 @@ import { readFile } from 'fs/promises' import LRUCache from 'lru-cache' import path from 'path' import { z } from 'zod' +import { NO_CACHE, SALT, createCache } from '~/utils/cache.server.ts' import { removeLastSlash } from '~/utils/removeEndSlashes.ts' import { type DocAttributes } from './attrs.server.ts' import { contentPath, getJsonFile, privateContentPath } from './fs.server.ts' @@ -20,8 +21,6 @@ declare global { var docCache: LRUCache } -let NO_CACHE = process.env.NO_CACHE ?? false - /** * While we're using HTTP caching, we have this memory cache too so that * document requests and data request to the same document can do less work for @@ -29,22 +28,15 @@ let NO_CACHE = process.env.NO_CACHE ?? false * let's have simpler and faster deployments with just one origin server, but * still distribute the documents across the CDN. */ -global.docCache ??= new LRUCache({ - // let docCache = new LRUCache({ - max: 300, - ttl: NO_CACHE ? 1 : 1000 * 60 * 60, // 1 hour - allowStale: !NO_CACHE, - noDeleteOnFetchRejection: true, - fetchMethod: async key => { - console.log('Fetching fresh doc', key) - const [access, product, version, slug] = key.split(':') - return getFreshDoc({ - product, - version, - slug, - isPrivate: access === 'private', - }) - }, +global.docCache ??= createCache(async key => { + console.log('Fetching fresh doc', key) + const [access, product, version, slug] = key.split(':') + return getFreshDoc({ + product, + version, + slug, + isPrivate: access === 'private', + }) }) export async function getDoc({ @@ -63,12 +55,12 @@ export async function getDoc({ } if (isPrivate) { - const key = `private:${product}:${version}:${slug}:2023-12-04` + const key = `private:${product}:${version}:${slug}:${SALT}` const doc = await docCache.fetch(key) if (doc) return doc } - const key = `public:${product}:${version}:${slug}:2023-12-04` + const key = `public:${product}:${version}:${slug}:${SALT}` const doc = await docCache.fetch(key) return doc } diff --git a/app/lib/docs/menu.server.ts b/app/lib/docs/menu.server.ts index d5d8ab2..2bae50e 100644 --- a/app/lib/docs/menu.server.ts +++ b/app/lib/docs/menu.server.ts @@ -1,5 +1,6 @@ import fs from 'fs/promises' import LRUCache from 'lru-cache' +import { NO_CACHE, SALT, createCache } from '~/utils/cache.server.ts' import { parseAttrs } from './attrs.server.ts' import { contentPath, privateContentPath, walk } from './fs.server.ts' import { makeSlug } from './utils.ts' @@ -26,14 +27,8 @@ declare global { var menuCache: LRUCache } -let NO_CACHE = process.env.NO_CACHE ?? false - -global.menuCache ??= new LRUCache({ - max: 10, - ttl: NO_CACHE ? 1 : 1000 * 60 * 60, // 1 hour - allowStale: !NO_CACHE, - noDeleteOnFetchRejection: true, - fetchMethod: async (cacheKey, _stale, { context }) => { +global.menuCache ??= createCache( + async (cacheKey, _stale, { context }) => { let [access, product, ref] = cacheKey.split(':') let menu = await getMenuFromDir({ product, @@ -43,7 +38,7 @@ global.menuCache ??= new LRUCache({ }) return menu }, -}) +) export async function getMenu({ product, @@ -59,7 +54,7 @@ export async function getMenu({ return NO_CACHE ? getMenuFromDir({ product, version, ref, isPrivate }) : menuCache.fetch( - `${isPrivate ? 'private' : 'public'}:${product}:${ref}:2023-12-04`, + `${isPrivate ? 'private' : 'public'}:${product}:${ref}:${SALT}`, { fetchContext: { version }, }, diff --git a/app/lib/docs/pdf.server.tsx b/app/lib/docs/pdf.server.tsx index 529b75b..145deb3 100644 --- a/app/lib/docs/pdf.server.tsx +++ b/app/lib/docs/pdf.server.tsx @@ -14,6 +14,7 @@ import LRUCache from 'lru-cache' import { getMDXComponent } from 'mdx-bundler/client/index.js' import stream from 'node:stream' import { useMemo } from 'react' +import { NO_CACHE, SALT, createCache } from '~/utils/cache.server.ts' import { removeEndSlashes } from '~/utils/removeEndSlashes.ts' import { getConfig, getDocFromDir } from './doc.server.ts' import { NavItem, getMenu } from './menu.server.ts' @@ -74,8 +75,6 @@ declare global { var pdfCache: LRUCache } -let NO_CACHE = process.env.NO_CACHE ?? false - /** * While we're using HTTP caching, we have this memory cache too so that * document requests and data request to the same document can do less work for @@ -83,12 +82,8 @@ let NO_CACHE = process.env.NO_CACHE ?? false * let's have simpler and faster deployments with just one origin server, but * still distribute the documents across the CDN. */ -global.pdfCache ??= new LRUCache({ - max: 1000, - ttl: NO_CACHE ? 1 : 1000 * 60 * 60, // 1 hour - allowStale: !NO_CACHE, - noDeleteOnFetchRejection: true, - fetchMethod: async (key, _stale, { context }) => { +global.pdfCache ??= createCache( + async (key, _stale, { context }) => { console.log('Fetching fresh pdf', key) const [access, product, version] = key.split(':') return getFreshPDFData({ @@ -98,7 +93,7 @@ global.pdfCache ??= new LRUCache({ isPrivate: access === 'private', }) }, -}) +) export async function getPDFData({ product, @@ -116,14 +111,14 @@ export async function getPDFData({ } if (isPrivate) { - const key = `private:${product}:${ref}:2023-12-04` + const key = `private:${product}:${ref}:${SALT}` if (pdfCache.has(key)) { const doc = await pdfCache.fetch(key, { fetchContext: { ref } }) return doc } } - const key = `public:${product}:${ref}:2023-12-04` + const key = `public:${product}:${ref}:${SALT}` const docs = await pdfCache.fetch(key, { fetchContext: { ref } }) return docs } diff --git a/app/lib/docs/search.server.ts b/app/lib/docs/search.server.ts index 4f6e633..fc08904 100644 --- a/app/lib/docs/search.server.ts +++ b/app/lib/docs/search.server.ts @@ -3,6 +3,7 @@ import LRUCache from 'lru-cache' import lunr from 'lunr' import { remark } from 'remark' import strip from 'strip-markdown' +import { NO_CACHE, SALT, createCache } from '~/utils/cache.server.ts' import { parseAttrs } from './attrs.server.ts' import { contentPath, privateContentPath, walk } from './fs.server.ts' import { makeSlug } from './utils.ts' @@ -22,8 +23,6 @@ declare global { var searchCache: LRUCache } -let NO_CACHE = process.env.NO_CACHE ?? false - /** * While we're using HTTP caching, we have this memory cache too so that * document requests and data request to the same document can do less work for @@ -31,13 +30,8 @@ let NO_CACHE = process.env.NO_CACHE ?? false * let's have simpler and faster deployments with just one origin server, but * still distribute the documents across the CDN. */ -global.searchCache ??= new LRUCache({ - // let docCache = new LRUCache({ - max: 300, - ttl: NO_CACHE ? 1 : 1000 * 60 * 60, // 1 hour - allowStale: !NO_CACHE, - noDeleteOnFetchRejection: true, - fetchMethod: async (key, _stale, { context }) => { +global.searchCache ??= createCache( + async (key, _stale, { context }) => { console.log('Fetching fresh doc', key) const [access, product] = key.split(':') return getFreshSearch({ @@ -46,7 +40,7 @@ global.searchCache ??= new LRUCache({ isPrivate: access === 'private', }) }, -}) +) export async function getSearch({ product, @@ -62,7 +56,7 @@ export async function getSearch({ return NO_CACHE ? getFreshSearch({ product, isPrivate, version }) : searchCache.fetch( - `${isPrivate ? 'private' : 'public'}:${product}:${ref}:2023-12-04`, + `${isPrivate ? 'private' : 'public'}:${product}:${ref}:${SALT}`, { fetchContext: { version } }, ) } diff --git a/app/lib/docs/versions.server.ts b/app/lib/docs/versions.server.ts index 708345e..2dd2e4f 100644 --- a/app/lib/docs/versions.server.ts +++ b/app/lib/docs/versions.server.ts @@ -3,6 +3,7 @@ import path from 'path' import semver from 'semver' import { z } from 'zod' import { type NavLink } from '~/types.ts' +import { createCache, NO_CACHE, SALT } from '~/utils/cache.server.ts' import { find, fromArray, @@ -33,20 +34,11 @@ declare global { var versionsCache: LRUCache } -let NO_CACHE = process.env.NO_CACHE ?? false - // global for SS "HMR", we need a better story here -global.versionsCache ??= new LRUCache({ - // let versionsCache = new LRUCache({ - max: 3, - ttl: 1000 * 60 * 60, // 5 minutes, so we can see new versions quickly - allowStale: true, - noDeleteOnFetchRejection: true, - fetchMethod: async key => { - console.log('Fetching fresh versions') - let [access, product] = key.split(':') - return getAllVersions({ product, isPrivate: access === 'private' }) - }, +global.versionsCache ??= createCache(async key => { + console.log('Fetching fresh versions') + let [access, product] = key.split(':') + return getAllVersions({ product, isPrivate: access === 'private' }) }) export async function getProductVersions({ @@ -60,7 +52,7 @@ export async function getProductVersions({ return getAllVersions({ product, isPrivate }) } return versionsCache.fetch( - `${isPrivate ? 'private' : 'public'}:${product}:v2`, + `${isPrivate ? 'private' : 'public'}:${product}:${SALT}`, ) } diff --git a/app/utils/cache.server.ts b/app/utils/cache.server.ts new file mode 100644 index 0000000..6526162 --- /dev/null +++ b/app/utils/cache.server.ts @@ -0,0 +1,14 @@ +import LRUCache from 'lru-cache' + +export let NO_CACHE = process.env.NO_CACHE ?? false +export let SALT = process.env.CRUNCHY_CACHE_SALT ?? Date.now() + +export function createCache(fetchMethod: LRUCache.Fetcher) { + return new LRUCache({ + max: 1000, + ttl: NO_CACHE ? 1 : 1000 * 60 * 60 * 24 * 365, // 1 year + allowStale: !NO_CACHE, + noDeleteOnFetchRejection: true, + fetchMethod, + }) +} diff --git a/app/utils/env.server.ts b/app/utils/env.server.ts index 8c393fa..807c2cf 100644 --- a/app/utils/env.server.ts +++ b/app/utils/env.server.ts @@ -1,11 +1,16 @@ import invariant from 'tiny-invariant' const requiredServerEnvs = ['NODE_ENV', 'SESSION_SECRET'] as const +const allServerEnvs = [ + ...requiredServerEnvs, + 'NO_CACHE', + 'CRUNCHY_CACHE_SALT', +] as const declare global { namespace NodeJS { interface ProcessEnv - extends Record<(typeof requiredServerEnvs)[number], string> {} + extends Record<(typeof allServerEnvs)[number], string> {} } } diff --git a/package.json b/package.json index 0382f21..c67f7e9 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "build:server": "tsx ./scripts/build-server.ts", "dev": "run-p dev:*", "dev:remix": "remix dev -c \"npm run dev-server\" --no-restart", - "dev-server": "cross-env MOCKS=true tsx watch --clear-screen=false --ignore \"app/**\" --ignore \"docs-build/**\" --ignore \"node_modules/**\" ./index.js", + "dev-server": "cross-env MOCKS=true tsx watch --clear-screen=false --ignore \"app/**\" --ignore \"docs-build/**\" --ignore \"node_modules/**\" ./docs-index.js", "format": "prettier --write .", "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .", "lint:files": "ts-node --esm scripts/lint-file-case.ts",