Skip to content

Commit cfe8dfb

Browse files
committed
sql locking
1 parent 0d28331 commit cfe8dfb

File tree

6 files changed

+120
-34
lines changed

6 files changed

+120
-34
lines changed

src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs

Lines changed: 104 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,142 @@
33
using System.Threading.Tasks;
44
using Microsoft.Extensions.Logging;
55
using WorkflowCore.Interface;
6+
using System.Data;
7+
using System.Collections.Generic;
8+
using System.Collections.Concurrent;
9+
using System.Threading;
610

711
namespace WorkflowCore.LockProviders.SqlServer
812
{
913
public class SqlLockProvider : IDistributedLockProvider
1014
{
1115
private const string Prefix = "wfc";
1216

13-
private readonly SqlConnection _connection;
17+
private readonly string _connectionString;
1418
private readonly ILogger _logger;
19+
private readonly Dictionary<string, SqlConnection> _locks = new Dictionary<string, SqlConnection>();
20+
private readonly AutoResetEvent _mutex = new AutoResetEvent(true);
1521

1622
public SqlLockProvider(string connectionString, ILoggerFactory logFactory)
1723
{
1824
_logger = logFactory.CreateLogger<SqlLockProvider>();
1925
var csb = new SqlConnectionStringBuilder(connectionString);
20-
csb.Pooling = false;
26+
csb.Pooling = true;
27+
csb.ApplicationName = "Workflow Core Lock Manager";
2128

22-
_connection = new SqlConnection(csb.ToString());
29+
_connectionString = csb.ToString();
2330
}
2431

2532

2633
public async Task<bool> AcquireLock(string Id)
2734
{
28-
var cmd = _connection.CreateCommand();
29-
cmd.CommandText = "EXEC @result = sp_getapplock @Resource = @id, @LockMode = 'Exclusive', @LockOwner = 'Session'";
30-
cmd.Parameters.AddWithValue("id", $"{Prefix}:{Id}");
31-
var result = Convert.ToInt32(await cmd.ExecuteScalarAsync());
32-
33-
switch (result)
35+
if (_mutex.WaitOne())
3436
{
35-
case -1:
36-
_logger.LogDebug($"The lock request timed out for {Id}");
37-
break;
38-
case -2:
39-
_logger.LogDebug($"The lock request was canceled for {Id}");
40-
break;
41-
case -3:
42-
_logger.LogDebug($"The lock request was chosen as a deadlock victim for {Id}");
43-
break;
44-
case -999:
45-
_logger.LogError($"Lock provider error for {Id}");
46-
break;
47-
}
37+
try
38+
{
39+
var connection = new SqlConnection(_connectionString);
40+
await connection.OpenAsync();
41+
try
42+
{
43+
var cmd = connection.CreateCommand();
44+
cmd.CommandText = "sp_getapplock";
45+
cmd.CommandType = CommandType.StoredProcedure;
46+
cmd.Parameters.AddWithValue("@Resource", $"{Prefix}:{Id}");
47+
cmd.Parameters.AddWithValue("@LockOwner", $"Session");
48+
cmd.Parameters.AddWithValue("@LockMode", $"Exclusive");
49+
cmd.Parameters.AddWithValue("@LockTimeout", 0);
50+
var returnParameter = cmd.Parameters.Add("RetVal", SqlDbType.Int);
51+
returnParameter.Direction = ParameterDirection.ReturnValue;
52+
await cmd.ExecuteNonQueryAsync();
53+
var result = Convert.ToInt32(returnParameter.Value);
4854

49-
return (result >= 0);
55+
switch (result)
56+
{
57+
case -1:
58+
_logger.LogDebug($"The lock request timed out for {Id}");
59+
break;
60+
case -2:
61+
_logger.LogDebug($"The lock request was canceled for {Id}");
62+
break;
63+
case -3:
64+
_logger.LogDebug($"The lock request was chosen as a deadlock victim for {Id}");
65+
break;
66+
case -999:
67+
_logger.LogError($"Lock provider error for {Id}");
68+
break;
69+
}
70+
if (result >= 0)
71+
{
72+
_locks[Id] = connection;
73+
return true;
74+
}
75+
else
76+
{
77+
connection.Close();
78+
return false;
79+
}
80+
}
81+
catch (Exception ex)
82+
{
83+
connection.Close();
84+
throw ex;
85+
}
86+
}
87+
finally
88+
{
89+
_mutex.Set();
90+
}
91+
}
92+
return false;
5093
}
5194

5295
public async Task ReleaseLock(string Id)
5396
{
54-
var cmd = _connection.CreateCommand();
55-
cmd.CommandText = "EXEC @result = sp_releaseapplock @Resource = @id, @LockOwner = 'Session'";
56-
cmd.Parameters.AddWithValue("id", $"{Prefix}:{Id}");
57-
var result = Convert.ToInt32(await cmd.ExecuteScalarAsync());
58-
59-
if (result < 0)
60-
_logger.LogError($"Unable to release lock for {Id}");
97+
if (_mutex.WaitOne())
98+
{
99+
try
100+
{
101+
SqlConnection connection = null;
102+
connection = _locks[Id];
103+
104+
if (connection == null)
105+
return;
106+
107+
try
108+
{
109+
var cmd = connection.CreateCommand();
110+
cmd.CommandText = "sp_releaseapplock";
111+
cmd.CommandType = CommandType.StoredProcedure;
112+
cmd.Parameters.AddWithValue("@Resource", $"{Prefix}:{Id}");
113+
cmd.Parameters.AddWithValue("@LockOwner", $"Session");
114+
var returnParameter = cmd.Parameters.Add("RetVal", SqlDbType.Int);
115+
returnParameter.Direction = ParameterDirection.ReturnValue;
116+
117+
await cmd.ExecuteNonQueryAsync();
118+
var result = Convert.ToInt32(returnParameter.Value);
119+
120+
if (result < 0)
121+
_logger.LogError($"Unable to release lock for {Id}");
122+
}
123+
finally
124+
{
125+
connection.Close();
126+
_locks.Remove(Id);
127+
}
128+
}
129+
finally
130+
{
131+
_mutex.Set();
132+
}
133+
}
61134
}
62135

63136
public async Task Start()
64-
{
65-
await _connection.OpenAsync();
137+
{
66138
}
67139

68140
public async Task Stop()
69141
{
70-
_connection.Close();
71142
}
72143
}
73144
}

src/samples/WorkflowCore.Sample04/EventSampleWorkflow.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void Build(IWorkflowBuilder<MyDataClass> builder)
1818
{
1919
builder
2020
.StartWith(context => ExecutionResult.Next())
21-
.WaitFor("MyEvent", data => "0")
21+
.WaitFor("MyEvent", data => "0", data => DateTime.Now)
2222
.Output(data => data.StrValue, step => step.EventData)
2323
.Then<CustomMessage>()
2424
.Name("Print custom message")

src/samples/WorkflowCore.Sample04/Program.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ private static IServiceProvider ConfigureServices()
5252
// x.UseMongoDB(@"mongodb://localhost:27017", "workflow9999");
5353
//});
5454

55+
//services.AddWorkflow(x =>
56+
//{
57+
// x.UseSqlServer(@"Server=.\SQLEXPRESS;Database=WorkflowCore;Trusted_Connection=True;", true, true);
58+
// x.UseSqlServerLocking(@"Server=.\SQLEXPRESS;Database=WorkflowCore;Trusted_Connection=True;");
59+
//});
60+
5561
//redis = ConnectionMultiplexer.Connect("127.0.0.1");
5662
//services.AddWorkflow(x =>
5763
//{

src/samples/WorkflowCore.Sample04/WorkflowCore.Sample04.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16+
<ProjectReference Include="..\..\providers\WorkflowCore.LockProviders.SqlServer\WorkflowCore.LockProviders.SqlServer.csproj" />
1617
<ProjectReference Include="..\..\providers\WorkflowCore.Persistence.PostgreSQL\WorkflowCore.Persistence.PostgreSQL.csproj" />
1718
<ProjectReference Include="..\..\providers\WorkflowCore.Persistence.Sqlite\WorkflowCore.Persistence.Sqlite.csproj" />
1819
<ProjectReference Include="..\..\providers\WorkflowCore.Persistence.SqlServer\WorkflowCore.Persistence.SqlServer.csproj" />

src/samples/WorkflowCore.Sample13/Program.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ private static IServiceProvider ConfigureServices()
3939
// x.UseMongoDB(@"mongodb://localhost:27017", "workflow-test002");
4040
//});
4141

42+
//services.AddWorkflow(x =>
43+
//{
44+
// x.UseSqlServer(@"Server=.\SQLEXPRESS;Database=WorkflowCore3;Trusted_Connection=True;", true, true);
45+
// x.UseSqlServerLocking(@"Server=.\SQLEXPRESS;Database=WorkflowCore3;Trusted_Connection=True;");
46+
//});
47+
4248
var serviceProvider = services.BuildServiceProvider();
4349

4450
//config logging

src/samples/WorkflowCore.Sample13/WorkflowCore.Sample13.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
</ItemGroup>
1313

1414
<ItemGroup>
15+
<ProjectReference Include="..\..\providers\WorkflowCore.LockProviders.SqlServer\WorkflowCore.LockProviders.SqlServer.csproj" />
1516
<ProjectReference Include="..\..\providers\WorkflowCore.Persistence.MongoDB\WorkflowCore.Persistence.MongoDB.csproj" />
17+
<ProjectReference Include="..\..\providers\WorkflowCore.Persistence.SqlServer\WorkflowCore.Persistence.SqlServer.csproj" />
1618
<ProjectReference Include="..\..\providers\WorkflowCore.Providers.Azure\WorkflowCore.Providers.Azure.csproj" />
1719
<ProjectReference Include="..\..\WorkflowCore\WorkflowCore.csproj" />
1820
</ItemGroup>

0 commit comments

Comments
 (0)