Skip to content

Commit 6a13a99

Browse files
heiskrCopilot
andauthored
Memoize secret scanning YAML reads to reduce disk IO (#60389)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent dcbc2c1 commit 6a13a99

File tree

3 files changed

+31
-13
lines changed

3 files changed

+31
-13
lines changed

src/article-api/transformers/secret-scanning-transformer.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import type { Context, Page, SecretScanningData } from '@/types'
1+
import type { Context, Page } from '@/types'
22
import type { PageTransformer } from './types'
3-
import fs from 'fs'
4-
import yaml from 'js-yaml'
3+
import { load } from 'js-yaml'
54
import path from 'path'
65
import { getVersionInfo } from '@/app/lib/constants'
76
import { liquid, renderContent } from '@/content-render/index'
87
import { allVersions } from '@/versions/lib/all-versions'
98
import { loadTemplate } from '@/article-api/lib/load-template'
9+
import { getSecretScanningData } from '@/secret-scanning/lib/get-secret-scanning-data'
1010

1111
/**
1212
* Transformer for Secret Scanning pages.
@@ -35,15 +35,15 @@ export class SecretScanningTransformer implements PageTransformer {
3535
const secretScanningDir = path.join(process.cwd(), 'src/secret-scanning/data/pattern-docs')
3636
const filepath = path.join(secretScanningDir, versionPath, 'public-docs.yml')
3737

38-
if (fs.existsSync(filepath)) {
39-
const data = yaml.load(fs.readFileSync(filepath, 'utf-8')) as SecretScanningData[]
38+
try {
39+
const data = await getSecretScanningData(filepath)
4040

4141
// Process Liquid in values
4242
for (const entry of data) {
4343
// Only process Liquid for the hasValidityCheck field, as in the middleware
4444
if (typeof entry.hasValidityCheck === 'string' && entry.hasValidityCheck.includes('{%')) {
4545
// Render Liquid and parse as YAML to get correct boolean type
46-
entry.hasValidityCheck = yaml.load(
46+
entry.hasValidityCheck = load(
4747
await liquid.parseAndRender(entry.hasValidityCheck, context),
4848
) as boolean
4949
}
@@ -54,9 +54,12 @@ export class SecretScanningTransformer implements PageTransformer {
5454
}
5555

5656
context.secretScanningData = data
57-
} else {
58-
// If the file does not exist, set to empty array to ensure predictable behavior
59-
context.secretScanningData = []
57+
} catch (error) {
58+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
59+
context.secretScanningData = []
60+
} else {
61+
throw error
62+
}
6063
}
6164
}
6265

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import fs from 'fs/promises'
2+
import path from 'path'
3+
import { load } from 'js-yaml'
4+
import type { SecretScanningData } from '@/types'
5+
6+
const cache = new Map<string, SecretScanningData[]>()
7+
8+
export async function getSecretScanningData(filepath: string): Promise<SecretScanningData[]> {
9+
const key = path.resolve(filepath)
10+
if (cache.has(key)) return structuredClone(cache.get(key)!)
11+
12+
const raw = await fs.readFile(key, 'utf-8')
13+
const data = (load(raw) as SecretScanningData[]) ?? []
14+
cache.set(key, data)
15+
return structuredClone(data)
16+
}

src/secret-scanning/middleware/secret-scanning.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import yaml from 'js-yaml'
44
import type { NextFunction, Response } from 'express'
55

66
import { liquid } from '@/content-render/index'
7-
import { ExtendedRequest, SecretScanningData } from '@/types'
7+
import { ExtendedRequest } from '@/types'
88
import { allVersions } from '@/versions/lib/all-versions'
99
import { getVersionInfo } from '@/app/lib/constants'
10+
import { getSecretScanningData } from '@/secret-scanning/lib/get-secret-scanning-data'
1011

1112
const secretScanningDir = 'src/secret-scanning/data/pattern-docs'
1213

@@ -41,9 +42,7 @@ export default async function secretScanning(
4142
: 'fpt'
4243
const filepath = `${secretScanningDir}/${versionPath}/public-docs.yml`
4344

44-
req.context.secretScanningData = yaml.load(
45-
fs.readFileSync(filepath, 'utf-8'),
46-
) as SecretScanningData[]
45+
req.context.secretScanningData = await getSecretScanningData(filepath)
4746

4847
// Some entries might use Liquid syntax, so we need
4948
// to execute that Liquid to get the actual value.

0 commit comments

Comments
 (0)