-
Notifications
You must be signed in to change notification settings - Fork 16
Closed
Labels
Description
Detail Bug Report
Summary
- Context: The
Tailcommand is used to follow a stream and output records, similar to the Unixtail -fcommand, and it handles both data records and command records (fence/trim operations). - Bug: The
Tailcommand always writes a newline after every record to stdout, including command records in text format, even though command records should not output anything to stdout. - Actual vs. expected: Command records in text format are written to stderr only and should not produce stdout output, but
Tailwrites an extra blank line to stdout for each command record, while theReadcommand correctly skips this newline. - Impact: When using
tail -fwith text format on streams containing command records (fence/trim operations), blank lines are inserted into the stdout output, corrupting the output stream and potentially breaking pipelines or downstream consumers.
Code with bug
From src/main.rs:
loop {
select! {
record = records.next() => {
match record {
Some(Ok(record)) => {
write_record(&record, &mut writer, args.format).await?;
writer
.write_all(b"\n") // <-- BUG π΄ Always writes newline, even for command records
.await
.map_err(|e| CliError::RecordWrite(e.to_string()))?;
writer
.flush()
.await
.map_err(|e| CliError::RecordWrite(e.to_string()))?;Codebase inconsistency
Reference (Read command in src/main.rs):
for record in &batch.records {
write_record(record, &mut writer, args.format).await?;
let skip_newline = matches!(args.format, RecordFormat::Text)
&& record.is_command_record();
if !skip_newline {
writer
.write_all(b"\n")
.await
.map_err(|e| CliError::RecordWrite(e.to_string()))?;
}
}Current (Tail command in src/main.rs):
write_record(&record, &mut writer, args.format).await?;
writer
.write_all(b"\n") // Always writes newline, no check
.await
.map_err(|e| CliError::RecordWrite(e.to_string()))?;Contradiction: Read correctly skips the newline for command records in text format, while Tail always writes it, causing inconsistent output on the same stream.
Recommended fix
Apply the same skip-newline logic used by Read to Tail:
write_record(&record, &mut writer, args.format).await?;
let skip_newline = matches!(args.format, RecordFormat::Text)
&& record.is_command_record(); // <-- FIX π’ Add skip-newline check for command records in text
if !skip_newline {
writer
.write_all(b"\n")
.await
.map_err(|e| CliError::RecordWrite(e.to_string()))?;
}Reactions are currently unavailable