Skip to main content

Module writer

Module writer 

Source
Expand description

Bounded, non-blocking background writer for observability persistence (Task 11, spec §6.5).

One cloneable TelemetryWriter handle serves BOTH record kinds:

  • legacy per-request TelemetryRecords → the existing ~/.cache/synaps/api-log.jsonl (format unchanged: one raw JSON object per line, exactly what the old synchronous write_record produced);
  • metadata-only RequestTraces (Tasks 7–10) → a private trace log at ~/.cache/synaps/request-trace.jsonl. Each line is the unwrapped synaps-request-trace/1 object, so any schema-aware reader can parse lines directly.

Design contract:

  • Never blocks the request path. Enqueue is a try_send on a bounded queue; overflow increments a dropped counter (never waits). No awaits, no hidden runtime dependency — the worker is a dedicated OS thread, so sync contexts can enqueue too.
  • One worker thread per writer, but not per file. Within one writer only its worker thread touches the files. Several writers can share the same files, though: every daemon session runtime starts its own writer on ~/.cache/synaps/*.jsonl, and a TUI or another daemon process can overlap. Appends are line-sized O_APPEND writes; size-capped rotation (stat, shift, rename) runs under an exclusive flock on <log>.lock with a re-stat after acquiring it, so two writers never rotate the same file twice. If the lock can’t be had within ROTATE_LOCK_WAIT the rotation is skipped (counted, warned once) and the line is appended past the cap: an oversized log beats a lost record.
  • Private files. All paths use the Phase 1 private_fs helpers: parent 0700, file 0600, O_NOFOLLOW (a planted symlink is refused, never followed).
  • Observable, quiet failure. Atomic counters for enqueued/written/dropped/serialization/I-O failures; at most one warning per persistent failure class (queue overflow, serialization, open/write, rotate). Warnings carry a class label only — never record content or paths. The open/write latch re-arms after a successful write so a new persistent failure warns again.
  • Bounded shutdown. TelemetryWriter::shutdown stops intake, drains until the deadline, and returns typed stats. Dropping the last handle simply disconnects the queue: the worker drains what is buffered and exits detached — Drop can never hang.

Production wiring rule (documented until Task 12 adds explicit trace config/UI): telemetry basic/full enables BOTH legacy telemetry persistence and metadata-only trace persistence through this writer; off disables both. The trace schema is structurally metadata-only, so this can never silently persist raw content.

Structs§

TelemetryWriter
Cloneable handle to the bounded background writer.
WriteGate
A gate the writer’s worker waits on before each job (WriterOptions::write_gate). Starts closed; open releases the worker for good.
WriterOptions
Writer configuration. None paths resolve to the installation defaults.
WriterStats
Snapshot of the writer’s atomic counters.
WriterTraceSink
Production TraceSink: enqueues each record onto the shared writer.

Enums§

ShutdownOutcome
Typed result of a bounded shutdown flush.

Constants§

DEFAULT_MAX_FILE_BYTES
Default per-file size cap before rotation (matches the legacy writer).
DEFAULT_MAX_GENERATIONS
Default rotated generations kept per log: one (<path>.1), the historical behaviour. Raised by the telemetry_log_generations config key.
DEFAULT_QUEUE_CAPACITY
Default bounded queue capacity: deep enough that a briefly slow disk never drops records, small enough to bound memory (records are boxed).
DEFAULT_SHUTDOWN_FLUSH_TIMEOUT
Default budget for the shutdown flush at process-exit epilogues (Task 11): long enough for a healthy disk to drain a full queue, short enough that a hung filesystem can never stall a clean exit.
ROTATE_LOCK_WAIT
Longest a writer waits for another writer’s rotation of the same log (<log>.lock) before appending past the cap instead.

Functions§

default_telemetry_log_path
Default telemetry log path: ~/.cache/synaps/api-log.jsonl.
default_trace_log_path
Default trace log path: ~/.cache/synaps/request-trace.jsonl.