Skip to content

Commit bec0362

Browse files
authored
Merge pull request #3 from sdc-ad/features/lock_event_write
Wrap writing events in a lock to avoid race condition
2 parents 161d7ee + faaadb6 commit bec0362

2 files changed

Lines changed: 12 additions & 3 deletions

File tree

src/AppCoreNet.EventStore.SqlServer/SqlServerEventStore.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed under the MIT license.
1+
// Licensed under the MIT license.
22
// Copyright (c) The AppCore .NET project.
33

44
using System;
@@ -62,6 +62,7 @@ public async Task WriteAsync(
6262
StreamId = streamId,
6363
ExpectedPosition = state.Value,
6464
Events = events,
65+
LockResource = _options.ApplicationName + "-WriteEvents",
6566
};
6667

6768
Model.WriteEventsResult result =

src/AppCoreNet.EventStore.SqlServer/WriteEventsStoredProcedure.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed under the MIT license.
1+
// Licensed under the MIT license.
22
// Copyright (c) The AppCore .NET project.
33

44
using System;
@@ -24,6 +24,8 @@ internal sealed class WriteEventsSqlStoredProcedure : SqlStoredProcedure<Model.W
2424

2525
required public IEnumerable<object> Events { get; init; }
2626

27+
required public string LockResource { get; init; }
28+
2729
public WriteEventsSqlStoredProcedure(DbContext dbContext, string? schema, IEventStoreSerializer serializer)
2830
: base(dbContext, $"[{SchemaUtils.GetEventStoreSchema(schema)}].{ProcedureName}")
2931
{
@@ -51,16 +53,21 @@ public static IEnumerable<string> GetCreateScripts(string? schema)
5153
CREATE PROCEDURE [{schema}].{ProcedureName} (
5254
@{nameof(StreamId)} NVARCHAR({Constants.StreamIdMaxLength}),
5355
@{nameof(ExpectedPosition)} BIGINT,
54-
@{nameof(Events)} [{schema}].[{nameof(Model.EventTableType)}] READONLY
56+
@{nameof(Events)} [{schema}].[{nameof(Model.EventTableType)}] READONLY,
57+
@{nameof(LockResource)} NVARCHAR(max)
5558
)
5659
AS
5760
BEGIN
5861
DECLARE @StreamKey AS INT;
5962
DECLARE @StreamSequence AS BIGINT;
6063
DECLARE @StreamIndex AS BIGINT;
64+
DECLARE @LockResult AS INT;
6165
6266
IF @{nameof(StreamId)} is NULL RAISERROR('The value for parameter ''{nameof(StreamId)}'' must not be NULL', 16, 1)
6367
68+
EXEC @LockResult = sp_getapplock @Resource = @{nameof(LockResource)}, @LockMode = 'Exclusive';
69+
IF @LockResult < 0 RAISERROR('Event write lock could not be acquired', 16, 1)
70+
6471
SELECT
6572
@StreamKey = Id,
6673
@StreamSequence = [{nameof(Model.EventStream.Sequence)}],
@@ -192,6 +199,7 @@ protected override SqlParameter[] GetParameters()
192199
TypeName = $"[{_schema}].[{nameof(Model.EventTableType)}]",
193200
Value = CreateEventDataTable(),
194201
},
202+
new SqlParameter($"@{nameof(LockResource)}", LockResource)
195203
];
196204
}
197205
}

0 commit comments

Comments
 (0)