Katkat

Implement tail N lines

ConfluentSoftware Engineer · Technical Screen

Last 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:

  1. 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.
  2. 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.
  3. Memory bound. Never read the entire file into memory; memory must be bounded by N (and one line), not by file size.
  4. Newline conventions. Correctly handle Unix LF (\n), Windows CRLF (\r\n), and classic Mac CR (\r) line endings, including a mix.
  5. 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.
  6. Missing trailing newline. A final line not terminated by a newline must still count as a line and be emitted.
  7. Extremely long lines. A single line may exceed your buffer/block size; handle lines larger than the read buffer without corruption.
  8. 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 N option: print the last N bytes instead of the last N lines.