{"id":21819865,"url":"https://github.com/gaevoy/gaev.durabletask","last_synced_at":"2025-10-08T03:30:31.538Z","repository":{"id":150764149,"uuid":"87977036","full_name":"gaevoy/Gaev.DurableTask","owner":"gaevoy","description":"Tiny library to build durable task, saga, process manager using the async/await capabilities. Inspired by Azure Durable Task Framework.","archived":false,"fork":false,"pushed_at":"2019-01-30T13:23:16.000Z","size":69,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-27T16:35:49.579Z","etag":null,"topics":["async-await","csharp","mssql","process-manager","saga"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gaevoy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-04-11T20:35:02.000Z","updated_at":"2023-12-14T10:25:05.000Z","dependencies_parsed_at":"2023-08-30T23:23:03.895Z","dependency_job_id":null,"html_url":"https://github.com/gaevoy/Gaev.DurableTask","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaevoy%2FGaev.DurableTask","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaevoy%2FGaev.DurableTask/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaevoy%2FGaev.DurableTask/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gaevoy%2FGaev.DurableTask/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gaevoy","download_url":"https://codeload.github.com/gaevoy/Gaev.DurableTask/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235675585,"owners_count":19027779,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async-await","csharp","mssql","process-manager","saga"],"created_at":"2024-11-27T16:26:47.870Z","updated_at":"2025-10-08T03:30:26.014Z","avatar_url":"https://github.com/gaevoy.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"**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](https://github.com/Azure/durabletask). \n\nJust 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. \n\nA 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*.\n\nTo 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.\n\n**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).\n\nLet's look closer to the durable task. It is easier to show an example:\n\n**Saga, process manager** [complete example](https://github.com/gaevoy/Gaev.DurableTask/blob/master/Gaev.DurableTask.Tests/Examples/UserRegistrationSaga.cs)\n```csharp\nasync Task DurableTask(string id, string email = null)\n{\n\tusing (var proc = _host.Spawn(id))\n\t{\n\t\t// Save email not to lose it if durable task resumes\n\t\temail = await proc.Get(email, \"SaveEmail\");\n\t\t// Register the user\n\t\tvar userId = await proc.Do(() =\u003e _service.RegisterUser(email), \"RegisterUser\");\n\t\t// Generate a secret for email verification\n\t\tvar secret = await proc.Get(Guid.NewGuid(), \"secret\");\n\t\t// Send email to the user with the secret to verify\n\t\tawait proc.Do(() =\u003e _service.VerifyEmail(email, secret), \"VerifyEmail\");\n\t\t// Wait when user receive verification email and send the secret here, it can take couple of days\n\t\tawait proc.Do(() =\u003e _service.WaitForEmailVerification(secret), \"WaitForEmailVerification\");\n\t\t// Activate the user in the system\n\t\tawait proc.Do(() =\u003e _service.ActivateUser(userId), \"ActivateUser\");\n\t}\n}\n```\n\n**Schedule** [complete example](https://github.com/gaevoy/Gaev.DurableTask/blob/master/Gaev.DurableTask.Tests/Examples/Schedule.cs)\n```csharp\nasync Task DurableTask(string id, string email = null)\n{\n\tusing (var proc = _host.Spawn(id))\n\t{\n\t\t// Save email not to lose it if durable task resumes\n\t\temail = await proc.Get(email, \"SaveEmail\");\n\t\tawait proc.Do(() =\u003e _smtp.Send(email, \"Welcome!\"), \"Welcome\");\n\t\t// Wait 1 month\n\t\tawait proc.Delay(TimeSpan.FromDays(30), \"Wait1m\");\n\t\tawait proc.Do(() =\u003e _smtp.Send(email, \"Your 1st month with us. Congrats!\"), \"CongratsMonth\");\n\t\t// Wait 11 months\n\t\tawait proc.Delay(TimeSpan.FromDays(365 - 30), \"Wait1y\");\n\t\tawait proc.Do(() =\u003e _smtp.Send(email, \"Your 1st year with us. Congrats!\"), \"CongratsYear\");\n\t}\n}\n```\n**Rollback logic** [complete example](https://github.com/gaevoy/Gaev.DurableTask/blob/master/Gaev.DurableTask.Tests/Examples/MoneyTransferSaga.cs)\n```csharp\nasync Task DurableTask(string id, string srcAccount = null, string destAccount = null, decimal amount = 0)\n{\n\tusing (var proc = _host.Spawn(id))\n\t{\n\t\t// Save values not to lose it if durable task resumes\n\t\tsrcAccount = await proc.Get(srcAccount, \"SaveSrcAccount\");\n\t\tdestAccount = await proc.Get(destAccount, \"SaveDestAccount\");\n\t\tamount = await proc.Get(amount, \"SaveAmount\");\n\t\tvar srcTranId = Guid.Empty;\n\t\tvar destTranId = Guid.Empty;\n\t\ttry\n\t\t{\n\t\t\t// Start transferring the money\n\t\t\tsrcTranId = await proc.Do(() =\u003e _service.StartTransfer(srcAccount, -amount), \"StartTransfer1\");\n\t\t\tdestTranId = await proc.Do(() =\u003e _service.StartTransfer(destAccount, +amount), \"StartTransfer2\");\n\t\t\t// Complete transferring the money\n\t\t\tawait proc.Do(() =\u003e _service.CompleteTransfer(srcAccount, srcTranId), \"CompleteTransfer1\");\n\t\t\tawait proc.Do(() =\u003e _service.CompleteTransfer(destAccount, destTranId), \"CompleteTransfer2\");\n\t\t}\n\t\tcatch (ProcessException ex) when (ex.Type == nameof(TransferFailedException))\n\t\t{\n\t\t\t// Rollback logic\n\t\t\tif (srcTranId != Guid.Empty)\n\t\t\t\tawait proc.Do(() =\u003e _service.RollbackTransfer(srcAccount, srcTranId), \"RollbackTransfer1\");\n\t\t\tif (destTranId != Guid.Empty)\n\t\t\t\tawait proc.Do(() =\u003e _service.RollbackTransfer(destAccount, destTranId), \"RollbackTransfer2\");\n\t\t\tthrow;\n\t\t}\n\t}\n}\n```\n**Sending a message to the durable task** [complete example](https://github.com/gaevoy/Gaev.DurableTask/tree/master/Gaev.DurableTask.ConsolePlayground)\n```csharp\nasync Task DurableTask(string processId, string companyId = null, string creditCard = null)\n{\n\tusing (var proc = _host.Spawn(processId).As\u003cCreditCardProcess\u003e())\n\t{\n\t\tcompanyId = await proc.Get(companyId, \"1\");\n\t\tcreditCard = await proc.Get(creditCard, \"2\");\n\t\tConsole.WriteLine($\"CreditCardFlow is up for companyId={companyId} creditCard={creditCard}\");\n\t\tvar email = await proc.Do(() =\u003e GetEmail(companyId), \"3\");\n\t\tawait proc.Do(() =\u003e SendEmail(email, $\"{creditCard} was assigned to you\"), \"4\");\n\t\tvar onCheckTime = proc.Delay(TimeSpan.FromMinutes(5), \"5\");\n\t\tvar onFirstTransaction = proc.Do(() =\u003e proc.OnTransactionAppeared(), \"6\");\n\t\tvar onDeleted = proc.Do(() =\u003e proc.OnCreditCardDeleted(), \"7\");\n\t\tTask.Run(async () =\u003e\n\t\t{\n\t\t\tawait onCheckTime;\n\t\t\tif (onDeleted.IsCompleted) return;\n\t\t\tif (!onFirstTransaction.IsCompleted)\n\t\t\t\tawait proc.Do(() =\u003e SendEmail(email, $\"{creditCard} is inactive long time\"), \"8\");\n\t\t});\n\t\tTask.Run(async () =\u003e\n\t\t{\n\t\t\tawait onFirstTransaction;\n\t\t\tif (onDeleted.IsCompleted) return;\n\t\t\tawait proc.Do(() =\u003e SendEmail(email, $\"{creditCard} received 1st transaction\"), \"9\");\n\t\t});\n\n\t\tawait onDeleted;\n\t\tawait proc.Do(() =\u003e SendEmail(email, $\"{creditCard} was deleted\"), \"10\");\n\t}\n}\nstatic void Main(string[] args)\n{\n\t...\n\tvar procId = creditCardFlow.Start(\"1111-1111-1111-1111\", \"user1@gmail.com\");\n\thost.Get(procId).As\u003cCreditCardProcess\u003e()?.RaiseOnTransactionAppeared();\n\thost.Get(procId).As\u003cCreditCardProcess\u003e()?.RaiseOnCreditCardDeleted();\n\t...\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaevoy%2Fgaev.durabletask","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgaevoy%2Fgaev.durabletask","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgaevoy%2Fgaev.durabletask/lists"}