Orleans.SyncWork
0.0.1
See the version list below for details.
dotnet add package Orleans.SyncWork --version 0.0.1
NuGet\Install-Package Orleans.SyncWork -Version 0.0.1
<PackageReference Include="Orleans.SyncWork" Version="0.0.1" />
paket add Orleans.SyncWork --version 0.0.1
#r "nuget: Orleans.SyncWork, 0.0.1"
// Install Orleans.SyncWork as a Cake Addin #addin nuget:?package=Orleans.SyncWork&version=0.0.1 // Install Orleans.SyncWork as a Cake Tool #tool nuget:?package=Orleans.SyncWork&version=0.0.1
This project contains the abstraction of "Long Running CPU bound Synchronous work" in the form of an abstract base class SyncWorker; which implements an interface ISyncWorker.
When long running work is identified, you can extend the base class SyncWorker
, providing a TRequest
and TResponse
unique to the long running work. This allows you to create as many ISyncWork<TRequest, TResponse>
implementations as necessary, for all your long running CPU bound needs! (At least that is the hope.)
Basic "flow" of the SyncWork:
Start
- Poll
GetStatus
until aCompleted
orFaulted
status is received GetResult
orGetException
depending on theGetStatus
This package introduces a few "requirements" against Orleans:
In order to not overload Orleans, a LimitedConcurrencyLevelTaskScheduler is introduced. This task scheduler is registered (either manually or through the provided extension method) with a maximum level of concurrency for the silo being set up. This maximum concurrency MUST allow for idle threads, lest the Orleans server be overloaded. In testing, the general rule of thumb was
Environment.ProcessorCount - 2
max concurrency. The important part is that the CPU is not fully "tapped out" such that the normal Orleans asynchronous messaging can't make it through due to the blocking sync work - this will make things start timing out.Blocking grains are stateful, and are currently keyed on a Guid. If in a situation where multiple grains of long running work is needed, each grain should be initialized with its own unique identity.
Blocking grains likely CAN NOT dispatch further blocking grains. This is not yet tested under the repository, but it stands to reason that with a limited concurrency scheduler, the following scenario would lead to a deadlock:
- Grain A is long running
- Grain B is long running
- Grain A initializes and fires off Grain B
- Grain A cannot complete its work until it gets the results of Grain B
In the following scenario, if "Grain A" is "actively being worked" and it fires off a "Grain B", but "Grain A" cannot complete its work until "Grain B" finishes is, but "Grain B" cannot start its work until "Grain A" finishes its work, you've run into a situation where the limited concurrency task scheduler can never finish the work of "Grain A".
There may be a way to avoid the above scenario, but I have not yet deeply explored it.
Usage
Extend the base class to implement a long running grain (example: PasswordVerifier).
public class PasswordVerifier : SyncWorker<PasswordVerifierRequest, PasswordVerifierResponse>, IGrain
{
private readonly IPasswordVerifier _passwordVerifier;
public PasswordVerifier(
ILogger<PasswordVerifier> logger,
LimitedConcurrencyLevelTaskScheduler limitedConcurrencyLevelTaskScheduler,
IPasswordVerifier passwordVerifier) : base(logger, limitedConcurrencyLevelTaskScheduler)
{
_passwordVerifier = passwordVerifier;
}
protected override async Task<PasswordVerifierResponse> PerformWork(PasswordVerifierRequest request)
{
var verifyResult = await _passwordVerifier.VerifyPassword(request.PasswordHash, request.Password);
return new PasswordVerifierResponse()
{
IsValid = verifyResult
};
}
}
public class PasswordVerifierRequest
{
public string Password { get; set; }
public string PasswordHash { get; set; }
}
public class PasswordVerifierResponse
{
public bool IsValid { get; set; }
}
Run the grain:
var request = new PasswordVerifierRequest()
{
Password = "my super neat password that's totally secure because it's super long",
PasswordHash = "$2a$11$vBzJ4Ewx28C127AG5x3kT.QCCS8ai0l4JLX3VOX3MzHRkF4/A5twy"
}
var passwordVerifyGrain = grainFactory.GetGrain<ISyncWorker<PasswordVerifierRequest, PasswordVerifierResponse>>(Guid.NewGuid());
var result = await passwordVerifyGrain.StartWorkAndPollUntilResult(request);
The above StartWorkAndPollUntilResult
is an extension method defined in the package (SyncWorkerExtensions) that Start
s, Poll
s, and finally GetResult
or GetException
upon completed work. There would seemingly be place for improvement here as it relates to testing unexpected scenarios, configuration based polling, etc.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. 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. |
-
net6.0
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.0)
- Microsoft.Orleans.Core (>= 3.5.1)
- Microsoft.Orleans.Core.Abstractions (>= 3.5.1)
- Microsoft.Orleans.Runtime.Abstractions (>= 3.5.1)
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 |
---|---|---|
8.1.12 | 6,043 | 2/10/2024 |
8.0.3 | 374 | 1/18/2024 |
7.2.6 | 3,013 | 10/8/2023 |
7.1.3 | 5,925 | 1/8/2023 |
7.0.1 | 774 | 12/22/2022 |
1.5.9 | 4,107 | 1/7/2022 |
1.4.10 | 750 | 12/22/2021 |
1.3.7 | 713 | 12/14/2021 |
1.2.10 | 812 | 12/12/2021 |
1.1.9 | 1,393 | 11/29/2021 |
1.1.8-prerelease | 1,894 | 11/29/2021 |
1.0.4 | 705 | 11/22/2021 |
0.0.1 | 1,278 | 11/19/2021 |