Skip to content

Commit

Permalink
Specify default action generator for hedging (#1263)
Browse files Browse the repository at this point in the history
  • Loading branch information
martintmk authored Jun 8, 2023
1 parent e3bf107 commit 9a3bf0f
Show file tree
Hide file tree
Showing 19 changed files with 185 additions and 80 deletions.
2 changes: 1 addition & 1 deletion src/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static ResilienceStrategy<string> CreateHedging()
builder.AddHedging(new HedgingStrategyOptions<string>
{
ShouldHandle = args => new ValueTask<bool>(args.Result == Failure),
HedgingActionGenerator = args => () => Task.FromResult("hedged response"),
HedgingActionGenerator = args => () => new ValueTask<Outcome<string>>(new Outcome<string>("hedged response")),
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public async Task TryWaitForCompletedExecutionAsync_TwiceWhenSecondaryGeneratorR
await LoadExecutionAsync(context);
await LoadExecutionAsync(context);

Generator = args => () => Task.FromResult(new DisposableResult { Name = "secondary" });
Generator = args => () => new DisposableResult { Name = "secondary" }.AsOutcomeAsync();

var task = await context.TryWaitForCompletedExecutionAsync(TimeSpan.Zero);
task!.Type.Should().Be(HedgedTaskType.Primary);
Expand Down Expand Up @@ -463,12 +463,15 @@ private void ConfigureSecondaryTasks(params TimeSpan[] delays)
{
args.Context.Properties.Set(new ResiliencePropertyKey<int>(attempt.ToString(CultureInfo.InvariantCulture)), attempt);
await _timeProvider.Delay(delays[attempt], args.Context.CancellationToken);
return new DisposableResult(delays[attempt].ToString());
return new DisposableResult(delays[attempt].ToString()).AsOutcome();
};
};
}

private Func<HedgingActionGeneratorArguments<DisposableResult>, Func<Task<DisposableResult>>?> Generator { get; set; } = args => () => Task.FromResult(new DisposableResult { Name = Handled });
private Func<HedgingActionGeneratorArguments<DisposableResult>, Func<ValueTask<Outcome<DisposableResult>>>?> Generator { get; set; } = args =>
{
return () => new DisposableResult { Name = Handled }.AsOutcomeAsync();
};

private HedgingExecutionContext Create()
{
Expand Down
9 changes: 6 additions & 3 deletions src/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public async Task Initialize_Secondary_Ok(string value, bool handled)
{
AssertSecondaryContext(args.Context, execution);
args.Attempt.Should().Be(4);
return () => Task.FromResult(new DisposableResult { Name = value });
return () => new DisposableResult { Name = value }.AsOutcomeAsync();
};

(await execution.InitializeAsync<DisposableResult, string>(HedgedTaskType.Secondary, _snapshot, null!, "dummy-state", 4)).Should().BeTrue();
Expand Down Expand Up @@ -151,7 +151,7 @@ public async Task Initialize_Cancelled_EnsureRespected(bool primary)
return async () =>
{
await _timeProvider.Delay(TimeSpan.FromDays(1), args.Context.CancellationToken);
return new DisposableResult { Name = Handled };
return new DisposableResult { Name = Handled }.AsOutcome();
};
};

Expand Down Expand Up @@ -271,7 +271,10 @@ private void CreateSnapshot(CancellationToken? token = null)
_snapshot.OriginalProperties.Set(_myKey, "dummy-value");
}

private Func<HedgingActionGeneratorArguments<DisposableResult>, Func<Task<DisposableResult>>?> Generator { get; set; } = args => () => Task.FromResult(new DisposableResult { Name = Handled });
private Func<HedgingActionGeneratorArguments<DisposableResult>, Func<ValueTask<Outcome<DisposableResult>>>?> Generator { get; set; } = args =>
{
return () => new DisposableResult { Name = Handled }.AsOutcomeAsync();
};

private TaskExecution Create() => new(_hedgingHandler.CreateHandler()!, CancellationTokenSourcePool.Create(TimeProvider.System));
}
23 changes: 13 additions & 10 deletions src/Polly.Core.Tests/Hedging/HedgingActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,34 @@ public HedgingActions(TimeProvider timeProvider)
};
}

public Func<HedgingActionGeneratorArguments<string>, Func<Task<string>>?> Generator { get; }
public Func<HedgingActionGeneratorArguments<string>, Func<ValueTask<Outcome<string>>>?> Generator { get; }

public Func<HedgingActionGeneratorArguments<string>, Func<Task<string>>?> EmptyFunctionsProvider { get; } = args => null;
public Func<HedgingActionGeneratorArguments<string>, Func<ValueTask<Outcome<string>>>?> EmptyFunctionsProvider { get; } = args => null;

public List<Func<ResilienceContext, Task<string>>> Functions { get; }
public List<Func<ResilienceContext, ValueTask<Outcome<string>>>> Functions { get; }

private async Task<string> GetApples(ResilienceContext context)
private async ValueTask<Outcome<string>> GetApples(ResilienceContext context)
{
await _timeProvider.Delay(TimeSpan.FromSeconds(10), context.CancellationToken);
return "Apples";
return "Apples".AsOutcome();
}

private async Task<string> GetPears(ResilienceContext context)
private async ValueTask<Outcome<string>> GetPears(ResilienceContext context)
{
await _timeProvider.Delay(TimeSpan.FromSeconds(3), context.CancellationToken);
return "Pears";
return "Pears".AsOutcome();
}

private async Task<string> GetOranges(ResilienceContext context)
private async ValueTask<Outcome<string>> GetOranges(ResilienceContext context)
{
await _timeProvider.Delay(TimeSpan.FromSeconds(2), context.CancellationToken);
return "Oranges";
return "Oranges".AsOutcome();
}

public static Func<HedgingActionGeneratorArguments<string>, Func<Task<string>>?> GetGenerator(Func<ResilienceContext, Task<string>> task) => args => () => task(args.Context);
public static Func<HedgingActionGeneratorArguments<string>, Func<ValueTask<Outcome<string>>>?> GetGenerator(Func<ResilienceContext, ValueTask<Outcome<string>>> task)
{
return args => () => task(args.Context);
}

public int MaxHedgedTasks => Functions.Count + 1;
}
24 changes: 12 additions & 12 deletions src/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ public void SetHedging_Empty_Discarded()
.SetHedging<int>(handler =>
{
handler.ShouldHandle = _ => PredicateResult.True;
handler.HedgingActionGenerator = args => () => Task.FromResult(10);
handler.HedgingActionGenerator = args => () => 10.AsOutcomeAsync();
})
.SetVoidHedging(handler =>
{
handler.ShouldHandle = _ => PredicateResult.True;
handler.HedgingActionGenerator = args => () => Task.CompletedTask;
handler.HedgingActionGenerator = args => () => default;
});

handler.IsEmpty.Should().BeFalse();
Expand All @@ -75,7 +75,7 @@ public async Task SetHedging_Ok()
var handler = new HedgingHandler()
.SetHedging<int>(handler =>
{
handler.HedgingActionGenerator = args => () => Task.FromResult(0);
handler.HedgingActionGenerator = args => () => 0.AsOutcomeAsync();
handler.ShouldHandle = args => new ValueTask<bool>(args.Result == -1);
})
.CreateHandler();
Expand All @@ -87,11 +87,11 @@ public async Task SetHedging_Ok()

handler.HandlesHedging<int>().Should().BeTrue();

var action = handler.TryCreateHedgedAction<int>(context, 0);
var action = handler.TryCreateHedgedAction(context, 0, context => 0.AsOutcomeAsync());
action.Should().NotBeNull();
(await (action!()!)).Should().Be(0);
(await (action!()!)).Result.Should().Be(0);

handler.TryCreateHedgedAction<double>(context, 0).Should().BeNull();
handler.TryCreateHedgedAction(context, 0, context => 0.0.AsOutcomeAsync());
}

[InlineData(true)]
Expand All @@ -111,7 +111,7 @@ public async Task SetVoidHedging_Ok(bool returnsNullAction)
return null;
}

return () => Task.CompletedTask;
return () => default;
};
handler.ShouldHandle = args => new ValueTask<bool>(args.Exception is InvalidOperationException);
})
Expand All @@ -124,15 +124,15 @@ public async Task SetVoidHedging_Ok(bool returnsNullAction)

handler.HandlesHedging<VoidResult>().Should().BeTrue();

var action = handler.TryCreateHedgedAction<VoidResult>(ResilienceContext.Get(), 0);
var action = handler.TryCreateHedgedAction<VoidResult>(ResilienceContext.Get(), 0, _ => VoidResult.Instance.AsOutcomeAsync());
if (returnsNullAction)
{
action.Should().BeNull();
}
else
{
action.Should().NotBeNull();
(await (action!()!)).Should().Be(VoidResult.Instance);
(await (action!()!)).Result.Should().Be(VoidResult.Instance);
}
}

Expand All @@ -143,15 +143,15 @@ public async Task ShouldHandleAsync_UnknownResultType_Null()
var handler = new HedgingHandler()
.SetHedging<int>(handler =>
{
handler.HedgingActionGenerator = args => () => Task.FromResult(0);
handler.HedgingActionGenerator = args => () => new Outcome<int>(0).AsValueTask();
handler.ShouldHandle = args => new ValueTask<bool>(args.Exception is InvalidOperationException);
})
.SetHedging<string>(handler =>
{
handler.HedgingActionGenerator = args =>
{
args.Context.Should().NotBeNull();
return () => Task.FromResult("dummy");
return () => "dummy".AsOutcomeAsync();
};

handler.ShouldHandle = args => new ValueTask<bool>(args.Exception is InvalidOperationException);
Expand All @@ -161,6 +161,6 @@ public async Task ShouldHandleAsync_UnknownResultType_Null()
var args = new HandleHedgingArguments();
(await handler!.ShouldHandleAsync<double>(new(context, new Outcome<double>(new InvalidOperationException()), args))).Should().BeFalse();
handler.HandlesHedging<double>().Should().BeFalse();
handler.TryCreateHedgedAction<double>(ResilienceContext.Get(), 0).Should().BeNull();
handler.TryCreateHedgedAction<double>(ResilienceContext.Get(), 0, _ => 0.0.AsOutcomeAsync()).Should().BeNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void AddHedging_Generic_Ok()
{
_genericBuilder.AddHedging(new HedgingStrategyOptions<string>
{
HedgingActionGenerator = args => () => Task.FromResult("dummy"),
HedgingActionGenerator = args => () => "dummy".AsOutcomeAsync(),
ShouldHandle = _ => PredicateResult.True
});
_genericBuilder.Build().Strategy.Should().BeOfType<HedgingResilienceStrategy>();
Expand Down Expand Up @@ -73,10 +73,10 @@ public async Task AddHedging_IntegrationTest()

if (args.Attempt == 3)
{
return "success";
return "success".AsOutcome();
}

return "error";
return "error".AsOutcome();
};
};
}),
Expand Down
Loading

0 comments on commit 9a3bf0f

Please sign in to comment.