Skip to content

Commit ef86c4d

Browse files
Copilotstephentoub
andcommitted
Rename polyfill to CreateVersion7(DateTimeOffset) to match .NET 9 API
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
1 parent 8ef5371 commit ef86c4d

File tree

2 files changed

+22
-14
lines changed

2 files changed

+22
-14
lines changed

src/Common/Polyfills/System/GuidPolyfills.cs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
namespace System;
22

33
/// <summary>
4-
/// Provides polyfills for GUID generation methods not available in older .NET versions.
4+
/// Provides polyfills for GUID generation methods not available in older .NET versions,
5+
/// with monotonic counter-based ordering for strict intra-millisecond sequencing.
56
/// </summary>
67
internal static class GuidPolyfills
78
{
@@ -10,12 +11,17 @@ internal static class GuidPolyfills
1011
private static readonly object s_lock = new();
1112

1213
/// <summary>
13-
/// Creates a monotonically increasing GUID using UUID v7 format with the specified timestamp.
14+
/// Creates a UUID v7 GUID with the specified timestamp.
1415
/// Uses a counter for intra-millisecond ordering to ensure strict monotonicity.
1516
/// </summary>
16-
/// <param name="timestamp">The Unix timestamp in milliseconds to embed in the GUID.</param>
17-
/// <returns>A new monotonically increasing GUID.</returns>
18-
public static Guid CreateMonotonicUuid(long timestamp)
17+
/// <param name="timestamp">The timestamp to embed in the GUID.</param>
18+
/// <returns>A new UUID v7 GUID.</returns>
19+
/// <remarks>
20+
/// Unlike the built-in <c>Guid.CreateVersion7(DateTimeOffset)</c> in .NET 9+,
21+
/// this implementation uses a counter to ensure strict monotonicity within the same millisecond,
22+
/// which is required for keyset pagination to work correctly.
23+
/// </remarks>
24+
public static Guid CreateVersion7(DateTimeOffset timestamp)
1925
{
2026
// UUID v7 format (RFC 9562):
2127
// - 48 bits: Unix timestamp in milliseconds (big-endian)
@@ -24,19 +30,20 @@ public static Guid CreateMonotonicUuid(long timestamp)
2430
// - 2 bits: variant (10)
2531
// - 62 bits: random
2632

33+
long timestampMs = timestamp.ToUnixTimeMilliseconds();
2734
long counter;
2835

2936
lock (s_lock)
3037
{
31-
if (timestamp == s_lastTimestamp)
38+
if (timestampMs == s_lastTimestamp)
3239
{
3340
// Same millisecond - increment counter
3441
s_counter++;
3542
}
3643
else
3744
{
3845
// New millisecond - reset counter
39-
s_lastTimestamp = timestamp;
46+
s_lastTimestamp = timestampMs;
4047
s_counter = 0;
4148
}
4249

@@ -56,12 +63,12 @@ public static Guid CreateMonotonicUuid(long timestamp)
5663
#endif
5764

5865
// Set timestamp (48 bits, big-endian) in first 6 bytes
59-
bytes[0] = (byte)(timestamp >> 40);
60-
bytes[1] = (byte)(timestamp >> 32);
61-
bytes[2] = (byte)(timestamp >> 24);
62-
bytes[3] = (byte)(timestamp >> 16);
63-
bytes[4] = (byte)(timestamp >> 8);
64-
bytes[5] = (byte)timestamp;
66+
bytes[0] = (byte)(timestampMs >> 40);
67+
bytes[1] = (byte)(timestampMs >> 32);
68+
bytes[2] = (byte)(timestampMs >> 24);
69+
bytes[3] = (byte)(timestampMs >> 16);
70+
bytes[4] = (byte)(timestampMs >> 8);
71+
bytes[5] = (byte)timestampMs;
6572

6673
// Set version 7 (0111) in high nibble of byte 6, and high 4 bits of counter in low nibble
6774
bytes[6] = (byte)(0x70 | ((counter >> 8) & 0x0F));
@@ -81,3 +88,4 @@ public static Guid CreateMonotonicUuid(long timestamp)
8188
bytes[12], bytes[13], bytes[14], bytes[15]);
8289
}
8390
}
91+

src/ModelContextProtocol.Core/Server/InMemoryMcpTaskStore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ public void Dispose()
438438
}
439439

440440
private string GenerateTaskId() =>
441-
GuidPolyfills.CreateMonotonicUuid(GetUtcNow().ToUnixTimeMilliseconds()).ToString("N");
441+
GuidPolyfills.CreateVersion7(GetUtcNow()).ToString("N");
442442

443443
private static bool IsTerminalStatus(McpTaskStatus status) =>
444444
status is McpTaskStatus.Completed or McpTaskStatus.Failed or McpTaskStatus.Cancelled;

0 commit comments

Comments
 (0)