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
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,11 @@ private static List<Uri> AgentsFor(IEnumerable<Uri> agents, string projectionNam
agents.Where(uri => uri.Segments.Any(segment =>
segment.Trim('/').Equals(projectionName, StringComparison.OrdinalIgnoreCase))).ToList();

// The (type, name, databaseId) prefix of an agent URI, mirroring the internal
// EventSubscriptionAgentFamily.DatabaseKeyOf that group affinity keys on.
private static string DatabaseKeyOf(Uri uri) =>
uri.Segments.Length >= 3
? $"{uri.Host}/{uri.Segments[1].Trim('/')}/{uri.Segments[2].Trim('/')}"
: uri.AbsoluteUri;
// Calls the real EventSubscriptionAgentFamily.DatabaseKeyOf rather than a local copy of it (GH-3819).
// This used to reimplement the URI grammar verbatim, which is exactly the drift risk that made the
// method public: a copy asserting co-location can quietly stop matching the key the distribution
// really groups on, and the test would keep passing while proving nothing.
private static string DatabaseKeyOf(Uri uri) => EventSubscriptionAgentFamily.DatabaseKeyOf(uri);
}

public record TripStarted(Guid Id, string Description);
Expand Down
17 changes: 12 additions & 5 deletions src/Wolverine/Runtime/Agents/EventSubscriptionAgentFamily.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,17 @@ private async ValueTask<HashSet<Uri>> RetireSupersededAgentsAsync(AssignmentGrid
return DatabaseId.TryParse(uri.Segments[2].Trim('/'), out var id) ? id : null;
}

// Agent URIs are event-subscriptions://{type}/{name}/{databaseId}/{shard...} (see UriFor); the
// (type, name, databaseId) prefix identifies the shard database an agent belongs to, so grouping on it
// co-locates every tenant/projection agent for one database on the same node.
internal static string DatabaseKeyOf(Uri uri)
/// <summary>
/// The (type, name, databaseId) prefix of an event-subscription agent URI — the key group affinity
/// distributes on, so every tenant/projection agent belonging to one shard database is co-located on
/// the same node. Agent URIs are <c>event-subscriptions://{type}/{name}/{databaseId}/{shard...}</c>
/// (see <see cref="UriFor"/>).
///
/// <para>Public so that callers reasoning about agent placement per database — an admin view, a
/// readiness probe, a test asserting co-location — can use the same key the distribution actually
/// uses instead of re-implementing the URI grammar and silently drifting from it. See GH-3819.</para>
/// </summary>
public static string DatabaseKeyOf(Uri uri)
=> uri.Segments.Length >= 3
? $"{uri.Host}/{uri.Segments[1].Trim('/')}/{uri.Segments[2].Trim('/')}"
: uri.AbsoluteUri;
Expand All @@ -348,7 +355,7 @@ internal static string StoreKeyOf(Uri uri)
/// else is a tenant. Returns null for a URI that doesn't parse as this scheme's grammar — such an
/// agent is never treated as superseded.
/// </summary>
internal static string? TenantNeutralKeyOf(Uri uri)
public static string? TenantNeutralKeyOf(Uri uri)
{
// Segments: "/", {storeName}, {databaseId}, {projectionName}, {shardKey}, [v{n} | tenant], [tenant]
var segments = uri.Segments.Skip(1).Select(x => x.Trim('/')).Where(x => x.Length > 0).ToArray();
Expand Down
Loading