feat(mesh): ClusterRoleInfo exposes the node's own cluster-scoped role
Phase 6 Task 1. IClusterRoleInfo gains ClusterRole/ClusterId, derived from the node's configured AkkaClusterOptions.Roles at construction time (not live Cluster.State) so the identity is available before the cluster forms. First cluster-scoped role wins when more than one is configured, logged as a Warning. Updates the FakeClusterRoleInfo test double in ServiceCollectionExtensionsTests to satisfy the new interface members. Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
@@ -20,6 +20,8 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
|
||||
private readonly ILogger<ClusterRoleInfo> _logger;
|
||||
private readonly CommonsNodeId _localNode;
|
||||
private readonly HashSet<string> _localRoles;
|
||||
private readonly string? _clusterRole;
|
||||
private readonly string? _clusterId;
|
||||
private readonly object _lock = new();
|
||||
private readonly Dictionary<string, Member?> _roleLeaders = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, HashSet<Member>> _membersByRole = new(StringComparer.Ordinal);
|
||||
@@ -39,6 +41,23 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
|
||||
_localNode = CommonsNodeId.Parse($"{options.Value.PublicHostname}:{options.Value.Port}");
|
||||
_localRoles = new HashSet<string>(options.Value.Roles, StringComparer.Ordinal);
|
||||
|
||||
// Derived from the node's OWN configured roles (not live Cluster.State) so this identity is
|
||||
// available at host-build time, before the cluster forms. Iterate the ORIGINAL array — not
|
||||
// _localRoles — because first-wins requires configuration order, which HashSet does not
|
||||
// guarantee.
|
||||
var clusterRoles = options.Value.Roles.Where(RoleParser.IsClusterRole).ToArray();
|
||||
if (clusterRoles.Length > 0)
|
||||
{
|
||||
_clusterRole = clusterRoles[0];
|
||||
_clusterId = RoleParser.ClusterIdFromRole(_clusterRole);
|
||||
if (clusterRoles.Length > 1)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Node configured with {Count} cluster-scoped roles ({Roles}); using the first ({ClusterRole})",
|
||||
clusterRoles.Length, string.Join(", ", clusterRoles), _clusterRole);
|
||||
}
|
||||
}
|
||||
|
||||
SeedFromCurrentState();
|
||||
_subscriber = system.ActorOf(Props.Create(() => new SubscriberActor(this)), "clusterroleinfo-subscriber");
|
||||
}
|
||||
@@ -49,6 +68,12 @@ public sealed class ClusterRoleInfo : IClusterRoleInfo, IDisposable
|
||||
/// <inheritdoc />
|
||||
public IReadOnlySet<string> LocalRoles => _localRoles;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? ClusterRole => _clusterRole;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? ClusterId => _clusterId;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasRole(string role) => _localRoles.Contains(role);
|
||||
|
||||
|
||||
@@ -14,6 +14,21 @@ public interface IClusterRoleInfo
|
||||
NodeId LocalNode { get; }
|
||||
/// <summary>Gets the set of roles assigned to the local node.</summary>
|
||||
IReadOnlySet<string> LocalRoles { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local node's cluster-scoped role (e.g. <c>cluster-SITE-A</c>), or <c>null</c> when
|
||||
/// the node carries none. Derived from the node's OWN configured roles at construction time —
|
||||
/// NOT from live cluster membership — so it is available before the cluster forms (per-cluster
|
||||
/// mesh, Phase 6). When more than one cluster-scoped role is configured, the first one wins.
|
||||
/// </summary>
|
||||
string? ClusterRole { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ClusterId extracted from <see cref="ClusterRole"/> (e.g. <c>SITE-A</c>), or
|
||||
/// <c>null</c> when the node carries no cluster-scoped role.
|
||||
/// </summary>
|
||||
string? ClusterId { get; }
|
||||
|
||||
/// <summary>Checks if the local node has the specified role.</summary>
|
||||
/// <param name="role">Role name to check.</param>
|
||||
/// <returns>True if the local node has the role; otherwise, false.</returns>
|
||||
|
||||
Reference in New Issue
Block a user