Skip to content
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
8 changes: 4 additions & 4 deletions src/Distributed/VectorClock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ public override string ToString()
/// </summary>
public static VectorClock Parse(string s)
{
if (string.IsNullOrEmpty(s))
if (string.IsNullOrWhiteSpace(s))
return new VectorClock();

var entries = s.Split(',', StringSplitOptions.RemoveEmptyEntries);
Expand All @@ -394,7 +394,7 @@ public static VectorClock Parse(string s)
var pairs = new List<(ushort nodeId, ulong counter)>();
foreach (var entry in entries)
{
var parts = entry.Split(':');
var parts = entry.Split(':', 2, StringSplitOptions.None);
if (parts.Length != 2)
throw new FormatException($"Invalid vector clock entry: {entry}");

Expand Down Expand Up @@ -428,7 +428,7 @@ private static VectorClock CreateCanonical(List<(ushort nodeId, ulong counter)>
counters.Add(pair.counter);
}

return new VectorClock(nodeIds.ToArray(), counters.ToArray());
return new VectorClock([.. nodeIds], [.. counters]);
}

/// <summary>
Expand All @@ -437,7 +437,7 @@ private static VectorClock CreateCanonical(List<(ushort nodeId, ulong counter)>
public static bool TryParse(string? s, out VectorClock result)
{
result = default;
if (string.IsNullOrEmpty(s))
if (string.IsNullOrWhiteSpace(s))
{
result = new VectorClock();
return true;
Expand Down
2 changes: 1 addition & 1 deletion src/Distributed/VectorClockCoordinator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace Clockworks.Distributed;
public sealed class VectorClockCoordinator
{
private readonly ushort _nodeId;
private readonly object _lock = new();
private readonly Lock _lock = new();
private VectorClock _current;

/// <summary>
Expand Down
21 changes: 10 additions & 11 deletions src/HlcGuidFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,18 @@ public void NewGuids(Span<Guid> destination)
// Counter overflow - must advance logical time
_logicalTimeMs++;
_counter = 0;

// Check drift bounds
var drift = _logicalTimeMs - physicalTimeMs;
if (drift > _options.MaxDriftMs)
{
if (_options.ThrowOnExcessiveDrift)
{
throw new HlcDriftException(drift, _options.MaxDriftMs);
}
// Otherwise, we accept the drift (for high-throughput scenarios)
}
}
}

var drift = _logicalTimeMs - physicalTimeMs;
if (drift > _options.MaxDriftMs)
{
if (_options.ThrowOnExcessiveDrift)
{
throw new HlcDriftException(drift, _options.MaxDriftMs);
}
// Otherwise, we accept the drift (for high-throughput scenarios)
}

timestamp = new HlcTimestamp(_logicalTimeMs, _counter, _nodeId);

Expand Down
43 changes: 43 additions & 0 deletions tests/HlcGuidFactoryDriftTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Xunit;

namespace Clockworks.Tests;

public sealed class HlcGuidFactoryDriftTests
{
[Fact]
public void NewGuidWithHlc_WhenClockMovesBackwardBeyondMaxDrift_ThrowsImmediately_WhenStrict()
{
var tp = new SimulatedTimeProvider();

using var factory = new HlcGuidFactory(
tp,
nodeId: 1,
options: new HlcOptions { MaxDriftMs = 0, ThrowOnExcessiveDrift = true });

_ = factory.NewGuidWithHlc();

var now = tp.GetUtcNow();
tp.SetUtcNow(now - TimeSpan.FromMilliseconds(1));

Assert.Throws<HlcDriftException>(() => factory.NewGuidWithHlc());
}

[Fact]
public void NewGuidWithHlc_WhenClockMovesBackwardBeyondMaxDrift_DoesNotThrow_WhenNotStrict()
{
var tp = new SimulatedTimeProvider();

using var factory = new HlcGuidFactory(
tp,
nodeId: 1,
options: new HlcOptions { MaxDriftMs = 0, ThrowOnExcessiveDrift = false });

_ = factory.NewGuidWithHlc();

var now = tp.GetUtcNow();
tp.SetUtcNow(now - TimeSpan.FromMilliseconds(1));

var ex = Record.Exception(() => factory.NewGuidWithHlc());
Assert.Null(ex);
}
}