Files
apophis-fastify/scripts/bench/hot-paths.mjs
T

164 lines
5.0 KiB
JavaScript

import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import * as fc from 'fast-check'
import { parse, clearParseCache } from '../../dist/formula/parser.js'
import { evaluate } from '../../dist/formula/evaluator.js'
import { matchRoutePattern, findMatchingRoute } from '../../dist/infrastructure/route-matcher.js'
import { convertSchema } from '../../dist/domain/schema-to-arbitrary.js'
import { qualifyCommand } from '../../dist/cli/commands/qualify/index.js'
import { createContext } from '../../dist/cli/core/context.js'
import { getBenchOptions, measure, printResults } from './_shared.mjs'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const repoRoot = resolve(__dirname, '..', '..')
process.chdir(repoRoot)
const options = getBenchOptions()
const MICRO_ITERS = Number.parseInt(process.env.BENCH_INNER_ITERS ?? '2000', 10)
const generationProfiles = (process.env.BENCH_GENERATION_PROFILES ?? 'quick,standard,thorough')
.split(',')
.map((value) => value.trim())
.filter(Boolean)
const formula = 'response_code(this) == 200 && response_body(this).id != null && response_time(this) < 500'
const formulaPool = [
formula,
'response_payload(this).user.id != null && response_code(this) == 200',
'request_headers(this).authorization != null => response_code(this) != 401',
"response_body(this).name matches '^[A-Za-z ]+$'",
]
const parsedFormula = parse(formula).ast
const evalCtx = {
request: {
body: { name: 'Alice' },
headers: { authorization: 'Bearer token' },
query: {},
params: {},
cookies: {},
},
response: {
body: { id: 'usr-1', name: 'Alice' },
headers: {},
statusCode: 200,
responseTime: 42,
},
}
const routePatterns = Array.from({ length: 50 }, (_, i) => `/v1/resources/${i}/:id`)
routePatterns.push('/v1/resources/target/:id')
const complexSchema = {
type: 'object',
required: ['id', 'email', 'profile'],
properties: {
id: { type: 'string', minLength: 1, maxLength: 64 },
email: { type: 'string', format: 'email' },
tags: { type: 'array', items: { type: 'string', minLength: 1 }, minItems: 0, maxItems: 10 },
profile: {
type: 'object',
required: ['age', 'active'],
properties: {
age: { type: 'integer', minimum: 18, maximum: 90 },
active: { type: 'boolean' },
},
},
},
additionalProperties: false,
}
const qualifyCtx = createContext({
cwd: repoRoot,
quiet: true,
format: 'human',
color: 'never',
})
async function run() {
const results = []
results.push(await measure('formula.parse.cache-hit', async () => {
for (let i = 0; i < MICRO_ITERS; i++) {
parse(formula)
}
}, options))
results.push(await measure('formula.parse.cache-miss', async () => {
for (let i = 0; i < Math.max(1, Math.floor(MICRO_ITERS / 20)); i++) {
clearParseCache()
for (const candidate of formulaPool) {
parse(candidate)
}
}
}, options))
results.push(await measure('formula.evaluate', async () => {
for (let i = 0; i < MICRO_ITERS; i++) {
const result = evaluate(parsedFormula, evalCtx)
if (!result.success) {
throw new Error(result.error)
}
}
}, options))
results.push(await measure('route.match.single', async () => {
let matched = true
for (let i = 0; i < MICRO_ITERS; i++) {
matched = matchRoutePattern('/v1/resources/target/:id', '/v1/resources/target/abc-123').matched
}
if (!matched) {
throw new Error('Expected route pattern to match')
}
}, options))
results.push(await measure('route.match.collection', async () => {
let match = null
for (let i = 0; i < MICRO_ITERS; i++) {
match = findMatchingRoute(routePatterns, '/v1/resources/target/abc-123')
}
if (!match) {
throw new Error('Expected to find matching route')
}
}, options))
for (const generationProfile of generationProfiles) {
const schemaArbitrary = convertSchema(complexSchema, { context: 'request', generationProfile })
results.push(await measure(`schema.convert[${generationProfile}]`, async () => {
for (let i = 0; i < Math.max(1, Math.floor(MICRO_ITERS / 10)); i++) {
convertSchema(complexSchema, { context: 'request', generationProfile })
}
}, options))
results.push(await measure(`schema.sample[${generationProfile}]`, async () => {
for (let i = 0; i < Math.max(1, Math.floor(MICRO_ITERS / 10)); i++) {
fc.sample(schemaArbitrary, 1)
}
}, options))
const qualifyOptions = {
cwd: 'src/cli/__fixtures__/protocol-lab',
profile: 'oauth-nightly',
generationProfile,
seed: 42,
format: 'human',
}
results.push(await measure(`qualify.command.in-process[${generationProfile}]`, async () => {
const result = await qualifyCommand(qualifyOptions, qualifyCtx)
if (result.exitCode !== 0) {
throw new Error(`qualifyCommand failed with ${result.exitCode}: ${result.message ?? ''}`)
}
}, options))
}
printResults('Hot Path Benchmarks', results, options)
}
run().catch((error) => {
console.error(error)
process.exit(1)
})