Skip to content

Print the newest log entries by default and re-read less while following - #11

Merged
cory-salad merged 1 commit into
mainfrom
logs-query-limits
Oct 20, 2026
Merged

cory-salad merged 1 commit into
mainfrom
logs-query-limits

Conversation

@cory-salad

Copy link
Copy Markdown
Contributor

Summary

When someone asks for a busy container group's logs over a long time range, the CLI reads them oldest first, 100 lines per request, so a few days of a chatty group meant tens of thousands of requests before the newest line appeared. --follow had the opposite problem: to catch lines that reach the log store late, every poll re-read the last two minutes, about 25 times more than was new, so on a busy group it read nonstop and fell minutes behind. Queries now print the newest 1000 lines by default, read newest first in about ten requests, and say so when older lines were left out. --follow now re-reads only the 10 seconds before each poll, plus each minute once more after it is two minutes old, and drops that second pass with a warning when it can't keep up.

What changed

logs / container-groups logs one-shot

  • --limit (default 1000, 0 for all) keeps the newest entries in the range. It reads newest first (sort_order: desc), stops one entry past the limit, and prints oldest first. logsq.FetchNewest mirrors FetchWindow's millisecond-boundary dedup downward.
  • When older entries were left out, a note: line goes to stderr in every output mode (scripts need to know), plus a hint: in table mode.
  • --limit 0 is the old oldest-first stream. --since still defaults to 15m and --page-size still caps at 100, the public spec's limit.

--follow

  • Polls start every 10s, measured start to start (--poll-interval, minimum now 5s). Each poll reads [previous poll − 10s, now).
    • The window ends at now. A window open a minute into the future made slow polls chase new lines and the next poll re-read them all.
    • Windows come from this machine's clock, not the newest entry seen, so a fast node clock can't pull them forward.
  • A settle scan runs once a minute and reads, once, the minute that has just aged past --lookback (default 2m), so an entry up to --lookback late still prints.
  • If a poll runs past the interval, the settle scan is skipped, with one warning, so the polls keep up.
  • The -f backlog takes the newest --limit entries the same way (11 requests instead of about 400 on the test group).

Docs: commands.md, the guide's Logs section, and notes.md (follow behavior, plus the single-reader throughput: about 100 entries per request at a second or more each). The validation table updates the --poll-interval minimum. No CHANGELOG entry, since nothing has been released yet.

Testing

  • just ci passes. New unit tests:
    • the newest-N reader at every combination of page size and limit, against a fake that pages like the API, with a millisecond split across pages and a stalled millisecond
    • follow windows, settle-scan windows (consecutive, each minute once), a failed settle retried, the overrun valve, the backlog limit, fast-clock entries, and start-to-start timing
    • CLI end to end against a fake log API honoring sort_order and page_size
  • just test-live with SALAD_CLI_TEST_REQUIRE_SSH=1 passed on the first version of this branch; the follow rework only touches --follow. A new live check compares --limit 3 with --limit 0 over the same fixed range.
  • Live, against a throwaway group logging about 45 lines/s:
    • polls ran every 16–25s (55–129s before)
    • a later full read of the same range found 0 of 10,277 covered entries missed and none printed twice
    • followed at a low rate (a 1% filter), polls ran every 10s and settle scans read consecutive minutes
    • all 497 matching lines printed once

Not covered

  • That node's lines arrived within a second, so late arrival at fleet scale (a few seconds at p90, up to minutes in the tail) is covered by unit tests only.
  • One sequential reader tops out around 70 lines/s at today's ~1.4s per request (filtered queries ~3.4s). Raising the public page_size cap from 100 would lift that about tenfold.

Checklist

  • just ci passes (formatting, lint, and the offline tests)
  • docs/ reflects the change (CHANGELOG deliberately not updated before the first release)
  • Anything that touches an API call was exercised live

🤖 Generated with Claude Code

A one-shot logs query read its whole range oldest first, a page of 100 at a time, so --since 3d on a busy group
was tens of thousands of sequential requests before the newest line printed. --limit (default 1000, 0 for all)
now reads the range newest first through the API's sort_order desc, stops one entry past the limit, and prints
what it kept oldest first, with a note: line on stderr in every mode (and a hint in table mode) when it left
older entries out. --limit 0 keeps the old oldest-first stream.

--follow re-read two minutes behind the newest entry it had seen on every 5s poll, about 25 times the new data,
so a busy group kept it reading back to back and minutes behind, and a fast node clock pulled its windows forward.
Its windows are now measured from this machine's clock: each poll reads from 10s before the previous poll up to
the moment it starts, polls start every 10s start to start (--poll-interval, 5s at least), and once a minute a
settle scan reads, once, the minute that has just aged past --lookback, so an entry up to --lookback late still
prints. When a poll runs longer than the interval the settle scan is skipped with one warning, so the polls keep
up. The -f backlog takes the newest --limit entries the same way, and --limit no longer conflicts with --follow.

Against a group logging 45 lines/s, polls ran every 16-25s (55-129s before), the backlog took 11 requests instead
of about 400, and a full read of the same range found no entry missed or printed twice; at a low rate, polls ran
every 10s and settle scans read consecutive minutes. docs/notes.md records the throughput a single reader gets
(about 100 entries per request, a second or more each). The live suite checks --limit against the same range read
whole.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cory-salad
cory-salad requested a review from a team as a code owner October 20, 2026 22:20
@cory-salad cory-salad self-assigned this Oct 20, 2026
@cory-salad
cory-salad merged commit 7738b51 into main Oct 20, 2026
16 checks passed
@cory-salad
cory-salad deleted the logs-query-limits branch October 20, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant