{"id":16482197,"url":"https://github.com/cajuncoding/sqlapplockhelper","last_synced_at":"2025-10-27T17:31:57.389Z","repository":{"id":65413852,"uuid":"323774426","full_name":"cajuncoding/SqlAppLockHelper","owner":"cajuncoding","description":"An ultra lightweight API for robust Distributed Application Mutex/Locking capabilities leveraging SQL Server.  The API provides a set of easy to use custom extensions for the SqlClient libraries (e.g. Microsoft.Data.SqlClient or System.Data.SqlClient) that provide robust distributed application mutex/locking support via the sp_getapplock \u0026 sp_releaseapplock stored procedures.","archived":false,"fork":false,"pushed_at":"2023-09-29T01:43:04.000Z","size":93,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-09-25T09:30:32.681Z","etag":null,"topics":["app-lock","application-lock-system","azure-functions","batch-loader","batch-loading","bulk-load","bulk-loader","bulk-loading","distributed-lock","distributed-lock-algorithm","distributed-locking","distributed-mutex","locking","locking-library","serverless","single-thread","sql-server","sqlserver","transactional-outbox","transactional-outbox-pattern"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cajuncoding.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2020-12-23T01:43:24.000Z","updated_at":"2024-06-19T08:36:50.000Z","dependencies_parsed_at":"2024-06-20T00:09:45.528Z","dependency_job_id":"b667dfab-d167-4232-abc6-f91ed7753b36","html_url":"https://github.com/cajuncoding/SqlAppLockHelper","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cajuncoding%2FSqlAppLockHelper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cajuncoding%2FSqlAppLockHelper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cajuncoding%2FSqlAppLockHelper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cajuncoding%2FSqlAppLockHelper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cajuncoding","download_url":"https://codeload.github.com/cajuncoding/SqlAppLockHelper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219860854,"owners_count":16556011,"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":["app-lock","application-lock-system","azure-functions","batch-loader","batch-loading","bulk-load","bulk-loader","bulk-loading","distributed-lock","distributed-lock-algorithm","distributed-locking","distributed-mutex","locking","locking-library","serverless","single-thread","sql-server","sqlserver","transactional-outbox","transactional-outbox-pattern"],"created_at":"2024-10-11T13:09:58.355Z","updated_at":"2025-10-27T17:31:52.055Z","avatar_url":"https://github.com/cajuncoding.png","language":"C#","readme":"# SqlAppLockHelper -- Easy \u0026 Robust Distributed Mutex Application Locking with Sql Server\nAn ultra lightweight library that provides an easy to use API for a robust distributed mutex locking capabilities that leverage \nSql Server (e.g. sp_getapplock \u0026 sp_releaseapplock). Sql Server provides a very robust \u0026 efficient distributed mutex/locking\ncapability and this library exposes this in an easy to use C# .Net Standard API using custom extension methods\non the SqlConnection and SqlTransaction classes of the SqlClient libraries.\n\n### Nuget Package\nTo use in your project, add the appropriate package to your project for the namespace you are using:\n-  [SqlAppLockHelper.MicrosoftData NuGet package](https://www.nuget.org/packages/SqlAppLockHelper.MicrosoftData/)\n-  [SqlAppLockHelper.SystemData NuGet package](https://www.nuget.org/packages/SqlAppLockHelper.SystemData/)\n\n### [Buy me a Coffee ☕](https://www.buymeacoffee.com/cajuncoding)\n*I'm happy to share with the community, but if you find this useful (e.g for professional use), and are so inclinded,\nthen I do love-me-some-coffee!*\n\n\u003ca href=\"https://www.buymeacoffee.com/cajuncoding\" target=\"_blank\"\u003e\n\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/default-orange.png\" alt=\"Buy Me A Coffee\" height=\"41\" width=\"174\"\u003e\n\u003c/a\u003e \n\n## Usage:\n#### Both SqlClient Namespaces are Supported for Sql Server:\nThe library supports both SqlClient libraries:\n - System.Data.SqlClient (Legacy; long term supported for existing applications)\n - Microsoft.Data.SqlClient (Future; recommended go-forward library for new applications)\n\nThe usage for both is identical, with only the import being different based on the library you are using (or both in some edge cases):\n - `using SqlAppLockHelper.SystemDataNS;`\n - `using SqlAppLockHelper.MicrosoftDataNS;`\n\n#### Both Transaction \u0026 Connection Locking Scopes are supported:\nThere are two scopes for Locks that are supported:\n - **Session Scope** (aka Connection) - will automatically be released by Sql Server when the Sql Connection is disposed/closed; or may be optionally explicitly released.\n - **Transaction Scope** - Will automatically be released by Sql Server when Sql Transaction is Commited/Rolled-back/Closed; or can be optionally explicitly released.\n\n_NOTE: These scopes map to the underlying maps to the `@LockOwner` parameter of `sp_getapplock`_)\n\n### Genral Usage Notes: \n - The generally recommended approach is to use the *Transaction* scope because it is slightly safer (e.g. more resilient against\nabandoned locks) by allowing the Locks to automatically expire with the Transaction; and is the default behavior of Sql Server.\n   - However the *Session* scope is reliably implemented as long as you always close/dispose of the connection and/or via the `SqlServerAppLock` class; which also implements IDisposable/IAsyncDisposable C# interfaces.\n - The lock _acquisition timeout_ value is the value (in seconds) for which Sql Server will try and wait for Lock Acquisition. By specifying Zero\n(0 seconds) then Sql Server will attempt to get the lock but immediately fail lock acquisition and return if it cannot\nacquire the lock.\n - All locks are acquired as Exclusive locks for true _distributed mutex_ functionality.\n - More info can be found here: \n   - [sp_getapplock](https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql?view=sql-server-ver15)\n   - [sp_releaseapplock](https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-releaseapplock-transact-sql?view=sql-server-ver15) \n\n### Releasing Locks with IDisposable/IAsyncDisposable Patterns:\nExplicit release can be done anytime from the `SqlServerAppLock` class returned from an acquired lock, and is also intrinsically done via IDisposable/IAsyncDisposable on the `SqlServerAppLock` class to provide reliable release when scope closes via C# `using` pattern.\n\n### Use Cases:\n - Provide a lock implementation similar to C# `lock (...) {}` but on a distributed scale across many instances of an \napplication (e.g. Azure Functions, Load Balanced Servers, etc.).\n - Provide a mutex lock to ensure code is only ever run by one instance at a time (e.g. Bulk Loading or Bulk Synchronization processing, \nQueue Processing logic, Transactional Outbox Pattern, etc.).\n- I'm sure there are many more... but these are the best examples that I've needed to implement in enterprises.\n\n## Code Samples/Snippets:\n\n#### Import the Custom Extensions:\nFirst import the extensions for the library you are using:\n```csharp\nusing Microsoft.Data.SqlClient;\nusing SqlAppLockHelper.MicrosoftDataNS;\n```\nOR\n```csharp\nusing System.Data.SqlClient;\nusing SqlAppLockHelper.SystemDataNS;\n```\n\n### Simple Example:\nUsage is very simple by using custom extensions of the SqlConnection or SqlTransaction. The following example shows\nthe recommended usage of Transaction Scope by calling `.AcquireAppLockAsync(...)` on the SqlTransaction instance:\n\n*NOTES:* \n - Async is recommended, but the sync implementation works exactly the same -- sans async/await.\n - Default behavior is to throw a `SqlServerAppLockAcquisitionException` when lock acquisition fails but this can be controlled via `throwsException` parameter.\n\n#### Using Sql Transaction (Transaction Scope will be used) - Default behavior will throw an Exception:\n```csharp\n    //Attempt Acquisition  of Lock and Handle Exception if Lock cannot be acquired...\n    try\n    {\n        await using var sqlConn = new SqlConnection(sqlConnectionString);\n        await sqlConn.OpenAsync();\n        \n        await using var sqlTrans = (SqlTransaction)await sqlConn.BeginTransactionAsync();\n\n        //Using any SqlTransaction (cast DbTransaction to SqlTransaction if needed), this will \n        //\tattempt to acquire a distributed mutex/lock, and will wait up to 5 seconds before timing out.\n        await using var appLock = await sqlTrans.AcquireAppLockAsync(\"MyAppBulkLoadingDistributedLock\", 5);\n\n        //.... Custom logic that should only occur when a lock is held....\n\n    }\n    catch (SqlServerAppLockAcquisitionException appLockException)\n    {\n        //.... A lock could not be acquired so handle as needed....\n    }\n```\n\n#### Using Sql Transaction (Transaction Scope will be used) - Without Exception Handling:\n```csharp\n    await using var sqlConn = new SqlConnection(sqlConnectionString);\n    await sqlConn.OpenAsync();\n\n    await using var sqlTrans = (SqlTransaction)await sqlConn.BeginTransactionAsync();\n\n    //Using any SqlTransaction (cast DbTransaction to SqlTransaction if needed), this will \n    //\tattempt to acquire a distributed mutex/lock, and will wait up to 5 seconds before timing out.\n    //Note: Default behavior is to throw and exception but this is controlled via throwsException param\n    //\t\tand can then be managed via the returned the SqlServerAppLock result.\n    await using var appLock = await sqlTrans.AcquireAppLockAsync(\"MyAppBulkLoadingDistributedLock\", 5, false);\n\n    if(appLock.IsAcquired)\n    {\n        //.... Custom logic that should only occur when a lock is held....\n    }\n\n```\n#### Using Sql Connection (Session Scope will be used) - Without Exception Handling:\n_*NOTE: *Application Lock should ALWAYS be explicity Disposed of to ensure Lock is released**_\nAs a Connection level lock this will use the Sql Server Session level scoping which has it's own set of pros/cons and \npotentially some additional risks of orphaned locks in the event of a thread crasching and not disposing correctly or \nreturning to the connection pool for the Session to end.\n\nFor more info see the [Microsoft Docs here](https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql?view=sql-server-ver16#remarks):\nhttps://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql?view=sql-server-ver16#remarks\n\n```csharp\n    await using var sqlConn = new SqlConnection(sqlConnectionString);\n    await sqlConn.OpenAsync();\n\n    //Using any SqlConnection (cast DbConnection to SqlConnection if needed), this will \n    //\tattempt to acquire a distributed mutex/lock at the connection level, and will wait\n    // up to 5 seconds before timing out (as specified).\n    //Note: Default behavior is to throw and exception but this is controlled via throwsException param\n    //\t\tand can then be managed via the returned the SqlServerAppLock result.\n    //Note: The IDisposable/IAsyncDisposable implementation ensures that the Lock is released!\n    await using var appLock = await sqlConn.AcquireAppLockAsync(\"MyAppBulkLoadingDistributedLock\", 5, false);\n\n    if(appLock.IsAcquired)\n    {\n        //.... Custom logic that should only occur when a lock is held....\n    }\n\n```\n\n_**NOTE: More Sample code is provided in the Tests Project...**_\n\n\n```\n  \nMIT License\n\nCopyright (c) 2020 - Brandon Bernard\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n```\n","funding_links":["https://www.buymeacoffee.com/cajuncoding"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcajuncoding%2Fsqlapplockhelper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcajuncoding%2Fsqlapplockhelper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcajuncoding%2Fsqlapplockhelper/lists"}