8.2 The Bootstrap Sequence

Startup proceeds in seven phases, and either completes or fails entirely.

8.2.1 Phase 1 — Configuration #

  1. Read every configuration key under Machine\System\eventd\. The six required keys are EventStorePath, LogStorePath, MetricStorePath, QuerySocketPath, LogSocketPath and MetricSocketPath; a missing or invalid one fails startup.
  2. Read the optional keys and apply compiled-in defaults for those absent (§A).
  3. Arm a persistent watch on the subtree, for runtime changes (§8.3).

8.2.2 Phase 2 — KMES attachment and shard sizing #

  1. Discover the CPU count by calling kmes_attach with incrementing CPU identifiers from 0 until EINVAL. The call requires SeSecurityPrivilege in the effective token. Discovering no CPUs fails startup.
  2. Map each per-CPU ring buffer.
  3. Resolve the active shard count from StorageShards — the CPU count when it is 0, the configured value otherwise.
  4. Compute the shard-to-CPU assignment (§2.3).

8.2.3 Phase 3 — Storage #

  1. Open or create each active event shard: verify the schema version, open in WAL mode with synchronous=FULL, create tables and indexes if new, and quarantine on reported corruption (§3.3). Discover historical shards matching the naming pattern, and open those with a recognised schema read-only; exclude the rest from the query path.
  2. Open or create the log store — schema verified, WAL, synchronous=NORMAL, quarantine on corruption (§4.3).
  3. Open or create the metric store, likewise (§5.4). The series cache starts empty and fills on demand.
  4. Open or create eventd-meta.db. Load the index and rollup counters and desired sets, load the sequence checkpoints for diagnostics only, and discover each shard's material indexes from its schema (§3.5).

8.2.4 Phase 4 — The boot boundary #

  1. Read the current boot ID from peinit.
  2. Search every readable event shard for committed rows carrying it.
  3. No rows — the boot's first eventd start. Reset every per-CPU sequence tracker to 0 and record the new boot ID for subsequent writes.
  4. Rows exist — a restart within the boot. Derive each CPU's resume point from the maximum committed sequence for (boot_id, cpu_id) across every readable shard; CPUs with no rows resume at 0 (§3.7).

8.2.5 Phase 5 — Sockets #

  1. Create the query socket at QuerySocketPath. A stale pathname left by a crash is unlinked first if it is an AF_UNIX socket; if the path exists and is not a socket, startup fails.
  2. Create the log socket at LogSocketPath, same rule.
  3. Create the metric socket at MetricSocketPath, same rule.
  4. Establish the Security Descriptor on all three, before any of them accepts or receives anything (§7.6).

The stale-socket rule distinguishes the two cases deliberately. Unlinking a leftover socket is recovery from eventd's own crash; unlinking a regular file at a configured path would be destroying something that is not eventd's, and a path pointing at the wrong thing is a configuration error worth failing on.

8.2.6 Phase 6 — Threads #

  1. One drain thread per CPU, each beginning to read its ring buffer.
  2. One writer thread per active shard.
  3. The log ingestion thread.
  4. The metric ingestion thread.
  5. The retention thread.
  6. The adaptive indexing and rollup policy thread.

8.2.7 Phase 7 — Ready #

  1. Write and commit a synthetic.startup event recording the boot ID, the shard count and the per-CPU resume points (§3.2). The commit happens before readiness is signalled.
  2. Signal readiness to peinit.

Committing before signalling is what makes the startup record trustworthy. A readiness signal sent before the commit could be followed by a crash that loses the record, leaving a boot in which eventd demonstrably ran and left no trace of having started.

8.2.8 Failure #

If any phase fails, eventd does not signal readiness. It logs the failure to standard error where standard error exists, and exits non-zero. peinit's restart policy decides what happens next.

Partial startup is not permitted. There is no degraded mode in which eventd runs without one of its three stores, or without KMES. It either completes the sequence or fails.

The all-or-nothing rule is a simplification. A later revision might allow log and metric ingestion to proceed with KMES unavailable, but that means partial-failure state to manage in every subsequent path — what a query against a store that was never opened does, what happens when the missing subsystem returns — and the failure mode it protects against is one peinit already handles by restarting.

Edit this page