Skip to content

Repository files navigation

Gaev.DurableTask is tiny library to build durable task, saga, process manager using the async/await capabilities. Main idea is to fit saga, process manager in one async method. Inspired by Azure Durable Task Framework.

Just imagine you can write regular code using the async/await capabilities which can last for long time, say 1 week or year. Moreover, if an application crashes the durable task will resume execution exactly from where it left.

A durable task must have some storage for storing current state in order to resume execution after restart/crash. There is MS SQL and file system based storage provider. However, you can implement your own provider, just implement IProcessStorage.

To estimate amount of used memory, a simple durable task was hosted in console application. As a result one instance of the durable task will use 4.3Kb in 32bit or 9KB in 64bit, so 250 000 instances will occupy 1Gb of 32bit console app.

Caveat: An exception type is limited due to serialization/deserialization. Any exception will be wrapped in ProcessException. So ProcessException is only one type allowed to catch in a durable task (see Rollback logic example below).

Let's look closer to the durable task. It is easier to show an example:

Saga, process managercomplete example

asyncTaskDurableTask(stringid,stringemail=null){using(varproc=_host.Spawn(id)){// Save email not to lose it if durable task resumesemail=awaitproc.Get(email,"SaveEmail");// Register the uservaruserId=awaitproc.Do(()=>_service.RegisterUser(email),"RegisterUser");// Generate a secret for email verificationvarsecret=awaitproc.Get(Guid.NewGuid(),"secret");// Send email to the user with the secret to verifyawaitproc.Do(()=>_service.VerifyEmail(email,secret),"VerifyEmail");// Wait when user receive verification email and send the secret here, it can take couple of daysawaitproc.Do(()=>_service.WaitForEmailVerification(secret),"WaitForEmailVerification");// Activate the user in the systemawaitproc.Do(()=>_service.ActivateUser(userId),"ActivateUser");}}

Schedulecomplete example

asyncTaskDurableTask(stringid,stringemail=null){using(varproc=_host.Spawn(id)){// Save email not to lose it if durable task resumesemail=awaitproc.Get(email,"SaveEmail");awaitproc.Do(()=>_smtp.Send(email,"Welcome!"),"Welcome");// Wait 1 monthawaitproc.Delay(TimeSpan.FromDays(30),"Wait1m");awaitproc.Do(()=>_smtp.Send(email,"Your 1st month with us. Congrats!"),"CongratsMonth");// Wait 11 monthsawaitproc.Delay(TimeSpan.FromDays(365-30),"Wait1y");awaitproc.Do(()=>_smtp.Send(email,"Your 1st year with us. Congrats!"),"CongratsYear");}}

Rollback logiccomplete example

asyncTaskDurableTask(stringid,stringsrcAccount=null,stringdestAccount=null,decimalamount=0){using(varproc=_host.Spawn(id)){// Save values not to lose it if durable task resumessrcAccount=awaitproc.Get(srcAccount,"SaveSrcAccount");destAccount=awaitproc.Get(destAccount,"SaveDestAccount");amount=awaitproc.Get(amount,"SaveAmount");varsrcTranId=Guid.Empty;vardestTranId=Guid.Empty;try{// Start transferring the moneysrcTranId=awaitproc.Do(()=>_service.StartTransfer(srcAccount,-amount),"StartTransfer1");destTranId=awaitproc.Do(()=>_service.StartTransfer(destAccount,+amount),"StartTransfer2");// Complete transferring the moneyawaitproc.Do(()=>_service.CompleteTransfer(srcAccount,srcTranId),"CompleteTransfer1");awaitproc.Do(()=>_service.CompleteTransfer(destAccount,destTranId),"CompleteTransfer2");}catch(ProcessExceptionex)when(ex.Type==nameof(TransferFailedException)){// Rollback logicif(srcTranId!=Guid.Empty)awaitproc.Do(()=>_service.RollbackTransfer(srcAccount,srcTranId),"RollbackTransfer1");if(destTranId!=Guid.Empty)awaitproc.Do(()=>_service.RollbackTransfer(destAccount,destTranId),"RollbackTransfer2");throw;}}}

Sending a message to the durable taskcomplete example

asyncTaskDurableTask(stringprocessId,stringcompanyId=null,stringcreditCard=null){using(varproc=_host.Spawn(processId).As<CreditCardProcess>()){companyId=awaitproc.Get(companyId,"1");creditCard=awaitproc.Get(creditCard,"2");Console.WriteLine($"CreditCardFlow is up for companyId={companyId} creditCard={creditCard}");varemail=awaitproc.Do(()=>GetEmail(companyId),"3");awaitproc.Do(()=>SendEmail(email,$"{creditCard} was assigned to you"),"4");varonCheckTime=proc.Delay(TimeSpan.FromMinutes(5),"5");varonFirstTransaction=proc.Do(()=>proc.OnTransactionAppeared(),"6");varonDeleted=proc.Do(()=>proc.OnCreditCardDeleted(),"7");Task.Run(async()=>{awaitonCheckTime;if(onDeleted.IsCompleted)return;if(!onFirstTransaction.IsCompleted)awaitproc.Do(()=>SendEmail(email,$"{creditCard} is inactive long time"),"8");});Task.Run(async()=>{awaitonFirstTransaction;if(onDeleted.IsCompleted)return;awaitproc.Do(()=>SendEmail(email,$"{creditCard} received 1st transaction"),"9");});awaitonDeleted;awaitproc.Do(()=>SendEmail(email,$"{creditCard} was deleted"),"10");}}staticvoidMain(string[]args){
...var procId =creditCardFlow.Start("1111-1111-1111-1111","user1@gmail.com");host.Get(procId).As<CreditCardProcess>()?.RaiseOnTransactionAppeared();host.Get(procId).As<CreditCardProcess>()?.RaiseOnCreditCardDeleted();
...}

About

Tiny library to build durable task, saga, process manager using the async/await capabilities. Inspired by Azure Durable Task Framework.

Topics

Resources

Stars

8 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages