Skip to main content

Module pid_file

Module pid_file 

Source
Expand description

The server’s pid file: <AION_HOME>/run/aion-server.pid.

The file is the stop/status verbs’ address for the running server. It is written at BIRTH — before the store is opened, before anything can take minutes — and it carries the incarnation’s whole life: the state it is in (IncarnationState), the boot stage it is working through, the addresses once they are bound, and the drain window once it is known.

RULING — the record appears at birth, not at bind. A big store takes minutes of WAL recovery, and a record written only at bind left every control verb blind for that whole window: status said “no server has claimed this home”, stop said “nothing to stop” (exit 0), and the launcher’s port probe read the home as empty and spawned ANOTHER server, which blocked silently on the store writer lock. Four servers stacked invisibly on 2026-08-26. The birth claim is what makes that impossible: the second boot’s claim_at_birth sees a live booting sibling and REFUSES.

Every mutation of the file — the birth claim’s rename-into-place, each stage write, the bind-time fill, the drain flip, the stop verb’s compare-and-delete, the guard’s own exit-time compare-and-delete — runs under an exclusive OS file lock (std::fs::File::lock: flock on Unix, LockFileEx on Windows) on a sibling lock file (aion-server.pid.lock). The lock is what makes compare-and-delete atomic: without it, a successor claiming the home between a remover’s read and its unlink would have ITS record deleted, leaving a live server no verb can address. The lock file itself is never renamed or removed — locking the pid file directly would be unsound, because the claim replaces that path’s inode and a lock on the old inode excludes nobody.

RULING — a lock failure at claim time REFUSES the boot. The lock is the instrument that keeps one home’s records from destroying each other; a home where it cannot be taken (permissions on the lock file, a filesystem without advisory locking) is a home where a claim can silently delete a live server’s address, and the honest answer is a refusal naming the remedy, not a boot that runs without the guarantee. The exit-time guard is deliberately more lenient (it leaves the file for stale reconciliation) because at that point refusing helps nobody — the process is exiting either way.

RULING — every future field added to PidRecord is #[serde(default)] on read. The record is a cross-process, cross-version contract: a newer CLI must be able to read the record an older running server wrote — upgrade time is exactly when the stop verb matters most — so a missing field reads as its honest default, never as a parse refusal.

RULING — records are compared by INCARNATION IDENTITY (pid + start instant), never by whole-record equality. The record now MUTATES during the incarnation’s life (every stage write bumps stage_seq), so a whole-record compare would make the stop verb’s reconciliation and the guard’s exit-time delete miss their own record the moment a stage landed between the read and the compare — leaving live-looking debris behind every boot that took long enough to report progress.

The record is an incarnation identity, never a bare pid: pid, start instant, and the serving binary’s content hash (plus the bound addresses and build identity, so readers talk to the server that IS running rather than the one today’s config would start). A stale file found at claim time is reconciled by incarnation check and reported — never silently overwritten, never trusted. See crate::control::incarnation for how a record is verified against the live process table.

Structs§

PidRecord
The pid file’s content: one JSON object describing the server incarnation that claimed the home.

Enums§

IncarnationState
Where a recorded incarnation is in its own life.
StaleReconciliation
What the birth claim found already sitting at the pid-file path, reconciled before the new record was written. Reported to the operator through the boot log; returned so tests can assert the reconciliation happened rather than trusting the log.

Functions§

now_unix_secs
Whole seconds since the Unix epoch, or 0 when the system clock is set before the epoch (which no reader can do arithmetic on anyway).
pid_file_path
Absolute path of the pid file under home.
read
Read and parse the pid file under home.
remove_if_matches
Remove the pid file under home if — and only if — it still holds the SAME INCARNATION as record. Used by the stop verb to reconcile the file a non-clean exit (forced, killed) left behind, after the process is proven gone.