nx icon indicating copy to clipboard operation
nx copied to clipboard

perf(core): optimize project graph cache validation

Open adwait1290 opened this issue 2 weeks ago โ€ข 3 comments

Current Behavior

The shouldRecomputeWholeGraph function runs on every Nx command that touches the project graph (build, test, run, etc.). The current implementation uses JSON.stringify for comparison:

// Path mappings - O(n) JSON.stringify calls in a loop
Object.keys(cache.pathMappings).some((t) => {
  const cached = JSON.stringify(cache.pathMappings[t]);
  const notCached = JSON.stringify(tsConfig.compilerOptions.paths[t]);
  return cached !== notCached;
});

// Plugins - 2 JSON.stringify calls
JSON.stringify(getNxJsonPluginsData(nxJson, packageJsonDeps)) !== JSON.stringify(cache.nxJsonPlugins)

// Config - 2 JSON.stringify calls
JSON.stringify(nxJson?.pluginsConfig) !== JSON.stringify(cache.pluginsConfig)

Additionally, deepClone uses the slower JSON.parse(JSON.stringify()) pattern.

Expected Behavior

Use pre-computed hashes for O(1) comparison instead of O(n) JSON.stringify calls. Use structuredClone which is 2-3x faster than JSON round-trip for deep cloning.

Changes

1. Hash-based cache validation (nx-deps-cache.ts)

  • Added optional hash fields to FileMapCache interface:
    • pathMappingsHash?: string
    • nxJsonPluginsHash?: string
    • pluginsConfigHash?: string
  • Pre-compute hashes when creating cache in createProjectFileMapCache
  • Compare hashes in shouldRecomputeWholeGraph instead of using JSON.stringify
  • Maintain backward compatibility: legacy caches without hashes fall back to original logic

2. Faster deep clone (project-configuration-utils.ts)

  • Replace JSON.parse(JSON.stringify(obj)) with structuredClone(obj)
  • Added TypeScript generic for type safety

Performance Impact

Operation Before After Improvement
Cache validation O(n) JSON.stringify per path O(1) hash comparison Significant for large monorepos
Deep clone JSON round-trip structuredClone 2-3x faster
Hash computation N/A Uses native xxhash Very fast

The native hasher (hashObject from hasher/file-hasher.ts) uses Rust's xxhash implementation which is significantly faster than JavaScript's JSON.stringify.

Backward Compatibility

  • Optional hash fields ensure old caches work seamlessly
  • Missing hashes trigger fallback to original JSON.stringify comparison
  • Cache version remains 6.0 (no breaking change)

Related Issue(s)

Contributes to #32265

Testing

All 129 project-graph tests pass, including the 17 cache-specific tests.

Merge Dependencies

This PR has no dependencies and can be merged independently.

Must be merged BEFORE: #33739, #33746


adwait1290 avatar Dec 08 '25 03:12 adwait1290

Deploy request for nx-docs pending review.

Visit the deploys page to approve it

Name Link
Latest commit 061186c98dabd85c40694b82868462f4e7bddc59

netlify[bot] avatar Dec 08 '25 03:12 netlify[bot]

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
nx-dev Ready Ready Preview Dec 9, 2025 4:59pm

vercel[bot] avatar Dec 08 '25 03:12 vercel[bot]

View your CI Pipeline Execution โ†— for commit 753c3adcec82e93a51a578d40cc2a350d6e84084

Command Status Duration Result
nx affected --targets=lint,test,test-kt,build,e... โŒ Failed 1h 39m 58s View โ†—
nx run-many -t check-imports check-lock-files c... โœ… Succeeded 2m 38s View โ†—
nx-cloud record -- nx-cloud conformance:check โœ… Succeeded 11s View โ†—
nx-cloud record -- nx format:check โœ… Succeeded 2s View โ†—
nx-cloud record -- nx sync:check โœ… Succeeded <1s View โ†—

โ˜๏ธ Nx Cloud last updated this comment at 2025-12-09 08:56:07 UTC

nx-cloud[bot] avatar Dec 09 '25 07:12 nx-cloud[bot]