DeferredTaskManager 13.0.4

There is a newer version of this package available.
See the version list below for details.
dotnet add package DeferredTaskManager --version 13.0.4
                    
NuGet\Install-Package DeferredTaskManager -Version 13.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DeferredTaskManager" Version="13.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DeferredTaskManager" Version="13.0.4" />
                    
Directory.Packages.props
<PackageReference Include="DeferredTaskManager" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DeferredTaskManager --version 13.0.4
                    
#r "nuget: DeferredTaskManager, 13.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=DeferredTaskManager&version=13.0.4
                    
Install DeferredTaskManager as a Cake Addin
#tool nuget:?package=DeferredTaskManager&version=13.0.4
                    
Install DeferredTaskManager as a Cake Tool

ru

Event-driven Deferred Task Manager C#

NuGet version (DeferredTaskManager)

The implementation allows you to use multiple background tasks (or "runners") for deferred processing of consolidated data. Runners are based on the PubSub template for asynchronous waiting for new tasks, which makes this approach more reactive but less resource-intensive.

Distinctive advantage

The solution allows data consolidation in the current instance with the possibility of variable deduplication or any other operations at the discretion of the developer, which can reduce resources during further transmission and processing, as well as increase performance.

<p align="center"> <img align="center" src="https://github.com/user-attachments/assets/ea3386f1-ce13-4542-b343-d24da7f25853" width=60%> </p>

It was applied in my work on scaling WebSockets within the framework of a microservice architecture, where consolidation and deduplication of events before they were sent directly had an important impact on performance and resource consumption by reducing overhead costs.

<p align="center"> <img align="center" src="https://github.com/user-attachments/assets/a525f458-a9ce-400f-84ca-9f405d08f72a" width=60%> </p>

The initial task of developing the solution was to exclude from the execution time of the main operation (request) the time for sending some events to third-party microservices. With the introduction of DeferredTaskManager, this task has been solved. In addition, the max(n) requests from each client instance were significantly reduced due to data consolidation and deduplication on their side. For example, instead of 1000 individual requests, only one will be sent, which reduces overhead costs in all areas and increases the overall system performance.

If the delivery process is multi-stage, and the server is also a client in the future (for example, it plays the role of a hub), using DeferredTaskManager in it is also advisable.

Usage example

1️⃣ Injection of the Singleton dependency with the required data type

As an example, DeferredTaskManager is registered in DI with the string type:

services.AddDeferredTaskManager<string>(options =>
{
    options.PoolSize = Environment.ProcessorCount;
    options.CollectionType = CollectionType.Bag;
    options.SendDelayOptions = new SendDelayOptions()
    {
        MillisecondsSendDelay = 60000,
        ConsiderDifference = true
    };
    options.RetryOptions = new RetryOptions<string>
    {
        RetryCount = 3,
        MillisecondsRetryDelay = 10000,
    };
});
PoolSize — pool size (number of available runners)

The pool size is variable and is selected by the developer for a specific range of tasks, focusing on the speed of execution and the amount of resources consumed.

CollectionType — collection type

You can also specify the collection type, «Bag» for the Unordered collection of objects (it works faster) or «Queue» for the Ordered collection of objects. It is advisable to use «Queue» only if PoolSize = 1, otherwise the execution order is not guaranteed.

SendDelayOptions — setting up sending events at a time interval

Configures the sending of added events for processing after a certain period of time with the possibility of variable deduction of the time of the previous operation. It makes sense to specify when the send Events = false flag is used when adding events, which adds events without sending them for processing.

RetryOptions — configuring exception handling

You can also specify parameters for repeated attempts to process events in case of exceptions.

Modules and their redefinition

The solution consists of 5 modules, each of which registers in DI.

IDeferredTaskManagerService is the public interface of the main module, which implements the main public methods for working with the deferred task manager.

The following public interfaces are used for internal interaction:

IEventSender — responsible for creating runners and contains the logic of their behavior.

IEventStorage is a layer for interacting with the event repository.

IStorageStrategy — used to implement event storage.

IPoolPubSub — performs lending with a pool of background runners.

All these interfaces are public, but, in fact, only the IDeferredTaskManagerService is used for external interaction. The implementation of each module can be redefined by adding its own dependencies and logic (which is why they also have public interfaces).

You can redefine modules by sending custom types to the DI services.AddDeferredTaskManager registration method. The type being redefined must be inherited from one of the above public interfaces.

2️⃣ Creating a Background Service

An example is the creation of a background service for DeferredTaskManager<string>:

internal sealed class EventManagerService : BackgroundService
{
    private readonly IDeferredTaskManagerService<string> _deferredTaskManager;

    public EventManagerService(IDeferredTaskManagerService<string> deferredTaskManager)
    {
        _deferredTaskManager = deferredTaskManager;
    }

    protected override Task ExecuteAsync(CancellationToken cancellationToken)
    {
       // A delegate for custom logic that receives events from running runners.
       // As an example, events are concatenated in it,
       // but any other variable processing or sending is possible.
       Func<List<string>, CancellationToken, Task> eventConsumer = async (events, cancellationToken) =>
        {
            // Concatenation of events
            var concatenatedEvents = string.Join(",", events);

            //throw new Exception("Test exception");

            // Any further processing/sending of concatenated events
            Thread.Sleep(1000);
            await Task.Delay(1000, cancellationToken);        
        };
        
        // The delegate where we get to in case of exceptions in the main eventConsumer delegate
        Func<List<string>, Exception, int, CancellationToken, Task> eventConsumerRetryExhausted = async (events, ex, retryCount, cancellationToken) =>
        {
            Console.WriteLine($"Repeat number: {retryCount}; {ex}");
        };

        return _deferredTaskManager.StartAsync(eventConsumer, eventConsumerRetryExhausted, cancellationToken);
    }
}
EventConsumer is the main delegate for custom logic

All custom logic is placed in the EventConsumer delegate, which receives a collection of consolidated events. This is where you can perform the necessary operations on them before further transmission/processing. You can also handle exceptions in the delegate (this is important if events are handled separately) by sending unprocessed events to the next session after the MillisecondsRetryDelay time delay specified in the parameters. In the example above, the delegate concatenates incoming events from running runners.

You can add your own exception handling logic to it, which can be useful if events are processed separately: successfully completed events can be deleted from the main event collection, then incomplete events will go to retry.

try
{
    // Custom operation on received events

    // Test exception
    throw new Exception("Test exception");     
}
catch (Exception ex)
{
    // Any custom logic (logging, etc.)

    // In case of event handling separately,
    // you can delete successfully completed events from the event collection,
    // then the unfinished events will go to retry
    events.RemoveRange(successEvents);   
}
EventConsumerRetryExhausted — delegate for exception handling

Optionally, you can specify this delegate, which gets into in case of exceptions in the main delegate EventConsumer. Logging and other custom operations can be performed in it.

3️⃣ Getting an embedded dependency and implementing the addition of event(s)

_deferredTaskManager.Add(events);

Alternative uses

The DeferredTaskManager can be used as a regular event store, receiving events on demand using the GetEventsAndClearStorage method, bypassing runners, or sending available events to a delegate to any available runner on demand using the SendEvents method.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.1

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
14.0.0 142 12 days ago
13.0.5 140 18 days ago
13.0.4 137 18 days ago
13.0.3 136 18 days ago
13.0.2 178 a month ago
13.0.1 191 a month ago
13.0.0 154 a month ago
12.0.1 200 a month ago 12.0.1 is deprecated because it is no longer maintained and has critical bugs.
12.0.0 201 a month ago 12.0.0 is deprecated because it is no longer maintained and has critical bugs.
11.0.0 213 2 months ago 11.0.0 is deprecated because it is no longer maintained and has critical bugs.
10.0.0 218 2 months ago 10.0.0 is deprecated because it is no longer maintained and has critical bugs.
9.2.0 228 2 months ago 9.2.0 is deprecated because it is no longer maintained and has critical bugs.
9.1.0 455 5 months ago 9.1.0 is deprecated because it is no longer maintained and has critical bugs.
9.0.2 202 5 months ago 9.0.2 is deprecated because it is no longer maintained and has critical bugs.
9.0.1 200 5 months ago 9.0.1 is deprecated because it is no longer maintained and has critical bugs.
9.0.0 188 5 months ago 9.0.0 is deprecated because it is no longer maintained and has critical bugs.
8.1.0 204 5 months ago 8.1.0 is deprecated because it is no longer maintained and has critical bugs.
8.0.3 200 5 months ago 8.0.3 is deprecated because it is no longer maintained and has critical bugs.
8.0.2 353 6 months ago 8.0.2 is deprecated because it is no longer maintained and has critical bugs.
8.0.1 207 6 months ago 8.0.1 is deprecated because it is no longer maintained and has critical bugs.
8.0.0 252 6 months ago 8.0.0 is deprecated because it is no longer maintained and has critical bugs.
2.0.1 243 6 months ago 2.0.1 is deprecated because it is no longer maintained and has critical bugs.
2.0.0 228 6 months ago 2.0.0 is deprecated because it is no longer maintained and has critical bugs.