Summary
The watcher performs full filesystem discovery every 500 ms and can rebuild all historical summaries whenever the active log changes. With a moderate corpus this consumes a sustained fraction of a CPU core and creates multi-second work on the request-serving process.
Tested against main at fb265549480ff0d3ad8b14b28007370539b70cf3 on macOS with 100 discovered sources.
Reproduction
-
Start Token Meter with approximately 100 Claude/Codex JSONL files.
-
Time source discovery:
import meter, statistics, time
samples=[]
for _ in range(20):
started=time.perf_counter(); meter.all_session_sources(); samples.append(time.perf_counter()-started)
print(statistics.median(samples))
-
Observe the server process while an agent is writing to the newest log.
-
Invalidate _xsess and time one cold cross_session().
Observed:
- Median
all_session_sources() time: 0.1712 s.
- The watcher calls it every 0.5 s, an intrinsic baseline near 34% of one core before other work.
- Server CPU samples ranged from 19% to 90% while active.
- A cold
cross_session() took 2.717 s.
Root cause
meter.py:5539-5574 calls all_session_sources() on every 500 ms loop.
source_mtime_signature() treats any active-log mtime change as an aggregate change.
refresh_cross_session_state() invalidates the entire aggregate cache.
cross_session() walks and summarizes every source.
Suggested fix
- Cache the source inventory; update it from directory change notifications (FSEvents/kqueue) or a much slower adaptive scan.
- Poll only the selected/active log at the fast interval.
- Cache each session summary by stable source key plus
(mtime, size) and recompute only changed files.
- Maintain aggregate totals incrementally by subtracting the prior summary and adding the new one.
- Back off the poll interval when idle and perform expensive work outside the HTTP request path.
Regression tests
- Instrument discovery and assert an idle watcher does not run a full inventory scan every 500 ms.
- With 100 fixture logs, modify one file and assert only that summary is recomputed.
- Assert additions/removals still appear within a documented latency budget.
- Add a benchmark/guardrail for idle CPU and refresh time.
Summary
The watcher performs full filesystem discovery every 500 ms and can rebuild all historical summaries whenever the active log changes. With a moderate corpus this consumes a sustained fraction of a CPU core and creates multi-second work on the request-serving process.
Tested against
mainatfb265549480ff0d3ad8b14b28007370539b70cf3on macOS with 100 discovered sources.Reproduction
Start Token Meter with approximately 100 Claude/Codex JSONL files.
Time source discovery:
Observe the server process while an agent is writing to the newest log.
Invalidate
_xsessand time one coldcross_session().Observed:
all_session_sources()time: 0.1712 s.cross_session()took 2.717 s.Root cause
meter.py:5539-5574callsall_session_sources()on every 500 ms loop.source_mtime_signature()treats any active-log mtime change as an aggregate change.refresh_cross_session_state()invalidates the entire aggregate cache.cross_session()walks and summarizes every source.Suggested fix
(mtime, size)and recompute only changed files.Regression tests