Skip to content

Conversation

@TennyZhuang
Copy link
Contributor

Summary

  • ensure async flush waits for worker completion
  • propagate the first flush error back to caller
  • add regression tests for blocking flush and error propagation

@tisonkun
Copy link
Contributor

pub(crate) fn destroy(&self) {
if let Some(state) = self.0.swap(None) {
// SAFETY: state has always one strong count before swapped.
let State { sender, handle } = Arc::into_inner(state).unwrap();
// drop our sender, threads will break the loop after receiving and processing
drop(sender);
// wait for the thread to finish
handle.join().expect("failed to join async appender thread");
}
}
}

The async appender threads would be joined on dropped or exit.

Not quite sure if flush should block on completion.

@TennyZhuang
Copy link
Contributor Author

pub(crate) fn destroy(&self) {
if let Some(state) = self.0.swap(None) {
// SAFETY: state has always one strong count before swapped.
let State { sender, handle } = Arc::into_inner(state).unwrap();
// drop our sender, threads will break the loop after receiving and processing
drop(sender);
// wait for the thread to finish
handle.join().expect("failed to join async appender thread");
}
}
}

The async appender threads would be joined on dropped or exit.

Not quite sure if flush should block on completion.

Not sure, but it seems somewhat unexpected that an operation named flush might not succeed.

@tisonkun
Copy link
Contributor

tisonkun commented Oct 20, 2025

pub(crate) fn destroy(&self) {
if let Some(state) = self.0.swap(None) {
// SAFETY: state has always one strong count before swapped.
let State { sender, handle } = Arc::into_inner(state).unwrap();
// drop our sender, threads will break the loop after receiving and processing
drop(sender);
// wait for the thread to finish
handle.join().expect("failed to join async appender thread");
}
}
}

The async appender threads would be joined on dropped or exit.
Not quite sure if flush should block on completion.

Not sure, but it seems somewhat unexpected that an operation named flush might not succeed.

This is generally how Async appender implements, such as log4j etc.

cc @SpriteOvO you may be interetsed in this API design topic :D

Comment on lines +65 to +71
match done.recv() {
Ok(Ok(())) => Ok(()),
Ok(Err(err)) => Err(err),
Err(_) => Err(Error::new(
"async appender worker exited before completing flush",
)),
}
Copy link

@SpriteOvO SpriteOvO Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I missed some context, but isn't the point of async appender not to block the current thread? If we wait for the flushing result here wouldn't it be no different than sync appender?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding, flush always represents persistence. async only indicates that operations like write are asynchronous.

Since this is the expected behavior, I will close this PR.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I get your point, the meaning of the term "async" is varied a little bit in different contexts -- performs in a different thread, or not to block the current thread, or more meanings such as co-routines related, etc.

IMHO, the "async" in logging is more in favor of not blocking the current thread thus minimizing the performance impact and making sure that the logs are delivered eventually as much as possible.

Appreciate your contribution attempt anyway! :)

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.

3 participants