Memoize loadRedirects() with disk (#22161)

* memoize loadRedirects with disk in development

* no need to await on sync functions

* un-uncomment

* wip

* wip

* cache with max age

* rename redirects memoize cache file
This commit is contained in:
Peter Bengtsson 2021-10-22 11:25:32 -04:00 коммит произвёл GitHub
Родитель 711bd22f02
Коммит da419fd869
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 47 добавлений и 16 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -15,3 +15,4 @@ coverage/
blc_output.log
blc_output_internal.log
broken_links.md
lib/redirects/.redirects-cache_*.json

Просмотреть файл

@ -1,22 +1,53 @@
import { languageKeys } from '../languages.js'
import assert from 'assert'
import fs from 'fs/promises'
import path from 'path'
import { isPromise } from 'util/types'
import { fileURLToPath } from 'url'
import readJsonFile from '../read-json-file.js'
import { latest } from '../../lib/enterprise-server-releases.js'
import getExceptionRedirects from './exception-redirects.js'
const developerRedirects = readJsonFile('./lib/redirects/static/developer.json')
const latestDevRedirects = {}
import { languageKeys } from '../languages.js'
// Replace hardcoded 'latest' with real value in the redirected path
Object.entries(developerRedirects).forEach(([oldPath, newPath]) => {
latestDevRedirects[oldPath] = newPath.replace(
'enterprise-server@latest',
`enterprise-server@${latest}`
)
})
function diskMemoize(filePath, asyncFn, maxAgeSeconds = 60 * 60) {
return async (...args) => {
try {
const stats = await fs.stat(filePath)
const ageSeconds = (new Date().getTime() - stats.mtime.getTime()) / 1000
if (ageSeconds < maxAgeSeconds) {
const value = JSON.parse(await fs.readFile(filePath, 'utf-8'))
console.log(`Redirects disk-cache HIT on ${filePath}`)
return value
}
console.log(`Redirects disk-cache ${filePath} too old`)
} catch (err) {
if (err.code !== 'ENOENT') throw err
}
console.log(`Redirects disk-cache MISS on ${filePath}`)
const promise = asyncFn(...args)
assert(isPromise(promise), "memoized function didn't return a promise")
return promise.then(async (value) => {
await fs.writeFile(filePath, JSON.stringify(value), 'utf-8')
return value
})
}
}
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const DISK_CACHE_FILEPATH = path.join(__dirname, `.redirects-cache_${languageKeys.join('_')}.json`)
// This function runs at server warmup and precompiles possible redirect routes.
// It outputs them in key-value pairs within a neat Javascript object: { oldPath: newPath }
export default async function precompileRedirects(pageList) {
const allRedirects = Object.assign({}, latestDevRedirects)
const precompileRedirects = diskMemoize(DISK_CACHE_FILEPATH, async (pageList) => {
const allRedirects = readJsonFile('./lib/redirects/static/developer.json')
// Replace hardcoded 'latest' with real value in the redirected path
Object.entries(allRedirects).forEach(([oldPath, newPath]) => {
allRedirects[oldPath] = newPath.replace(
'enterprise-server@latest',
`enterprise-server@${latest}`
)
})
// Exception redirects are those that are essentially unicorn one-offs.
// For example, we have redirects for documents that used to be on
@ -35,9 +66,8 @@ export default async function precompileRedirects(pageList) {
// CURRENT PAGES PERMALINKS AND FRONTMATTER
// create backwards-compatible old paths for page permalinks and frontmatter redirects
await Promise.all(
pageList.map(async (page) => Object.assign(allRedirects, page.buildRedirects()))
)
pageList.forEach((page) => Object.assign(allRedirects, page.buildRedirects()))
return allRedirects
}
})
export default precompileRedirects