/** * The trace-uploader daemon — long-lived background process that drains * the WAL on behalf of all hooks running for this OS user. */ import { Server, Socket } from 'node:net'; import { MlflowClient } from '../../clients/client'; import { BatchingWriter } from './batching_writer'; import { PidLock } from './pid_lock'; import { WalRecord } from './types'; /** * Append a single timestamped line to the daemon log file. Uses sync * I/O so two concurrent log calls cannot interleave; */ export declare function log(line: string): void; /** Awaits `ms` real milliseconds. Used for the batch loop's pacing. */ export declare function sleep(ms: number): Promise; /** * Exponential backoff with a fixed cap. */ export declare function backoff(attempt: number): number; /** * Handle returned by {@link acquireLock} on success. Pairs the bound * server with the optional cross-process PID lock so {@link releaseLock} * can tear both down together. `pidLock` is `null` on Windows because * named pipes already provide kernel-refcounted cross-process * exclusion; the file-based lock would be pure overhead there. */ export interface DaemonLock { server: Server; pidLock: PidLock | null; } /** * Acquire the singleton daemon lock. * * Returns the {@link DaemonLock} on success, or `null` when another * daemon is already running (the caller should `process.exit(0)` * cleanly). * * ## How exclusion is enforced * * On POSIX, exclusion is enforced by the atomic PID lock acquired in * {@link tryAcquirePidLock} *before* the socket bind. The PID lock uses * `link()`, a single kernel-atomic syscall — only one process can win, * the rest see `EEXIST` and concede. With the lock held, no sibling * daemon is past this point at the same time as us, so the subsequent * `bind()` runs uncontested. * * On Windows, named pipes are kernel-refcounted: two daemons cannot * bind the same pipe name simultaneously regardless of the order of * operations. We skip the PID lock there and rely on `listen()`'s * native exclusion — the loser of a concurrent-bind race sees * `EADDRINUSE` from {@link bindUnderPidLock}, which returns `null` so * we concede here the same way the PID-lock-lost path does on POSIX. */ export declare function acquireLock(onConnection?: (socket: Socket) => void): Promise; /** * Release the lock acquired by {@link acquireLock}. Closes the server, * unlinks the POSIX socket file, and (when present) releases the PID * lock so the next daemon's `acquireLock` finds a clean slate. */ export declare function releaseLock(server: Server, pidLock?: PidLock | null): Promise; /** * One-shot retention sweep for the daily-rotated `failed.log.` and * `daemon.log.` files in the WAL dir. * * Called once per daemon startup, after the liveness lock is held and * before the batch loop starts. * * The sweep is best-effort: any failure (missing dir, EPERM on a file the * user hand-edited, malformed filenames) is logged at debug and * swallowed. Retention housekeeping must never abort daemon startup. */ export declare function pruneOldLogs(opts?: { now?: Date; retentionDays?: number; }): Promise; /** * Process a single WAL record: deserialize, push to the backend, * tombstone on success or schedule retry / dead-letter on failure. */ export declare function uploadOne(record: WalRecord, client: MlflowClient, writer: BatchingWriter, factory?: ClientFactory): Promise; export type ClientFactory = (trackingUri: string) => MlflowClient; /** * Group `records` by `trackingUri` and upload each group in parallel. */ export declare function processBatch(records: WalRecord[], writer: BatchingWriter, factory?: ClientFactory): Promise; /** * The batch loop. Broken out from {@link main} so the dependencies * (client factory, writer, timings, shutdown signal) can be injected * in tests without standing up the full daemon lifecycle. * * Pass an `AbortSignal` to request a graceful shutdown — the loop * finishes its current iteration and returns. Without a signal the * loop only exits via the `idleMs` timeout path (or by throwing). */ export declare function runBatchLoop({ factory, writer, batchIntervalMs, idleMs, signal, }?: { factory?: ClientFactory; writer?: BatchingWriter; batchIntervalMs?: number; idleMs?: number; signal?: AbortSignal; }): Promise; /** * Daemon entry point. */ export declare function main(): Promise;