Couchbase.Extensions.Locks
1.0.0-beta
Prefix Reserved
See the version list below for details.
dotnet add package Couchbase.Extensions.Locks --version 1.0.0-beta
NuGet\Install-Package Couchbase.Extensions.Locks -Version 1.0.0-beta
<PackageReference Include="Couchbase.Extensions.Locks" Version="1.0.0-beta" />
paket add Couchbase.Extensions.Locks --version 1.0.0-beta
#r "nuget: Couchbase.Extensions.Locks, 1.0.0-beta"
// Install Couchbase.Extensions.Locks as a Cake Addin #addin nuget:?package=Couchbase.Extensions.Locks&version=1.0.0-beta&prerelease // Install Couchbase.Extensions.Locks as a Cake Tool #tool nuget:?package=Couchbase.Extensions.Locks&version=1.0.0-beta&prerelease
Couchbase.Extensions.Locks
A system for managing distributed mutexs backed by Couchbase. This can prevent multiple simultaneous processes in separate application instances, which is useful for microservices or other horizontally scaled architectures.
Getting Started
Assuming you have an installation of Couchbase Server and Visual Studio VSCODE forthcoming), do the following:
- Install the package from NuGet or build from source and add a reference.
Example Application
There is an example MVC application in the examples directory.
Requesting a Mutex
To request a Mutex, use the RequestMutex
extension method on IBucket
. This can be any type of bucket, but Memcached or Ephemeral would be the most efficient since they never write to disk.
The lock expiration controls when the lock will expire if it isn't explicitly released. The mutex may also be disposed to release the lock early. If the lock cannot be acquired because another process is holding the lock, a CouchbaseLockUnavailableException
will be thrown.
using Couchbase.Extensions.DependencyInjection;
using Couchbase.Extensions.Locks;
using Microsoft.AspNetCore.Mvc;
public class MyController : Controller
{
private readonly IBucketProvider _bucketProvider;
public MyController(IBucketProvider bucketProvider)
{
_bucketProvider = bucketProvider;
}
public IActionResult Index()
{
var bucket = _bucketProvider.GetBucket("default");
try {
using (var mutex = await bucket.RequestMutexAsync("my-lock-name", TimeSpan.FromSeconds(15)))
{
// This lock will be held for the shorter of the using statement lifespan or 15 seconds
}
}
catch (CouchbaseLockUnavailableException ex)
{
// This exception indicates the lock is already held by another process
}
}
}
Renewing the lock
If your process is long-running, the lock may be renewed before it expires. This will succeed so long as the lock hasn't been released and acquired by another process. If the lock already expired but is still released, it will be automatically reacquired.
using Couchbase.Extensions.DependencyInjection;
using Couchbase.Extensions.Locks;
using Microsoft.AspNetCore.Mvc;
public class MyController : Controller
{
private readonly IBucketProvider _bucketProvider;
public MyController(IBucketProvider bucketProvider)
{
_bucketProvider = bucketProvider;
}
public IActionResult Index()
{
var bucket = _bucketProvider.GetBucket("default");
try {
using (var mutex = await bucket.RequestMutexAsync("my-lock-name", TimeSpan.FromSeconds(15)))
{
while (true)
{
// Do some work here
// This lock will be held until the end of the using statement,
// unless one of the loop iterations takes longer than 15 seconds.
await mutex.RenewAsync(TimeSpan.FromSeconds(15));
}
}
}
catch (CouchbaseLockUnavailableException ex)
{
// This exception indicates the lock is already held by another process
}
}
}
Automatically Renewing Locks
In many cases, it may be desirable for locks to automatically renew themselves until they expire, allowing a shorter expiration time. This means that if the process crashes the lock is freed soon, but the lock can constantly be refreshed so long as the process is still running.
This feature is enabled by calling AutoRenew
on the mutex after it is acquired. The first parameter is how often to renew the lock, and should be set to significantly shorter than the original lock expiration.
The second parameter is the maximum lifetime of the lock. This is a safety mechanism in case the call to Dispose is somehow missed. Auto renewal will cease after this amount of time.
using Couchbase.Extensions.DependencyInjection;
using Couchbase.Extensions.Locks;
using Microsoft.AspNetCore.Mvc;
public class MyController : Controller
{
private readonly IBucketProvider _bucketProvider;
public MyController(IBucketProvider bucketProvider)
{
_bucketProvider = bucketProvider;
}
public IActionResult Index()
{
var bucket = _bucketProvider.GetBucket("default");
try {
using (var mutex = await bucket.RequestMutexAsync("my-lock-name", TimeSpan.FromSeconds(10)))
{
mutex.AutoRenew(TimeSpan.FromSeconds(5), TimeSpan.FromMinutes(1));
while (true)
{
// Do some work here
// This lock will be held until the end of the using statement,
// so long as the total loop time is less than one minute
}
}
}
catch (CouchbaseLockUnavailableException ex)
{
// This exception indicates the lock is already held by another process
}
}
}
Retrying Locks
By design, requesting a mutex is only attempted once. If the lock is unavailable, CouchbaseLockUnavailableException
is thrown immediately. This allows the consumer to implement advanced retry logic. We recommend using Polly or a similar library. This allows waits, exponential backoffs, circuit breakers, bulkhead isolation, and other advanced logic.
using Couchbase.Extensions.DependencyInjection;
using Couchbase.Extensions.Locks;
using Microsoft.AspNetCore.Mvc;
using Polly;
public class MyController : Controller
{
// Retry up to 10 times, waiting one second between attempts
private static readonly Policy LockPolicy =
Policy.Handle<CouchbaseLockUnavailableException>()
.WaitAndRetryAsync(10, _ => TimeSpan.FromSeconds(1));
private readonly IBucketProvider _bucketProvider;
public MyController(IBucketProvider bucketProvider)
{
_bucketProvider = bucketProvider;
}
public IActionResult Index()
{
var bucket = _bucketProvider.GetBucket("default");
try {
// Wrapping the call in LockPolicy.ExecuteAsync applies the wait and retry logic
// for any CouchbaseLockUnavailableException. All other exceptions throw immediately.
using (var mutex = await LockPolicy.ExecuteAsync(
() => bucket.RequestMutexAsync("my-lock-name", TimeSpan.FromSeconds(10))))
{
// Do work here
}
}
catch (CouchbaseLockUnavailableException ex)
{
// This exception indicates the lock is already held by another process,
// for 10 consequetive lock attempts
}
}
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- CouchbaseNetClient (>= 2.6.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Couchbase.Extensions.Locks:
Package | Downloads |
---|---|
Whipstaff.Couchbase
Re-usable logic for working with Couchbase. |
|
Hyperbee.Migrations.Providers.Couchbase
Hyperbee Migrations Postgres Provider adds Couchbase support to Hyperbee Migrations. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
2.0.0 | 186,582 | 11/18/2020 |
1.0.0 | 103,475 | 9/18/2019 |
1.0.0-beta | 8,415 | 11/2/2018 |