Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add couchbase and foundatio lock #947

Merged
merged 1 commit into from
Jan 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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