PvWay.MsSqlSemaphoreService.nc8
1.0.0
See the version list below for details.
dotnet add package PvWay.MsSqlSemaphoreService.nc8 --version 1.0.0
NuGet\Install-Package PvWay.MsSqlSemaphoreService.nc8 -Version 1.0.0
<PackageReference Include="PvWay.MsSqlSemaphoreService.nc8" Version="1.0.0" />
paket add PvWay.MsSqlSemaphoreService.nc8 --version 1.0.0
#r "nuget: PvWay.MsSqlSemaphoreService.nc8, 1.0.0"
// Install PvWay.MsSqlSemaphoreService.nc8 as a Cake Addin #addin nuget:?package=PvWay.MsSqlSemaphoreService.nc8&version=1.0.0 // Install PvWay.MsSqlSemaphoreService.nc8 as a Cake Tool #tool nuget:?package=PvWay.MsSqlSemaphoreService.nc8&version=1.0.0
MsSql SemaphoreService for .Net Core 8 by pvWay
This nuGet implements the SemaphoreService interfaces using a tiny DAO connection towards an Ms Sql Server Database.
Interfaces and enums as defined in the abstraction nuGet
SemaphoreStatus enum
This enum enumerates the different possible statuses of a semaphore when trying to acquire it
- Acquired: (success status) the semaphore was acquired
- ReleasedInTheMeanTime: the semaphore was locked by someone else but when getting more info it finally appeared released.
- OwnedBySomeoneElse: another process currently owns the semaphore. Other processes will have to wait until the semaphore will be released by the owner process.
- ForcedReleased: the semaphore was locked by another process that seems not being responding for a while. As such, the release of the semaphore was forced.
namespace PvWay.SemaphoreService.Abstractions.nc8;
public enum SemaphoreStatusEnu
{
Acquired,
ReleasedInTheMeanTime,
OwnedSomeoneElse,
ForcedReleased
}
IDbSemaphore
Small object that holds some useful information about the semaphore
namespace PvWay.SemaphoreService.Abstractions.nc8;
public interface ISemaphoreInfo
{
SemaphoreStatusEnu Status { get; }
string Name {get; }
string Owner { get; }
TimeSpan Timeout { get; }
DateTime ExpiresAtUtc { get; }
DateTime CreateDateUtc { get; }
DateTime UpdateUtcDate { get; }
}
ISemaphoreService
namespace PvWay.SemaphoreService.Abstractions.nc8;
public interface ISemaphoreService
{
/// <summary>
/// This method will actually try to acquire the semaphore
/// </summary>
/// <param name="name">The semaphore name</param>
/// <param name="owner">
/// The name of the process that tries to acquire the semaphore
/// </param>
/// <param name="timeout">
/// The estimated time out timespan that the lock will stay active (if not refreshed).
/// If the semaphore is locked longer than the timeout period it will be forced release
/// by any other process trying to acquire the semaphore</param>
/// <returns>On success the status will be Acquired.</returns>
Task<ISemaphoreInfo> AcquireSemaphoreAsync(
string name, string owner, TimeSpan timeout);
/// <summary>
/// Extend the validity timespan of the semaphore
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task TouchSemaphoreAsync(string name);
/// <summary>
/// Free the semaphore so that any other process can now acquire it
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task ReleaseSemaphoreAsync(string name);
/// <summary>
/// Get some info about a given semaphore
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<ISemaphoreInfo?> GetSemaphoreAsync(string name);
/// <summary>
/// this method provides a mutex (semaphore) protected
/// work session when a provided work can be performed.
/// </summary>
/// <param name="semaphoreName">The name of the mutex</param>
/// <param name="owner">the owner of the mutex (usually the machineName)</param>
/// <param name="timeout">The validity time of the lock</param>
/// <param name="workAsync">
/// The work to be performed.
/// </param>
/// <param name="notify">An optional notifier that will be used
/// for notifying sleep times</param>
/// <param name="sleepBetweenAttemptsInSeconds">
/// The number of seconds between two attempts for locking the semaphore</param>
/// <typeparam name="T">Type return by the workAsync method</typeparam>
/// <returns>The T result of the function invoked</returns>
Task<T> IsolateWorkAsync<T>(
string semaphoreName, string owner,
TimeSpan timeout,
Func<Task<T>> workAsync,
Action<string>? notify = null,
int sleepBetweenAttemptsInSeconds = 15);
}
Pre-conditions
The service expects to find a semaphore table into the Ms Sql Server database.
The schema and the name of the table are free to choose.
However the table has to respect the following DDL
CREATE TABLE [dbo].[Semaphore]
(
[Name] VARCHAR(50) NOT NULL,
[Owner] VARCHAR(128) NOT NULL,
[TimeoutInSeconds] INT NOT NULL,
[CreateDateUtc] DATETIME NOT NULL,
[UpdateDateUtc] DATETIME NOT NULL
CONSTRAINT [PK_app.AppSemaphore]
PRIMARY KEY CLUSTERED ([Name] ASC)
)
Injection
The AddPvWayMsSqlSemaphoreService request several parameters
- config of type IConfiguration: should map a section of the appSettings that contains
- schemaName: the name of the schema
- tableName: the name of the table
{
"pvWaySemaphoreService": {
"schemaName": "dbo",
"tableName": "Semaphore"
},
}
- getCsAsync: an async method for retrieving the connection string of the database
- logException: a delegate that will log exceptions
- logInfo: an optional delegate for getting verbose notifications
public static void AddPvWayMsSqlSemaphoreService(
this IServiceCollection services,
IConfiguration config,
Func<Task<string>> getCsAsync,
Action<Exception>? logException,
Action<string>? logInfo)
{
services.AddSingleton<ISemaphoreServiceConfig>(_ =>
new SemaphoreServiceConfig(
config, getCsAsync, logException, logInfo));
services.AddTransient<ISemaphoreService, SemaphoreService>();
}
Factories
The following factory is also provided
public static ISemaphoreService Create(
string schemaName,
string tableName,
Func<Task<string>> getCsAsync,
Action<Exception>? logException,
Action<string>? logInfo)
{
var config = new SemaphoreServiceConfig(
schemaName, tableName, getCsAsync,
logException, logInfo);
return new SemaphoreService(config);
}
Usage
// See https://aka.ms/new-console-template for more information
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PvWay.MsSqlSemaphoreService.nc8.Di;
using PvWay.SemaphoreService.Abstractions.nc8;
Console.WriteLine("Hello, PvWay Semaphore Service");
Console.WriteLine("==============================");
/*
Pre-Conditions
==============
The database must contain the semaphore table
The table structure must respect the following DDL.
The the schema and the name can be modified.
CREATE TABLE [dbo].[Semaphore]
(
[Name] VARCHAR(50) NOT NULL,
[Owner] VARCHAR(128) NOT NULL,
[TimeoutInSeconds] INT NOT NULL,
[CreateDateUtc] DATETIME NOT NULL,
[UpdateDateUtc] DATETIME NOT NULL
CONSTRAINT [PK_app.AppSemaphore]
PRIMARY KEY CLUSTERED ([Name] ASC)
)
*/
// we need to read the app settings
var config = new ConfigurationBuilder()
.AddJsonFile("appSettings.json", false, false)
.Build();
// let's now provision the Semaphore service
var semaphoreSection = config.GetSection("pvWaySemaphoreService");
var services = new ServiceCollection();
services.AddPvWayMsSqlSemaphoreService(
semaphoreSection,
// a callback that gets the connection string
() =>
{
var cs = config.GetConnectionString("semaphore")!;
return Task.FromResult(cs);
},
// a callback that log exceptions
Console.WriteLine,
// a callback that log some info
Console.WriteLine);
var sp = services.BuildServiceProvider();
var svc = sp.GetService<ISemaphoreService>()!;
const string resourceName = "SharedResource";
// Acquire the semaphore
Console.WriteLine("Program: acquiring the semaphore");
var si = await svc.AcquireSemaphoreAsync(resourceName,
Environment.MachineName, TimeSpan.FromSeconds(1));
Console.WriteLine($"Program: semaphore {resourceName} was acquired by {si.Owner}");
Console.WriteLine();
// Try acquiring a second time
// the semaphore was created with a timeout of 1 second
// as such acquiring the semaphore should not succeed
// because the semaphore is still in use
Console.WriteLine("Program: acquiring again the semaphore");
si = await svc.AcquireSemaphoreAsync(resourceName,
Environment.MachineName, TimeSpan.FromSeconds(1));
Console.WriteLine($"Program: status={si.Status}");
Console.WriteLine();
//
// // extend the life time of the semaphore by touching its
// // last update date
Console.WriteLine("Program: extend the semaphore");
await svc.TouchSemaphoreAsync(resourceName);
Console.WriteLine();
// let's display the semaphore values
var semaphore = await svc.GetSemaphoreAsync(resourceName);
Console.WriteLine(semaphore);
Console.WriteLine();
// // sleep 2 second so that the semaphore expires
Console.WriteLine("Program: now sleeping 2 seconds");
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine();
// // Try acquiring a third time
// // the semaphore expired
// // as such the acquiring method will force release it
// // warning: the semaphore is not acquired at this stage
Console.WriteLine("Program: trying to acquire a third time");
await svc.AcquireSemaphoreAsync(resourceName,
Environment.MachineName, TimeSpan.FromSeconds(1));
Console.WriteLine();
// // acquiring the released semaphore
// // the semaphore was forced release at the previous step
// // as such acquiring it again will succeed
Console.WriteLine("Program: trying to acquire again");
await svc.AcquireSemaphoreAsync(resourceName,
Environment.MachineName, TimeSpan.FromSeconds(1));
Console.WriteLine();
// finally release the semaphore
Console.WriteLine("Program: trying to acquire again");
await svc.ReleaseSemaphoreAsync(resourceName);
Console.WriteLine();
// Let's now test the isolate work method
var res = await svc.IsolateWorkAsync(
resourceName, Environment.MachineName,
TimeSpan.FromSeconds(10),
() => Task.FromResult("some work was done alone"),
Console.WriteLine,
5);
Console.WriteLine(res);
Happy coding
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. 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. |
-
net8.0
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- PvWay.SemaphoreService.Abstractions.nc8 (>= 1.0.2)
- System.Data.SqlClient (>= 4.8.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Initial version for dotNet 8