Skip to content

fix(tui): bound log preview reads to avoid startup/shutdown memory spikes - #167

Merged
oschwartz10612 merged 1 commit into
fosrl:devfrom
tursom:fix/bounded-log-preview
Oct 26, 2026
Merged

oschwartz10612 merged 1 commit into
fosrl:devfrom
tursom:fix/bounded-log-preview

Conversation

@tursom

@tursom tursom commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Community Contribution License Agreement

By creating this pull request, I grant the project maintainers an unlimited,
perpetual license to use, modify, and redistribute these contributions under any terms they
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.

AI Disclosure

OpenAI Codex was used to investigate the runtime behavior and upstream source, implement the fix, write and run the regression tests and benchmarks, and prepare this description. A separate Codex session reviewed the patch.

Description

pangolin up and pangolin down show only five log lines, but the first preview starts at offset zero, scans the entire log, and appends every line to a slice before keeping its final five entries. This makes startup/shutdown memory usage and UI blocking time scale with the accumulated log file. In an observed deployment, a 5.45 GiB client.log drove the foreground pangolin up process to approximately 7.69 GiB RSS while the detached tunnel process remained around 100 MiB or less.

Limit each preview read to the newest 64 KiB of the unread range, plus one byte to identify the first line boundary. Read up to the observed file size so concurrent appends cannot extend the work. Select lines backwards and allocate strings only for the lines returned. The same bound applies when a large amount of output arrives between refreshes.

Preserve small-file and incremental behavior, omit a leading line cut by the byte window, and reset the offset when the file shrinks. Since this is a bounded preview, fewer than five lines may be returned if the latest lines do not fit in the window. Full log output and log retention are unchanged.

With the same 16 MiB synthetic log on Linux/amd64 (Go 1.27.2), the included benchmark measured:

First preview Before After
Allocated bytes per call 45,161,165 74,584
Allocations per call 349,561 14
Time per call 24.85 ms 9.72 µs

Incremental reads with a large unread backlog show the same bounded behavior.

How to test?

go test ./internal/...
go test -race ./internal/tui
go vet ./internal/...
go test ./internal/tui -run '^$' -bench BenchmarkGetLastLogLines -benchmem
sh scripts/test-get-cli.sh

These checks pass. Regression tests cover normal and incremental reads, empty/unchanged files, CRLF, blank lines, missing trailing newlines, byte-window boundaries, oversized lines, truncation, and an 8 GiB sparse file for both initial and incremental reads. The TUI tests also cross-compile for Windows/amd64 and Darwin/arm64.

go test ./... on the current dev base (b53ccd5) is blocked by an existing, unrelated compilation error: cmd/up/client/client.go:774: unknown field OlmVersion in olm.OlmConfig. The unmodified base reproduces the same failure. Applying these two changed files to current main (8bcb8db) passes go test ./..., go vet ./..., and go build ./....

@oschwartz10612

Copy link
Copy Markdown
Member

Thanks!

@oschwartz10612

Copy link
Copy Markdown
Member

Error unrelated. LGTM. Thanks!

@oschwartz10612
oschwartz10612 merged commit e42f592 into fosrl:dev Oct 26, 2026
1 of 8 checks passed
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.

2 participants