Skip to content

Commit e3c5d74

Browse files
committed
Enhance heap snapshot consistency and labeling
To facilitate more accurate and consistent heap memory snapshots, a new function forceConsistentGC was added before taking a snapshot. This ensures the garbage collector (GC) runs multiple times, stabilizing the heap state for more reliable analysis. This is particularly helpful when debugging memory-related issues. Updates to the memory-leak-detector script now allow labeling of snapshot runs using the --label flag. This helps in distinguishing different runs for easier tracking and comparison of memory usage across test sessions. Additionally, the --expose-gc flag ensures that the GC can be manually triggered during test runs, leading to more consistent memory states and potentially uncovering hidden memory leaks.
1 parent 0bc7c98 commit e3c5d74

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

apps/webapp/app/routes/admin.api.v1.snapshot.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,27 @@ function formatDate(date: Date) {
2424
.padStart(2, "0")}_${milliseconds.toString().padStart(3, "0")}`;
2525
}
2626

27+
// Force consistent garbage collection before taking heap snapshot
28+
async function forceConsistentGC(rounds = 5): Promise<void> {
29+
if (typeof global.gc !== 'function') {
30+
console.warn('⚠️ global.gc not available - heap snapshots may be inconsistent');
31+
return;
32+
}
33+
34+
// Force multiple GC rounds to ensure consistent state
35+
for (let i = 0; i < rounds; i++) {
36+
global.gc(true); // Major GC
37+
await new Promise(resolve => setTimeout(resolve, 20));
38+
global.gc(false); // Minor GC (if available)
39+
if (i < rounds - 1) {
40+
await new Promise(resolve => setTimeout(resolve, 50));
41+
}
42+
}
43+
44+
// Final wait for any pending cleanup
45+
await new Promise(resolve => setTimeout(resolve, 200));
46+
}
47+
2748
export async function loader({ request }: DataFunctionArgs) {
2849
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
2950

@@ -41,6 +62,9 @@ export async function loader({ request }: DataFunctionArgs) {
4162
throw new Response("You must be an admin to perform this action", { status: 403 });
4263
}
4364

65+
// Force consistent GC state before taking snapshot
66+
await forceConsistentGC();
67+
4468
const tempDir = os.tmpdir();
4569
const filepath = path.join(
4670
tempDir,

apps/webapp/memory-leak-detector.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class MemoryLeakDetector {
2020
// Create timestamped directory for this run
2121
const runTimestamp = new Date().toISOString().replace(/[:.]/g, "-");
2222
const baseDir = options.baseDir || "./.memory-snapshots";
23-
const runDir = `${baseDir}/${runTimestamp}`;
23+
const runDir = `${baseDir}/${runTimestamp}-${options.label || "memory-leak-detector"}`;
2424

2525
this.options = {
2626
// Server configuration
@@ -234,7 +234,7 @@ class MemoryLeakDetector {
234234
this.log(`Using NODE_PATH: ${nodePath}`);
235235

236236
// Start the server with memory inspection flags
237-
this.serverProcess = spawn("node", ["--max-old-space-size=16384", "./build/server.js"], {
237+
this.serverProcess = spawn("node", ["--max-old-space-size=16384", "--expose-gc", "./build/server.js"], {
238238
cwd: process.cwd(),
239239
stdio: ["ignore", "pipe", "pipe"],
240240
env: {
@@ -809,6 +809,9 @@ function parseArgs() {
809809
case "--sentry-dsn":
810810
options.sentryDsn = value;
811811
break;
812+
case "--label":
813+
options.label = value;
814+
break;
812815
case "--verbose":
813816
options.verbose = true;
814817
i--; // No value for this flag
@@ -824,6 +827,7 @@ Options:
824827
--requests <number> Number of test requests (default: 100)
825828
--delay <ms> Delay between requests (default: 50)
826829
--endpoints <list> Comma-separated API endpoints to test
830+
--label <string> Label for the run
827831
--threshold <MB> Memory leak threshold in MB (default: 50)
828832
--token <string> Admin Bearer token for V8 heap snapshots
829833
--api-key <string> API key for API requests

0 commit comments

Comments
 (0)