This repository was archived by the owner on Aug 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 129
Handle SetStreamMetadata idempotently #129
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
070147e
Add a deterministic guid generator and tests
damianh a16c282
Set LangVersion to latest in all projects
damianh d205bbb
Add failing test for handling SetStreamMetadata idempotently
damianh 30ad68f
The metadata message id generator with a fixed guid namespace. Should…
damianh 276f90d
InMemoryStreamStore: use MetadataMessageIdGenerator to generate the m…
damianh 6c75a1a
MsSql: use MetadataMessageIdGenerator to generate the message id when…
damianh d598533
Just take an array
damianh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -24,7 +24,7 @@ protected override async Task<ReadStreamPage> ReadStreamForwardsInternal( | |||
| await connection.OpenAsync(cancellationToken).NotOnCapturedContext(); | ||||
| var streamIdInfo = new StreamIdInfo(streamId); | ||||
| return await ReadStreamInternal(streamIdInfo.SqlStreamId, start, count, ReadDirection.Forward, | ||||
| prefetch, readNext, connection, cancellationToken); | ||||
| prefetch, readNext, connection, null, cancellationToken); | ||||
| } | ||||
| } | ||||
|
|
||||
|
|
@@ -41,7 +41,7 @@ protected override async Task<ReadStreamPage> ReadStreamBackwardsInternal( | |||
| await connection.OpenAsync(cancellationToken).NotOnCapturedContext(); | ||||
| var streamIdInfo = new StreamIdInfo(streamId); | ||||
| return await ReadStreamInternal(streamIdInfo.SqlStreamId, start, count, ReadDirection.Backward, | ||||
| prefetch, readNext, connection, cancellationToken); | ||||
| prefetch, readNext, connection, null, cancellationToken); | ||||
| } | ||||
| } | ||||
|
|
||||
|
|
@@ -52,7 +52,9 @@ private async Task<ReadStreamPage> ReadStreamInternal( | |||
| ReadDirection direction, | ||||
| bool prefetch, | ||||
| ReadNextStreamPage readNext, | ||||
| SqlConnection connection, CancellationToken cancellationToken) | ||||
| SqlConnection connection, | ||||
| SqlTransaction transaction, | ||||
| CancellationToken cancellationToken) | ||||
| { | ||||
| // If the count is int.MaxValue, TSql will see it as a negative number. | ||||
| // Users shouldn't be using int.MaxValue in the first place anyway. | ||||
|
|
@@ -87,7 +89,7 @@ private async Task<ReadStreamPage> ReadStreamInternal( | |||
| }; | ||||
| } | ||||
|
|
||||
| using(var command = new SqlCommand(commandText, connection)) | ||||
| using (var command = new SqlCommand(commandText, connection, transaction)) | ||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needed to pass this down because command wouldn't work if in a transaction scope. In other paths, this is null, which is fine.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not completely following - are you saying that particular overload needs to be called even if transaction is null?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because this transcation
|
||||
| { | ||||
| command.Parameters.AddWithValue("streamId", sqlStreamId.Id); | ||||
| command.Parameters.AddWithValue("count", count + 1); //Read extra row to see if at end or not | ||||
|
|
||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
src/SqlStreamStore.Tests/Infrastructure/DeterministicGuidGeneratorTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| namespace SqlStreamStore.Infrastructure | ||
| { | ||
| using System; | ||
| using Shouldly; | ||
| using Xunit; | ||
|
|
||
| public class DeterministicGuidGeneratorTests | ||
| { | ||
| [Fact] | ||
| public void Given_same_input_should_generate_same_Guid() | ||
| { | ||
| var generator = new DeterministicGuidGenerator(Guid.NewGuid()); | ||
| var guid1 = generator.Create("some-data"); | ||
| var guid2 = generator.Create("some-data"); | ||
|
|
||
| guid2.ShouldBe(guid1); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Given_different_input_should_generate_different_Guid() | ||
| { | ||
| var generator = new DeterministicGuidGenerator(Guid.NewGuid()); | ||
| var guid1 = generator.Create("some-data"); | ||
| var guid2 = generator.Create("other-data"); | ||
|
|
||
| guid2.ShouldNotBe(guid1); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
src/SqlStreamStore/Infrastructure/DeterministicGuidGenerator.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| namespace SqlStreamStore.Infrastructure | ||
| { | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Security.Cryptography; | ||
| using System.Text; | ||
|
|
||
| // Adapted from https://github.com/LogosBible/Logos.Utility/blob/master/src/Logos.Utility/GuidUtility.cs | ||
| // MIT Licence | ||
|
|
||
| /// <summary> | ||
| /// A helper utility to generate deterministed GUIDS. | ||
| /// </summary> | ||
| public class DeterministicGuidGenerator | ||
| { | ||
| private readonly byte[] _namespaceBytes; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of <see cref="DeterministicGuidGenerator"/> | ||
| /// </summary> | ||
| /// <param name="guidNameSpace"> | ||
| /// A namespace that ensures that the GUID generated with this instance | ||
| /// do not collided with other generators. Your application should define | ||
| /// it's namespace as a constant. | ||
| /// </param> | ||
| public DeterministicGuidGenerator(Guid guidNameSpace) | ||
| { | ||
| _namespaceBytes = guidNameSpace.ToByteArray(); | ||
| SwapByteOrder(_namespaceBytes); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Creates a deterministic GUID. | ||
| /// </summary> | ||
| /// <param name="source"> | ||
| /// A source to generate the GUID from. | ||
| /// </param> | ||
| /// <returns> | ||
| /// A deterministically generated GUID. | ||
| /// </returns> | ||
| public Guid Create(string source) | ||
| { | ||
| return Create(Encoding.UTF8.GetBytes(source)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Creates a deterministic GUID. | ||
| /// </summary> | ||
| /// <param name="source"> | ||
| /// A source to generate the GUID from. | ||
| /// </param> | ||
| /// <returns> | ||
| /// A deterministically generated GUID. | ||
| /// </returns> | ||
| public Guid Create(byte[] source) | ||
| { | ||
| byte[] hash; | ||
| using (var algorithm = SHA1.Create()) | ||
| { | ||
| algorithm.TransformBlock(_namespaceBytes, 0, _namespaceBytes.Length, null, 0); | ||
| algorithm.TransformFinalBlock(source, 0, source.Length); | ||
|
|
||
| hash = algorithm.Hash; | ||
| } | ||
|
|
||
| var newGuid = new byte[16]; | ||
| Array.Copy(hash, 0, newGuid, 0, 16); | ||
|
|
||
| newGuid[6] = (byte)((newGuid[6] & 0x0F) | (5 << 4)); | ||
| newGuid[8] = (byte)((newGuid[8] & 0x3F) | 0x80); | ||
|
|
||
| SwapByteOrder(newGuid); | ||
| return new Guid(newGuid); | ||
| } | ||
|
|
||
| private static void SwapByteOrder(byte[] guid) | ||
| { | ||
| SwapBytes(guid, 0, 3); | ||
| SwapBytes(guid, 1, 2); | ||
| SwapBytes(guid, 4, 5); | ||
| SwapBytes(guid, 6, 7); | ||
| } | ||
|
|
||
| private static void SwapBytes(byte[] guid, int left, int right) | ||
| { | ||
| var temp = guid[left]; | ||
| guid[left] = guid[right]; | ||
| guid[right] = temp; | ||
| } | ||
| } | ||
| } |
32 changes: 32 additions & 0 deletions
32
src/SqlStreamStore/Infrastructure/MetadataMessageIdGenerator.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| namespace SqlStreamStore.Infrastructure | ||
| { | ||
| using System; | ||
|
|
||
| /// <summary> | ||
| /// A deterministic GUID generator for metadata messages. | ||
| /// </summary> | ||
| public static class MetadataMessageIdGenerator | ||
| { | ||
| private static readonly DeterministicGuidGenerator s_deterministicGuidGenerator; | ||
|
|
||
| static MetadataMessageIdGenerator() | ||
| { | ||
| s_deterministicGuidGenerator | ||
| = new DeterministicGuidGenerator(Guid.Parse("8D1E0B02-0D78-408E-8211-F899BE6F8AA2")); | ||
| } | ||
|
|
||
| /// <summary> 47C2 ; | ||
| /// Create a GUID for metadata message Ids. | ||
| /// </summary> | ||
| /// <param name="message"> | ||
| /// The metadata message uses as input into the generation algorithim. | ||
| /// </param> | ||
| /// <returns> | ||
| /// A deterministically generated GUID. | ||
| /// </returns> | ||
| public static Guid Create(string message) | ||
| { | ||
| return s_deterministicGuidGenerator.Create(message); | ||
| } | ||
| } | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The primary test. StreamVersion should not have been incremented and stay at 0.