Skip to content

Commit

Permalink
Memoize loadRedirects() with disk (github#22161)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
peterbe authored Oct 22, 2021
1 parent 711bd22 commit da419fd
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 17 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ coverage/
blc_output.log
blc_output_internal.log
broken_links.md
lib/redirects/.redirects-cache_*.json
64 changes: 47 additions & 17 deletions lib/redirects/precompile.js
Original file line number Diff line number Diff line change
@@ -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 = {}

// 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}`
)
})
import { languageKeys } from '../languages.js'

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
Expand All @@ -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

0 comments on commit da419fd

Please sign in to comment.