Implement tail N lines
ConfluentSoftware Engineer · Technical ScreenLast updated
Confluent software-engineer technical-screen coding question: implement tail(path, N) to print the last N lines of a possibly multi-GB file or an unbounded stream without loading it into memory. It tests a backward block scan for seekable files versus an O(N) ring buffer for non-seekable streams, plus correct handling of \n/\r\n/\r newlines, UTF-8 multibyte characters, missing trailing newlines, and extremely long lines, with follow-ups on tail -f, log rotation/truncation, and the -c last-N-bytes option.
Question
Implement a utility tail(path: string, N: int) (and an equivalent stream variant) that prints the last N lines of input without loading the whole input into memory. Assume variable-length lines, an unknown total line count, and a file that may be many gigabytes.
Your solution must address both input modes and the full set of correctness constraints below:
- Seekable / random-access file. Use either a backward scan (seek to the end and read fixed-size blocks toward the front, counting newlines until you have collected N lines) or a fixed-size ring buffer over a forward read. Avoid re-reading the whole file.
- Non-seekable / unbounded stream (stdin or a pipe). Seeking is impossible and the length is unknown, so maintain only O(N) memory — a ring buffer of the last N lines — while reading forward exactly once.
- Memory bound. Never read the entire file into memory; memory must be bounded by N (and one line), not by file size.
- Newline conventions. Correctly handle Unix
LF(\n), WindowsCRLF(\r\n), and classic MacCR(\r) line endings, including a mix. - UTF-8 correctness. Split on line boundaries at the byte level but preserve UTF-8 multibyte characters — never cut a character in the middle when reading or buffering blocks.
- Missing trailing newline. A final line not terminated by a newline must still count as a line and be emitted.
- Extremely long lines. A single line may exceed your buffer/block size; handle lines larger than the read buffer without corruption.
- State the time and space complexity of each approach and give the key test cases you would write.
Follow-ups the interviewer will push on:
- Extend to
tail -f: follow the file as it grows, emitting new lines as they are appended. - Handle log rotation / truncation and symlinks (detect when the file is replaced or truncated and re-open / reset position).
- Support the
-c Noption: print the last N bytes instead of the last N lines.
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.