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

Performance analysis of IPipelineBehavior for validation #813

Closed
arslanaybars opened this issue Dec 26, 2022 · 8 comments
Closed

Performance analysis of IPipelineBehavior for validation #813

arslanaybars opened this issue Dec 26, 2022 · 8 comments
Labels

Comments

@arslanaybars
Copy link

arslanaybars commented Dec 26, 2022

I use IPipelineBehavior with FluentValidation for validating my requests.

Most of us probably already use ValidationBehavior like CleanArchitecture sample

But when I made a load test. I can receive three times as many responses without this pipeline behavior.

Example:

With IPipelineBehavior
image

Without IPipelineBehavior
image

I used k6 for load testing.

Is this expected result or are there any suggestions to make it better?

Here is my ValidationBehavior class

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators;
    }

    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        if (!_validators.Any())
        {
            return await next();
        }
        
        var context = new ValidationContext<TRequest>(request);

        var validationResults = await Task.WhenAll(
            _validators.Select(v => v.ValidateAsync(context, cancellationToken)));
                
        var failures = validationResults
            .SelectMany(r => r.Errors)
            .Where(f => f != null)
            .ToArray();

        if (failures.Any())
        {
            throw new ValidationException(failures);
        }

        return await next();
    }
}
@arslanaybars arslanaybars changed the title IPipeline behavior performance ideas IPipeline behavior performance analysis for validation Dec 26, 2022
@arslanaybars arslanaybars changed the title IPipeline behavior performance analysis for validation Performance analysis of IPipelineBehavior for validation Dec 26, 2022
@neozhu
Copy link

neozhu commented Dec 27, 2022

try this, do not use await


public  Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        var context = new ValidationContext<TRequest>(request);
        var failrules = _validators
            .Select(validator => validator.Validate(context))
            .SelectMany(result => result.Errors)
            .Where(failrules => failrules != null)
            .ToList();
        if (failrules.Count != 0)
        {
            throw new ValidationException(failrules);
        }
        return next();
    }



@arslanaybars
Copy link
Author

@neozhu I tried but it doesn't affect with a big difference

@kentaasvang
Copy link

If you're throwing a lot of exceptions during your stress-test than I would suspect that to be the culprit

@arslanaybars
Copy link
Author

I made the test in the happy path case so it means I don't throw any exceptions. 🤔

@PeterKneale
Copy link

PeterKneale commented Jan 5, 2023

What work is actually being performed in your handler? At just 3ms i'd be curious if you are actually reading / writing anything out of process.

I did a quick investigation into the perf impact of using something similar for both validation and logging some time back but after benchmarking found that if my handler was performing an out of process action like reading or writing to a database, cache or filesystem then the impact of the pipeline components was almost imperceptable compared to the overall duration (ie XXms for handler and XXXus for the pipeline).

This is inline with what Jimmy says more succintly here:
https://jimmybogard.com/you-probably-dont-need-to-worry-about-mediatr/

My handlers always hit a database or something, so anything MediatR does is a drop in the bucket

@jbogard
Copy link
Owner

jbogard commented Jan 31, 2023

See #828 , it might help with this as I won't register the built-in behaviors if they're not needed

@github-actions
Copy link

github-actions bot commented Apr 2, 2023

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions github-actions bot added the Stale label Apr 2, 2023
@github-actions
Copy link

This issue was closed because it has been stalled for 14 days with no activity.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Apr 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants