diff --git a/README.md b/README.md index 1576a2a2..504a420e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ DistributedLock is a .NET library that provides robust and easy-to-use distributed mutexes, reader-writer locks, and semaphores based on a variety of underlying technologies. With DistributedLock, synchronizing access to a region of code across multiple applications/machines is as simple as: + ```C# await using (await myDistributedLock.AcquireAsync()) { @@ -12,12 +13,13 @@ await using (await myDistributedLock.AcquireAsync()) ## Implementations -DistributedLock contains implementations based on various technologies; you can install implementation packages individually or just install the [DistributedLock NuGet package](https://www.nuget.org/packages/DistributedLock) [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.svg?style=flat)](https://www.nuget.org/packages/DistributedLock/), a ["meta" package](https://endjin.com/blog/2020/09/streamline-dependency-management-with-nuget-meta-packages) which includes all implementations as dependencies. *Note that each package is versioned independently according to SemVer*. +DistributedLock contains implementations based on various technologies; you can install implementation packages individually or just install the [DistributedLock NuGet package](https://www.nuget.org/packages/DistributedLock) [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.svg?style=flat)](https://www.nuget.org/packages/DistributedLock/), a ["meta" package](https://endjin.com/blog/2020/09/streamline-dependency-management-with-nuget-meta-packages) which includes all implementations as dependencies. _Note that each package is versioned independently according to SemVer_. - **[DistributedLock.SqlServer](docs/DistributedLock.SqlServer.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.SqlServer.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.SqlServer/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.SqlServer.html) -: uses Microsoft SQL Server + : uses Microsoft SQL Server - **[DistributedLock.Postgres](docs/DistributedLock.Postgres.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.Postgres.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.Postgres/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.Postgres.html) -: uses Postgresql + : uses Postgresql +- **[DistributedLock.MongoDB](docs/DistributedLock.MongoDB.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.MongoDB.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.MongoDB/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.MongoDB.html): uses MongoDB - **[DistributedLock.MySql](docs/DistributedLock.MySql.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.MySql.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.MySql/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.MySql.html): uses MySQL or MariaDB - **[DistributedLock.Oracle](docs/DistributedLock.Oracle.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.Oracle.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.Oracle/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.Oracle.html): uses Oracle - **[DistributedLock.Redis](docs/DistributedLock.Redis.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.Redis.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.Redis/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.Redis.html): uses Redis @@ -108,7 +110,7 @@ public class SomeService { this._synchronizationProvider = synchronizationProvider; } - + public void InitializeUserAccount(int id) { // use the provider to construct a lock @@ -117,7 +119,7 @@ public class SomeService { // do stuff } - + // ALTERNATIVELY, for common use-cases extension methods allow this to be done with a single call using (this._synchronizationProvider.AcquireLock($"UserAccount{id}")) { @@ -141,96 +143,97 @@ Contributions are welcome! If you are interested in contributing towards a new o Setup steps for working with the repository locally are documented [here](docs/Developing%20DistributedLock.md). ## Release notes + - 2.7.1 - Improve compatibility with Redis clusters that require keys in Lua scripts to be passed via the KEYS array. Thanks [@pengweiqhca](https://github.com/pengweiqhca) for reporting a identifying the fix! ([#254](https://github.com/madelson/DistributedLock/issues/254), DistributedLock.Redis 1.1.1) - 2.7 - - Add support for fetching a Redis-based semaphore's current available count. Thanks [@teesoftech](https://github.com/teesofttech) for implementing! ([#234](https://github.com/madelson/DistributedLock/issues/234), DistributedLock.Redis 1.1) + - Add support for fetching a Redis-based semaphore's current available count. Thanks [@teesoftech](https://github.com/teesofttech) for implementing! ([#234](https://github.com/madelson/DistributedLock/issues/234), DistributedLock.Redis 1.1) - 2.6 - - Add support for acquiring transaction-scoped Postgres locks using externally-owned transactions. Thanks [@Tzachi009](https://github.com/Tzachi009) for implementing! ([#213](https://github.com/madelson/DistributedLock/issues/213), DistributedLock.Postgres 1.3) + - Add support for acquiring transaction-scoped Postgres locks using externally-owned transactions. Thanks [@Tzachi009](https://github.com/Tzachi009) for implementing! ([#213](https://github.com/madelson/DistributedLock/issues/213), DistributedLock.Postgres 1.3) - 2.5.1 - - Increase efficiency of Azure blob locks when the blob does not exist. Thanks [@richardkooiman](https://github.com/richardkooiman) for implementing! ([#227](https://github.com/madelson/DistributedLock/pull/227), DistributedLock.Azure 1.0.2) - - Improve error handling in race condition scenarios for Azure blobs. Thanks [@MartinDembergerR9](https://github.com/MartinDembergerR9) for implementing! ([#228](https://github.com/madelson/DistributedLock/pull/228), DistributedLock.Azure 1.0.2) - - Bump Microsoft.Data.SqlClient to 5.2.2 to avoid vulnerability. Thanks [@steve85](https://github.com/steve85) for implementing! ([#229](https://github.com/madelson/DistributedLock/pull/229), DistributedLock.SqlServer 1.0.6) - - Bump Oracle.ManagedDataAccess to latest to avoid bringing in vulnerable packages (DistributedLock.Core 1.0.8, DistributedLock.Oracle 1.0.4) - - Bump Npgsql to latest patch to avoid bringing in vulnerable packages (DistributedLock.Postgres 1.2.1) - - Improve directory creation concurrency handling for `FileDistributedLock` (DistributedLock.FileSystem 1.0.3) + - Increase efficiency of Azure blob locks when the blob does not exist. Thanks [@richardkooiman](https://github.com/richardkooiman) for implementing! ([#227](https://github.com/madelson/DistributedLock/pull/227), DistributedLock.Azure 1.0.2) + - Improve error handling in race condition scenarios for Azure blobs. Thanks [@MartinDembergerR9](https://github.com/MartinDembergerR9) for implementing! ([#228](https://github.com/madelson/DistributedLock/pull/228), DistributedLock.Azure 1.0.2) + - Bump Microsoft.Data.SqlClient to 5.2.2 to avoid vulnerability. Thanks [@steve85](https://github.com/steve85) for implementing! ([#229](https://github.com/madelson/DistributedLock/pull/229), DistributedLock.SqlServer 1.0.6) + - Bump Oracle.ManagedDataAccess to latest to avoid bringing in vulnerable packages (DistributedLock.Core 1.0.8, DistributedLock.Oracle 1.0.4) + - Bump Npgsql to latest patch to avoid bringing in vulnerable packages (DistributedLock.Postgres 1.2.1) + - Improve directory creation concurrency handling for `FileDistributedLock` (DistributedLock.FileSystem 1.0.3) - 2.5 - - Add support for creating Postgres locks off `DbDataSource` which is helpful for apps using `NpgsqlMultiHostDataSource`. Thanks [davidngjy](https://github.com/davidngjy) for implementing! ([#153](https://github.com/madelson/DistributedLock/issues/153), DistributedLock.Postgres 1.2.0) - - Upgrade Npgsql to 8.0.3 to avoid vulnerability. Thanks [@Meir017](https://github.com/Meir017)/[@davidngjy](https://github.com/davidngjy) for implementing! ([#218](https://github.com/madelson/DistributedLock/issues/218), DistributedLock.Postgres 1.2.0) - - Fix Postgres race condition with connection keepalive enabled ([#216](https://github.com/madelson/DistributedLock/issues/216), DistributedLock.Core 1.0.7) - - Upgrade Microsoft.Data.SqlClient to 5.2.1 to avoid vulnerability ([#210](https://github.com/madelson/DistributedLock/issues/210), DistributedLock.SqlServer 1.0.5) + - Add support for creating Postgres locks off `DbDataSource` which is helpful for apps using `NpgsqlMultiHostDataSource`. Thanks [davidngjy](https://github.com/davidngjy) for implementing! ([#153](https://github.com/madelson/DistributedLock/issues/153), DistributedLock.Postgres 1.2.0) + - Upgrade Npgsql to 8.0.3 to avoid vulnerability. Thanks [@Meir017](https://github.com/Meir017)/[@davidngjy](https://github.com/davidngjy) for implementing! ([#218](https://github.com/madelson/DistributedLock/issues/218), DistributedLock.Postgres 1.2.0) + - Fix Postgres race condition with connection keepalive enabled ([#216](https://github.com/madelson/DistributedLock/issues/216), DistributedLock.Core 1.0.7) + - Upgrade Microsoft.Data.SqlClient to 5.2.1 to avoid vulnerability ([#210](https://github.com/madelson/DistributedLock/issues/210), DistributedLock.SqlServer 1.0.5) - 2.4 - - Add support for transaction-scoped locking in Postgres using `pg_advisory_xact_lock` which is helpful when using PgBouncer ([#168](https://github.com/madelson/DistributedLock/issues/168), DistributedLock.Postgres 1.1.0) - - Improve support for newer versions of StackExchange.Redis, especially when using the default backlog policy ([#162](https://github.com/madelson/DistributedLock/issues/162), DistributedLock.Redis 1.0.3). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for helping with this! - - Drop `net461` support (`net462` remains supported). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for implementing! - - Reduce occurrence of `UnobservedTaskException`s thrown by the library ([#192](https://github.com/madelson/DistributedLock/issues/192), DistributedLock.Core 1.0.6) - - Update dependencies to modern versions without known issues/vulnerabilities ([#111](https://github.com/madelson/DistributedLock/issues/111)/[#177](https://github.com/madelson/DistributedLock/issues/177)/[#184](https://github.com/madelson/DistributedLock/issues/184)/[#185](https://github.com/madelson/DistributedLock/issues/185), all packages). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for helping with this! - - Improve directory creation concurrency handling for `FileDistributedLock` on Linux/.NET 8 ([#195](https://github.com/madelson/DistributedLock/issues/195), DistributedLock.FileSystem 1.0.2) - - Allow using transaction-scoped locks in SQL Server without explicitly disabling multiplexing ([#189](https://github.com/madelson/DistributedLock/issues/189), DistributedLock.SqlServer 1.0.4) - - New API documentation on [dndocs](https://dndocs.com/). Thanks [@NeuroXiq](https://github.com/NeuroXiq)! - - New documentation for contributors to get the project running locally (see [Contributing](#contributing)) + - Add support for transaction-scoped locking in Postgres using `pg_advisory_xact_lock` which is helpful when using PgBouncer ([#168](https://github.com/madelson/DistributedLock/issues/168), DistributedLock.Postgres 1.1.0) + - Improve support for newer versions of StackExchange.Redis, especially when using the default backlog policy ([#162](https://github.com/madelson/DistributedLock/issues/162), DistributedLock.Redis 1.0.3). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for helping with this! + - Drop `net461` support (`net462` remains supported). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for implementing! + - Reduce occurrence of `UnobservedTaskException`s thrown by the library ([#192](https://github.com/madelson/DistributedLock/issues/192), DistributedLock.Core 1.0.6) + - Update dependencies to modern versions without known issues/vulnerabilities ([#111](https://github.com/madelson/DistributedLock/issues/111)/[#177](https://github.com/madelson/DistributedLock/issues/177)/[#184](https://github.com/madelson/DistributedLock/issues/184)/[#185](https://github.com/madelson/DistributedLock/issues/185), all packages). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for helping with this! + - Improve directory creation concurrency handling for `FileDistributedLock` on Linux/.NET 8 ([#195](https://github.com/madelson/DistributedLock/issues/195), DistributedLock.FileSystem 1.0.2) + - Allow using transaction-scoped locks in SQL Server without explicitly disabling multiplexing ([#189](https://github.com/madelson/DistributedLock/issues/189), DistributedLock.SqlServer 1.0.4) + - New API documentation on [dndocs](https://dndocs.com/). Thanks [@NeuroXiq](https://github.com/NeuroXiq)! + - New documentation for contributors to get the project running locally (see [Contributing](#contributing)) - 2.3.4 - - Support Npgsql 8.0's [ExecuteScalar breaking change](https://github.com/npgsql/npgsql/issues/5143) ([#174](https://github.com/madelson/DistributedLock/issues/174), DistributedLock.Postgres 1.0.5). Thanks [@Kaffeetasse](https://github.com/Kaffeetasse) for diagnosing and fixing! + - Support Npgsql 8.0's [ExecuteScalar breaking change](https://github.com/npgsql/npgsql/issues/5143) ([#174](https://github.com/madelson/DistributedLock/issues/174), DistributedLock.Postgres 1.0.5). Thanks [@Kaffeetasse](https://github.com/Kaffeetasse) for diagnosing and fixing! - 2.3.3 - - Update Microsoft.Data.SqlClient due to vulnerabilities ([#149](https://github.com/madelson/DistributedLock/issues/149), DistributedLock.SqlServer 1.0.3) - - Update versions of Oracle.ManagedDataAccess and Oracle.ManagedDataAccess.Core due to vulnerabilities (DistributedLock.Oracle 1.0.2) + - Update Microsoft.Data.SqlClient due to vulnerabilities ([#149](https://github.com/madelson/DistributedLock/issues/149), DistributedLock.SqlServer 1.0.3) + - Update versions of Oracle.ManagedDataAccess and Oracle.ManagedDataAccess.Core due to vulnerabilities (DistributedLock.Oracle 1.0.2) - 2.3.2 - - Work around underlying Postgres race condition when waiting on advisory locks with a short non-zero timeout ([#147](https://github.com/madelson/DistributedLock/issues/147), DistributedLock.Postgres 1.0.4). Thanks [@Tzachi009](https://github.com/Tzachi009) for reporting and isolating the issue! + - Work around underlying Postgres race condition when waiting on advisory locks with a short non-zero timeout ([#147](https://github.com/madelson/DistributedLock/issues/147), DistributedLock.Postgres 1.0.4). Thanks [@Tzachi009](https://github.com/Tzachi009) for reporting and isolating the issue! - 2.3.1 - - Fixed concurrency issue with `HandleLostToken` for relational database locks ([#133](https://github.com/madelson/DistributedLock/issues/133), DistributedLock.Core 1.0.5, DistributedLock.MySql 1.0.1, DistributedLock.Oracle 1.0.1, DistributedLock.Postgres 1.0.3, DistributedLock.SqlServer 1.0.2). Thanks [@OskarKlintrot](https://github.com/OskarKlintrot) for testing! - - Fixed misleading error message why trying to disable auto-extension in Redis ([#130](https://github.com/madelson/DistributedLock/issues/130), DistributedLock.Redis 1.0.2) - - Fixed concurrency issue with canceling async waits on `WaitHandle`s ([#120](https://github.com/madelson/DistributedLock/issues/120), DistributedLock.WaitHandles 1.0.1) + - Fixed concurrency issue with `HandleLostToken` for relational database locks ([#133](https://github.com/madelson/DistributedLock/issues/133), DistributedLock.Core 1.0.5, DistributedLock.MySql 1.0.1, DistributedLock.Oracle 1.0.1, DistributedLock.Postgres 1.0.3, DistributedLock.SqlServer 1.0.2). Thanks [@OskarKlintrot](https://github.com/OskarKlintrot) for testing! + - Fixed misleading error message why trying to disable auto-extension in Redis ([#130](https://github.com/madelson/DistributedLock/issues/130), DistributedLock.Redis 1.0.2) + - Fixed concurrency issue with canceling async waits on `WaitHandle`s ([#120](https://github.com/madelson/DistributedLock/issues/120), DistributedLock.WaitHandles 1.0.1) - 2.3.0 - - Added Oracle-based implementation ([#45](https://github.com/madelson/DistributedLock/issues/45), DistributedLock.Oracle 1.0.0). Thanks [@odin568](https://github.com/odin568) for testing! - - Made file-based locking more robust to transient `UnauthorizedAccessException`s ([#106](https://github.com/madelson/DistributedLock/issues/106) & [#109](https://github.com/madelson/DistributedLock/issues/109), DistributedLock.FileSystem 1.0.1) - - Work around cancellation bug in Npgsql command preparation ([#112](https://github.com/madelson/DistributedLock/issues/112), DistributedLock.Postgres 1.0.2) + - Added Oracle-based implementation ([#45](https://github.com/madelson/DistributedLock/issues/45), DistributedLock.Oracle 1.0.0). Thanks [@odin568](https://github.com/odin568) for testing! + - Made file-based locking more robust to transient `UnauthorizedAccessException`s ([#106](https://github.com/madelson/DistributedLock/issues/106) & [#109](https://github.com/madelson/DistributedLock/issues/109), DistributedLock.FileSystem 1.0.1) + - Work around cancellation bug in Npgsql command preparation ([#112](https://github.com/madelson/DistributedLock/issues/112), DistributedLock.Postgres 1.0.2) - 2.2.0 - - Added MySQL/MariaDB-based implementation ([#95](https://github.com/madelson/DistributedLock/issues/95), DistributedLock.MySql 1.0.0). Thanks [@theplacefordev](https://github.com/theplacefordev) for testing! + - Added MySQL/MariaDB-based implementation ([#95](https://github.com/madelson/DistributedLock/issues/95), DistributedLock.MySql 1.0.0). Thanks [@theplacefordev](https://github.com/theplacefordev) for testing! - 2.1.0 - - Added ZooKeeper-based implementation ([#41](https://github.com/madelson/DistributedLock/issues/41), DistributedLock.ZooKeeper 1.0.0) + - Added ZooKeeper-based implementation ([#41](https://github.com/madelson/DistributedLock/issues/41), DistributedLock.ZooKeeper 1.0.0) - 2.0.2 - - Fixed bug where `HandleLostToken` would hang when accessed on a SqlServer or Postgres lock handle that used keepalive ([#85](https://github.com/madelson/DistributedLock/issues/85), DistributedLock.Core 1.0.1) - - Fixed bug where broken database connections could result in future lock attempts failing when using SqlServer or Postgres locks with multiplexing ([#83](https://github.com/madelson/DistributedLock/issues/83), DistributedLock.Core 1.0.1) - - Updated Npgsql dependency to 5.x to take advantage of various bugfixes ([#61](https://github.com/madelson/DistributedLock/issues/61), DistributedLock.Postgres 1.0.1) + - Fixed bug where `HandleLostToken` would hang when accessed on a SqlServer or Postgres lock handle that used keepalive ([#85](https://github.com/madelson/DistributedLock/issues/85), DistributedLock.Core 1.0.1) + - Fixed bug where broken database connections could result in future lock attempts failing when using SqlServer or Postgres locks with multiplexing ([#83](https://github.com/madelson/DistributedLock/issues/83), DistributedLock.Core 1.0.1) + - Updated Npgsql dependency to 5.x to take advantage of various bugfixes ([#61](https://github.com/madelson/DistributedLock/issues/61), DistributedLock.Postgres 1.0.1) - 2.0.1 - - Fixed Redis lock behavior when using a database with `WithKeyPrefix` ([#66](https://github.com/madelson/DistributedLock/issues/66), DistributedLock.Redis 1.0.1). Thanks [@skomis-mm](https://github.com/skomis-mm) for contributing! + - Fixed Redis lock behavior when using a database with `WithKeyPrefix` ([#66](https://github.com/madelson/DistributedLock/issues/66), DistributedLock.Redis 1.0.1). Thanks [@skomis-mm](https://github.com/skomis-mm) for contributing! - 2.0.0 (see also [Migrating from 1.x to 2.x](docs/Migrating%20from%201.x%20to%202.x.md#migrating-from-1x-to-2x)) - - Revamped package structure so that DistributedLock is now an umbrella package and each implementation technology has its own package (BREAKING CHANGE) - - Added Postgresql-based locking ([#56](https://github.com/madelson/DistributedLock/issues/56), DistributedLock.Postgres 1.0.0) - - Added Redis-based locking ([#24](https://github.com/madelson/DistributedLock/issues/24), DistributedLock.Redis 1.0.0) - - Added Azure blob-based locking ([#42](https://github.com/madelson/DistributedLock/issues/42), DistributedLock.Azure 1.0.0) - - Added file-based locking ([#28](https://github.com/madelson/DistributedLock/issues/28), DistributedLock.FileSystem 1.0.0) - - Added provider classes for improved IOC integration ([#13](https://github.com/madelson/DistributedLock/issues/13)) - - Added strong naming to assemblies. Thanks [@pedropaulovc](https://github.com/pedropaulovc) for contributing! ([#47](https://github.com/madelson/DistributedLock/issues/47), BREAKING CHANGE) - - Made lock handles implement `IAsyncDisposable` in addition to `IDisposable` [#20](https://github.com/madelson/DistributedLock/issues/20), BREAKING CHANGE) - - Exposed implementation-agnostic interfaces (e. g. `IDistributedLock`) for all synchronization primitives ([#10](https://github.com/madelson/DistributedLock/issues/10)) - - Added `HandleLostToken` API for tracking if a lock's underlying connection dies ([#6](https://github.com/madelson/DistributedLock/issues/6), BREAKING CHANGE) - - Added SourceLink support ([#57](https://github.com/madelson/DistributedLock/issues/57)) - - Removed `GetSafeName` API in favor of safe naming by default (BREAKING CHANGE) - - Renamed "SystemDistributedLock" to "EventWaitHandleDistributedLock" (DistributedLock.WaitHandles 1.0.0) - - Stopped supporting net45 (BREAKING CHANGE) - - Removed `DbConnection` and `DbTransaction` constructors form `SqlDistributedLock`, leaving the constructors that take `IDbConnection`/`IDbTransaction` ([#35](https://github.com/madelson/DistributedLock/issues/35), BREAKING CHANGE) - - Changed methods returning `Task` to instead return `ValueTask`, making it so that `using (@lock.AcquireAsync()) { ... } without an `await` no longer compiles (#34, BREAKING CHANGE) - - Changed `UpgradeableLockHandle.UpgradeToWriteLock` to return `void` ([#33](https://github.com/madelson/DistributedLock/issues/33), BREAKING CHANGE) - - Switched to Microsoft.Data.SqlClient by default for all target frameworks (BREAKING CHANGE) - - Changed all locking implementations to be non-reentrant (BREAKING CHANGE) + - Revamped package structure so that DistributedLock is now an umbrella package and each implementation technology has its own package (BREAKING CHANGE) + - Added Postgresql-based locking ([#56](https://github.com/madelson/DistributedLock/issues/56), DistributedLock.Postgres 1.0.0) + - Added Redis-based locking ([#24](https://github.com/madelson/DistributedLock/issues/24), DistributedLock.Redis 1.0.0) + - Added Azure blob-based locking ([#42](https://github.com/madelson/DistributedLock/issues/42), DistributedLock.Azure 1.0.0) + - Added file-based locking ([#28](https://github.com/madelson/DistributedLock/issues/28), DistributedLock.FileSystem 1.0.0) + - Added provider classes for improved IOC integration ([#13](https://github.com/madelson/DistributedLock/issues/13)) + - Added strong naming to assemblies. Thanks [@pedropaulovc](https://github.com/pedropaulovc) for contributing! ([#47](https://github.com/madelson/DistributedLock/issues/47), BREAKING CHANGE) + - Made lock handles implement `IAsyncDisposable` in addition to `IDisposable` [#20](https://github.com/madelson/DistributedLock/issues/20), BREAKING CHANGE) + - Exposed implementation-agnostic interfaces (e. g. `IDistributedLock`) for all synchronization primitives ([#10](https://github.com/madelson/DistributedLock/issues/10)) + - Added `HandleLostToken` API for tracking if a lock's underlying connection dies ([#6](https://github.com/madelson/DistributedLock/issues/6), BREAKING CHANGE) + - Added SourceLink support ([#57](https://github.com/madelson/DistributedLock/issues/57)) + - Removed `GetSafeName` API in favor of safe naming by default (BREAKING CHANGE) + - Renamed "SystemDistributedLock" to "EventWaitHandleDistributedLock" (DistributedLock.WaitHandles 1.0.0) + - Stopped supporting net45 (BREAKING CHANGE) + - Removed `DbConnection` and `DbTransaction` constructors form `SqlDistributedLock`, leaving the constructors that take `IDbConnection`/`IDbTransaction` ([#35](https://github.com/madelson/DistributedLock/issues/35), BREAKING CHANGE) + - Changed methods returning `Task` to instead return `ValueTask`, making it so that `using (@lock.AcquireAsync()) { ... } without an `await` no longer compiles (#34, BREAKING CHANGE) + - Changed `UpgradeableLockHandle.UpgradeToWriteLock` to return `void` ([#33](https://github.com/madelson/DistributedLock/issues/33), BREAKING CHANGE) + - Switched to Microsoft.Data.SqlClient by default for all target frameworks (BREAKING CHANGE) + - Changed all locking implementations to be non-reentrant (BREAKING CHANGE) - 1.5.0 - - Added cross-platform support via Microsoft.Data.SqlClient ([#25](https://github.com/madelson/DistributedLock/issues/25)). This feature is available for .NET Standard >= 2.0. Thanks to [@alesebi91](https://github.com/alesebi91) for helping with the implementation and testing! - - Added C#8 nullable annotations ([#31](https://github.com/madelson/DistributedLock/issues/31)) - - Fixed minor bug in connection multiplexing which could lead to more lock contention ([#32](https://github.com/madelson/DistributedLock/issues/32)) + - Added cross-platform support via Microsoft.Data.SqlClient ([#25](https://github.com/madelson/DistributedLock/issues/25)). This feature is available for .NET Standard >= 2.0. Thanks to [@alesebi91](https://github.com/alesebi91) for helping with the implementation and testing! + - Added C#8 nullable annotations ([#31](https://github.com/madelson/DistributedLock/issues/31)) + - Fixed minor bug in connection multiplexing which could lead to more lock contention ([#32](https://github.com/madelson/DistributedLock/issues/32)) - 1.4.0 - - Added a SQL-based distributed semaphore ([#7](https://github.com/madelson/DistributedLock/issues/7)) - - Fix bug where SqlDistributedLockConnectionStrategy.Azure would leak connections, relying on GC to reclaim them ([#14](https://github.com/madelson/DistributedLock/issues/14)). Thanks [zavalita1](https://github.com/zavalita1) for investigating this issue! - - Throw a specific exception type (`DeadlockException`) rather than the generic `InvalidOperationException` when a deadlock is detected ([#11](https://github.com/madelson/DistributedLock/issues/11)) + - Added a SQL-based distributed semaphore ([#7](https://github.com/madelson/DistributedLock/issues/7)) + - Fix bug where SqlDistributedLockConnectionStrategy.Azure would leak connections, relying on GC to reclaim them ([#14](https://github.com/madelson/DistributedLock/issues/14)). Thanks [zavalita1](https://github.com/zavalita1) for investigating this issue! + - Throw a specific exception type (`DeadlockException`) rather than the generic `InvalidOperationException` when a deadlock is detected ([#11](https://github.com/madelson/DistributedLock/issues/11)) - 1.3.1 Minor fix to avoid "leaking" isolation level changes in transaction-based locks ([#8](https://github.com/madelson/DistributedLock/issues/8)). Also switched to the VS2017 project file format - 1.3.0 Added an Azure connection strategy to keep lock connections from becoming idle and being reclaimed by Azure's connection governor ([#5](https://github.com/madelson/DistributedLock/issues/5)) - 1.2.0 - - Added a SQL-based distributed reader-writer lock - - .NET Core support via .NET Standard - - Changed the default locking scope for SQL distributed lock to be a connection rather than a transaction, avoiding cases where long-running transactions can block backups - - Allowed for customization of the SQL distributed lock connection strategy when connecting via a connection string - - Added a new connection strategy which allows for multiplexing multiple held locks onto one connection - - Added IDbConnection/IDbTransaction constructors ([#3](https://github.com/madelson/DistributedLock/issues/3)) + - Added a SQL-based distributed reader-writer lock + - .NET Core support via .NET Standard + - Changed the default locking scope for SQL distributed lock to be a connection rather than a transaction, avoiding cases where long-running transactions can block backups + - Allowed for customization of the SQL distributed lock connection strategy when connecting via a connection string + - Added a new connection strategy which allows for multiplexing multiple held locks onto one connection + - Added IDbConnection/IDbTransaction constructors ([#3](https://github.com/madelson/DistributedLock/issues/3)) - 1.1.0 Added support for SQL distributed locks scoped to existing connections/transactions - 1.0.1 Minor fix when using infinite timeouts - 1.0.0 Initial release diff --git a/docs/DistributedLock.MongoDB.md b/docs/DistributedLock.MongoDB.md new file mode 100644 index 00000000..231bd594 --- /dev/null +++ b/docs/DistributedLock.MongoDB.md @@ -0,0 +1,82 @@ +# DistributedLock.MongoDB + +[Download the NuGet package](https://www.nuget.org/packages/DistributedLock.MongoDB) [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.MongoDB.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.MongoDB/) + +The DistributedLock.MongoDB package offers distributed locks based on [MongoDB](https://www.mongodb.com/). For example: + +```C# +var client = new MongoClient("mongodb://localhost:27017"); +var database = client.GetDatabase("myDatabase"); +var @lock = new MongoDistributedLock("MyLockName", database); +await using (await @lock.AcquireAsync()) +{ + // I have the lock +} +``` + +## APIs + +- The `MongoDistributedLock` class implements the `IDistributedLock` interface. +- The `MongoDistributedSynchronizationProvider` class implements the `IDistributedLockProvider` interface. + +## Implementation notes + +MongoDB-based locks use MongoDB's document upsert and update operations to implement distributed locking. The implementation works as follows: + +1. **Acquisition**: Attempts to insert or update a document with the lock key and a unique lock ID. +2. **Extension**: Automatically extends the lock expiry while held to prevent timeout. +3. **Release**: Deletes the lock document when disposed. +4. **Expiry**: Locks automatically expire if not extended, allowing recovery from crashed processes. + +MongoDB locks can be constructed with an `IMongoDatabase` and an optional collection name. If no collection name is specified, locks will be stored in a collection named `"DistributedLocks"`. The collection will automatically have an index created on the `expiresAt` field for efficient queries. + +When using the provider pattern, you can create multiple locks with different names from the same provider: + +```C# +var client = new MongoClient(connectionString); +var database = client.GetDatabase("myDatabase"); +var provider = new MongoDistributedSynchronizationProvider(database); + +var lock1 = provider.CreateLock("lock1"); +var lock2 = provider.CreateLock("lock2"); + +await using (await lock1.AcquireAsync()) +{ + // Do work with lock1 +} +``` + +**NOTE**: Lock extension happens automatically in the background while the lock is held. If lock extension fails (for example, due to network issues), the `HandleLostToken` will be signaled to notify you that the lock may have been lost. + +## Options + +In addition to specifying the name and database, several tuning options are available: + +- `Expiry` determines how long the lock will be initially claimed for. Because of automatic extension, locks can be held for longer than this value. Defaults to 30 seconds. +- `ExtensionCadence` determines how frequently the hold on the lock will be renewed to the full `Expiry`. Defaults to 1/3 of `Expiry` (approximately 10 seconds when using the default expiry). +- `BusyWaitSleepTime` specifies a range of times that the implementation will sleep between attempts to acquire a lock that is currently held by someone else. A random time in the range will be chosen for each sleep. If you expect contention, lowering these values may increase responsiveness (how quickly a lock detects that it can now be taken) but will increase the number of calls made to MongoDB. Raising the values will have the reverse effects. Defaults to a range of 10ms to 800ms. + +Example of using options: + +```C# +var @lock = new MongoDistributedLock( + "MyLockName", + database, + options => options + .Expiry(TimeSpan.FromSeconds(30)) + .ExtensionCadence(TimeSpan.FromSeconds(10)) + .BusyWaitSleepTime( + min: TimeSpan.FromMilliseconds(10), + max: TimeSpan.FromMilliseconds(800)) +); +``` + +You can also specify a custom collection name: + +```C# +var @lock = new MongoDistributedLock("MyLockName", database, "MyCustomLocks"); +``` + +## Stale lock cleanup + +Stale locks from crashed processes will automatically expire based on the `Expiry` setting. MongoDB's built-in TTL index support ensures that expired lock documents are cleaned up automatically by the database. This means that if a process crashes while holding a lock, the lock will become available again after the expiry time has elapsed. diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index f0fe5922..c8bdc85e 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -7,6 +7,7 @@ + diff --git a/src/DistributedLock.Core/AssemblyAttributes.cs b/src/DistributedLock.Core/AssemblyAttributes.cs index d2e9bff3..888ed9ef 100644 --- a/src/DistributedLock.Core/AssemblyAttributes.cs +++ b/src/DistributedLock.Core/AssemblyAttributes.cs @@ -15,4 +15,5 @@ [assembly: InternalsVisibleTo("DistributedLock.ZooKeeper, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] [assembly: InternalsVisibleTo("DistributedLock.MySql, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] [assembly: InternalsVisibleTo("DistributedLock.Oracle, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] +[assembly: InternalsVisibleTo("DistributedLock.MongoDB, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] #endif diff --git a/src/DistributedLock.Core/packages.lock.json b/src/DistributedLock.Core/packages.lock.json index 2f96fece..86c0653a 100644 --- a/src/DistributedLock.Core/packages.lock.json +++ b/src/DistributedLock.Core/packages.lock.json @@ -11,12 +11,6 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, - "Microsoft.CodeAnalysis.PublicApiAnalyzers": { - "type": "Direct", - "requested": "[3.3.4, )", - "resolved": "3.3.4", - "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" - }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", "requested": "[1.0.3, )", @@ -81,12 +75,6 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, - "Microsoft.CodeAnalysis.PublicApiAnalyzers": { - "type": "Direct", - "requested": "[3.3.4, )", - "resolved": "3.3.4", - "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" - }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -136,12 +124,6 @@ } }, ".NETStandard,Version=v2.1": { - "Microsoft.CodeAnalysis.PublicApiAnalyzers": { - "type": "Direct", - "requested": "[3.3.4, )", - "resolved": "3.3.4", - "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" - }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -164,17 +146,11 @@ } }, "net8.0": { - "Microsoft.CodeAnalysis.PublicApiAnalyzers": { - "type": "Direct", - "requested": "[3.3.4, )", - "resolved": "3.3.4", - "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" - }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/src/DistributedLock.MongoDB/AssemblyAttributes.cs b/src/DistributedLock.MongoDB/AssemblyAttributes.cs new file mode 100644 index 00000000..f3102190 --- /dev/null +++ b/src/DistributedLock.MongoDB/AssemblyAttributes.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; + +[assembly: + InternalsVisibleTo( + "DistributedLock.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj new file mode 100644 index 00000000..7259e5a4 --- /dev/null +++ b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj @@ -0,0 +1,62 @@ + + + + netstandard2.1;net8.0;net472; + Medallion.Threading.MongoDB + True + 4 + Latest + enable + enable + + + + 1.3.0 + 1.0.0.0 + Michael Adelson, joesdu + Provides a distributed lock implementation based on MongoDB + Copyright © 2020 Michael Adelson + MIT + distributed lock async mongodb + https://github.com/madelson/DistributedLock + https://github.com/madelson/DistributedLock + 1.0.0.0 + See https://github.com/madelson/DistributedLock#release-notes + true + ..\DistributedLock.snk + + + + True + True + True + + + embedded + + true + true + + + + False + 1591 + TRACE;DEBUG + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs new file mode 100644 index 00000000..33ffa43a --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs @@ -0,0 +1,81 @@ +using Medallion.Threading.Internal; + +namespace Medallion.Threading.MongoDB; + +public partial class MongoDistributedLock +{ + // AUTO-GENERATED + + IDistributedSynchronizationHandle? IDistributedLock.TryAcquire(TimeSpan timeout, CancellationToken cancellationToken) => + this.TryAcquire(timeout, cancellationToken); + IDistributedSynchronizationHandle IDistributedLock.Acquire(TimeSpan? timeout, CancellationToken cancellationToken) => + this.Acquire(timeout, cancellationToken); + ValueTask IDistributedLock.TryAcquireAsync(TimeSpan timeout, CancellationToken cancellationToken) => + this.TryAcquireAsync(timeout, cancellationToken).Convert(To.ValueTask); + ValueTask IDistributedLock.AcquireAsync(TimeSpan? timeout, CancellationToken cancellationToken) => + this.AcquireAsync(timeout, cancellationToken).Convert(To.ValueTask); + + /// + /// Attempts to acquire the lock synchronously. Usage: + /// + /// using (var handle = myLock.TryAcquire(...)) + /// { + /// if (handle != null) { /* we have the lock! */ } + /// } + /// // dispose releases the lock if we took it + /// + /// + /// How long to wait before giving up on the acquisition attempt. Defaults to 0 + /// Specifies a token by which the wait can be canceled + /// A which can be used to release the lock or null on failure + public MongoDistributedLockHandle? TryAcquire(TimeSpan timeout = default, CancellationToken cancellationToken = default) => + DistributedLockHelpers.TryAcquire(this, timeout, cancellationToken); + + /// + /// Acquires the lock synchronously, failing with if the attempt times out. Usage: + /// + /// using (myLock.Acquire(...)) + /// { + /// /* we have the lock! */ + /// } + /// // dispose releases the lock + /// + /// + /// How long to wait before giving up on the acquisition attempt. Defaults to + /// Specifies a token by which the wait can be canceled + /// A which can be used to release the lock + public MongoDistributedLockHandle Acquire(TimeSpan? timeout = null, CancellationToken cancellationToken = default) => + DistributedLockHelpers.Acquire(this, timeout, cancellationToken); + + /// + /// Attempts to acquire the lock asynchronously. Usage: + /// + /// await using (var handle = await myLock.TryAcquireAsync(...)) + /// { + /// if (handle != null) { /* we have the lock! */ } + /// } + /// // dispose releases the lock if we took it + /// + /// + /// How long to wait before giving up on the acquisition attempt. Defaults to 0 + /// Specifies a token by which the wait can be canceled + /// A which can be used to release the lock or null on failure + public ValueTask TryAcquireAsync(TimeSpan timeout = default, CancellationToken cancellationToken = default) => + this.As>().InternalTryAcquireAsync(timeout, cancellationToken); + + /// + /// Acquires the lock asynchronously, failing with if the attempt times out. Usage: + /// + /// await using (await myLock.AcquireAsync(...)) + /// { + /// /* we have the lock! */ + /// } + /// // dispose releases the lock + /// + /// + /// How long to wait before giving up on the acquisition attempt. Defaults to + /// Specifies a token by which the wait can be canceled + /// A which can be used to release the lock + public ValueTask AcquireAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default) => + DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken); +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.cs new file mode 100644 index 00000000..ab2e2554 --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.cs @@ -0,0 +1,172 @@ +using Medallion.Threading.Internal; +using MongoDB.Bson; +using MongoDB.Driver; +using System.Collections.Concurrent; + +namespace Medallion.Threading.MongoDB; + +/// +/// Implements a using MongoDB. +/// +public sealed partial class MongoDistributedLock : IInternalDistributedLock +{ +#if !NETSTANDARD2_1_OR_GREATER && !NET8_0_OR_GREATER + private static readonly DateTime EpochUtc = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); +#endif + + // We want to ensure indexes at most once per process per (db, collection) + private static readonly ConcurrentDictionary> IndexInitializationTasks = new(StringComparer.Ordinal); + + private readonly string _collectionName; + private readonly IMongoDatabase _database; + private readonly MongoDistributedLockOptions _options; + + /// + /// The MongoDB key used to implement the lock + /// + public string Key { get; } + + /// + /// Implements + /// + public string Name => this.Key; + + /// + /// Constructs a lock named using the provided and . + /// The locks will be stored in a collection named "DistributedLocks" by default. + /// + public MongoDistributedLock(string key, IMongoDatabase database, Action? options = null) + : this(key, database, "DistributedLocks", options) { } + + /// + /// Constructs a lock named using the provided , , and + /// . + /// + public MongoDistributedLock(string key, IMongoDatabase database, string collectionName, Action? options = null) + { + if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } + this._database = database ?? throw new ArgumentNullException(nameof(database)); + this._collectionName = collectionName ?? throw new ArgumentNullException(nameof(collectionName)); + this.Key = key; + this._options = MongoDistributedSynchronizationOptionsBuilder.GetOptions(options); + } + + ValueTask IInternalDistributedLock.InternalTryAcquireAsync(TimeoutValue timeout, CancellationToken cancellationToken) + { + return BusyWaitHelper.WaitAsync(this, + (@this, ct) => @this.TryAcquireAsync(ct), + timeout, + this._options.MinBusyWaitSleepTime, + this._options.MaxBusyWaitSleepTime, + cancellationToken); + } + + private async ValueTask TryAcquireAsync(CancellationToken cancellationToken) + { + var collection = this._database.GetCollection(this._collectionName); + + // Ensure indexes exist (TTL cleanup); do this at most once per process per (db, collection) + await EnsureIndexesCreatedAsync(collection).ConfigureAwait(false); + + // Use a unique token per acquisition attempt (like Redis' value token) + var lockId = Guid.NewGuid().ToString("N"); + var expiryMs = this._options.Expiry.InMilliseconds; + + // We avoid exception-driven contention (DuplicateKey) by using a single upsert on {_id == Key} + // and an update pipeline that only overwrites fields when the existing lock is expired. + // This is conceptually similar to Redis: SET key value NX PX . + var filter = Builders.Filter.Eq(d => d.Id, this.Key); + var update = CreateAcquireUpdate(lockId, expiryMs); + var options = new FindOneAndUpdateOptions + { + IsUpsert = true, + ReturnDocument = ReturnDocument.After + }; + + var result = await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken).ConfigureAwait(false); + + // Verify we actually got the lock + if (result != null && result.LockId == lockId) + { + return new(collection, + this.Key, + lockId, + this._options.Expiry, + this._options.ExtensionCadence); + } + + return null; + } + + private static UpdateDefinition CreateAcquireUpdate(string lockId, int expiryMs) + { + // expired := ifNull(expiresAt, epoch) <= $$NOW + var expiredOrMissing = new BsonDocument( + "$lte", + new BsonArray + { + new BsonDocument("$ifNull", new BsonArray { "$expiresAt", new BsonDateTime( +#if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER + DateTime.UnixEpoch +#else + EpochUtc +#endif + ) }), + "$$NOW" + } + ); + + var newExpiresAt = new BsonDocument( + "$dateAdd", + new BsonDocument + { + { "startDate", "$$NOW" }, + { "unit", "millisecond" }, + { "amount", expiryMs } + } + ); + + var setStage = new BsonDocument( + "$set", + new BsonDocument + { + // Only overwrite lock fields when the previous lock is expired/missing + { "lockId", new BsonDocument("$cond", new BsonArray { expiredOrMissing, lockId, "$lockId" }) }, + { "expiresAt", new BsonDocument("$cond", new BsonArray { expiredOrMissing, newExpiresAt, "$expiresAt" }) }, + { "acquiredAt", new BsonDocument("$cond", new BsonArray { expiredOrMissing, "$$NOW", "$acquiredAt" }) } + } + ); + + return new PipelineUpdateDefinition(new[] { setStage }); + } + + private static Task EnsureIndexesCreatedAsync(IMongoCollection collection) + { + // Best-effort TTL index to clean up expired rows over time. + // Note: TTL monitors run on a schedule; correctness MUST NOT depend on this. + var databaseName = collection.Database.DatabaseNamespace.DatabaseName; + var key = databaseName + "/" + collection.CollectionNamespace.CollectionName; + + var lazy = IndexInitializationTasks.GetOrAdd(key, _ => new Lazy(() => CreateIndexesAsync(collection))); + return lazy.Value; + } + + private static async Task CreateIndexesAsync(IMongoCollection collection) + { + try + { + var indexKeys = Builders.IndexKeys.Ascending(d => d.ExpiresAt); + var indexOptions = new CreateIndexOptions + { + // TTL cleanup: remove documents once expiresAt < now + ExpireAfter = TimeSpan.Zero, + }; + var indexModel = new CreateIndexModel(indexKeys, indexOptions); + await collection.Indexes.CreateOneAsync(indexModel, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + catch + { + // Index may already exist, or server may reject options (e.g., conflicts). Ignore. + } + } +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs new file mode 100644 index 00000000..2c0b694f --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs @@ -0,0 +1,167 @@ +using Medallion.Threading.Internal; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Medallion.Threading.MongoDB; + +/// +/// Implements for +/// +public sealed class MongoDistributedLockHandle : IDistributedSynchronizationHandle +{ + private readonly CancellationTokenSource _cts; + private readonly IMongoCollection _collection; + private readonly Task _extensionTask; + private readonly string _key; + private readonly string _lockId; + private int _disposed; + + /// + /// Implements + /// + public CancellationToken HandleLostToken => this._cts.Token; + + internal MongoDistributedLockHandle( + IMongoCollection collection, + string key, + string lockId, + TimeoutValue expiry, + TimeoutValue extensionCadence) + { + this._collection = collection; + this._key = key; + this._lockId = lockId; + this._cts = new(); + + // Start background task to extend the lock + this._extensionTask = this.ExtendLockAsync(expiry, extensionCadence, this._cts.Token); + } + + /// + /// Releases the lock + /// + public void Dispose() + { + if (Interlocked.Exchange(ref this._disposed, 1) is not 0) + { + return; + } + + this._cts.Cancel(); + try + { + // Do not use HandleLostToken here: it is backed by _cts and has been canceled above. + this._extensionTask.GetAwaiter().GetResult(); + } + catch + { + // Ignore exceptions during cleanup + } + finally + { + this._cts.Dispose(); + try + { + this.ReleaseLockAsync(CancellationToken.None).AsTask().GetAwaiter().GetResult(); + } + catch + { + // Ignore errors during release + } + } + } + + /// + /// Releases the lock asynchronously + /// + public async ValueTask DisposeAsync() + { + if (Interlocked.Exchange(ref this._disposed, 1) is 0) + { +#if NET8_0_OR_GREATER + await this._cts.CancelAsync().ConfigureAwait(false); +#else + this._cts.Cancel(); +#endif + try + { + await this._extensionTask.ConfigureAwait(false); + } + catch + { + // Ignore exceptions during cleanup + } + finally + { + this._cts.Dispose(); + await this.ReleaseLockAsync(CancellationToken.None).ConfigureAwait(false); + } + } + } + + private async Task ExtendLockAsync(TimeoutValue expiry, TimeoutValue extensionCadence, CancellationToken cancellationToken) + { + try + { + while (!cancellationToken.IsCancellationRequested) + { + await Task.Delay(extensionCadence.TimeSpan, cancellationToken).ConfigureAwait(false); + var filter = Builders.Filter.Eq(d => d.Id, this._key) & Builders.Filter.Eq(d => d.LockId, this._lockId); + + // Use server time ($$NOW) for expiry to avoid client clock skew. + var newExpiresAt = new BsonDocument( + "$dateAdd", + new BsonDocument + { + { "startDate", "$$NOW" }, + { "unit", "millisecond" }, + { "amount", expiry.InMilliseconds } + } + ); + var update = new PipelineUpdateDefinition( + new[] { new BsonDocument("$set", new BsonDocument("expiresAt", newExpiresAt)) } + ); + + var result = await this._collection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken).ConfigureAwait(false); + + // If we failed to extend, the lock was lost + if (result.MatchedCount is not 0) + { + continue; + } +#if NET8_0_OR_GREATER + await this._cts.CancelAsync().ConfigureAwait(false); +#else + this._cts.Cancel(); +#endif + break; + } + } + catch (OperationCanceledException) + { + // Expected when disposing + } + catch + { + // Lock extension failed, signal that the lock is lost +#if NET8_0_OR_GREATER + await this._cts.CancelAsync().ConfigureAwait(false); +#else + this._cts.Cancel(); +#endif + } + } + + private async ValueTask ReleaseLockAsync(CancellationToken cancellationToken) + { + try + { + var filter = Builders.Filter.Eq(d => d.Id, this._key) & Builders.Filter.Eq(d => d.LockId, this._lockId); + await this._collection.DeleteOneAsync(filter, cancellationToken).ConfigureAwait(false); + } + catch + { + // Ignore errors during release + } + } +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs new file mode 100644 index 00000000..b021030d --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs @@ -0,0 +1,124 @@ +using Medallion.Threading.Internal; + +namespace Medallion.Threading.MongoDB; + +/// +/// Options for configuring a MongoDB-based distributed synchronization algorithm +/// +public sealed class MongoDistributedSynchronizationOptionsBuilder +{ + private static readonly TimeoutValue DefaultExpiry = TimeSpan.FromSeconds(30); + + /// + /// We don't want to allow expiry to go too low, since then the lock doesn't even work + /// + private static readonly TimeoutValue MinimumExpiry = TimeSpan.FromSeconds(.1); + + private TimeoutValue? _expiry, _extensionCadence, _minBusyWaitSleepTime, _maxBusyWaitSleepTime; + + private MongoDistributedSynchronizationOptionsBuilder() { } + + /// + /// Specifies how long the lock will last, absent auto-extension. Because auto-extension exists, + /// this value generally will have little effect on program behavior. However, making the expiry longer means that + /// auto-extension requests can occur less frequently, saving resources. On the other hand, when a lock is abandoned + /// without explicit release (e.g. if the holding process crashes), the expiry determines how long other processes + /// would need to wait in order to acquire it. + /// Defaults to 30s. + /// + public MongoDistributedSynchronizationOptionsBuilder Expiry(TimeSpan expiry) + { + var expiryTimeoutValue = new TimeoutValue(expiry, nameof(expiry)); + if (expiryTimeoutValue.IsInfinite || expiryTimeoutValue.CompareTo(MinimumExpiry) < 0) + { + throw new ArgumentOutOfRangeException(nameof(expiry), expiry, $"Must be >= {MinimumExpiry.TimeSpan} and < ∞"); + } + _expiry = expiryTimeoutValue; + return this; + } + + /// + /// Determines how frequently the lock will be extended while held. More frequent extension means more unnecessary requests + /// but also a lower chance of losing the lock due to the process hanging or otherwise failing to get its extension request in + /// before the lock expiry elapses. + /// Defaults to 1/3 of the expiry time. + /// + public MongoDistributedSynchronizationOptionsBuilder ExtensionCadence(TimeSpan extensionCadence) + { + _extensionCadence = new TimeoutValue(extensionCadence, nameof(extensionCadence)); + return this; + } + + /// + /// Waiting to acquire a lock requires a busy wait that alternates acquire attempts and sleeps. + /// This determines how much time is spent sleeping between attempts. Lower values will raise the + /// volume of acquire requests under contention but will also raise the responsiveness (how long + /// it takes a waiter to notice that a contended the lock has become available). + /// Specifying a range of values allows the implementation to select an actual value in the range + /// at random for each sleep. This helps avoid the case where two clients become "synchronized" + /// in such a way that results in one client monopolizing the lock. + /// The default is [10ms, 800ms] + /// + public MongoDistributedSynchronizationOptionsBuilder BusyWaitSleepTime(TimeSpan min, TimeSpan max) + { + var minTimeoutValue = new TimeoutValue(min, nameof(min)); + var maxTimeoutValue = new TimeoutValue(max, nameof(max)); + if (minTimeoutValue.IsInfinite) { throw new ArgumentOutOfRangeException(nameof(min), "may not be infinite"); } + if (maxTimeoutValue.IsInfinite || maxTimeoutValue.CompareTo(min) < 0) + { + throw new ArgumentOutOfRangeException(nameof(max), max, "must be non-infinite and greater than " + nameof(min)); + } + _minBusyWaitSleepTime = minTimeoutValue; + _maxBusyWaitSleepTime = maxTimeoutValue; + return this; + } + + internal static MongoDistributedLockOptions GetOptions(Action? optionsBuilder) + { + MongoDistributedSynchronizationOptionsBuilder? options; + if (optionsBuilder != null) + { + options = new(); + optionsBuilder(options); + } + else + { + options = null; + } + var expiry = options?._expiry ?? DefaultExpiry; + TimeoutValue extensionCadence; + if (options?._extensionCadence is { } specifiedExtensionCadence) + { + if (specifiedExtensionCadence.CompareTo(expiry) >= 0) + { + throw new ArgumentOutOfRangeException(nameof(extensionCadence), + specifiedExtensionCadence.TimeSpan, + $"{nameof(extensionCadence)} must be less than {nameof(expiry)} ({expiry.TimeSpan})"); + } + extensionCadence = specifiedExtensionCadence; + } + else + { + extensionCadence = TimeSpan.FromMilliseconds(expiry.InMilliseconds / 3.0); + } + return new(expiry, + extensionCadence, + options?._minBusyWaitSleepTime ?? TimeSpan.FromMilliseconds(10), + options?._maxBusyWaitSleepTime ?? TimeSpan.FromSeconds(0.8)); + } +} + +internal readonly struct MongoDistributedLockOptions( + TimeoutValue expiry, + TimeoutValue extensionCadence, + TimeoutValue minBusyWaitSleepTime, + TimeoutValue maxBusyWaitSleepTime) +{ + public TimeoutValue Expiry { get; } = expiry; + + public TimeoutValue ExtensionCadence { get; } = extensionCadence; + + public TimeoutValue MinBusyWaitSleepTime { get; } = minBusyWaitSleepTime; + + public TimeoutValue MaxBusyWaitSleepTime { get; } = maxBusyWaitSleepTime; +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs new file mode 100644 index 00000000..c3a46ace --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs @@ -0,0 +1,44 @@ +using MongoDB.Driver; + +namespace Medallion.Threading.MongoDB; + +/// +/// Implements for . +/// +public sealed class MongoDistributedSynchronizationProvider : IDistributedLockProvider +{ + private readonly string _collectionName; + private readonly IMongoDatabase _database; + private readonly Action? _options; + + /// + /// Constructs a that connects to the provided + /// and uses the provided . Locks will be stored in a collection named "DistributedLocks" by default. + /// + public MongoDistributedSynchronizationProvider(IMongoDatabase database, Action? options = null) + : this(database, "DistributedLocks", options) { } + + /// + /// Constructs a that connects to the provided , + /// stores locks in the specified , and uses the provided . + /// + public MongoDistributedSynchronizationProvider(IMongoDatabase database, string collectionName, Action? options = null) + { + _database = database ?? throw new ArgumentNullException(nameof(database)); + _collectionName = collectionName ?? throw new ArgumentNullException(nameof(collectionName)); + _options = options; + } + + /// + /// Creates a using the given . + /// + public MongoDistributedLock CreateLock(string name) + { + return new(name, _database, _collectionName, _options); + } + + IDistributedLock IDistributedLockProvider.CreateLock(string name) + { + return CreateLock(name); + } +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoLockDocument.cs b/src/DistributedLock.MongoDB/MongoLockDocument.cs new file mode 100644 index 00000000..d6359a29 --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoLockDocument.cs @@ -0,0 +1,39 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace Medallion.Threading.MongoDB; + +/// +/// Represents a lock document stored in MongoDB +/// +// ReSharper disable once ClassNeverInstantiated.Global +internal sealed class MongoLockDocument +{ + /// + /// When the lock was acquired (UTC) + /// + [BsonElement("acquiredAt")] + [BsonRepresentation(BsonType.DateTime)] + public DateTime AcquiredAt { get; set; } + + /// + /// When the lock expires (UTC) + /// + [BsonElement("expiresAt")] + [BsonRepresentation(BsonType.DateTime)] + public DateTime ExpiresAt { get; set; } + + /// + /// The lock name/key (MongoDB document ID) + /// + [BsonId] + [BsonRepresentation(BsonType.String)] + public string Id { get; set; } = string.Empty; + + /// + /// Unique identifier for this lock acquisition + /// + [BsonElement("lockId")] + [BsonRepresentation(BsonType.String)] + public string LockId { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/PublicAPI.Shipped.txt b/src/DistributedLock.MongoDB/PublicAPI.Shipped.txt new file mode 100644 index 00000000..5e440dc2 --- /dev/null +++ b/src/DistributedLock.MongoDB/PublicAPI.Shipped.txt @@ -0,0 +1,22 @@ +#nullable enable +Medallion.Threading.MongoDB.MongoDistributedLock +Medallion.Threading.MongoDB.MongoDistributedLock.Acquire(System.TimeSpan? timeout = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Medallion.Threading.MongoDB.MongoDistributedLockHandle! +Medallion.Threading.MongoDB.MongoDistributedLock.AcquireAsync(System.TimeSpan? timeout = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +Medallion.Threading.MongoDB.MongoDistributedLock.Key.get -> string! +Medallion.Threading.MongoDB.MongoDistributedLock.Name.get -> string! +Medallion.Threading.MongoDB.MongoDistributedLock.MongoDistributedLock(string! key, MongoDB.Driver.IMongoDatabase! database, System.Action? options = null) -> void +Medallion.Threading.MongoDB.MongoDistributedLock.MongoDistributedLock(string! key, MongoDB.Driver.IMongoDatabase! database, string! collectionName, System.Action? options = null) -> void +Medallion.Threading.MongoDB.MongoDistributedLock.TryAcquire(System.TimeSpan timeout = default(System.TimeSpan), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Medallion.Threading.MongoDB.MongoDistributedLockHandle? +Medallion.Threading.MongoDB.MongoDistributedLock.TryAcquireAsync(System.TimeSpan timeout = default(System.TimeSpan), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +Medallion.Threading.MongoDB.MongoDistributedLockHandle +Medallion.Threading.MongoDB.MongoDistributedLockHandle.Dispose() -> void +Medallion.Threading.MongoDB.MongoDistributedLockHandle.DisposeAsync() -> System.Threading.Tasks.ValueTask +Medallion.Threading.MongoDB.MongoDistributedLockHandle.HandleLostToken.get -> System.Threading.CancellationToken +Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder +Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder.BusyWaitSleepTime(System.TimeSpan min, System.TimeSpan max) -> Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder! +Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder.Expiry(System.TimeSpan expiry) -> Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder! +Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder.ExtensionCadence(System.TimeSpan extensionCadence) -> Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder! +Medallion.Threading.MongoDB.MongoDistributedSynchronizationProvider +Medallion.Threading.MongoDB.MongoDistributedSynchronizationProvider.CreateLock(string! name) -> Medallion.Threading.MongoDB.MongoDistributedLock! +Medallion.Threading.MongoDB.MongoDistributedSynchronizationProvider.MongoDistributedSynchronizationProvider(MongoDB.Driver.IMongoDatabase! database, System.Action? options = null) -> void +Medallion.Threading.MongoDB.MongoDistributedSynchronizationProvider.MongoDistributedSynchronizationProvider(MongoDB.Driver.IMongoDatabase! database, string! collectionName, System.Action? options = null) -> void diff --git a/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt b/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt new file mode 100644 index 00000000..815c9200 --- /dev/null +++ b/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/README.md b/src/DistributedLock.MongoDB/README.md new file mode 100644 index 00000000..2cac9f0d --- /dev/null +++ b/src/DistributedLock.MongoDB/README.md @@ -0,0 +1,106 @@ +# DistributedLock.MongoDB + +This library provides distributed lock implementation using MongoDB as the backing store. + +## Installation + +```bash +dotnet add package DistributedLock.MongoDB +``` + +## Usage + +### Basic Lock Usage + +```csharp +using Medallion.Threading.MongoDB; +using MongoDB.Driver; + +// Create MongoDB client and database +var client = new MongoClient("mongodb://localhost:27017"); +var database = client.GetDatabase("myDatabase"); + +// Create a lock +var @lock = new MongoDistributedLock("myLockName", database); + +// Acquire the lock +await using (var handle = await @lock.AcquireAsync()) +{ + // Critical section protected by the lock + Console.WriteLine("Lock acquired!"); +} +// Lock is automatically released when disposed +``` + +### Using the Provider + +```csharp +using Medallion.Threading.MongoDB; +using MongoDB.Driver; + +var client = new MongoClient("mongodb://localhost:27017"); +var database = client.GetDatabase("myDatabase"); + +// Create a provider +var provider = new MongoDistributedSynchronizationProvider(database); + +// Use the provider to create locks +var lock1 = provider.CreateLock("lock1"); +var lock2 = provider.CreateLock("lock2"); + +await using (var handle = await lock1.AcquireAsync()) +{ + // Do work... +} +``` + +### Configuration Options + +You can customize the lock behavior using the options builder: + +```csharp +var @lock = new MongoDistributedLock( + "myLockName", + database, + options => options + .Expiry(TimeSpan.FromSeconds(30)) // Lock expiry time + .ExtensionCadence(TimeSpan.FromSeconds(10)) // How often to extend the lock + .BusyWaitSleepTime( // Sleep time between acquire attempts + min: TimeSpan.FromMilliseconds(10), + max: TimeSpan.FromMilliseconds(800)) +); +``` + +### Custom Collection Name + +By default, locks are stored in a collection named "DistributedLocks". You can specify a custom collection name: + +```csharp +var @lock = new MongoDistributedLock("myLockName", database, "MyCustomLocks"); +``` + +## How It Works + +The MongoDB distributed lock uses MongoDB's document upsert and update operations to implement distributed locking: + +1. **Acquisition**: Attempts to insert or update a document with the lock key and a unique lock ID +2. **Extension**: Automatically extends the lock expiry while held to prevent timeout +3. **Release**: Deletes the lock document when disposed +4. **Expiry**: Locks automatically expire if not extended, allowing recovery from crashed processes + +## Features + +- ✅ Async/await support +- ✅ Automatic lock extension while held +- ✅ Configurable expiry and extension cadence +- ✅ Lock abandonment protection via expiry +- ✅ `CancellationToken` support +- ✅ Handle lost token notification +- ✅ Multi-target support: .NET 8, .NET Standard 2.1, .NET Framework 4.7.2 + +## Notes + +- The lock collection will have an index on the `expiresAt` field for efficient queries +- Lock extension happens automatically in the background +- If lock extension fails, the `HandleLostToken` will be signaled +- Stale locks (from crashed processes) will automatically expire based on the expiry setting diff --git a/src/DistributedLock.MongoDB/packages.lock.json b/src/DistributedLock.MongoDB/packages.lock.json new file mode 100644 index 00000000..bfc80002 --- /dev/null +++ b/src/DistributedLock.MongoDB/packages.lock.json @@ -0,0 +1,518 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.7.2": { + "Microsoft.CodeAnalysis.PublicApiAnalyzers": { + "type": "Direct", + "requested": "[3.3.4, )", + "resolved": "3.3.4", + "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "MongoDB.Driver": { + "type": "Direct", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.2", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "System.Net.Http": "4.3.4", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "ZstdSharp.Port": "0.7.3" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0", + "System.Buffers": "4.5.1" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "4.7.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "System.Security.Cryptography.X509Certificates": "4.3.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==" + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==" + }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5" + } + }, + "distributedlock.core": { + "type": "Project", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "[8.0.0, )", + "System.ValueTuple": "[4.5.0, )" + } + } + }, + ".NETStandard,Version=v2.1": { + "Microsoft.CodeAnalysis.PublicApiAnalyzers": { + "type": "Direct", + "requested": "[3.3.4, )", + "resolved": "3.3.4", + "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "MongoDB.Driver": { + "type": "Direct", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.2", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.7.1" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "distributedlock.core": { + "type": "Project" + } + }, + "net8.0": { + "Microsoft.CodeAnalysis.PublicApiAnalyzers": { + "type": "Direct", + "requested": "[3.3.4, )", + "resolved": "3.3.4", + "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" + }, + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "MongoDB.Driver": { + "type": "Direct", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.2", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "distributedlock.core": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/src/DistributedLock.Postgres/packages.lock.json b/src/DistributedLock.Postgres/packages.lock.json index 797320b6..80b0048d 100644 --- a/src/DistributedLock.Postgres/packages.lock.json +++ b/src/DistributedLock.Postgres/packages.lock.json @@ -518,9 +518,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/src/DistributedLock.Tests/DistributedLock.Tests.csproj b/src/DistributedLock.Tests/DistributedLock.Tests.csproj index be82219e..07020675 100644 --- a/src/DistributedLock.Tests/DistributedLock.Tests.csproj +++ b/src/DistributedLock.Tests/DistributedLock.Tests.csproj @@ -31,6 +31,7 @@ + \ No newline at end of file diff --git a/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs new file mode 100644 index 00000000..d873e300 --- /dev/null +++ b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs @@ -0,0 +1,45 @@ +using Medallion.Threading.MongoDB; +using MongoDB.Driver; + +namespace Medallion.Threading.Tests.MongoDB; + +public sealed class TestingMongoDistributedLockProvider : TestingLockProvider +{ + private readonly string _collectionName = "DistributedLocks_" + Guid.NewGuid().ToString("N"); + private readonly IMongoDatabase _database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + + public override IDistributedLock CreateLockWithExactName(string name) + { + var @lock = new MongoDistributedLock(name, _database, _collectionName); + Strategy.KillHandleAction = () => + { + var collection = _database.GetCollection(_collectionName); + collection.DeleteOne(Builders.Filter.Eq(d => d.Id, name)); + }; + return @lock; + } + + public override string GetSafeName(string name) + { + return new MongoDistributedLock(name, _database, _collectionName).Name; + } + + public override string GetCrossProcessLockType() + { + return nameof(MongoDistributedLock); + } + + public override void Dispose() + { + // Clean up test collection + try + { + _database.DropCollection(_collectionName); + } + catch + { + // Ignore cleanup errors + } + base.Dispose(); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs new file mode 100644 index 00000000..4324c1d5 --- /dev/null +++ b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs @@ -0,0 +1,11 @@ +namespace Medallion.Threading.Tests.MongoDB; + +public sealed class TestingMongoDbSynchronizationStrategy : TestingSynchronizationStrategy +{ + public Action? KillHandleAction { get; set; } + + public override void PrepareForHandleAbandonment() + { + KillHandleAction?.Invoke(); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs b/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs new file mode 100644 index 00000000..8c8ce172 --- /dev/null +++ b/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs @@ -0,0 +1,31 @@ +using MongoDB.Driver; +using System.IO; + +namespace Medallion.Threading.Tests.MongoDB; + +internal static class MongoDbCredentials +{ + private static string? _connectionString; + + public static string GetConnectionString(string baseDirectory) + { + if (_connectionString != null) { return _connectionString; } + var file = Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", "..", "credentials", "mongodb.txt")); + if (File.Exists(file)) + { + _connectionString = File.ReadAllText(file).Trim(); + } + else + { + // Default local MongoDB connection + _connectionString = "mongodb://localhost:27017"; + } + return _connectionString; + } + + public static IMongoDatabase GetDefaultDatabase(string baseDirectory) + { + var client = new MongoClient(GetConnectionString(baseDirectory)); + return client.GetDatabase("DistributedLockTests"); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Tests/CombinatorialTests.cs b/src/DistributedLock.Tests/Tests/CombinatorialTests.cs index cfa4c9ab..90dd2ca3 100644 --- a/src/DistributedLock.Tests/Tests/CombinatorialTests.cs +++ b/src/DistributedLock.Tests/Tests/CombinatorialTests.cs @@ -11,6 +11,11 @@ namespace Medallion.Threading.Tests.FileSystem [Category("CI")] public class Core_File_FileSynchronizationStrategyTest : DistributedLockCoreTestCases { } } +namespace Medallion.Threading.Tests.MongoDB +{ + public class Core_Mongo_MongoDbSynchronizationStrategyTest : DistributedLockCoreTestCases { } +} + namespace Medallion.Threading.Tests.MySql { public class ConnectionStringStrategy_MySql_ConnectionMultiplexingSynchronizationStrategy_MariaDbDb_MariaDbDb_ConnectionMultiplexingSynchronizationStrategy_MariaDbDb_MariaDbDbTest : ConnectionStringStrategyTestCases, TestingMariaDbDb>, TestingConnectionMultiplexingSynchronizationStrategy, TestingMariaDbDb> { } diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs new file mode 100644 index 00000000..9c753478 --- /dev/null +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs @@ -0,0 +1,120 @@ +using Medallion.Threading.MongoDB; +using MongoDB.Driver; +using Moq; +using NUnit.Framework; + +namespace Medallion.Threading.Tests.MongoDB; + +public class MongoDistributedLockTest +{ + [Test] + public async Task TestBasicLockFunctionality() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + var @lock = new MongoDistributedLock(lockName, database); + await using (var handle = await @lock.AcquireAsync()) + { + Assert.That(handle, Is.Not.Null); + // Use async TryAcquireAsync instead of synchronous IsHeld() + var handle2 = await @lock.TryAcquireAsync(TimeSpan.Zero); + Assert.That(handle2, Is.Null, "Lock should be held"); + if (handle2 != null) + { + await handle2.DisposeAsync(); + } + } + // Verify lock is released + await using (var handle = await @lock.TryAcquireAsync(TimeSpan.FromSeconds(1))) + { + Assert.That(handle, Is.Not.Null, "Lock should be released"); + } + } + + [Test] + public async Task TestCustomCollectionName() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + const string CustomCollectionName = "CustomLocks"; + var @lock = new MongoDistributedLock(lockName, database, CustomCollectionName); + await using (var handle = await @lock.AcquireAsync()) + { + Assert.That(handle, Is.Not.Null); + } + + // Verify the collection was created + var collectionExists = (await database.ListCollectionNamesAsync()).ToList().Contains(CustomCollectionName); + Assert.That(collectionExists, Is.True); + + // Cleanup + await database.DropCollectionAsync(CustomCollectionName); + } + + [Test] + public async Task TestHandleLostToken() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + // Configure a short extension cadence so the test doesn't have to wait too long + var @lock = new MongoDistributedLock(lockName, database, options: o => o.ExtensionCadence(TimeSpan.FromMilliseconds(500))); + await using var handle = await @lock.AcquireAsync(); + Assert.That(handle, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(handle.HandleLostToken.CanBeCanceled, Is.True); + Assert.That(handle.HandleLostToken.IsCancellationRequested, Is.False); + }); + + // Manually delete the lock document to simulate lock loss + var collection = database.GetCollection("DistributedLocks"); + await collection.DeleteOneAsync(Builders.Filter.Eq(d => d.Id, lockName)); + + // Wait a bit for the extension task to detect the loss + await Task.Delay(TimeSpan.FromSeconds(2)); + Assert.That(handle.HandleLostToken.IsCancellationRequested, Is.True, "HandleLostToken should be signaled when lock is lost"); + } + + [Test] + public async Task TestLockContentionAsync() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + var lock1 = new MongoDistributedLock(lockName, database); + var lock2 = new MongoDistributedLock(lockName, database); + await using (var handle1 = await lock1.AcquireAsync()) + { + Assert.That(handle1, Is.Not.Null); + var handle2 = await lock2.TryAcquireAsync(TimeSpan.FromMilliseconds(100)); + Assert.That(handle2, Is.Null, "Should not acquire lock while held by another instance"); + } + + // After release, lock2 should be able to acquire + await using (var handle2 = await lock2.AcquireAsync(TimeSpan.FromSeconds(5))) + { + Assert.That(handle2, Is.Not.Null); + } + } + + [Test] + [Category("CI")] + public void TestName() + { + const string Name = "\0🐉汉字\b\r\n\\"; + var database = new Mock(MockBehavior.Strict).Object; + var @lock = new MongoDistributedLock(Name, database); + @lock.Name.ShouldEqual(Name); + @lock.Key.ShouldEqual(Name); + } + + [Test] + [Category("CI")] + public void TestValidatesConstructorParameters() + { + var database = new Mock(MockBehavior.Strict).Object; + Assert.Throws(() => new MongoDistributedLock(null!, database)); + Assert.Throws(() => new MongoDistributedLock(string.Empty, database)); + Assert.Throws(() => new MongoDistributedLock("key", null!)); + Assert.Throws(() => new MongoDistributedLock("key", database, (string)null!)); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs new file mode 100644 index 00000000..1e2ae9c0 --- /dev/null +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs @@ -0,0 +1,63 @@ +using Medallion.Threading.MongoDB; +using NUnit.Framework; + +namespace Medallion.Threading.Tests.MongoDB; + +public class MongoDistributedSynchronizationOptionsBuilderTest +{ + [Test] + public void TestBusyWaitSleepTimeValidation() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options + .BusyWaitSleepTime(TimeSpan.FromSeconds(1), TimeSpan.FromMilliseconds(500)))); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options + .BusyWaitSleepTime(Timeout.InfiniteTimeSpan, TimeSpan.FromSeconds(1)))); + Assert.DoesNotThrow(() => + new MongoDistributedLock("test", database, options => options + .BusyWaitSleepTime(TimeSpan.FromMilliseconds(10), TimeSpan.FromSeconds(1)))); + } + + [Test] + public void TestExpiryValidation() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options.Expiry(TimeSpan.FromMilliseconds(50)))); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options.Expiry(Timeout.InfiniteTimeSpan))); + Assert.DoesNotThrow(() => + new MongoDistributedLock("test", database, options => options.Expiry(TimeSpan.FromSeconds(1)))); + } + + [Test] + public void TestExtensionCadenceValidation() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options + .Expiry(TimeSpan.FromSeconds(5)) + .ExtensionCadence(TimeSpan.FromSeconds(10)))); + Assert.DoesNotThrow(() => + new MongoDistributedLock("test", database, options => options + .Expiry(TimeSpan.FromSeconds(10)) + .ExtensionCadence(TimeSpan.FromSeconds(3)))); + } + + [Test] + public async Task TestOptionsAreApplied() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + var @lock = new MongoDistributedLock(lockName, database, options => options + .Expiry(TimeSpan.FromSeconds(60)) + .ExtensionCadence(TimeSpan.FromSeconds(20)) + .BusyWaitSleepTime(TimeSpan.FromMilliseconds(5), TimeSpan.FromMilliseconds(100))); + await using (var handle = await @lock.AcquireAsync()) + { + Assert.That(handle, Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs new file mode 100644 index 00000000..1aa97e1e --- /dev/null +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs @@ -0,0 +1,70 @@ +using Medallion.Threading.MongoDB; +using MongoDB.Driver; +using NUnit.Framework; + +namespace Medallion.Threading.Tests.MongoDB; + +public class MongoDistributedSynchronizationProviderTest +{ + [Test] + public void TestArgumentValidation() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + Assert.Throws(() => new MongoDistributedSynchronizationProvider(null!)); + Assert.Throws(() => new MongoDistributedSynchronizationProvider(database, (string)null!)); + Assert.DoesNotThrow(() => new MongoDistributedSynchronizationProvider(database)); + Assert.DoesNotThrow(() => new MongoDistributedSynchronizationProvider(database, "CustomCollection")); + } + + [Test] + public void TestIDistributedLockProviderInterface() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + IDistributedLockProvider provider = new MongoDistributedSynchronizationProvider(database); + var @lock = provider.CreateLock("interfaceTest"); + Assert.That(@lock, Is.Not.Null); + Assert.That(@lock, Is.InstanceOf()); + Assert.That(@lock.Name, Is.EqualTo("interfaceTest")); + } + + [Test] + public async Task TestProviderCreateLock() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var provider = new MongoDistributedSynchronizationProvider(database); + var lock1 = provider.CreateLock("testLock1"); + var lock2 = provider.CreateLock("testLock2"); + Assert.That(lock1, Is.Not.Null); + Assert.That(lock2, Is.Not.Null); + Assert.That(lock1.Name, Is.EqualTo("testLock1")); + Assert.That(lock2.Name, Is.EqualTo("testLock2")); + + // Test that locks work + await using (var handle1 = await lock1.AcquireAsync()) + await using (var handle2 = await lock2.AcquireAsync()) + { + Assert.That(handle1, Is.Not.Null); + Assert.That(handle2, Is.Not.Null); + } + } + + [Test] + public async Task TestProviderWithCustomCollection() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + const string CustomCollection = "TestProviderLocks"; + var provider = new MongoDistributedSynchronizationProvider(database, CustomCollection); + var @lock = provider.CreateLock("testLock"); + await using (var handle = await @lock.AcquireAsync()) + { + Assert.That(handle, Is.Not.Null); + } + + // Verify the custom collection was used + var collectionExists = (await database.ListCollectionNamesAsync()).ToList().Contains(CustomCollection); + Assert.That(collectionExists, Is.True); + + // Cleanup + await database.DropCollectionAsync(CustomCollection); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/packages.lock.json b/src/DistributedLock.Tests/packages.lock.json index 89913e0e..9178bc4c 100644 --- a/src/DistributedLock.Tests/packages.lock.json +++ b/src/DistributedLock.Tests/packages.lock.json @@ -1,593 +1,6 @@ { "version": 2, "dependencies": { - ".NETFramework,Version=v4.7.2": { - "MedallionShell.StrongName": { - "type": "Direct", - "requested": "[1.6.2, )", - "resolved": "1.6.2", - "contentHash": "x7kIh8HiLHQrm5tcLEwNXhYfIHjQoK8ZS9MPx/LcCgNubtfFVJZm8Kk5/FSOalHjlXizcLAm6733L691l8cr/Q==" - }, - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.9.0" - } - }, - "Moq": { - "type": "Direct", - "requested": "[4.20.70, )", - "resolved": "4.20.70", - "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", - "dependencies": { - "Castle.Core": "5.1.1", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "NUnit": { - "type": "Direct", - "requested": "[3.14.0, )", - "resolved": "3.14.0", - "contentHash": "R7iPwD7kbOaP3o2zldWJbWeMQAvDKD0uld27QvA3PAALl1unl7x0v2J7eGiJOYjimV/BuGT4VJmr45RjS7z4LA==" - }, - "NUnit.Analyzers": { - "type": "Direct", - "requested": "[4.1.0, )", - "resolved": "4.1.0", - "contentHash": "Odd1RusSMnfswIiCPbokAqmlcCCXjQ20poaXWrw+CWDnBY1vQ/x6ZGqgyJXpebPq5Uf8uEBe5iOAySsCdSrWdQ==" - }, - "NUnit3TestAdapter": { - "type": "Direct", - "requested": "[4.5.0, )", - "resolved": "4.5.0", - "contentHash": "s8JpqTe9bI2f49Pfr3dFRfoVSuFQyraTj68c3XXjIS/MRGvvkLnrg6RLqnTjdShX+AdFUCCU/4Xex58AdUfs6A==" - }, - "System.Data.SqlClient": { - "type": "Direct", - "requested": "[4.8.6, )", - "resolved": "4.8.6", - "contentHash": "2Ij/LCaTQRyAi5lAv7UUTV9R2FobC8xN9mE0fXBZohum/xLl8IZVmE98Rq5ugQHjCgTBRKqpXRb4ORulRdA6Ig==" - }, - "Azure.Core": { - "type": "Transitive", - "resolved": "1.38.0", - "contentHash": "IuEgCoVA0ef7E4pQtpC3+TkPbzaoQfa77HlfJDmfuaJUCVJmn7fT0izamZiryW5sYUFKizsftIxMkXKbgIcPMQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "System.ClientModel": "1.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.1", - "System.Memory.Data": "1.0.2", - "System.Numerics.Vectors": "4.5.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Azure.Identity": { - "type": "Transitive", - "resolved": "1.11.4", - "contentHash": "Sf4BoE6Q3jTgFkgBkx7qztYOFELBCo+wQgpYDwal/qJ1unBH73ywPztIJKXBXORRzAeNijsuxhk94h0TIMvfYg==", - "dependencies": { - "Azure.Core": "1.38.0", - "Microsoft.Identity.Client": "4.61.3", - "Microsoft.Identity.Client.Extensions.Msal": "4.61.3", - "System.Memory": "4.5.4", - "System.Security.Cryptography.ProtectedData": "4.7.0", - "System.Text.Json": "4.7.2", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Azure.Storage.Common": { - "type": "Transitive", - "resolved": "12.18.1", - "contentHash": "ohCslqP9yDKIn+DVjBEOBuieB1QwsUCz+BwHYNaJ3lcIsTSiI4Evnq81HcKe8CqM8qvdModbipVQKpnxpbdWqA==", - "dependencies": { - "Azure.Core": "1.36.0", - "System.IO.Hashing": "6.0.0" - } - }, - "Castle.Core": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Bcl.HashCode": { - "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" - }, - "Microsoft.Data.SqlClient.SNI": { - "type": "Transitive", - "resolved": "5.2.0", - "contentHash": "0p2KMVc8WSC5JWgO+OdhYJiRM41dp6w2Dsd9JfEiHLPc6nyxBQgSrx9TYlbC8fRT2RK+HyWzDlv9ofFtxMOwQg==" - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Identity.Client": { - "type": "Transitive", - "resolved": "4.61.3", - "contentHash": "naJo/Qm35Caaoxp5utcw+R8eU8ZtLz2ALh8S+gkekOYQ1oazfCQMWVT4NJ/FnHzdIJlm8dMz0oMpMGCabx5odA==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "6.35.0", - "System.Diagnostics.DiagnosticSource": "6.0.1" - } - }, - "Microsoft.Identity.Client.Extensions.Msal": { - "type": "Transitive", - "resolved": "4.61.3", - "contentHash": "PWnJcznrSGr25MN8ajlc2XIDW4zCFu0U6FkpaNLEWLgd1NgFCp5uDY3mqLDgM8zCN8hqj8yo5wHYfLB2HjcdGw==", - "dependencies": { - "Microsoft.Identity.Client": "4.61.3", - "System.IO.FileSystem.AccessControl": "5.0.0", - "System.Security.Cryptography.ProtectedData": "4.5.0" - } - }, - "Microsoft.IdentityModel.Abstractions": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "xuR8E4Rd96M41CnUSCiOJ2DBh+z+zQSmyrYHdYhD6K4fXBcQGVnRCFQ0efROUYpP+p0zC1BLKr0JRpVuujTZSg==" - }, - "Microsoft.IdentityModel.JsonWebTokens": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "9wxai3hKgZUb4/NjdRKfQd0QJvtXKDlvmGMYACbEC8DFaicMFCFhQFZq9ZET1kJLwZahf2lfY5Gtcpsx8zYzbg==", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "6.35.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2" - } - }, - "Microsoft.IdentityModel.Logging": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "jePrSfGAmqT81JDCNSY+fxVWoGuJKt9e6eJ+vT7+quVS55nWl//jGjUQn4eFtVKt4rt5dXaleZdHRB9J9AJZ7Q==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "6.35.0" - } - }, - "Microsoft.IdentityModel.Protocols": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "BPQhlDzdFvv1PzaUxNSk+VEPwezlDEVADIKmyxubw7IiELK18uJ06RQ9QKKkds30XI+gDu9n8j24XQ8w7fjWcg==", - "dependencies": { - "Microsoft.IdentityModel.Logging": "6.35.0", - "Microsoft.IdentityModel.Tokens": "6.35.0" - } - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "LMtVqnECCCdSmyFoCOxIE5tXQqkOLrvGrL7OxHg41DIm1bpWtaCdGyVcTAfOQpJXvzND9zUKIN/lhngPkYR8vg==", - "dependencies": { - "Microsoft.IdentityModel.Protocols": "6.35.0", - "System.IdentityModel.Tokens.Jwt": "6.35.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2" - } - }, - "Microsoft.IdentityModel.Tokens": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "RN7lvp7s3Boucg1NaNAbqDbxtlLj5Qeb+4uSS1TeK5FSBVM40P4DKaTKChT43sHyKfh7V0zkrMph6DdHvyA4bg==", - "dependencies": { - "Microsoft.IdentityModel.Logging": "6.35.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2" - } - }, - "Pipelines.Sockets.Unofficial": { - "type": "Transitive", - "resolved": "2.2.8", - "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", - "dependencies": { - "System.IO.Pipelines": "5.0.1" - } - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.ClientModel": { - "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "I3CVkvxeqFYjIVEP59DnjbeoGNfo/+SZrCLpRz2v/g0gpCHaEMPtWSY0s9k/7jR1rAsLNg2z2u1JRB76tPjnIw==", - "dependencies": { - "System.Memory.Data": "1.0.2", - "System.Text.Json": "4.7.2" - } - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==", - "dependencies": { - "System.Security.Permissions": "6.0.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Formats.Asn1": { - "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "XqKba7Mm/koKSjKMfW82olQdmfbI5yqeoLV/tidRp7fbh5rmHAQ5raDI/7SU0swTzv+jgqtUGkzmFxuUg0it1A==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.ValueTuple": "4.5.0" - } - }, - "System.IdentityModel.Tokens.Jwt": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "yxGIQd3BFK7F6S62/7RdZk3C/mfwyVxvh6ngd1VYMBmbJ1YZZA9+Ku6suylVtso0FjI0wbElpJ0d27CdsyLpBQ==", - "dependencies": { - "Microsoft.IdentityModel.JsonWebTokens": "6.35.0", - "Microsoft.IdentityModel.Tokens": "6.35.0" - } - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==" - }, - "System.IO.FileSystem.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "SxHB3nuNrpptVk+vZ/F+7OHEpoHUIKKMl02bUmYHQr1r+glbZQxs7pRtsf4ENO29TVm2TH3AEeep2fJcy92oYw==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.IO.Hashing": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Rfm2jYCaUeGysFEZjDe7j1R4x6Z6BzumS/vUT5a1AA/AWJuGX71PoGB0RmpyX3VmrGqVnAwtfMn39OHR8Y/5+g==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4" - } - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "5.0.1", - "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Memory.Data": { - "type": "Transitive", - "resolved": "1.0.2", - "contentHash": "JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==", - "dependencies": { - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.6.0" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==", - "dependencies": { - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==", - "dependencies": { - "System.Security.AccessControl": "6.0.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "8.0.5", - "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4", - "System.ValueTuple": "4.5.0" - } - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.ValueTuple": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" - }, - "distributedlock": { - "type": "Project", - "dependencies": { - "DistributedLock.Azure": "[1.0.2, )", - "DistributedLock.FileSystem": "[1.0.3, )", - "DistributedLock.MySql": "[1.0.2, )", - "DistributedLock.Oracle": "[1.0.4, )", - "DistributedLock.Postgres": "[1.3.0, )", - "DistributedLock.Redis": "[1.1.0, )", - "DistributedLock.SqlServer": "[1.0.6, )", - "DistributedLock.WaitHandles": "[1.0.1, )", - "DistributedLock.ZooKeeper": "[1.0.0, )" - } - }, - "distributedlock.azure": { - "type": "Project", - "dependencies": { - "Azure.Storage.Blobs": "[12.19.1, )", - "DistributedLock.Core": "[1.0.8, )" - } - }, - "distributedlock.core": { - "type": "Project", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "[8.0.0, )", - "System.ValueTuple": "[4.5.0, )" - } - }, - "distributedlock.filesystem": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )" - } - }, - "distributedlock.mysql": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "MySqlConnector": "[2.3.5, )" - } - }, - "distributedlock.oracle": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "Oracle.ManagedDataAccess": "[23.6.1, )" - } - }, - "distributedlock.postgres": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "Npgsql": "[8.0.6, )" - } - }, - "distributedlock.redis": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "StackExchange.Redis": "[2.7.33, )" - } - }, - "distributedlock.sqlserver": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "Microsoft.Data.SqlClient": "[5.2.2, )" - } - }, - "distributedlock.waithandles": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )" - } - }, - "distributedlock.zookeeper": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "ZooKeeperNetEx": "[3.4.12.4, )" - } - }, - "Azure.Storage.Blobs": { - "type": "CentralTransitive", - "requested": "[12.19.1, )", - "resolved": "12.19.1", - "contentHash": "x43hWFJ4sPQ23TD4piCwT+KlQpZT8pNDAzqj6yUCqh+WJ2qcQa17e1gh6ZOeT2QNFQTTDSuR56fm2bIV7i11/w==", - "dependencies": { - "Azure.Storage.Common": "12.18.1", - "System.Text.Json": "4.7.2" - } - }, - "Microsoft.Data.SqlClient": { - "type": "CentralTransitive", - "requested": "[5.2.2, )", - "resolved": "5.2.2", - "contentHash": "mtoeRMh7F/OA536c/Cnh8L4H0uLSKB5kSmoi54oN7Fp0hNJDy22IqyMhaMH4PkDCqI7xL//Fvg9ldtuPHG0h5g==", - "dependencies": { - "Azure.Identity": "1.11.4", - "Microsoft.Data.SqlClient.SNI": "5.2.0", - "Microsoft.Identity.Client": "4.61.3", - "Microsoft.IdentityModel.JsonWebTokens": "6.35.0", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.35.0", - "System.Buffers": "4.5.1", - "System.Configuration.ConfigurationManager": "6.0.1", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "MySqlConnector": { - "type": "CentralTransitive", - "requested": "[2.3.5, )", - "resolved": "2.3.5", - "contentHash": "AmEfUPkFl+Ev6jJ8Dhns3CYHBfD12RHzGYWuLt6DfG6/af6YvOMyPz74ZPPjBYQGRJkumD2Z48Kqm8s5DJuhLA==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "7.0.1", - "System.Diagnostics.DiagnosticSource": "7.0.2", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Npgsql": { - "type": "CentralTransitive", - "requested": "[8.0.6, )", - "resolved": "8.0.6", - "contentHash": "KaS6CY5kY2Sd0P00MSeFcOI3t2DiQ4UWG8AuRpVOUeDWITOKfoEEG91DP3cmT6aerixPkjwKgXxnpDxIkDpO6g==", - "dependencies": { - "Microsoft.Bcl.HashCode": "1.1.1", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "System.Collections.Immutable": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Json": "8.0.5", - "System.Threading.Channels": "8.0.0" - } - }, - "Oracle.ManagedDataAccess": { - "type": "CentralTransitive", - "requested": "[23.6.1, )", - "resolved": "23.6.1", - "contentHash": "EZi+mahzUwQFWs9Is8ed94eTzWOlfCLMd+DDWukf/h/brTz1wB9Qk3fsxBrjw9+fEXrxDgx4uXNiPHNPRS3BeQ==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "6.0.1", - "System.Formats.Asn1": "8.0.1", - "System.Text.Json": "8.0.5", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "StackExchange.Redis": { - "type": "CentralTransitive", - "requested": "[2.7.33, )", - "resolved": "2.7.33", - "contentHash": "2kCX5fvhEE824a4Ab5Imyi8DRuGuTxyklXV01kegkRpsWJcPmO6+GAQ+HegKxvXAxlXZ8yaRspvWJ8t3mMClfQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Pipelines.Sockets.Unofficial": "2.2.8", - "System.IO.Compression": "4.3.0", - "System.Threading.Channels": "5.0.0" - } - }, - "ZooKeeperNetEx": { - "type": "CentralTransitive", - "requested": "[3.4.12.4, )", - "resolved": "3.4.12.4", - "contentHash": "YECtByVSH7TRjQKplwOWiKyanCqYE5eEkGk5YtHJgsnbZ6+p1o0Gvs5RIsZLotiAVa6Niez1BJyKY/RDY/L6zg==" - } - }, "net8.0": { "MedallionShell.StrongName": { "type": "Direct", @@ -692,6 +105,14 @@ "System.Diagnostics.EventLog": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "1.1.1", @@ -797,8 +218,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "3.1.0", - "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -829,11 +250,20 @@ }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { - "System.Security.AccessControl": "4.7.0", - "System.Security.Principal.Windows": "4.7.0" + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "NETStandard.Library": { @@ -893,6 +323,21 @@ "resolved": "4.4.0", "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==" }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, "System.ClientModel": { "type": "Transitive", "resolved": "1.0.0", @@ -963,8 +408,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Memory.Data": { "type": "Transitive", @@ -1009,11 +454,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.0", - "System.Security.Principal.Windows": "4.7.0" + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" } }, "System.Security.Cryptography.Cng": { @@ -1036,8 +481,8 @@ }, "System.Security.Principal.Windows": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "ojD0PX0XhneCsUbAZVKdb7h/70vyYMDYs85lwEI+LngEONe/17A0cFaRFqZU+sOEidcVswYWikYOQ9PPfjlbtQ==" + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, "System.Text.Encoding": { "type": "Transitive", @@ -1064,15 +509,21 @@ "resolved": "4.5.4", "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, "distributedlock": { "type": "Project", "dependencies": { "DistributedLock.Azure": "[1.0.2, )", "DistributedLock.FileSystem": "[1.0.3, )", + "DistributedLock.MongoDB": "[1.3.0, )", "DistributedLock.MySql": "[1.0.2, )", "DistributedLock.Oracle": "[1.0.4, )", "DistributedLock.Postgres": "[1.3.0, )", - "DistributedLock.Redis": "[1.1.0, )", + "DistributedLock.Redis": "[1.1.1, )", "DistributedLock.SqlServer": "[1.0.6, )", "DistributedLock.WaitHandles": "[1.0.1, )", "DistributedLock.ZooKeeper": "[1.0.0, )" @@ -1094,6 +545,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.2, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -1169,6 +627,21 @@ "System.Runtime.Caching": "8.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.2", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", diff --git a/src/DistributedLock.sln b/src/DistributedLock.sln index 61d17d38..30c8dd91 100644 --- a/src/DistributedLock.sln +++ b/src/DistributedLock.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.9.34616.47 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11116.177 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DistributedLock", "DistributedLock\DistributedLock.csproj", "{C1F56B68-C2EE-48E5-A99B-B40D397AE34F}" EndProject @@ -47,6 +47,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution package.readme.md = package.readme.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistributedLock.MongoDB", "DistributedLock.MongoDB\DistributedLock.MongoDB.csproj", "{92074E6D-99D1-46B1-A0AE-442EA1FEA397}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -109,6 +111,10 @@ Global {1CAB9A1D-0C02-459C-A90E-47819832BD58}.Debug|Any CPU.Build.0 = Debug|Any CPU {1CAB9A1D-0C02-459C-A90E-47819832BD58}.Release|Any CPU.ActiveCfg = Release|Any CPU {1CAB9A1D-0C02-459C-A90E-47819832BD58}.Release|Any CPU.Build.0 = Release|Any CPU + {92074E6D-99D1-46B1-A0AE-442EA1FEA397}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92074E6D-99D1-46B1-A0AE-442EA1FEA397}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92074E6D-99D1-46B1-A0AE-442EA1FEA397}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92074E6D-99D1-46B1-A0AE-442EA1FEA397}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/DistributedLock/DistributedLock.csproj b/src/DistributedLock/DistributedLock.csproj index 0d23135b..f75fd8cb 100644 --- a/src/DistributedLock/DistributedLock.csproj +++ b/src/DistributedLock/DistributedLock.csproj @@ -54,10 +54,11 @@ + - + \ No newline at end of file diff --git a/src/DistributedLock/packages.lock.json b/src/DistributedLock/packages.lock.json index 44b4b439..ad3986a5 100644 --- a/src/DistributedLock/packages.lock.json +++ b/src/DistributedLock/packages.lock.json @@ -621,6 +621,15 @@ "System.IO.Hashing": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0", + "System.Buffers": "4.5.1" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "8.0.0", @@ -743,6 +752,24 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", @@ -751,6 +778,25 @@ "System.IO.Pipelines": "5.0.1" } }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "4.7.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -810,6 +856,11 @@ "Microsoft.IdentityModel.Tokens": "6.35.0" } }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==" + }, "System.IO.Compression": { "type": "Transitive", "resolved": "4.3.0", @@ -862,11 +913,24 @@ "System.Text.Json": "4.6.0" } }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "System.Security.Cryptography.X509Certificates": "4.3.0" + } + }, "System.Numerics.Vectors": { "type": "Transitive", "resolved": "4.5.0", "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==" + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", @@ -885,11 +949,41 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==" + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==" + }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", "resolved": "4.7.0", "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0" + } + }, "System.Security.Permissions": { "type": "Transitive", "resolved": "6.0.0", @@ -908,6 +1002,14 @@ "resolved": "4.3.0", "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==" }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "System.Text.Encodings.Web": { "type": "Transitive", "resolved": "8.0.0", @@ -953,6 +1055,15 @@ "resolved": "4.5.0", "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5" + } + }, "distributedlock.azure": { "type": "Project", "dependencies": { @@ -973,6 +1084,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.2, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -1048,6 +1166,23 @@ "System.Text.Encodings.Web": "6.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.2", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "System.Net.Http": "4.3.4", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", @@ -1812,6 +1947,14 @@ "System.IO.Hashing": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "8.0.0", @@ -1955,6 +2098,15 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", @@ -1963,6 +2115,22 @@ "System.IO.Pipelines": "5.0.1" } }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.7.1" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -2263,6 +2431,14 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "distributedlock.azure": { "type": "Project", "dependencies": { @@ -2279,6 +2455,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.2, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -2361,6 +2544,21 @@ "System.Text.Encodings.Web": "6.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.2", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", diff --git a/src/DistributedLockTaker/packages.lock.json b/src/DistributedLockTaker/packages.lock.json index 1584b3e4..83e81fa8 100644 --- a/src/DistributedLockTaker/packages.lock.json +++ b/src/DistributedLockTaker/packages.lock.json @@ -40,6 +40,15 @@ "System.IO.Hashing": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0", + "System.Buffers": "4.5.1" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "8.0.0", @@ -152,6 +161,24 @@ "System.Text.Json": "4.7.2" } }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", @@ -160,6 +187,25 @@ "System.IO.Pipelines": "5.0.1" } }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "4.7.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -219,6 +265,11 @@ "Microsoft.IdentityModel.Tokens": "6.35.0" } }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==" + }, "System.IO.Compression": { "type": "Transitive", "resolved": "4.3.0", @@ -271,11 +322,24 @@ "System.Text.Json": "4.6.0" } }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "System.Security.Cryptography.X509Certificates": "4.3.0" + } + }, "System.Numerics.Vectors": { "type": "Transitive", "resolved": "4.5.0", "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==" + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", @@ -294,11 +358,41 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==" + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==" + }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", "resolved": "4.7.0", "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0" + } + }, "System.Security.Permissions": { "type": "Transitive", "resolved": "6.0.0", @@ -317,6 +411,14 @@ "resolved": "4.3.0", "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==" }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "System.Text.Encodings.Web": { "type": "Transitive", "resolved": "8.0.0", @@ -362,15 +464,25 @@ "resolved": "4.5.0", "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5" + } + }, "distributedlock": { "type": "Project", "dependencies": { "DistributedLock.Azure": "[1.0.2, )", "DistributedLock.FileSystem": "[1.0.3, )", + "DistributedLock.MongoDB": "[1.3.0, )", "DistributedLock.MySql": "[1.0.2, )", "DistributedLock.Oracle": "[1.0.4, )", "DistributedLock.Postgres": "[1.3.0, )", - "DistributedLock.Redis": "[1.1.0, )", + "DistributedLock.Redis": "[1.1.1, )", "DistributedLock.SqlServer": "[1.0.6, )", "DistributedLock.WaitHandles": "[1.0.1, )", "DistributedLock.ZooKeeper": "[1.0.0, )" @@ -396,6 +508,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.2, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -471,6 +590,23 @@ "System.Text.Encodings.Web": "6.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.2", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "System.Net.Http": "4.3.4", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", @@ -529,7 +665,16 @@ "contentHash": "YECtByVSH7TRjQKplwOWiKyanCqYE5eEkGk5YtHJgsnbZ6+p1o0Gvs5RIsZLotiAVa6Niez1BJyKY/RDY/L6zg==" } }, - ".NETFramework,Version=v4.7.2/win7-x86": { + ".NETFramework,Version=v4.7.2/win-x86": { + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "System.Configuration.ConfigurationManager": { "type": "Transitive", "resolved": "6.0.1", @@ -552,6 +697,14 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "System.Security.Cryptography.X509Certificates": "4.3.0" + } + }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", "resolved": "4.3.0", @@ -565,11 +718,36 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==" + }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", "resolved": "4.7.0", "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0" + } + }, "System.Security.Permissions": { "type": "Transitive", "resolved": "6.0.0", @@ -583,6 +761,14 @@ "resolved": "5.0.0", "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Microsoft.Data.SqlClient": { "type": "CentralTransitive", "requested": "[5.2.2, )", @@ -604,9 +790,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Azure.Core": { "type": "Transitive", @@ -646,6 +832,14 @@ "System.IO.Hashing": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "1.1.1", @@ -746,8 +940,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -759,6 +953,24 @@ "resolved": "1.0.0", "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Oracle.ManagedDataAccess.Core": { "type": "Transitive", "resolved": "23.6.1", @@ -778,6 +990,21 @@ "System.IO.Pipelines": "5.0.1" } }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, "System.ClientModel": { "type": "Transitive", "resolved": "1.0.0", @@ -848,8 +1075,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Memory.Data": { "type": "Transitive", @@ -887,6 +1114,15 @@ "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "System.Security.Cryptography.Cng": { "type": "Transitive", "resolved": "4.5.0", @@ -905,6 +1141,11 @@ "resolved": "8.0.0", "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, "System.Text.Encoding": { "type": "Transitive", "resolved": "4.3.0", @@ -930,15 +1171,21 @@ "resolved": "4.5.4", "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, "distributedlock": { "type": "Project", "dependencies": { "DistributedLock.Azure": "[1.0.2, )", "DistributedLock.FileSystem": "[1.0.3, )", + "DistributedLock.MongoDB": "[1.3.0, )", "DistributedLock.MySql": "[1.0.2, )", "DistributedLock.Oracle": "[1.0.4, )", "DistributedLock.Postgres": "[1.3.0, )", - "DistributedLock.Redis": "[1.1.0, )", + "DistributedLock.Redis": "[1.1.1, )", "DistributedLock.SqlServer": "[1.0.6, )", "DistributedLock.WaitHandles": "[1.0.1, )", "DistributedLock.ZooKeeper": "[1.0.0, )" @@ -960,6 +1207,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.2, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -1035,6 +1289,21 @@ "System.Runtime.Caching": "8.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.2", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", @@ -1076,22 +1345,26 @@ "contentHash": "YECtByVSH7TRjQKplwOWiKyanCqYE5eEkGk5YtHJgsnbZ6+p1o0Gvs5RIsZLotiAVa6Niez1BJyKY/RDY/L6zg==" } }, - "net8.0/win7-x86": { + "net8.0/win-x86": { "Microsoft.Data.SqlClient.SNI.runtime": { "type": "Transitive", "resolved": "5.2.0", "contentHash": "po1jhvFd+8pbfvJR/puh+fkHi0GRanAdvayh/0e47yaM6CXWZ6opUjCMFuYlAnD2LcbyvQE7fPJKvogmaUcN+w==" }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==" - }, "Microsoft.NETCore.Targets": { "type": "Transitive", "resolved": "1.1.3", "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "runtime.any.System.Runtime": { "type": "Transitive", "resolved": "4.3.0", @@ -1105,11 +1378,6 @@ "resolved": "4.3.0", "contentHash": "+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==" }, - "runtime.win7.System.Private.Uri": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Q+IBgaPYicSQs2tBlmXqbS25c/JLIthWrgrpMwxKSOobW/OqIMVFruUGfuaz4QABVzV8iKdCAbN7APY7Tclbnw==" - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "8.0.0", @@ -1146,6 +1414,15 @@ "System.Configuration.ConfigurationManager": "8.0.0" } }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "System.Security.Cryptography.Cng": { "type": "Transitive", "resolved": "4.5.0", @@ -1159,6 +1436,11 @@ "System.Formats.Asn1": "8.0.0" } }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, "System.Text.Encoding": { "type": "Transitive", "resolved": "4.3.0", @@ -1193,8 +1475,7 @@ "contentHash": "o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3", - "runtime.win7.System.Private.Uri": "4.3.0" + "Microsoft.NETCore.Targets": "1.1.3" } }, "System.Threading.AccessControl": {