Git Product home page Git Product logo

kinetic2's Introduction

Cloudsiders.Kinetic2

While admitting weakness may be a strength, adding resilience and trying again after failure is usually the more rewarding path ... said no one ever. Well, maybe Aaliyah did. Dust yourself off and try again!

Small project which uses a source generator to add Polly reslience pipelines logic to methods using just an attribute.

You can add a resilience pipeline with just the following code to your service method:

// the attribute will be used by the source generator
[ResiliencePipeline("NotificationServicePipeline")]
public async ValueTask SendNotification(Message notification, CancellationToken cancellation) { ... }

Polly policies support not only Http calls but may be added to almost any logic.

What does it do?

Consider the following code

internal interface INotificationService {
    ValueTask SendNotification(Message notification, CancellationToken cancellation);
}

internal sealed class NotificationService : INotificationService {
    public async ValueTask SendNotification(Message notification, CancellationToken cancellation) {
        // perform whatever happy path logic you have
        ...

    }
}

In your main logic, you register this service with the dependency injection container

builder.Services.AddTransient<INotificationService, NotificationService>();

and you inject it in some other service which orchestrates the logic

internal class UserOnboardingService(INotificationService _notificationService) {

    ValueTask OnboardUser(Request request, CancellationToken cancellation) {
        ...
        var message = ConstructMessage(request);
        await _notificationService.SendNotification(message, cancellation);
        ...
    }
}

note: I don't like primary constructors that much ...

Adding reslience ... how does it work?

First, you add a reference to Polly and possibly related Polly nugets like Polly.RateLimiting ...

    <PackageReference Include="Polly" Version="8.3.1" />
    <PackageReference Include="Polly.RateLimiting" Version="8.3.1" />

Register the reslience pipeline with the DI container

builder.Services.AddResiliencePipeline("NotificationServicePipeline", builder => {
    builder
        .AddRetry(new RetryStrategyOptions() { BackoffType = DelayBackoffType.Exponential, MaxRetryAttempts = 6, UseJitter = true })
        .AddConcurrencyLimiter(1, 5)
        .AddTimeout(TimeSpan.FromSeconds(10));
});

Then, add the reference to Kinetic2

    <PackageReference Include="Kinetic2.Analyzers" Version="1.0.2">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>

Just add the attribute to the method. You can apply it to the method in the class or the interface or both ...

internal sealed class NotificationService : INotificationService {
    [ResiliencePipeline("NotificationServicePipeline")]
    public async ValueTask SendNotification(Message notification, CancellationToken cancellation) {
        // perform whatever happy path logic you have
        // this logic may fail because it sends some message to a remote system or inserts a row into a database table or whatever
        ...

    }
}

No other changes are required.

What does it do?

for a DI registration that uses and interface and an implementation type (builder.Services.AddTransient<INotificationService, NotificationService>( _ => new NotificationService()), we

  • create a new type which implements the interface type
  • inject IServiceProvider and an instance of the original NotificationService implementation in the ctor
  • for each method which has the attribute applied, generate code which intercepts the call, resolves and executes the resilience pipeline and calls the original instance/method inside the pipeline
  • modify the DI registration to use the newly generated type instead of the original NotificationService (builder.Services.AddTransient<INotificationService, NewDerivedNotificationService>(...))

You can use factory functions in your DI registration, however, you must use the generic extension methods from Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions (which you will be most likely anyway - no hardship here. Non generic versions AddTransient(typeof(NotificationService)) or using ServiceDescriptor directly won't work).

We then register the newly generated type instead of the original type under the interface.

For a DI registration which does not use an interface (builder.Services.AddTransient<NotificationService>()), we a) derive a type from NotificationService and implement the interception in this derived type. NotificationService must not be sealed and the method must be virtual and not sealed in this case.

It doesn't support anything, but it should

  1. applying the attribute to default implementations in interfaces
  2. applying the attribute somewhere in the inheritance hierarchy
  3. applying the attribute multiple times in inheritance hierarchy (it will use the most derived).

Generated source code

In your .csproj, add

<PropertyGroup>
    ...    
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
    ...
</PropertyGroup>    

you will be able to see the generated source code under the ./obj/$(Configuration)/$(TargetFramework)/generated/Kinetic2.Analyzers/Kinetic2.Analyzers.K2PollyGenerator

Anything else?

This is a small sample subset from a larger project injecting more complex logic (distributed, reliable, durable workflows).

kinetic2's People

Contributors

dlosch avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.