Wait for host DB readiness at startup

---
Build: pass | Tests: FAIL — Tests  3 failed | 2081 passed (2084)
This commit is contained in:
Operator & Codex 2026-05-01 11:41:01 +02:00
parent 0457d2d503
commit 371b237b03
2 changed files with 54 additions and 3 deletions

View file

@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { getControlplaneDbRuntimeIdentity } from './controlplane.js';
import { getControlplaneDbRuntimeIdentity, waitForHostDbReady } from './controlplane.js';
describe('getControlplaneDbRuntimeIdentity', () => {
it('uses platform-owned naming for host postgres', () => {
@ -28,4 +28,22 @@ describe('getControlplaneDbRuntimeIdentity', () => {
jailName: 'db',
});
});
it('waits until host postgres becomes reachable', async () => {
let attempts = 0;
const probe = vi.fn(async () => {
attempts += 1;
return attempts >= 2;
});
const ready = await waitForHostDbReady({
host: '127.0.0.1',
timeoutMs: 200,
pollMs: 10,
probe,
});
expect(ready).toBe(true);
expect(attempts).toBeGreaterThanOrEqual(2);
});
});

View file

@ -21,6 +21,8 @@ import { SOCKET_PATH } from './hostd/types.js';
import { logger } from './logger.js';
export const CONTROLPLANE_CHECK_INTERVAL_MS = 5 * 60_000; // 5 minutes
const HOST_DB_READINESS_TIMEOUT_MS = 30_000;
const HOST_DB_READINESS_POLL_MS = 1_000;
// ── Types ─────────────────────────────────────────────────────────────────────
@ -78,6 +80,34 @@ function unixReachable(socketPath: string, timeoutMs = 3_000): Promise<boolean>
});
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export async function waitForHostDbReady(options: {
host: string;
port?: number;
timeoutMs?: number;
pollMs?: number;
probe?: (host: string, port: number, timeoutMs: number) => Promise<boolean>;
}): Promise<boolean> {
const host = options.host;
const port = options.port ?? 5432;
const timeoutMs = options.timeoutMs ?? HOST_DB_READINESS_TIMEOUT_MS;
const pollMs = options.pollMs ?? HOST_DB_READINESS_POLL_MS;
const probe = options.probe ?? tcpReachable;
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (await probe(host, port, Math.min(3_000, pollMs))) {
return true;
}
await sleep(pollMs);
}
return await probe(host, port, Math.min(3_000, pollMs));
}
// ── Checks ────────────────────────────────────────────────────────────────────
async function checkDbJail(hostdAvailable: boolean): Promise<ControlPlaneCheckResult> {
@ -88,7 +118,10 @@ async function checkDbJail(hostdAvailable: boolean): Promise<ControlPlaneCheckRe
const id = dbIdentity.id;
const label = dbIdentity.label;
const reachable = await tcpReachable(DB_HOST, 5432);
const reachable =
DB_RUNTIME === 'host'
? await waitForHostDbReady({ host: DB_HOST })
: await tcpReachable(DB_HOST, 5432);
if (reachable) return { id, label, status: 'ok', fixAttempted: false };
if (!hostdAvailable) {