Skip to content

[Proposal] Should we keep or remove the non-generic ResilienceStrategy #1429

@martintmk

Description

@martintmk

Recent enhancements to ResilienceStrategy<T> (#1428) have significantly improved the usability of generic resilience strategies, expanding use-case scenarios:

ResilienceStrategy<object> strategy = NullResilienceStrategy<object>.Instance;

int v1 = strategy.Execute(() => 10);
string v2 = strategy.Execute(() => "val");
bool v3 = strategy.Execute(() => true);

This progress paves the way for potential removal of the non-generic ResilienceStrategy type and associated infrastructure. This proposal brings both advantages and disadvantages that should be thoroughly assessed before making a decision.

Advantages

  1. Reduced API Surface: This major advantage simplifies maintenance efforts.
  2. Inherent Support for Reactive Strategies: Currently, reactive strategies utilize OutcomeResilienceStrategy as a base class, bridging ResilienceStrategy to ResilienceStrategy<T>. This change would render the bridge class obsolete, integrating support for reactive strategies directly.

Disadvantages

  1. Complex Strategy Composition: The existing resilience strategy implements ResilienceStrategy, which lacks a generic constraint, facilitating strategy composition. If non-generic strategies are removed, the new ResilienceStrategy<T> signature would restrict composability to strategies of T or ResilienceStrategy<object>.
  2. Imposed Constraints on Non-Reactive Strategies: Strategies such as Timeout and RateLimiter are indifferent to result types and can naturally be executed across all result types. Under the new model, these strategies would no longer have this "natural implementation."
  3. Lack of Inherent Support for Void Result Types: The removal of ResilienceStrategy would eliminate a straightforward method for executing void-based callbacks. While a VoidResult type could be introduced, usage would be more complex than the existing system.
  4. Complicated Usage of ResilienceStrategy: Presently, it's simple to pass and flow ResilienceStrategy in the API for basic scenarios or when exceptions are exclusively handled by the consumer. In the proposed model, passing ResilienceStrategy<object> feels less intuitive.

Current Usage:

ResilienceStrategy strategy = new ResilienceStrategyBuilder()
    .AddTimeout(TimeSpan.FromSeconds(10))
    .AddRetry(new RetryStrategyOptions
    {
        RetryCount = 4,
        BaseDelay = TimeSpan.FromSeconds(10),
    })
    .Build();

ResilienceStrategyRegistry<string> registry = new ResilienceStrategyRegistry<string>();
ResilienceStrategy strategyFromRegistry = registry.GetStrategy("my-strategy");

strategy.Execute(() => "dummy");
strategy.Execute(() => 10);
strategy.Execute(() => { SomeCall(); });

New usage:

ResilienceStrategy<object> strategy = new ResilienceStrategyBuilder<object>()
    .AddTimeout(TimeSpan.FromSeconds(10))
    .AddRetry(new RetryStrategyOptions<object>
    {
        RetryCount = 4,
        BaseDelay = TimeSpan.FromSeconds(10),
    })
    .Build();

ResilienceStrategyRegistry<string> registry = new ResilienceStrategyRegistry<string>();
ResilienceStrategy<object> strategyFromRegistry = registry.GetStrategy<object>("my-strategy");

strategy.Execute(() => "dummy");
strategy.Execute(() => 10);
strategy.Execute(() => { SomeCall(); return VoidResult.Instance; });

Contributes to #1365

Would love your feedback and insights on this matter before we proceed with any modifications, as these changes could significantly affect the usability of V8.

@martincostello
@joelhulen
@juraj-blazek
@geeknoid
@PeterCsalaHbo
@andrey-noskov
@vany0114

Metadata

Metadata

Assignees

No one assigned

    Labels

    community feedback wantedv8Issues related to the new version 8 of the Polly library.

    Type

    No type

    Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions