Skip to content

Commit

Permalink
add couchbase and foundatio lock (#947)
Browse files Browse the repository at this point in the history
  • Loading branch information
dpvreony committed Jan 14, 2022
1 parent 7cc8826 commit 6eaf6b8
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 1 deletion.
76 changes: 76 additions & 0 deletions src/Whipstaff.Couchbase/CouchbaseLock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) 2020 DHGMS Solutions and Contributors. All rights reserved.
// DHGMS Solutions and Contributors licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.Threading.Tasks;
using Couchbase.Extensions.Locks;

namespace Whipstaff.Couchbase
{
/// <summary>
/// Exclusive lock integration for Couchbase.
/// </summary>
public sealed class CouchbaseLock : global::Foundatio.Lock.ILock
{
private readonly ICouchbaseMutex _mutex;

/// <summary>
/// Initializes a new instance of the <see cref="CouchbaseLock"/> class.
/// </summary>
/// <param name="mutex">
/// Couchbase mutex manager.
/// </param>
/// <param name="resource">Name of the resource.</param>
/// <param name="aquiredTimeUtc">The timestamp for when the lock was aquired.</param>
/// <param name="timeWaitedForLock">The time waited for the lock</param>
public CouchbaseLock(
ICouchbaseMutex mutex,
string resource,
DateTime aquiredTimeUtc,
TimeSpan timeWaitedForLock)
{
_mutex = mutex ?? throw new ArgumentNullException(nameof(mutex));
Resource = resource ?? throw new ArgumentNullException(nameof(resource));
AcquiredTimeUtc = aquiredTimeUtc;
TimeWaitedForLock = timeWaitedForLock;
RenewalCount = 0;
}

/// <inheritdoc/>
public string LockId => _mutex.Name;

/// <inheritdoc/>
public string Resource { get; }

/// <inheritdoc/>
public DateTime AcquiredTimeUtc { get; }

/// <inheritdoc/>
public TimeSpan TimeWaitedForLock { get; }

/// <inheritdoc/>
public int RenewalCount { get; private set; }

/// <inheritdoc/>
public ValueTask DisposeAsync()
{
return default(ValueTask);
}

/// <inheritdoc/>
public async Task RenewAsync(TimeSpan? timeUntilExpires = null)
{
await _mutex.Renew(timeUntilExpires ?? TimeSpan.FromMinutes(1))
.ConfigureAwait(false);
RenewalCount++;
}

/// <inheritdoc/>
public Task ReleaseAsync()
{
_mutex.Dispose();
return Task.CompletedTask;
}
}
}
125 changes: 125 additions & 0 deletions src/Whipstaff.Couchbase/CouchbaseLockProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (c) 2020 DHGMS Solutions and Contributors. All rights reserved.
// DHGMS Solutions and Contributors licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Couchbase;
using Couchbase.Extensions.DependencyInjection;
using Couchbase.Extensions.Locks;
using Couchbase.KeyValue;
using Foundatio.Lock;
using Whipstaff.Couchbase;

namespace Dhgms.NetContrib.Playground.Features.Couchbase.Foundatio
{
/// <summary>
/// A Couchbase Lock Distributed Log Provider.
/// </summary>
public sealed class CouchbaseLockProvider : global::Foundatio.Lock.ILockProvider
{
private readonly ConcurrentDictionary<string, ICouchbaseMutex> _mutexDictionary = new ConcurrentDictionary<string, ICouchbaseMutex>();
private readonly ICouchbaseCollection _couchbaseCollection;

/// <summary>
/// Initializes a new instance of the <see cref="CouchbaseLockProvider"/> class.
/// </summary>
/// <param name="couchbaseCollection">The couchbase collection.</param>
public CouchbaseLockProvider(ICouchbaseCollection couchbaseCollection)
{
_couchbaseCollection = couchbaseCollection ?? throw new ArgumentNullException(nameof(couchbaseCollection));
}

/// <summary>
/// Gets a Couchbase Lock Provider using a bucket provider.
/// </summary>
/// <param name="bucketProvider">Bucket Provider Instance to use.</param>
/// <returns>Instance of <see cref="CouchbaseLockProvider"/>.</returns>
public static async Task<CouchbaseLockProvider> GetInstance(IBucketProvider bucketProvider)
{
if (bucketProvider == null)
{
throw new ArgumentNullException(nameof(bucketProvider));
}

var bucket = await bucketProvider.GetBucketAsync("default")
.ConfigureAwait(false);

return GetInstance(bucket);
}

/// <summary>
/// Gets a Couchbase Lock Provider using a bucket provider.
/// </summary>
/// <param name="bucket">Couchbase Bucket to use.</param>
/// <returns>Instance of <see cref="CouchbaseLockProvider"/>.</returns>
public static CouchbaseLockProvider GetInstance(IBucket bucket)
{
if (bucket == null)
{
throw new ArgumentNullException(nameof(bucket));
}

var collection = bucket.DefaultCollection();

return new CouchbaseLockProvider(collection);
}

/// <inheritdoc/>
public async Task<ILock> AcquireAsync(
string resource,
TimeSpan? timeUntilExpires,
bool releaseOnDispose,
CancellationToken cancellationToken)
{
var attemptStarted = DateTime.UtcNow;

var mutex = await _couchbaseCollection.RequestMutexAsync(
resource,
timeUntilExpires ?? TimeSpan.FromMinutes(1),
cancellationToken)
.ConfigureAwait(false);

var dateTimeAquired = DateTime.UtcNow;
var timeWaited = dateTimeAquired - attemptStarted;

_ = _mutexDictionary.TryAdd(resource, mutex);

return new CouchbaseLock(mutex, resource, dateTimeAquired, timeWaited);
}

/// <inheritdoc/>
public async Task<bool> IsLockedAsync(string resource)
{
var existsResult = await _couchbaseCollection.ExistsAsync(resource)
.ConfigureAwait(false);

return existsResult.Exists;
}

/// <inheritdoc/>
public Task ReleaseAsync(string resource, string lockId)
{
if (_mutexDictionary.TryGetValue(resource, out var mutex))
{
mutex.Dispose();
}

return Task.CompletedTask;
}

/// <inheritdoc/>
public async Task RenewAsync(string resource, string lockId, TimeSpan? timeUntilExpires = null)
{
if (!_mutexDictionary.TryGetValue(resource, out var mutex))
{
return;
}

await mutex.Renew(timeUntilExpires ?? TimeSpan.FromMinutes(1))
.ConfigureAwait(false);
}
}
}
12 changes: 12 additions & 0 deletions src/Whipstaff.Couchbase/Whipstaff.Couchbase.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Couchbase.Extensions.Locks" Version="2.0.0" />
<PackageReference Include="Foundatio" Version="10.2.4" />
</ItemGroup>
</Project>
8 changes: 7 additions & 1 deletion src/Whipstaff.sln
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Whipstaff.Windows", "Whipst
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Whipstaff.ReactiveUI", "Whipstaff.ReactiveUI\Whipstaff.ReactiveUI.csproj", "{FC16B508-41FF-47CE-A16A-A8AB3B26309A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Whipstaff.Entityframework.Relational", "Whipstaff.Entityframework.Relational\Whipstaff.Entityframework.Relational.csproj", "{783BF3CB-4095-4C5F-8C0C-D5E17979CC56}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Whipstaff.Entityframework.Relational", "Whipstaff.Entityframework.Relational\Whipstaff.Entityframework.Relational.csproj", "{783BF3CB-4095-4C5F-8C0C-D5E17979CC56}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Whipstaff.Couchbase", "Whipstaff.Couchbase\Whipstaff.Couchbase.csproj", "{8107A239-69D2-40AC-918B-59A77159EC52}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -131,6 +133,10 @@ Global
{783BF3CB-4095-4C5F-8C0C-D5E17979CC56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{783BF3CB-4095-4C5F-8C0C-D5E17979CC56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{783BF3CB-4095-4C5F-8C0C-D5E17979CC56}.Release|Any CPU.Build.0 = Release|Any CPU
{8107A239-69D2-40AC-918B-59A77159EC52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8107A239-69D2-40AC-918B-59A77159EC52}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8107A239-69D2-40AC-918B-59A77159EC52}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8107A239-69D2-40AC-918B-59A77159EC52}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down

0 comments on commit 6eaf6b8

Please sign in to comment.