Open gabyx opened 7 months ago
I think this is an excellent addition! Relying on an explicit mem::drop
doesn't always flush correctly in the presence of Logger::clone
.
I've wanted this for a while. Thank you for adding it :)
Right now, the thread:sleep
in Async::flush
seems surprising to me.
It's specific to the Async
logger. If the user switches to another logger like slog-term
, it would just flush the underlying .
I think there are two options to make the waiting more explicit. I would appreciate your input on them.
FlushStatus
Instead of a function that returns io::Result<()>
, it should return io::Result<FlushStatus>
:
enum FlushStatus {
Success,
QueuedEntries,
}
Then the user can call thread:sleep
if they really want to block until success.
FlushWait
enum FlushWait {
NoWait,
BlockUntilEmpty(Duration)
}
Then it's more explicit that passing a duration would cause the loop to block. Let me know what you think about these ideas.
@Techcable: Thanks for the review. Cool if its usable =). I was not yet sure about the function either. Your suggestion sounds very good. Both options are totally legit and better then what I provided!.
The only thing is can we make it such that the signature for an another flush
e.g. on term
also makes sense?
With both approaches you suggest there is still a slight mismatch, either QueuedEntries
(term
does not have that, and might just return Success
anyway), and NoWait
makes no sense for term
but BlockUntilEmpty(duration)
also not because duration
is probably obsolete. But I like the type-guarded input! or the output.
Maybe the first one makes more sense, however thinking about what you use in generic code is a Logger
where you dont know the underlying thing. So you also do not really care about QueuedEntries
.
A flush()
would be nicest, but does not work for Async
, so we need somekind of duration if we block inside.
On the other hand if we dont block inside and let the user handle this, its kind of weird that you can call flush
and then for some logger its not guaranteed that it happened and you need to run a loop.
Furthermore: What I do not quite understand so far ist:
The blocking loop which we do, does that really work?
since when you have a producing thread (which produces records fast) and a thread which calls flush
at the same time, the loop might never return, because there is never a window where the queue is empty???
Doesn't the queue need something like a DoFlush
message (?) which will increment a counting semaphore by 1 (or mutexed integer) which then gets handled by the worker which then decreases the counting semaphore by 1 (until 0), the flush
awaits just till the counter reached the level it had before atomically incrementing it, only then we know that all message before that DoFlush
message (timestamp wise) are flushed.
Queue: a,b,c,d,e
Flush Counter: 0
Some thread 1 requests a flush
Queue: Flush(0) a,b,c,d,e
Flush Counter: 1
Some more message arrive, some have been handled
Queue: k, j, l , Flush(0)
Flush Counter: 1
Some thread 2 requests a flush
Queue: Flush(1), k, j, l, Flush(0) a,b,c,d,e
Flush Counter: 2
Flush(0)
is handled == decrement the counter which notifies the thread.
Queue: Flush(1), k, j, l
Flush Counter: 1
Thread 1 continues because its flush has been handled.
Might that be a solution which makes more sense?
Closes: https://github.com/slog-rs/async/issues/35 This needs to be thoroughly reviewed, not sure if its good enough =)
io:Result<()>
could not be turned intoresult::Result<Self::Ok, Self::Err>
since that would need some more redirection or, or I useresult::Result<Self::Ok, Self::Err>
and returnOk(Self::Ok::new())
if possible in the defaultflush()
implementation.Make sure to: