Skip to content

wn-agent: idle SubscribeInbound drives sustained CPU and SQLite read amplification #1380

Description

@Datawav

Summary

A stock wn-agent v0.9.11 process sustains approximately 94–100% of one CPU core while serving one normal, long-lived SubscribeInbound client, even when network traffic is low and the client is otherwise idle.

Over a 20-second observation window, CPU averaged about 94% without long idle gaps. One Tokio worker remained hot. Process I/O counters increased by approximately 54 MB/s of rchar and 13,191 read syscalls/s, while physical disk reads remained at zero. RSS was approximately 345 MB. The encrypted session SQLite database was approximately 183 MiB with an approximately 4.2 MiB WAL. These measurements indicate page-cache/database read amplification rather than physical-disk or network load. The tested binary's SHA-256 was b508f58d9c14e062ef83a54e1edf0a11143feb0680273e6a6ad7b088695dd5c4.

Source-confirmed behavior

The current master source still defines both the inbound catch-up and invite-policy reconciliation intervals as five seconds (constants).

SubscribeInbound registers with the shared catch-up driver and immediately requests an initial catch-up (subscription path). While at least one subscription remains active, the driver invokes runtime.catch_up_accounts() every five seconds; calls are serialized, so they do not overlap with each other (catch-up driver). Each catch-up first reconciles account workers and then sends a catch-up command to every running account worker (runtime catch-up).

Separately, the invite-policy worker reconciles on its own five-second timer as well as on relevant runtime events (worker loop). Its periodic path enumerates every local-signing account and calls app.groups() for each account before filtering pending invitations (candidate enumeration).

The profile evidence does not yet attribute the measured read volume between these two paths, so both should be measured on the large-session fixture before choosing the fix.

Expected behavior

An idle, healthy inbound subscription should not continuously consume most of a CPU core or repeatedly scan database state proportional to the full retained session. Recovery and invite-policy correctness must be preserved without fixed-rate full-state polling.

Acceptance criteria

  • Profile and separately attribute the five-second inbound catch-up and invite-policy reconciliation paths against a representative large encrypted session database.
  • Make steady-state reconciliation event- or cursor-driven where possible. Any safety-net polling should adaptively back off when no relevant state changes, and should reset promptly when work arrives.
  • Prevent overlapping or redundant account, group, and message-state scans across reconciliation sources while preserving initial catch-up, broadcast-lag recovery, invite-policy retries, and reconnect correctness.
  • Expose privacy-safe telemetry that identifies which reconciliation source is active, its duration/result, and aggregate work such as accounts/groups/rows considered. Telemetry must not include account IDs, group IDs, message IDs, relay URLs, content, or key material.
  • Add an agent-connector large-session regression harness for one long-lived, otherwise-idle SubscribeInbound client. It should bound repeated catch-up/reconciliation passes and database-read amplification over multiple former five-second intervals; use deterministic operation/read counters rather than a flaky wall-clock CPU assertion in normal CI.
  • Record an opt-in benchmark/profile result demonstrating that steady-state idle work no longer scales as repeated full-state scans, while a state change still triggers timely reconciliation.

Related work

#574 fixed dead SubscribeInbound connections that accumulated zombie catch-up loops and introduced the shared driver. This report covers the remaining cost of the single shared loop during one legitimate active subscription, so it is not a recurrence of the zombie-connection bug.

#444 bounded full-history replay after broadcast lag. The current observation occurs without a lag event and points to scheduled reconciliation, so it is a separate path.

#675 documented synchronous SQLite/SQLCipher work on Tokio workers. That architectural concern may amplify the hot-worker symptom, but it does not cover the fixed-rate full-state reconciliation and idle read-volume contract reported here.

Environment

  • wn-agent v0.9.11, stock build
  • Linux
  • One normal long-lived SubscribeInbound client

Metadata

Metadata

Assignees

No one assigned

    Labels

    HIGHbugSomething isn't workingperformancePerformance issue

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions