Skip to content

Race condition in option validation for StandardResilienceHandler #4917

Closed

Description

@NatMarchand

Description

My unit tests randomly crash while starting the host due to a InvalidCastException in option validation for standard resilience handler.

Reproduction Steps

Hard to reproduce as it is a race condition and occurs randomly.

However my tests looks like something like this :

        await new HostBuilder()
            .ConfigureDefaults([])
            .ConfigureServices(services =>
            {
                services.AddHttpClient<FooClient>()
                    .AddStandardResilienceHandler();
                services.AddHttpClient<BarClient>()
                    .AddStandardResilienceHandler();
            })
            .StartAsync();

Expected behavior

Works correclty

Actual behavior

The current exception

fail: Microsoft.Extensions.Hosting.Internal.Host[11]
      Hosting failed to start
      System.InvalidCastException: Unable to cast object of type 'System.TimeSpan' to type 'System.String'.
         at __OptionValidationGeneratedAttributes.<Validators_g>F8B1F3D42962A35D8FF1B1489612AEF6C36F3713335EFF79DB68A25973333C495____SourceGen__RangeAttribute.IsValid(Object value)
         at System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(Object value, ValidationContext validationContext)
         at System.ComponentModel.DataAnnotations.ValidationAttribute.GetValidationResult(Object value, ValidationContext validationContext)
         at System.ComponentModel.DataAnnotations.Validator.TryValidate(Object value, ValidationContext validationContext, ValidationAttribute attribute, ValidationError& validationError)
         at System.ComponentModel.DataAnnotations.Validator.GetValidationErrors(Object value, ValidationContext validationContext, IEnumerable`1 attributes, Boolean breakOnFirstError)
         at System.ComponentModel.DataAnnotations.Validator.TryValidateValue(Object value, ValidationContext validationContext, ICollection`1 validationResults, IEnumerable`1 validationAttributes)
         at Microsoft.Extensions.Http.Resilience.__HttpRetryStrategyOptionsValidator__.Validate(String name, HttpRetryStrategyOptions options)
         at Microsoft.Extensions.Http.Resilience.Internal.Validators.HttpStandardResilienceOptionsValidator.Validate(String name, HttpStandardResilienceOptions options)
         at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
         at Microsoft.Extensions.Options.OptionsMonitor`1.<>c.<Get>b__10_0(String name, IOptionsFactory`1 factory)
         at Microsoft.Extensions.Options.OptionsCache`1.<>c__DisplayClass3_1`1.<GetOrAdd>b__2()
         at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
         at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
         at System.Lazy`1.CreateValue()
         at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd[TArg](String name, Func`3 createOptions, TArg factoryArgument)
         at Microsoft.Extensions.Options.OptionsMonitor`1.Get(String name)
         at Microsoft.Extensions.DependencyInjection.OptionsBuilderExtensions.<>c__DisplayClass0_1`1.<ValidateOnStart>b__1()
         at Microsoft.Extensions.Options.StartupValidator.Validate()
         at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)

Regression?

No response

Known Workarounds

No response

Configuration

dotnet 8
Microsoft.Extensions.Http.Resilience Version="8.1.0"

Other information

I've been able to debug the exception and walk through the stack.
As you can see we are in the !IsInitialized if branch even though the IsInitialized is true, an other thread must have set it concurrently and then Minimum and Maximum are already Timespan, not strings.
image

Activity

  1. added
    bugThis issue describes a behavior which is not expected - a bug.
    on Feb 2, 2024
  2. NatMarchand commented on Feb 2, 2024

    @NatMarchand
    ContributorAuthor

    Ah! Just found that it was corrected in the source generator !
    dotnet/runtime#97045

  3. martincostello commented on Feb 13, 2024

    @martincostello
    Member

    The package with the fix for the source generator has been released today, 8.0.2, but I think we need a new version of the Microsoft.Extensions.Http.Resilience library built using that version of the source generator to propagate out the fix.

  4. NatMarchand commented on Feb 15, 2024

    @NatMarchand
    ContributorAuthor

    Looks like 8.2.0 contains the fix. Closing the issue !

  5. locked and limited conversation to collaborators on Mar 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-resiliencebugThis issue describes a behavior which is not expected - a bug.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions