perf(backend): parallelize independent startup initializers (#816)

The boot path awaited SelfUpdateService.initialize, DockerEventManager.start,
and TrivyService.initialize one at a time even though none of them depend on
each other. Group them into a single Promise.all so total cold-start time is
the slowest one rather than the sum.

Also convert the inner `docker compose version` probe in
SelfUpdateService.initialize from execFileSync to execFileAsync. Without that,
the synchronous spawn would block the event loop for up to 5 seconds and
silently serialize the other two members of the parallel block, defeating
the parallelization win for in-container deployments.

The synchronous service starts (Monitor, AutoHeal, ImageUpdate, Scheduler,
Mfa) are grouped together up front. They schedule timers whose first ticks
fire 5+ seconds out, so they safely run alongside the awaited block.
This commit is contained in:
Anso
2026-04-28 00:33:17 -04:00
committed by GitHub
parent 61a7e43d82
commit 18cf2e65e8
2 changed files with 19 additions and 14 deletions
+6 -3
View File
@@ -1,4 +1,4 @@
import { execFileSync, execFile } from 'child_process';
import { execFile } from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs';
import DockerController from './DockerController';
@@ -63,9 +63,12 @@ class SelfUpdateService {
return;
}
// Verify docker compose CLI is available inside the container
// Verify docker compose CLI is available inside the container.
// execFileAsync (not Sync) so the parallel boot-task block in
// bootstrap/startup.ts can actually run TrivyService and DockerEventManager
// concurrently instead of waiting on a 5s blocking spawn here.
try {
execFileSync('docker', ['compose', 'version'], { stdio: 'pipe', timeout: 5000 });
await execFileAsync('docker', ['compose', 'version'], { timeout: 5000 });
} catch {
console.log('[SelfUpdate] docker compose CLI not available in container');
disableCapability('self-update');