rtk-ai / rtk-ai/rtk

rtk read consumes the whole input before windowing: unbounded streams never terminate, peak RSS scales with file size

Open
#4,012 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:performance bug help wanted priority:high
Dominant language
Rust
Stars
81.1k
Forks
5.1k
Avg merge
4d 21h
Merged PRs (30d)
35

Description

Summary

rtk read consumes its entire input before applying a line window. Every head/tail hook rewrite routes through it, so a bounded request against an unbounded or very large input behaves quite differently from the native command: head -2 /dev/urandom returns instantly, the rewritten form never returns.

What is already fixed

The non-UTF-8 content loss described in the original version of this issue is fixed on develop by #3941 (merged as cfe248c1). read::run now reads bytes and applies the window on byte offsets when filtering and line numbering are off. Verified against the develop binary:

$ printf '\xff\xfe bad\nline2\nline3\n' > bin.log
$ head -2 bin.log                       → 13 bytes, rc=0
$ rtk read bin.log --head-lines 2       → 13 bytes, rc=0   byte-identical

That holds for head -N, head -n N, head --lines[=]N, bare head, and the tail equivalents. This issue is narrowed to what that change deliberately left open.

1. Unbounded streams never terminate

$ head -2 /dev/urandom                  # returns instantly
$ rtk read /dev/urandom --head-lines 2  # no output, killed at 5s

head -2 /dev/urandom is rewritten today. read/read_to_end has no line budget, so it reads until an EOF that never arrives.

Note for whoever picks this up: a FIFO whose writer closes is not affected — rtk read fifo --head-lines 1 completes normally, because EOF does arrive. The failure is specific to inputs with no EOF (character devices, yes |, a FIFO held open by a live writer), not to non-regular files as a category. The original title said "FIFOs"; that was too broad.

2. Peak memory is proportional to input size, not window size

On a 143 MB file, head -n 5, measured against develop @ cfe248c1:

wall peak RSS
native head -n 5 0.00 s 11 MB
rtk read --head-lines 5 0.08 s 151 MB

#3941 improved this (the byte path removed a String round-trip, so the same case was 293 MB before it), but the whole file is still resident to emit five lines.

Suggested direction

Read incrementally and stop once the window is satisfied. head_window already scans forward for the Nth \n, so a buffered reader that stops there closes both (1) and (2) for the --head-lines case. --tail-lines needs the tail kept in a ring buffer rather than the whole input. Falling back to the native binary when the input is not a regular file would close (1) alone.

Related

  • #3941 — fixed the content-loss half; merged.
  • #3339 — savings accounting on these windows, including the from_utf8_lossy inflation that shares this code path.

Environment

  • develop @ cfe248c1, Linux, LC_ALL=C

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the Rust read::run/read_to_end path and inspect the existing head_window scan. Reproduce the /dev/urandom and large-file commands, then verify that head stops at its window and tail uses bounded buffering; completion should avoid waiting for EOF and memory should scale with the requested window.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.