-
Notifications
You must be signed in to change notification settings - Fork 7
feat: ⚡️ Improve Caching Performance for multi runs #531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a comprehensive caching system to dramatically improve Moonwall CLI startup and test execution performance. The changes include a new startup artifact caching service, enhanced retry logic for better reliability in parallel environments, and structured logging improvements.
Key changes:
- New
StartupCacheServicethat caches precompiled WASM and raw chain specs for ~10x faster node startup - Increased retry attempts and reduced intervals for port discovery and node readiness checks to handle high-contention scenarios
- Replaced ad-hoc console logging with structured logger throughout command parsing and port discovery
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| test/suites/papi/dev.test.ts | Code formatting fixes (spacing and semicolon) |
| test/moonwall.config.json | Enables new caching features for test environment |
| packages/types/src/config.ts | Adds type definitions for caching configuration options |
| packages/types/config_schema.json | JSON schema updates for new caching options |
| packages/cli/src/internal/effect/StartupCacheService.ts | New Effect-based service for caching precompiled artifacts |
| packages/cli/src/internal/commandParsers.ts | Integrates startup cache into launch command flow |
| packages/cli/src/cmds/runTests.ts | Adds Vitest dependency optimizer configuration |
| packages/cli/src/internal/providerFactories.ts | Implements metadata caching for PolkadotJS connections |
| packages/cli/src/lib/globalContext.ts | Increases retry attempts and reduces delay for provider connections |
| packages/cli/src/internal/effect/NodeReadinessService.ts | Adjusts retry parameters for faster convergence |
| packages/cli/src/internal/effect/PortDiscoveryService.ts | Increases retry attempts and reduces interval |
| packages/cli/src/internal/effect/RpcPortDiscoveryService.ts | Increases retry attempts and reduces interval |
| packages/cli/src/lib/runnerContext.ts | Simplifies context creation with ternary operator |
| packages/cli/src/lib/repoDefinitions/moonbeam.ts | Adds ParityDB database backend flag |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Check if using --dev flag | ||
| const hasDevFlag = this.args.includes("--dev"); | ||
| // Extract chain name from --chain=XXX or --chain XXX | ||
| const existingChainName = chainArg?.match(/--chain[=\s]?(\S+)/)?.[1]; |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regex pattern --chain[=\s]?(\S+) used to extract the chain name may not handle all valid chain argument formats correctly. Specifically:
- The pattern allows optional whitespace or
=, but if whitespace is matched, it won't be consumed, causing the chain name to start with a space - The pattern uses
\S+which captures any non-whitespace, but this could capture additional arguments if--chainis followed by a space and the next argument (e.g.,--chain moonbase-dev --other-flagcould capturemoonbase-dev)
Consider using a more precise pattern:
const existingChainName = chainArg?.includes('=')
? chainArg.split('=')[1]
: this.args[this.args.indexOf(chainArg) + 1];Or if keeping the regex approach:
const existingChainName = chainArg?.match(/--chain(?:=(\S+)|\s+(\S+))/)?.[1] || chainArg?.match(/--chain(?:=(\S+)|\s+(\S+))/)?.[2];| const existingChainName = chainArg?.match(/--chain[=\s]?(\S+)/)?.[1]; | |
| const existingChainName = chainArg | |
| ? (chainArg.includes("=") | |
| ? chainArg.split("=")[1] | |
| : this.args[this.args.indexOf(chainArg) + 1]) | |
| : undefined; |
| this.args.push("--alice"); | ||
| this.args.push("--force-authoring"); |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When replacing --dev with a raw chain spec, the code adds --alice and --force-authoring flags using .push() instead of overrideArg(). This could lead to duplicate flags if these arguments already exist in the args array (e.g., from default args or user-specified options).
Use the overrideArg() method consistently to prevent duplicates:
this.overrideArg("--alice");
this.overrideArg("--force-authoring");However, note that overrideArg() may not handle flag-style arguments without = correctly (see the implementation at line 70-78), which could also cause issues.
| return; | ||
| } | ||
|
|
||
| const data = JSON.stringify({ [genesisHash]: metadataHex }); |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The metadata cache implementation only stores a single genesis hash entry, overwriting any previously cached metadata from different chains. This will cause cache invalidation issues when testing multiple chains or environments.
The saveCachedMetadata function should merge with existing cache data rather than replacing it:
const saveCachedMetadata = (genesisHash: string, metadataHex: string): void => {
const cacheDir = process.env.MOONWALL_CACHE_DIR;
if (!cacheDir) return;
const metadataPath = path.join(cacheDir, "metadata-cache.json");
const lockPath = `${metadataPath}.lock`;
try {
// Simple lock to prevent concurrent writes
try {
fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL);
} catch {
// Another process is writing, skip
return;
}
// Load existing cache and merge
let existingCache: Record<string, string> = {};
try {
const existing = fs.readFileSync(metadataPath, "utf-8");
existingCache = JSON.parse(existing);
} catch {
// No existing cache or parse error
}
const data = JSON.stringify({ ...existingCache, [genesisHash]: metadataHex });
fs.writeFileSync(metadataPath, data, "utf-8");
debug(`Saved metadata cache for genesis: ${genesisHash}`);
} catch (e) {
debug(`Failed to save metadata cache: ${e}`);
} finally {
try {
fs.unlinkSync(lockPath);
} catch {
/* ignore */
}
}
};| const data = JSON.stringify({ [genesisHash]: metadataHex }); | |
| // Load existing cache and merge | |
| let existingCache: Record<string, string> = {}; | |
| try { | |
| const existing = fs.readFileSync(metadataPath, "utf-8"); | |
| existingCache = JSON.parse(existing); | |
| } catch { | |
| // No existing cache or parse error | |
| } | |
| const data = JSON.stringify({ ...existingCache, [genesisHash]: metadataHex }); |
| try { | ||
| // Simple lock to prevent concurrent writes | ||
| try { | ||
| fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL); |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lock file descriptor is not being closed before deletion. When using fs.openSync, the file descriptor should be closed with fs.closeSync(fd) to prevent resource leaks.
try {
const fd = fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL);
fs.closeSync(fd); // Close the file descriptor
} catch {
// Another process is writing, skip
return;
}| fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL); | |
| const fd = fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL); | |
| fs.closeSync(fd); // Close the file descriptor to prevent resource leaks |
This pull request introduces significant improvements to the Moonwall CLI startup and test execution performance, primarily through a new startup artifact caching mechanism and enhanced retry logic for port and readiness checks. The changes also refactor logging for better maintainability and debugging.
Startup performance optimizations:
StartupCacheService(packages/cli/src/internal/effect/StartupCacheService.ts) that caches precompiled WASM and raw chain specs, reducing node startup time by ~10x. This service is integrated into theLaunchCommandParserand is triggered whencacheStartupArtifactsis enabled in the launch spec. [1] [2]Reliability and retry logic improvements:
Logging and maintainability:
console.logandchalkusage with a structured logger (createLoggerfrom@moonwall/util) for more consistent and configurable logging throughout command parsing and port discovery. [1] [2] [3]Test execution enhancements:
.setCacheImports()option inVitestOptionsBuilder, enabling dependency optimizer for selected libraries. [1] [2]