Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fulfills #20 - add option to block when the queue is full, instead of dropping events #21

Merged
merged 3 commits into from
Aug 7, 2017
Merged

Conversation

cocowalla
Copy link
Contributor

Also, use GetConsumingEnumerable to enumerate the queue, instead of a while loop

Also, use `GetConsumingEnumerable` to enumerate the queue, instead of a `while` loop
@cocowalla cocowalla changed the title Fulfills #20. Add option to block when the queue is full, instead of dropping events Fulfills #20 - add option to block when the queue is full, instead of dropping events Aug 3, 2017
Copy link
Member

@nblumhardt nblumhardt left a comment

Choose a reason for hiding this comment

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

Looks great. Taking a while for me to get my head back int the space of this one - a couple of questions/comments. Cheers!

}

[Fact]
public async Task WhenEmitSingle_ThenRelaysToInnerSink()
{
var logEvent = CreateEvent();
_sink.Emit(logEvent);
_sink.Dispose();
Copy link
Member

Choose a reason for hiding this comment

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

I think the goal of this test is to check the normal operation (not dispose/flush) case; should still pass without Dispose() here.

readonly MemorySink _innerSink;
readonly BackgroundWorkerSink _sink;
BackgroundWorkerSink _sink;
Copy link
Member

Choose a reason for hiding this comment

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

Since some tests now use _sink as initialized, and others replace it, perhaps it would be clearer to get rid of this field altogether and use something like:

var sink = CreateWithDefaultOptions();

in each test that needs it, where the method would just be a shortcut to new BackgroundWorkerSink(_logger, 10000, false)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, BackgroundWorkerSinkSpec implements IDisposable so it has a Dispose() method which dispoes of the existing _sink field. If we still want to properly dispose of the sink that would mean wrapping creation in a using block in every test, e.g.:

using (var sink = CreateWithDefaultOptions())
{
  // Test here
}

This adds quite a bit of monkey code to each test - do you still want to do it that way, or stick with the way it is?

Copy link
Member

Choose a reason for hiding this comment

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

Good point. I think I'd still probably prefer the using block to the mutated field - easier to refactor, and less opportunity for odd behavior (e.g. in the current version of the PR, we don't dispose _sink before replacing it).


// If we *weren't* dropped events, the delay in the inner sink would mean the 5 events would take
// at least 15 seconds to process
await Task.Delay(TimeSpan.FromSeconds(18));
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps this could be scaled down into 200 ms steps, rather than 2 seconds, so that it doesn't take so long to run the tests? 18 seconds here plus 12 below adds quite a bit to testing turnaround.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. The existing code was using a 3s delay somewhere, so I just did the same without thinking about it.


public void Emit(LogEvent logEvent)
{
if (DelayEmit)
Task.Delay(TimeSpan.FromSeconds(3)).Wait();
Copy link
Member

Choose a reason for hiding this comment

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

Could bool DelayEmit become TimeSpan? EmitDelay?

if (_disposed)
return;

if (!this._blockWhenFull)
Copy link
Member

Choose a reason for hiding this comment

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

Inverting the condition here to if (_blockWhenFull) would make the code read a little more smoothly.

Nit: don't need the explicit this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry about the this- I generally use this in my own code with no underscore prefix for fields. Was trying to follow Serilog style, but I guess Resharper slipped one through for me :)

}

public void Dispose()
{
_disposed = true;
_cancel.Cancel();
_queue.CompleteAdding();
Copy link
Member

Choose a reason for hiding this comment

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

Not sure why this wasn't used originally - looks like a good change to make. Can we grab some benchmark output from dev vs the PR branch, just to make sure perf wasn't an issue with this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, it's late now so will add something tomorrow

Choose a reason for hiding this comment

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

The issue with this one is that TryAdd can potentially throw with InvalidOperationException on some race condition

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@skomis-mm yes, you're right - it's probably best to just do away with the disposed check in Emit and wrap it in a try...catch for InvalidOperationException

/// <returns>A <see cref="LoggerConfiguration"/> allowing configuration to continue.</returns>
public static LoggerConfiguration Async(
this LoggerSinkConfiguration loggerSinkConfiguration,
Action<LoggerSinkConfiguration> configure,
int bufferSize = 10000)
int bufferSize = 10000,
bool blockWhenFull = false)
Copy link
Member

Choose a reason for hiding this comment

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

Since this is used by quite a lot of downstream packages, we'll need to follow the old pattern of adding an overload with the old parameter set, marked [EditorBrowsable(EditorBrowsableState.Never)] with no default values applied.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I figured using a default would preserve current behaviour for existing users. Wasn't aware of that attribute

Response to review of #21
@cocowalla
Copy link
Contributor Author

Before and after benchmarks

@nblumhardt
Copy link
Member

Added some thoughts on the test factoring; I still need a quiet moment to sit and think through the implementation thoroughly, but all seems good as far as I can see right now 👍

Instantiate sink in in test instead. Response to feedback in #21
@nblumhardt nblumhardt merged commit dd2e03e into serilog:dev Aug 7, 2017
@nblumhardt
Copy link
Member

Awesome 👍 - will be published as 1.2.0-dev-nnnnn shortly.

@bartelink
Copy link
Member

See also PR for monitoring:- #29 - I added an example to the README for this, so it would be good to get suggestions for improvement.

This pull request was closed.
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.

4 participants