From 8057558b58010b52667d76f96078bdaf8d212dce Mon Sep 17 00:00:00 2001 From: stdrickforce Date: Tue, 24 Sep 2024 12:05:55 +0800 Subject: [PATCH] apply file-scoped namespaces --- .../ServiceConnectionContainerBase.cs | 1163 ++++++++--------- .../RuntimeServicePingMessages.cs | 281 ++-- .../HubHost/ServiceHubDispatcher.cs | 381 +++--- 3 files changed, 911 insertions(+), 914 deletions(-) diff --git a/src/Microsoft.Azure.SignalR.Common/ServiceConnections/ServiceConnectionContainerBase.cs b/src/Microsoft.Azure.SignalR.Common/ServiceConnections/ServiceConnectionContainerBase.cs index 70abf6360..52cfa92f2 100644 --- a/src/Microsoft.Azure.SignalR.Common/ServiceConnections/ServiceConnectionContainerBase.cs +++ b/src/Microsoft.Azure.SignalR.Common/ServiceConnections/ServiceConnectionContainerBase.cs @@ -12,817 +12,816 @@ using Microsoft.Azure.SignalR.Protocol; using Microsoft.Extensions.Logging; -namespace Microsoft.Azure.SignalR +namespace Microsoft.Azure.SignalR; + +internal abstract class ServiceConnectionContainerBase : IServiceConnectionContainer, IServiceMessageHandler { - internal abstract class ServiceConnectionContainerBase : IServiceConnectionContainer, IServiceMessageHandler - { - private const int CheckWindow = 5; + private const int CheckWindow = 5; - // Give interval(5s) * 24 = 2min window for retry considering abnormal case. - private const int MaxRetryRemoveSeverConnection = 24; + // Give interval(5s) * 24 = 2min window for retry considering abnormal case. + private const int MaxRetryRemoveSeverConnection = 24; - private static readonly TimeSpan CheckTimeSpan = TimeSpan.FromMinutes(10); + private static readonly TimeSpan CheckTimeSpan = TimeSpan.FromMinutes(10); - private static readonly int MaxReconnectBackOffInternalInMilliseconds = 1000; + private static readonly int MaxReconnectBackOffInternalInMilliseconds = 1000; - private static readonly TimeSpan MessageWriteRetryDelay = TimeSpan.FromMilliseconds(200); - private static readonly int MessageWriteMaxRetry = 3; + private static readonly TimeSpan MessageWriteRetryDelay = TimeSpan.FromMilliseconds(200); + private static readonly int MessageWriteMaxRetry = 3; - // Give (interval * 3 + 1) delay when check value expire. - private static readonly long DefaultServersPingTimeoutTicks = Stopwatch.Frequency * ((long)Constants.Periods.DefaultServersPingInterval.TotalSeconds * 3 + 1); + // Give (interval * 3 + 1) delay when check value expire. + private static readonly long DefaultServersPingTimeoutTicks = Stopwatch.Frequency * ((long)Constants.Periods.DefaultServersPingInterval.TotalSeconds * 3 + 1); - private static readonly Tuple DefaultServersTagContext = new Tuple(string.Empty, 0); + private static readonly Tuple DefaultServersTagContext = new Tuple(string.Empty, 0); - private readonly IReadOnlyDictionary>> _partitionedCache; + private readonly IReadOnlyDictionary>> _partitionedCache; - private readonly BackOffPolicy _backOffPolicy = new BackOffPolicy(); + private readonly BackOffPolicy _backOffPolicy = new BackOffPolicy(); - private readonly object _lock = new object(); + private readonly object _lock = new object(); - private readonly object _statusLock = new object(); + private readonly object _statusLock = new object(); - private readonly AckHandler _ackHandler; + private readonly AckHandler _ackHandler; - private readonly CustomizedPingTimer _statusPing; + private readonly CustomizedPingTimer _statusPing; - private readonly CustomizedPingTimer _serversPing; + private readonly CustomizedPingTimer _serversPing; - private readonly ServiceDiagnosticLogsContext _serviceDiagnosticLogsContext = new ServiceDiagnosticLogsContext { EnableMessageLog = false }; + private readonly ServiceDiagnosticLogsContext _serviceDiagnosticLogsContext = new ServiceDiagnosticLogsContext { EnableMessageLog = false }; - private (int count, DateTime? last) _inactiveInfo; + private (int count, DateTime? last) _inactiveInfo; - private volatile List _serviceConnections; + private volatile List _serviceConnections; - private volatile ServiceConnectionStatus _status; + private volatile ServiceConnectionStatus _status; - // - private volatile Tuple _serversTagContext = DefaultServersTagContext; + // + private volatile Tuple _serversTagContext = DefaultServersTagContext; - private volatile bool _hasClients; + private volatile bool _hasClients; - private volatile bool _terminated = false; + private volatile bool _terminated = false; - public HubServiceEndpoint Endpoint { get; } + public HubServiceEndpoint Endpoint { get; } - public string ServersTag => _serversTagContext.Item1; + public string ServersTag => _serversTagContext.Item1; - public bool HasClients => _hasClients; + public bool HasClients => _hasClients; - public ServiceConnectionStatus Status - { - get => _status; + public ServiceConnectionStatus Status + { + get => _status; - private set + private set + { + if (_status != value) { - if (_status != value) + lock (_statusLock) { - lock (_statusLock) + if (_status != value) { - if (_status != value) - { - var prev = _status; - _status = value; - ConnectionStatusChanged?.Invoke(new StatusChange(prev, value)); - } + var prev = _status; + _status = value; + ConnectionStatusChanged?.Invoke(new StatusChange(prev, value)); } } } } + } - public Task ConnectionInitializedTask => Task.WhenAny(from connection in ServiceConnections - select connection.ConnectionInitializedTask); + public Task ConnectionInitializedTask => Task.WhenAny(from connection in ServiceConnections + select connection.ConnectionInitializedTask); - protected ILogger Logger { get; } + protected ILogger Logger { get; } - protected List ServiceConnections - { - get => _serviceConnections; - set => _serviceConnections = value; - } + protected List ServiceConnections + { + get => _serviceConnections; + set => _serviceConnections = value; + } - protected IServiceConnectionFactory ServiceConnectionFactory { get; } + protected IServiceConnectionFactory ServiceConnectionFactory { get; } - protected int FixedConnectionCount { get; } + protected int FixedConnectionCount { get; } - protected virtual ServiceConnectionType InitialConnectionType { get; } = ServiceConnectionType.Default; + protected virtual ServiceConnectionType InitialConnectionType { get; } = ServiceConnectionType.Default; - private static TimeSpan ReconnectInterval => - TimeSpan.FromMilliseconds(StaticRandom.Next(MaxReconnectBackOffInternalInMilliseconds)); + private static TimeSpan ReconnectInterval => + TimeSpan.FromMilliseconds(StaticRandom.Next(MaxReconnectBackOffInternalInMilliseconds)); - protected ServiceConnectionContainerBase(IServiceConnectionFactory serviceConnectionFactory, - int minConnectionCount, - HubServiceEndpoint endpoint, - IReadOnlyList initialConnections = null, - ILogger logger = null, - AckHandler ackHandler = null) - { - Logger = logger ?? throw new ArgumentNullException(nameof(logger)); - ServiceConnectionFactory = serviceConnectionFactory; - Endpoint = endpoint; - // use globally unique AckHanlder if not specified - // It is possible that the multiple MapHub calls the same hub, so that ack messages could be received by another instance of ServiceConnectionContainer - // Use the ack handler singleton to allow ack message to be acked by another container instance - _ackHandler = ackHandler ?? AckHandler.Singleton; + protected ServiceConnectionContainerBase(IServiceConnectionFactory serviceConnectionFactory, + int minConnectionCount, + HubServiceEndpoint endpoint, + IReadOnlyList initialConnections = null, + ILogger logger = null, + AckHandler ackHandler = null) + { + Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + ServiceConnectionFactory = serviceConnectionFactory; + Endpoint = endpoint; + // use globally unique AckHanlder if not specified + // It is possible that the multiple MapHub calls the same hub, so that ack messages could be received by another instance of ServiceConnectionContainer + // Use the ack handler singleton to allow ack message to be acked by another container instance + _ackHandler = ackHandler ?? AckHandler.Singleton; - // make sure it is after _endpoint is set - // init initial connections - List initial; - if (initialConnections == null) - { - initial = new List(); - } - else + // make sure it is after _endpoint is set + // init initial connections + List initial; + if (initialConnections == null) + { + initial = new List(); + } + else + { + initial = new List(initialConnections); + foreach (var conn in initial) { - initial = new List(initialConnections); - foreach (var conn in initial) - { - conn.ConnectionStatusChanged += OnConnectionStatusChanged; - } + conn.ConnectionStatusChanged += OnConnectionStatusChanged; } + } - var remainingCount = minConnectionCount - (initialConnections?.Count ?? 0); - if (remainingCount > 0) - { - // if still not match or greater than minConnectionCount, create more - var remaining = CreateFixedServiceConnection(remainingCount); - initial.AddRange(remaining); - } + var remainingCount = minConnectionCount - (initialConnections?.Count ?? 0); + if (remainingCount > 0) + { + // if still not match or greater than minConnectionCount, create more + var remaining = CreateFixedServiceConnection(remainingCount); + initial.AddRange(remaining); + } - ServiceConnections = initial; - FixedConnectionCount = initial.Count; - ConnectionStatusChanged += OnStatusChanged; + ServiceConnections = initial; + FixedConnectionCount = initial.Count; + ConnectionStatusChanged += OnStatusChanged; - _statusPing = new CustomizedPingTimer(Logger, Constants.CustomizedPingTimer.ServiceStatus, WriteServiceStatusPingAsync, Constants.Periods.DefaultStatusPingInterval, Constants.Periods.DefaultStatusPingInterval); + _statusPing = new CustomizedPingTimer(Logger, Constants.CustomizedPingTimer.ServiceStatus, WriteServiceStatusPingAsync, Constants.Periods.DefaultStatusPingInterval, Constants.Periods.DefaultStatusPingInterval); - // when server connection count is specified to 0, the app server only handle negotiate requests - if (initial.Count > 0) - { - _statusPing.Start(); - } + // when server connection count is specified to 0, the app server only handle negotiate requests + if (initial.Count > 0) + { + _statusPing.Start(); + } - _serversPing = new CustomizedPingTimer(Logger, Constants.CustomizedPingTimer.Servers, WriteServersPingAsync, Constants.Periods.DefaultServersPingInterval, Constants.Periods.DefaultServersPingInterval); + _serversPing = new CustomizedPingTimer(Logger, Constants.CustomizedPingTimer.Servers, WriteServersPingAsync, Constants.Periods.DefaultServersPingInterval, Constants.Periods.DefaultServersPingInterval); - _partitionedCache = Enumerable.Range(0, 256).ToDictionary(i => (byte)i, i => new StrongBox>(new WeakReference(null))); - } + _partitionedCache = Enumerable.Range(0, 256).ToDictionary(i => (byte)i, i => new StrongBox>(new WeakReference(null))); + } - public event Action ConnectionStatusChanged; + public event Action ConnectionStatusChanged; - public async Task StartAsync() + public async Task StartAsync() + { + using (new ServiceConnectionContainerScope(_serviceDiagnosticLogsContext)) { - using (new ServiceConnectionContainerScope(_serviceDiagnosticLogsContext)) - { - await Task.WhenAll(ServiceConnections.Select(c => StartCoreAsync(c))); - } + await Task.WhenAll(ServiceConnections.Select(c => StartCoreAsync(c))); } + } - public virtual Task StopAsync() + public virtual Task StopAsync() + { + _terminated = true; + _statusPing.Stop(); + return Task.WhenAll(ServiceConnections.Select(c => c.StopAsync())); + } + + public virtual Task HandlePingAsync(PingMessage pingMessage) + { + if (RuntimeServicePingMessage.TryGetClientCount(pingMessage, out var clientCount)) { - _terminated = true; - _statusPing.Stop(); - return Task.WhenAll(ServiceConnections.Select(c => c.StopAsync())); + Endpoint.EndpointMetrics.ClientConnectionCount = clientCount; } - - public virtual Task HandlePingAsync(PingMessage pingMessage) + if (RuntimeServicePingMessage.TryGetServerCount(pingMessage, out var serverCount)) { - if (RuntimeServicePingMessage.TryGetClientCount(pingMessage, out var clientCount)) - { - Endpoint.EndpointMetrics.ClientConnectionCount = clientCount; - } - if (RuntimeServicePingMessage.TryGetServerCount(pingMessage, out var serverCount)) - { - Endpoint.EndpointMetrics.ServerConnectionCount = serverCount; - } - if (RuntimeServicePingMessage.TryGetConnectionCapacity(pingMessage, out var connectionCapacity)) - { - Endpoint.EndpointMetrics.ConnectionCapacity = connectionCapacity; - } - if (RuntimeServicePingMessage.TryGetStatus(pingMessage, out var status)) - { - Log.ReceivedServiceStatusPing(Logger, status, Endpoint); - _hasClients = status; - Endpoint.IsActive = GetServiceStatus(status, CheckWindow, CheckTimeSpan); - } - else if (RuntimeServicePingMessage.TryGetServersTag(pingMessage, out var serversTag, out var updatedTime)) - { - Log.ReceivedServersTagPing(Logger, Endpoint); - if (updatedTime > _serversTagContext.Item2) - { - _serversTagContext = Tuple.Create(serversTag, updatedTime); - } - } - else if (RuntimeServicePingMessage.TryGetMessageLogEnableFlag(pingMessage, out var enableMessageLog)) - { - _serviceDiagnosticLogsContext.EnableMessageLog = enableMessageLog; - } - return Task.CompletedTask; + Endpoint.EndpointMetrics.ServerConnectionCount = serverCount; } - - public void HandleAck(AckMessage ackMessage) + if (RuntimeServicePingMessage.TryGetConnectionCapacity(pingMessage, out var connectionCapacity)) { - _ackHandler.TriggerAck(ackMessage.AckId, (AckStatus)ackMessage.Status); + Endpoint.EndpointMetrics.ConnectionCapacity = connectionCapacity; } - - public virtual Task WriteAsync(ServiceMessage serviceMessage) + if (RuntimeServicePingMessage.TryGetStatus(pingMessage, out var status)) { - return WriteMessageAsync(serviceMessage); + Log.ReceivedServiceStatusPing(Logger, status, Endpoint); + _hasClients = status; + Endpoint.IsActive = GetServiceStatus(status, CheckWindow, CheckTimeSpan); } - - public async Task WriteAckableMessageAsync(ServiceMessage serviceMessage, CancellationToken cancellationToken = default) + else if (RuntimeServicePingMessage.TryGetServersTag(pingMessage, out var serversTag, out var updatedTime)) { - if (serviceMessage is not IAckableMessage ackableMessage) + Log.ReceivedServersTagPing(Logger, Endpoint); + if (updatedTime > _serversTagContext.Item2) { - throw new ArgumentException($"{nameof(serviceMessage)} is not {nameof(IAckableMessage)}"); + _serversTagContext = Tuple.Create(serversTag, updatedTime); } - - var task = _ackHandler.CreateSingleAck(out var id, null, cancellationToken); - ackableMessage.AckId = id; - - // Sending regular messages completes as soon as the data leaves the outbound pipe, - // whereas ackable ones complete upon full roundtrip of the message and the ack (or timeout). - // Therefore sending them over different connections creates a possibility for processing them out of original order. - // By sending both message types over the same connection we ensure that they are sent (and processed) in their original order. - await WriteMessageAsync(serviceMessage); - - var status = await task; - return AckHandler.HandleAckStatus(ackableMessage, status); } - - public virtual Task OfflineAsync(GracefulShutdownMode mode) + else if (RuntimeServicePingMessage.TryGetMessageLogEnableFlag(pingMessage, out var enableMessageLog)) { - _terminated = true; - return Task.WhenAll(ServiceConnections.Select(c => RemoveConnectionAsync(c, mode))); + _serviceDiagnosticLogsContext.EnableMessageLog = enableMessageLog; } + return Task.CompletedTask; + } - public Task StartGetServersPing() - { - if (_serversPing.Start()) - { - // reset old value when true start. - _serversTagContext = DefaultServersTagContext; - } - return Task.CompletedTask; - } + public void HandleAck(AckMessage ackMessage) + { + _ackHandler.TriggerAck(ackMessage.AckId, (AckStatus)ackMessage.Status); + } + + public virtual Task WriteAsync(ServiceMessage serviceMessage) + { + return WriteMessageAsync(serviceMessage); + } - public Task StopGetServersPing() + public async Task WriteAckableMessageAsync(ServiceMessage serviceMessage, CancellationToken cancellationToken = default) + { + if (serviceMessage is not IAckableMessage ackableMessage) { - _serversPing.Stop(); - return Task.CompletedTask; + throw new ArgumentException($"{nameof(serviceMessage)} is not {nameof(IAckableMessage)}"); } - // Ready for scalable containers - public void Dispose() + var task = _ackHandler.CreateSingleAck(out var id, null, cancellationToken); + ackableMessage.AckId = id; + + // Sending regular messages completes as soon as the data leaves the outbound pipe, + // whereas ackable ones complete upon full roundtrip of the message and the ack (or timeout). + // Therefore sending them over different connections creates a possibility for processing them out of original order. + // By sending both message types over the same connection we ensure that they are sent (and processed) in their original order. + await WriteMessageAsync(serviceMessage); + + var status = await task; + return AckHandler.HandleAckStatus(ackableMessage, status); + } + + public virtual Task OfflineAsync(GracefulShutdownMode mode) + { + _terminated = true; + return Task.WhenAll(ServiceConnections.Select(c => RemoveConnectionAsync(c, mode))); + } + + public Task StartGetServersPing() + { + if (_serversPing.Start()) { - StopAsync().GetAwaiter().GetResult(); - _statusPing.Dispose(); - _serversPing.Dispose(); - Dispose(true); - GC.SuppressFinalize(this); + // reset old value when true start. + _serversTagContext = DefaultServersTagContext; } + return Task.CompletedTask; + } + + public Task StopGetServersPing() + { + _serversPing.Stop(); + return Task.CompletedTask; + } - internal static TimeSpan GetRetryDelay(int retryCount) + // Ready for scalable containers + public void Dispose() + { + StopAsync().GetAwaiter().GetResult(); + _statusPing.Dispose(); + _serversPing.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + internal static TimeSpan GetRetryDelay(int retryCount) + { + // retry count: 0, 1, 2, 3, 4, 5, 6, ... + // delay seconds: 1, 2, 4, 8, 16, 32, 60, ... + if (retryCount > 5) { - // retry count: 0, 1, 2, 3, 4, 5, 6, ... - // delay seconds: 1, 2, 4, 8, 16, 32, 60, ... - if (retryCount > 5) - { - return TimeSpan.FromMinutes(1) + ReconnectInterval; - } - return TimeSpan.FromSeconds(1 << retryCount) + ReconnectInterval; + return TimeSpan.FromMinutes(1) + ReconnectInterval; } + return TimeSpan.FromSeconds(1 << retryCount) + ReconnectInterval; + } - internal bool GetServiceStatus(bool active, int checkWindow, TimeSpan checkTimeSpan) + internal bool GetServiceStatus(bool active, int checkWindow, TimeSpan checkTimeSpan) + { + if (active) { - if (active) - { - _inactiveInfo = (0, null); - return true; - } - else - { - var info = _inactiveInfo; - var last = info.last ?? DateTime.UtcNow; - var count = info.count; - count++; - _inactiveInfo = (count, last); - - // Inactive it only when it checks over 5 times and elapsed for over 10 minutes - var inactive = count >= checkWindow && DateTime.UtcNow - last >= checkTimeSpan; - return !inactive; - } + _inactiveInfo = (0, null); + return true; } - - /// - /// Start and manage the whole connection lifetime - /// - /// - protected async Task StartCoreAsync(IServiceConnection connection, string target = null) + else { - if (_terminated) - { - return; - } + var info = _inactiveInfo; + var last = info.last ?? DateTime.UtcNow; + var count = info.count; + count++; + _inactiveInfo = (count, last); - try - { - await connection.StartAsync(target); - } - finally - { - await OnConnectionComplete(connection); - } + // Inactive it only when it checks over 5 times and elapsed for over 10 minutes + var inactive = count >= checkWindow && DateTime.UtcNow - last >= checkTimeSpan; + return !inactive; } + } - /// - /// Create a connection for a specific service connection type - /// - protected IServiceConnection CreateServiceConnectionCore(ServiceConnectionType type) + /// + /// Start and manage the whole connection lifetime + /// + /// + protected async Task StartCoreAsync(IServiceConnection connection, string target = null) + { + if (_terminated) { - var connection = ServiceConnectionFactory.Create(Endpoint, this, _ackHandler, type); - - connection.ConnectionStatusChanged += OnConnectionStatusChanged; - return connection; + return; } - protected virtual async Task OnConnectionComplete(IServiceConnection serviceConnection) + try { - if (serviceConnection == null) - { - throw new ArgumentNullException(nameof(serviceConnection)); - } + await connection.StartAsync(target); + } + finally + { + await OnConnectionComplete(connection); + } + } - serviceConnection.ConnectionStatusChanged -= OnConnectionStatusChanged; + /// + /// Create a connection for a specific service connection type + /// + protected IServiceConnection CreateServiceConnectionCore(ServiceConnectionType type) + { + var connection = ServiceConnectionFactory.Create(Endpoint, this, _ackHandler, type); - var index = ServiceConnections.IndexOf(serviceConnection); - if (index != -1) - { - // always try to restart first FixedConnectionCount connections - if (index < FixedConnectionCount) - { - await RestartFixedServiceConnectionCoreAsync(index); - } + connection.ConnectionStatusChanged += OnConnectionStatusChanged; + return connection; + } - // the rest are "on demand" and are only created upon request - else - { - RemoveOnDemandConnection(serviceConnection); - } - } + protected virtual async Task OnConnectionComplete(IServiceConnection serviceConnection) + { + if (serviceConnection == null) + { + throw new ArgumentNullException(nameof(serviceConnection)); } - protected void AddOnDemandConnection(IServiceConnection serviceConnection) + serviceConnection.ConnectionStatusChanged -= OnConnectionStatusChanged; + + var index = ServiceConnections.IndexOf(serviceConnection); + if (index != -1) { - lock (_lock) + // always try to restart first FixedConnectionCount connections + if (index < FixedConnectionCount) { - var newImmutableConnections = ServiceConnections.ToList(); - newImmutableConnections.Add(serviceConnection); - Debug.Assert(newImmutableConnections.IndexOf(serviceConnection) >= FixedConnectionCount); - ServiceConnections = newImmutableConnections; + await RestartFixedServiceConnectionCoreAsync(index); } - } - protected virtual void Dispose(bool disposing) - { - if (disposing) + // the rest are "on demand" and are only created upon request + else { - _ackHandler.Dispose(); + RemoveOnDemandConnection(serviceConnection); } } + } - protected virtual ServiceConnectionStatus GetStatus() + protected void AddOnDemandConnection(IServiceConnection serviceConnection) + { + lock (_lock) { - return ServiceConnections.Any(s => s.Status == ServiceConnectionStatus.Connected) - ? ServiceConnectionStatus.Connected - : ServiceConnectionStatus.Disconnected; + var newImmutableConnections = ServiceConnections.ToList(); + newImmutableConnections.Add(serviceConnection); + Debug.Assert(newImmutableConnections.IndexOf(serviceConnection) >= FixedConnectionCount); + ServiceConnections = newImmutableConnections; } + } - protected async Task WriteFinAsync(IServiceConnection c, GracefulShutdownMode mode) + protected virtual void Dispose(bool disposing) + { + if (disposing) { - var message = RuntimeServicePingMessage.GetFinPingMessage(mode); - await c.WriteAsync(message); + _ackHandler.Dispose(); } + } + + protected virtual ServiceConnectionStatus GetStatus() + { + return ServiceConnections.Any(s => s.Status == ServiceConnectionStatus.Connected) + ? ServiceConnectionStatus.Connected + : ServiceConnectionStatus.Disconnected; + } - protected async Task RemoveConnectionAsync(IServiceConnection c, GracefulShutdownMode mode) + protected async Task WriteFinAsync(IServiceConnection c, GracefulShutdownMode mode) + { + var message = RuntimeServicePingMessage.GetFinPingMessage(mode); + await c.WriteAsync(message); + } + + protected async Task RemoveConnectionAsync(IServiceConnection c, GracefulShutdownMode mode) + { + var retry = 0; + while (retry < MaxRetryRemoveSeverConnection) { - var retry = 0; - while (retry < MaxRetryRemoveSeverConnection) - { - using var source = new CancellationTokenSource(); - _ = WriteFinAsync(c, mode); + using var source = new CancellationTokenSource(); + _ = WriteFinAsync(c, mode); - var task = await Task.WhenAny(c.ConnectionOfflineTask, Task.Delay(Constants.Periods.RemoveFromServiceTimeout, source.Token)); + var task = await Task.WhenAny(c.ConnectionOfflineTask, Task.Delay(Constants.Periods.RemoveFromServiceTimeout, source.Token)); - if (task == c.ConnectionOfflineTask) - { - source.Cancel(); - Log.ReceivedFinAckPing(Logger); - return; - } - retry++; + if (task == c.ConnectionOfflineTask) + { + source.Cancel(); + Log.ReceivedFinAckPing(Logger); + return; } - Log.TimeoutWaitingForFinAck(Logger, retry); + retry++; } + Log.TimeoutWaitingForFinAck(Logger, retry); + } - private async Task WriteMessageAsync(ServiceMessage serviceMessage) - { - var connection = SelectConnection(serviceMessage); + private async Task WriteMessageAsync(ServiceMessage serviceMessage) + { + var connection = SelectConnection(serviceMessage); - var retry = 0; - var maxRetry = MessageWriteMaxRetry; - var delay = MessageWriteRetryDelay; - while (true) + var retry = 0; + var maxRetry = MessageWriteMaxRetry; + var delay = MessageWriteRetryDelay; + while (true) + { + try { - try + await connection.WriteAsync(serviceMessage); + return; + } + catch (ServiceConnectionNotActiveException) + { + // enter the re-select logic + retry++; + if (retry == maxRetry) { - await connection.WriteAsync(serviceMessage); - return; + throw; } - catch (ServiceConnectionNotActiveException) - { - // enter the re-select logic - retry++; - if (retry == maxRetry) - { - throw; - } - await Task.Delay(delay); - connection = SelectConnection(serviceMessage); - } + await Task.Delay(delay); + connection = SelectConnection(serviceMessage); } } + } - private IServiceConnection SelectConnection(ServiceMessage message) + private IServiceConnection SelectConnection(ServiceMessage message) + { + IServiceConnection connection = null; + if (ClientConnectionScope.IsScopeEstablished) { - IServiceConnection connection = null; - if (ClientConnectionScope.IsScopeEstablished) + // see if the execution context already has the connection stored for this container + var containers = ClientConnectionScope.OutboundServiceConnections; + if (!(containers.TryGetValue(Endpoint.UniqueIndex, out var connectionWeakReference) + && connectionWeakReference.TryGetTarget(out connection) + && IsActiveConnection(connection))) { - // see if the execution context already has the connection stored for this container - var containers = ClientConnectionScope.OutboundServiceConnections; - if (!(containers.TryGetValue(Endpoint.UniqueIndex, out var connectionWeakReference) - && connectionWeakReference.TryGetTarget(out connection) - && IsActiveConnection(connection))) - { - connection = GetRandomActiveConnection(); - ClientConnectionScope.OutboundServiceConnections[Endpoint.UniqueIndex] = new WeakReference(connection); - } + connection = GetRandomActiveConnection(); + ClientConnectionScope.OutboundServiceConnections[Endpoint.UniqueIndex] = new WeakReference(connection); } - else + } + else + { + // if it is not in scope + // if message is partitionable, use the container's partition cache, otherwise use a random connection + if (message is IPartitionableMessage partitionable) { - // if it is not in scope - // if message is partitionable, use the container's partition cache, otherwise use a random connection - if (message is IPartitionableMessage partitionable) + var box = _partitionedCache[partitionable.PartitionKey]; + if (!box.Value.TryGetTarget(out connection) || !IsActiveConnection(connection)) { - var box = _partitionedCache[partitionable.PartitionKey]; - if (!box.Value.TryGetTarget(out connection) || !IsActiveConnection(connection)) + lock (box) { - lock (box) + if (!box.Value.TryGetTarget(out connection) || !IsActiveConnection(connection)) { - if (!box.Value.TryGetTarget(out connection) || !IsActiveConnection(connection)) - { - connection = GetRandomActiveConnection(); - box.Value.SetTarget(connection); - } + connection = GetRandomActiveConnection(); + box.Value.SetTarget(connection); } } } - else - { - connection = GetRandomActiveConnection(); - } } - - if (connection == null) + else { - throw new ServiceConnectionNotActiveException(); + connection = GetRandomActiveConnection(); } - - return connection; } - private bool IsActiveConnection(IServiceConnection connection) + if (connection == null) { - return connection != null && connection.Status == ServiceConnectionStatus.Connected; + throw new ServiceConnectionNotActiveException(); } - private IServiceConnection GetRandomActiveConnection() - { - var currentConnections = ServiceConnections; - - // go through all the connections, it can be useful when one of the remote service instances is down - var count = currentConnections.Count; - var initial = StaticRandom.Next(-count, count); - var maxRetry = count; - var retry = 0; - var index = (initial & int.MaxValue) % count; - var direction = initial > 0 ? 1 : count - 1; + return connection; + } - while (retry < maxRetry) - { - var connection = currentConnections[index]; - if (IsActiveConnection(connection)) - { - return connection; - } + private bool IsActiveConnection(IServiceConnection connection) + { + return connection != null && connection.Status == ServiceConnectionStatus.Connected; + } - retry++; - index = (index + direction) % count; - } + private IServiceConnection GetRandomActiveConnection() + { + var currentConnections = ServiceConnections; - return null; - } + // go through all the connections, it can be useful when one of the remote service instances is down + var count = currentConnections.Count; + var initial = StaticRandom.Next(-count, count); + var maxRetry = count; + var retry = 0; + var index = (initial & int.MaxValue) % count; + var direction = initial > 0 ? 1 : count - 1; - private async Task RestartFixedServiceConnectionCoreAsync(int index) + while (retry < maxRetry) { - if (_terminated) + var connection = currentConnections[index]; + if (IsActiveConnection(connection)) { - return; + return connection; } - Func> tryNewConnection = async () => - { - var connection = CreateServiceConnectionCore(InitialConnectionType); - ReplaceFixedConnection(index, connection); + retry++; + index = (index + direction) % count; + } - _ = StartCoreAsync(connection); - await connection.ConnectionInitializedTask; + return null; + } - return connection.Status == ServiceConnectionStatus.Connected; - }; - await _backOffPolicy.CallProbeWithBackOffAsync(tryNewConnection, GetRetryDelay); + private async Task RestartFixedServiceConnectionCoreAsync(int index) + { + if (_terminated) + { + return; } - private void ReplaceFixedConnection(int index, IServiceConnection serviceConnection) + Func> tryNewConnection = async () => { - lock (_lock) - { - var newImmutableConnections = ServiceConnections.ToList(); - newImmutableConnections[index] = serviceConnection; - ServiceConnections = newImmutableConnections; - } - } + var connection = CreateServiceConnectionCore(InitialConnectionType); + ReplaceFixedConnection(index, connection); + + _ = StartCoreAsync(connection); + await connection.ConnectionInitializedTask; + + return connection.Status == ServiceConnectionStatus.Connected; + }; + await _backOffPolicy.CallProbeWithBackOffAsync(tryNewConnection, GetRetryDelay); + } - private void RemoveOnDemandConnection(IServiceConnection serviceConnection) + private void ReplaceFixedConnection(int index, IServiceConnection serviceConnection) + { + lock (_lock) { - lock (_lock) - { - var newImmutableConnections = ServiceConnections.ToList(); - Debug.Assert(newImmutableConnections.IndexOf(serviceConnection) >= FixedConnectionCount); - newImmutableConnections.Remove(serviceConnection); - ServiceConnections = newImmutableConnections; - } + var newImmutableConnections = ServiceConnections.ToList(); + newImmutableConnections[index] = serviceConnection; + ServiceConnections = newImmutableConnections; } + } - private void OnStatusChanged(StatusChange obj) + private void RemoveOnDemandConnection(IServiceConnection serviceConnection) + { + lock (_lock) { - var online = obj.NewStatus == ServiceConnectionStatus.Connected; - Endpoint.Online = online; - if (!online) - { - Log.EndpointOffline(Logger, Endpoint); - } - else - { - Log.EndpointOnline(Logger, Endpoint); - } + var newImmutableConnections = ServiceConnections.ToList(); + Debug.Assert(newImmutableConnections.IndexOf(serviceConnection) >= FixedConnectionCount); + newImmutableConnections.Remove(serviceConnection); + ServiceConnections = newImmutableConnections; } + } - private void OnConnectionStatusChanged(StatusChange obj) + private void OnStatusChanged(StatusChange obj) + { + var online = obj.NewStatus == ServiceConnectionStatus.Connected; + Endpoint.Online = online; + if (!online) { - if (obj.NewStatus == ServiceConnectionStatus.Connected && Status != ServiceConnectionStatus.Connected) - { - Status = GetStatus(); - } - else if (obj.NewStatus == ServiceConnectionStatus.Disconnected && Status != ServiceConnectionStatus.Disconnected) - { - Status = GetStatus(); - } + Log.EndpointOffline(Logger, Endpoint); } - - private IEnumerable CreateFixedServiceConnection(int count) + else { - for (int i = 0; i < count; i++) - { - yield return CreateServiceConnectionCore(InitialConnectionType); - } + Log.EndpointOnline(Logger, Endpoint); } + } - private Task WriteServiceStatusPingAsync() + private void OnConnectionStatusChanged(StatusChange obj) + { + if (obj.NewStatus == ServiceConnectionStatus.Connected && Status != ServiceConnectionStatus.Connected) { - return SafeWriteAsync(RuntimeServicePingMessage.GetStatusPingMessage(true)); + Status = GetStatus(); } - - private Task WriteServersPingAsync() + else if (obj.NewStatus == ServiceConnectionStatus.Disconnected && Status != ServiceConnectionStatus.Disconnected) { - if (Stopwatch.GetTimestamp() - _serversTagContext.Item2 > DefaultServersPingTimeoutTicks) - { - // reset value if expired. - _serversTagContext = DefaultServersTagContext; - } + Status = GetStatus(); + } + } - return SafeWriteAsync(RuntimeServicePingMessage.GetServersPingMessage()); + private IEnumerable CreateFixedServiceConnection(int count) + { + for (int i = 0; i < count; i++) + { + yield return CreateServiceConnectionCore(InitialConnectionType); } + } - private async Task SafeWriteAsync(ServiceMessage serviceMessage) + private Task WriteServiceStatusPingAsync() + { + return SafeWriteAsync(RuntimeServicePingMessage.GetStatusPingMessage(true)); + } + + private Task WriteServersPingAsync() + { + if (Stopwatch.GetTimestamp() - _serversTagContext.Item2 > DefaultServersPingTimeoutTicks) { - try - { - await WriteAsync(serviceMessage); - } - catch - { - } + // reset value if expired. + _serversTagContext = DefaultServersTagContext; } - protected internal sealed class CustomizedPingTimer : IDisposable + return SafeWriteAsync(RuntimeServicePingMessage.GetServersPingMessage()); + } + + private async Task SafeWriteAsync(ServiceMessage serviceMessage) + { + try + { + await WriteAsync(serviceMessage); + } + catch { - private readonly object _lock = new object(); + } + } - private readonly long _defaultPingTicks; + protected internal sealed class CustomizedPingTimer : IDisposable + { + private readonly object _lock = new object(); + + private readonly long _defaultPingTicks; - private readonly string _pingName; + private readonly string _pingName; - private readonly Func _writePing; + private readonly Func _writePing; - private readonly TimeSpan _dueTime; + private readonly TimeSpan _dueTime; - private readonly TimeSpan _intervalTime; + private readonly TimeSpan _intervalTime; - private readonly ILogger _logger; + private readonly ILogger _logger; - // Considering parallel add endpoints to save time, - // Add a counter control multiple time call Start() and Stop() correctly. - private long _counter = 0; + // Considering parallel add endpoints to save time, + // Add a counter control multiple time call Start() and Stop() correctly. + private long _counter = 0; - private long _lastSendTimestamp = 0; + private long _lastSendTimestamp = 0; - private TimerAwaitable _timer; + private TimerAwaitable _timer; + + public CustomizedPingTimer(ILogger logger, string pingName, Func writePing, TimeSpan dueTime, TimeSpan intervalTime) + { + _logger = logger; + _pingName = pingName; + _writePing = writePing; + _dueTime = dueTime; + _intervalTime = intervalTime; + _defaultPingTicks = intervalTime.Seconds * Stopwatch.Frequency; + } - public CustomizedPingTimer(ILogger logger, string pingName, Func writePing, TimeSpan dueTime, TimeSpan intervalTime) + public bool Start() + { + if (Interlocked.Increment(ref _counter) == 1) { - _logger = logger; - _pingName = pingName; - _writePing = writePing; - _dueTime = dueTime; - _intervalTime = intervalTime; - _defaultPingTicks = intervalTime.Seconds * Stopwatch.Frequency; + _timer = Init(); + _timer.Start(); + _ = PingAsync(_timer); + return true; } + return false; + } - public bool Start() + public void Stop() + { + // might be called by multi-thread, lock to ensure thread-safety for _counter update + lock (_lock) { - if (Interlocked.Increment(ref _counter) == 1) + if (Interlocked.Read(ref _counter) == 0) { - _timer = Init(); - _timer.Start(); - _ = PingAsync(_timer); - return true; + // Avoid wrong Stop() to break _counter in further scale + Log.TimerAlreadyStopped(_logger, _pingName); + return; } - return false; - } - - public void Stop() - { - // might be called by multi-thread, lock to ensure thread-safety for _counter update - lock (_lock) + if (Interlocked.Decrement(ref _counter) == 0) { - if (Interlocked.Read(ref _counter) == 0) - { - // Avoid wrong Stop() to break _counter in further scale - Log.TimerAlreadyStopped(_logger, _pingName); - return; - } - if (Interlocked.Decrement(ref _counter) == 0) - { - CleanupTimer(); - } + CleanupTimer(); } } + } - public void Dispose() - { - CleanupTimer(); - } + public void Dispose() + { + CleanupTimer(); + } - private void CleanupTimer() - { - var timer = Interlocked.Exchange(ref _timer, null); - timer?.Stop(); - (timer as IDisposable)?.Dispose(); - } + private void CleanupTimer() + { + var timer = Interlocked.Exchange(ref _timer, null); + timer?.Stop(); + (timer as IDisposable)?.Dispose(); + } - private TimerAwaitable Init() - { - Log.StartingPingTimer(_logger, _pingName, _intervalTime); + private TimerAwaitable Init() + { + Log.StartingPingTimer(_logger, _pingName, _intervalTime); - _lastSendTimestamp = Stopwatch.GetTimestamp(); - var timer = new TimerAwaitable(_dueTime, _intervalTime); + _lastSendTimestamp = Stopwatch.GetTimestamp(); + var timer = new TimerAwaitable(_dueTime, _intervalTime); - return timer; - } + return timer; + } - private async Task PingAsync(TimerAwaitable timer) + private async Task PingAsync(TimerAwaitable timer) + { + while (await timer) { - while (await timer) + try { - try + // Check if last send time is longer than default keep-alive ticks and then send ping + if (Stopwatch.GetTimestamp() - Interlocked.Read(ref _lastSendTimestamp) > _defaultPingTicks) { - // Check if last send time is longer than default keep-alive ticks and then send ping - if (Stopwatch.GetTimestamp() - Interlocked.Read(ref _lastSendTimestamp) > _defaultPingTicks) - { - await _writePing.Invoke(); + await _writePing.Invoke(); - Interlocked.Exchange(ref _lastSendTimestamp, Stopwatch.GetTimestamp()); - Log.SentPing(_logger, _pingName); - } - } - catch (Exception e) - { - Log.FailedSendingPing(_logger, _pingName, e); + Interlocked.Exchange(ref _lastSendTimestamp, Stopwatch.GetTimestamp()); + Log.SentPing(_logger, _pingName); } } + catch (Exception e) + { + Log.FailedSendingPing(_logger, _pingName, e); + } } } + } - private static class Log - { - private static readonly Action _endpointOnline = - LoggerMessage.Define(LogLevel.Information, new EventId(1, "EndpointOnline"), "Hub '{hub}' is now connected to '{endpoint}'."); + private static class Log + { + private static readonly Action _endpointOnline = + LoggerMessage.Define(LogLevel.Information, new EventId(1, "EndpointOnline"), "Hub '{hub}' is now connected to '{endpoint}'."); - private static readonly Action _endpointOffline = - LoggerMessage.Define(LogLevel.Error, new EventId(2, "EndpointOffline"), "Hub '{hub}' is now disconnected from '{endpoint}'. Please check log for detailed info."); + private static readonly Action _endpointOffline = + LoggerMessage.Define(LogLevel.Error, new EventId(2, "EndpointOffline"), "Hub '{hub}' is now disconnected from '{endpoint}'. Please check log for detailed info."); - private static readonly Action _receivedFinAckPing = - LoggerMessage.Define(LogLevel.Information, new EventId(3, "ReceivedFinAckPing"), "Received FinAck ping."); + private static readonly Action _receivedFinAckPing = + LoggerMessage.Define(LogLevel.Information, new EventId(3, "ReceivedFinAckPing"), "Received FinAck ping."); - private static readonly Action _timeoutWaitingForFinAck = - LoggerMessage.Define(LogLevel.Error, new EventId(4, "TimeoutWaitingForFinAck"), "Fail to receive FinAckPing after retry {retryCount} times."); + private static readonly Action _timeoutWaitingForFinAck = + LoggerMessage.Define(LogLevel.Error, new EventId(4, "TimeoutWaitingForFinAck"), "Fail to receive FinAckPing after retry {retryCount} times."); - private static readonly Action _startingPingTimer = - LoggerMessage.Define(LogLevel.Debug, new EventId(5, "StartingPingTimer"), "Starting { pingName } ping timer. Duration: {KeepAliveInterval:0.00}ms"); + private static readonly Action _startingPingTimer = + LoggerMessage.Define(LogLevel.Debug, new EventId(5, "StartingPingTimer"), "Starting { pingName } ping timer. Duration: {KeepAliveInterval:0.00}ms"); - private static readonly Action _sentPing = - LoggerMessage.Define(LogLevel.Debug, new EventId(6, "SentPing"), "Sent a { pingName } ping message to service."); + private static readonly Action _sentPing = + LoggerMessage.Define(LogLevel.Debug, new EventId(6, "SentPing"), "Sent a { pingName } ping message to service."); - private static readonly Action _failedSendingPing = - LoggerMessage.Define(LogLevel.Warning, new EventId(7, "FailedSendingPing"), "Failed sending a { pingName } ping message to service."); + private static readonly Action _failedSendingPing = + LoggerMessage.Define(LogLevel.Warning, new EventId(7, "FailedSendingPing"), "Failed sending a { pingName } ping message to service."); - private static readonly Action _receivedServiceStatusPing = - LoggerMessage.Define(LogLevel.Debug, new EventId(8, "ReceivedServiceStatusPing"), "Received a service status active={isActive} from {endpoint} for hub {hub}."); + private static readonly Action _receivedServiceStatusPing = + LoggerMessage.Define(LogLevel.Debug, new EventId(8, "ReceivedServiceStatusPing"), "Received a service status active={isActive} from {endpoint} for hub {hub}."); - private static readonly Action _receivedServersTagPing = - LoggerMessage.Define(LogLevel.Debug, new EventId(9, "ReceivedServersTagPing"), "Received a servers tag ping from {endpoint} for hub {hub}."); + private static readonly Action _receivedServersTagPing = + LoggerMessage.Define(LogLevel.Debug, new EventId(9, "ReceivedServersTagPing"), "Received a servers tag ping from {endpoint} for hub {hub}."); - private static readonly Action _timerAlreadyStopped = - LoggerMessage.Define(LogLevel.Debug, new EventId(10, "TimerAlreadyStopped"), "Failed to stop {pingName} timer as it's not started"); + private static readonly Action _timerAlreadyStopped = + LoggerMessage.Define(LogLevel.Debug, new EventId(10, "TimerAlreadyStopped"), "Failed to stop {pingName} timer as it's not started"); - public static void EndpointOnline(ILogger logger, HubServiceEndpoint endpoint) - { - _endpointOnline(logger, endpoint.Hub, endpoint.ToString(), null); - } + public static void EndpointOnline(ILogger logger, HubServiceEndpoint endpoint) + { + _endpointOnline(logger, endpoint.Hub, endpoint.ToString(), null); + } - public static void EndpointOffline(ILogger logger, HubServiceEndpoint endpoint) - { - _endpointOffline(logger, endpoint.Hub, endpoint.ToString(), null); - } + public static void EndpointOffline(ILogger logger, HubServiceEndpoint endpoint) + { + _endpointOffline(logger, endpoint.Hub, endpoint.ToString(), null); + } - public static void ReceivedFinAckPing(ILogger logger) - { - _receivedFinAckPing(logger, null); - } + public static void ReceivedFinAckPing(ILogger logger) + { + _receivedFinAckPing(logger, null); + } - public static void TimeoutWaitingForFinAck(ILogger logger, int retryCount) - { - _timeoutWaitingForFinAck(logger, retryCount, null); - } + public static void TimeoutWaitingForFinAck(ILogger logger, int retryCount) + { + _timeoutWaitingForFinAck(logger, retryCount, null); + } - public static void StartingPingTimer(ILogger logger, string pingName, TimeSpan keepAliveInterval) - { - _startingPingTimer(logger, pingName, keepAliveInterval.TotalMilliseconds, null); - } + public static void StartingPingTimer(ILogger logger, string pingName, TimeSpan keepAliveInterval) + { + _startingPingTimer(logger, pingName, keepAliveInterval.TotalMilliseconds, null); + } - public static void SentPing(ILogger logger, string pingName) - { - _sentPing(logger, pingName, null); - } + public static void SentPing(ILogger logger, string pingName) + { + _sentPing(logger, pingName, null); + } - public static void FailedSendingPing(ILogger logger, string pingName, Exception exception) - { - _failedSendingPing(logger, pingName, exception); - } + public static void FailedSendingPing(ILogger logger, string pingName, Exception exception) + { + _failedSendingPing(logger, pingName, exception); + } - public static void ReceivedServiceStatusPing(ILogger logger, bool isActive, HubServiceEndpoint endpoint) - { - _receivedServiceStatusPing(logger, isActive, endpoint, endpoint.Hub, null); - } + public static void ReceivedServiceStatusPing(ILogger logger, bool isActive, HubServiceEndpoint endpoint) + { + _receivedServiceStatusPing(logger, isActive, endpoint, endpoint.Hub, null); + } - public static void ReceivedServersTagPing(ILogger logger, HubServiceEndpoint endpoint) - { - _receivedServersTagPing(logger, endpoint, endpoint.Hub, null); - } + public static void ReceivedServersTagPing(ILogger logger, HubServiceEndpoint endpoint) + { + _receivedServersTagPing(logger, endpoint, endpoint.Hub, null); + } - public static void TimerAlreadyStopped(ILogger logger, string pingName) - { - _timerAlreadyStopped(logger, pingName, null); - } + public static void TimerAlreadyStopped(ILogger logger, string pingName) + { + _timerAlreadyStopped(logger, pingName, null); } } } diff --git a/src/Microsoft.Azure.SignalR.Common/ServiceMessages/RuntimeServicePingMessages.cs b/src/Microsoft.Azure.SignalR.Common/ServiceMessages/RuntimeServicePingMessages.cs index 341c64386..222976278 100644 --- a/src/Microsoft.Azure.SignalR.Common/ServiceMessages/RuntimeServicePingMessages.cs +++ b/src/Microsoft.Azure.SignalR.Common/ServiceMessages/RuntimeServicePingMessages.cs @@ -5,197 +5,196 @@ using Microsoft.Azure.SignalR.Protocol; using ServicePingMessage = Microsoft.Azure.SignalR.Protocol.PingMessage; -namespace Microsoft.Azure.SignalR +namespace Microsoft.Azure.SignalR; + +internal static class RuntimeServicePingMessage { - internal static class RuntimeServicePingMessage - { - private const string EchoKey = "echo"; - private const string OfflineKey = "offline"; - private const string TargetKey = "target"; - private const string StatusKey = "status"; - private const string ShutdownKey = "shutdown"; - private const string ServersKey = "servers"; - private const string ClientCountKey = "clientcount"; - private const string ServerCountKey = "servercount"; - private const string CapacityKey = "capacity"; - private const string DiagnosticLogsMessagingTypeKey = "d-m"; + private const string EchoKey = "echo"; + private const string OfflineKey = "offline"; + private const string TargetKey = "target"; + private const string StatusKey = "status"; + private const string ShutdownKey = "shutdown"; + private const string ServersKey = "servers"; + private const string ClientCountKey = "clientcount"; + private const string ServerCountKey = "servercount"; + private const string CapacityKey = "capacity"; + private const string DiagnosticLogsMessagingTypeKey = "d-m"; - private const string MessagingLogEnableValue = "1"; + private const string MessagingLogEnableValue = "1"; - private const string StatusActiveValue = "1"; - private const string StatusInactiveValue = "0"; + private const string StatusActiveValue = "1"; + private const string StatusInactiveValue = "0"; - private const string ShutdownFinKeepAliveValue = "fin:2"; - private const string ShutdownFinMigratableValue = "fin:1"; - private const string ShutdownFinValue = "fin:0"; + private const string ShutdownFinKeepAliveValue = "fin:2"; + private const string ShutdownFinMigratableValue = "fin:1"; + private const string ShutdownFinValue = "fin:0"; - private const string ShutdownFinAckValue = "finack"; - private const char ServerListSeparator = ';'; + private const string ShutdownFinAckValue = "finack"; + private const char ServerListSeparator = ';'; - private static readonly ServicePingMessage StatusActive = - new ServicePingMessage { Messages = new[] { StatusKey, StatusActiveValue } }; + private static readonly ServicePingMessage StatusActive = + new ServicePingMessage { Messages = new[] { StatusKey, StatusActiveValue } }; - private static readonly ServicePingMessage StatusInactive = - new ServicePingMessage { Messages = new[] { StatusKey, StatusInactiveValue } }; + private static readonly ServicePingMessage StatusInactive = + new ServicePingMessage { Messages = new[] { StatusKey, StatusInactiveValue } }; - private static readonly ServicePingMessage ShutdownFin = - new ServicePingMessage { Messages = new[] { ShutdownKey, ShutdownFinValue } }; + private static readonly ServicePingMessage ShutdownFin = + new ServicePingMessage { Messages = new[] { ShutdownKey, ShutdownFinValue } }; - private static readonly ServicePingMessage ShutdownFinMigratable = - new ServicePingMessage { Messages = new[] { ShutdownKey, ShutdownFinMigratableValue } }; + private static readonly ServicePingMessage ShutdownFinMigratable = + new ServicePingMessage { Messages = new[] { ShutdownKey, ShutdownFinMigratableValue } }; - private static readonly ServicePingMessage ShutdownFinKeepAlive = - new ServicePingMessage { Messages = new[] { ShutdownKey, ShutdownFinKeepAliveValue } }; + private static readonly ServicePingMessage ShutdownFinKeepAlive = + new ServicePingMessage { Messages = new[] { ShutdownKey, ShutdownFinKeepAliveValue } }; - private static readonly ServicePingMessage ShutdownFinAck = - new ServicePingMessage { Messages = new[] { ShutdownKey, ShutdownFinAckValue } }; + private static readonly ServicePingMessage ShutdownFinAck = + new ServicePingMessage { Messages = new[] { ShutdownKey, ShutdownFinAckValue } }; - private static readonly ServicePingMessage ServersTag = - new ServicePingMessage { Messages = new[] { ServersKey, string.Empty } }; + private static readonly ServicePingMessage ServersTag = + new ServicePingMessage { Messages = new[] { ServersKey, string.Empty } }; - public static bool IsEchoMessage(this ServicePingMessage ping) - { - return TryGetValue(ping, EchoKey, out _); - } + public static bool IsEchoMessage(this ServicePingMessage ping) + { + return TryGetValue(ping, EchoKey, out _); + } - public static bool TryGetMessageLogEnableFlag(this ServicePingMessage ping, out bool enableMessageLog) + public static bool TryGetMessageLogEnableFlag(this ServicePingMessage ping, out bool enableMessageLog) + { + if (TryGetValue(ping, DiagnosticLogsMessagingTypeKey, out var value)) { - if (TryGetValue(ping, DiagnosticLogsMessagingTypeKey, out var value)) - { - enableMessageLog = value == MessagingLogEnableValue; - return true; - } - enableMessageLog = default; - return false; + enableMessageLog = value == MessagingLogEnableValue; + return true; } + enableMessageLog = default; + return false; + } - public static bool TryGetOffline(this ServicePingMessage ping, out string instanceId) => - TryGetValue(ping, OfflineKey, out instanceId); + public static bool TryGetOffline(this ServicePingMessage ping, out string instanceId) => + TryGetValue(ping, OfflineKey, out instanceId); - public static bool TryGetRebalance(this ServicePingMessage ping, out string target) => - TryGetValue(ping, TargetKey, out target); + public static bool TryGetRebalance(this ServicePingMessage ping, out string target) => + TryGetValue(ping, TargetKey, out target); - // ping to runtime ask for status - public static ServicePingMessage GetStatusPingMessage(bool isActive) => - isActive ? StatusActive : StatusInactive; + // ping to runtime ask for status + public static ServicePingMessage GetStatusPingMessage(bool isActive) => + isActive ? StatusActive : StatusInactive; - public static ServicePingMessage GetStatusPingMessage(bool isActive, int clientCount) => - isActive - ? new ServicePingMessage { Messages = new[] { StatusKey, StatusActiveValue, ClientCountKey, clientCount.ToString() } } - : new ServicePingMessage { Messages = new[] { StatusKey, StatusInactiveValue, ClientCountKey, clientCount.ToString() } }; + public static ServicePingMessage GetStatusPingMessage(bool isActive, int clientCount) => + isActive + ? new ServicePingMessage { Messages = new[] { StatusKey, StatusActiveValue, ClientCountKey, clientCount.ToString() } } + : new ServicePingMessage { Messages = new[] { StatusKey, StatusInactiveValue, ClientCountKey, clientCount.ToString() } }; - public static bool TryGetStatus(this ServicePingMessage ping, out bool isActive) + public static bool TryGetStatus(this ServicePingMessage ping, out bool isActive) + { + if (!TryGetValue(ping, StatusKey, out var value)) { - if (!TryGetValue(ping, StatusKey, out var value)) - { - isActive = false; - return false; - } - isActive = value == StatusActiveValue; - return true; + isActive = false; + return false; } + isActive = value == StatusActiveValue; + return true; + } - public static bool TryGetClientCount(this ServicePingMessage ping, out int clientCount) + public static bool TryGetClientCount(this ServicePingMessage ping, out int clientCount) + { + if (!TryGetValue(ping, ClientCountKey, out var value) || !int.TryParse(value, out var count)) { - if (!TryGetValue(ping, ClientCountKey, out var value) || !int.TryParse(value, out var count)) - { - clientCount = 0; - return false; - } - clientCount = count; - return true; + clientCount = 0; + return false; } + clientCount = count; + return true; + } - public static bool TryGetServerCount(this ServicePingMessage ping, out int serverCount) + public static bool TryGetServerCount(this ServicePingMessage ping, out int serverCount) + { + if (!TryGetValue(ping, ServerCountKey, out var value) || !int.TryParse(value, out var count)) { - if (!TryGetValue(ping, ServerCountKey, out var value) || !int.TryParse(value, out var count)) - { - serverCount = 0; - return false; - } - serverCount = count; - return true; + serverCount = 0; + return false; } + serverCount = count; + return true; + } - public static bool TryGetConnectionCapacity(this ServicePingMessage ping, out int connectionCapacity) + public static bool TryGetConnectionCapacity(this ServicePingMessage ping, out int connectionCapacity) + { + if (!TryGetValue(ping, CapacityKey, out var value) || !int.TryParse(value, out var count)) { - if (!TryGetValue(ping, CapacityKey, out var value) || !int.TryParse(value, out var count)) - { - connectionCapacity = 0; - return false; - } - connectionCapacity = count; - return true; + connectionCapacity = 0; + return false; } + connectionCapacity = count; + return true; + } - public static bool TryGetServersTag(this ServicePingMessage ping, out string serversTag, out long updatedTime) + public static bool TryGetServersTag(this ServicePingMessage ping, out string serversTag, out long updatedTime) + { + // servers ping format: { "servers", "1234567890:server1;server2;server3" } + if (TryGetValue(ping, ServersKey, out var value) && !string.IsNullOrEmpty(value)) { - // servers ping format: { "servers", "1234567890:server1;server2;server3" } - if (TryGetValue(ping, ServersKey, out var value) && !string.IsNullOrEmpty(value)) + var indexPos = value.IndexOf(":"); + if (long.TryParse(value.Substring(0, indexPos), out updatedTime)) { - var indexPos = value.IndexOf(":"); - if (long.TryParse(value.Substring(0, indexPos), out updatedTime)) - { - serversTag = value.Substring(indexPos + 1); - return true; - } + serversTag = value.Substring(indexPos + 1); + return true; } - serversTag = string.Empty; - updatedTime = DateTime.MinValue.Ticks; - return false; } + serversTag = string.Empty; + updatedTime = DateTime.MinValue.Ticks; + return false; + } - public static ServicePingMessage GetFinPingMessage(GracefulShutdownMode mode = GracefulShutdownMode.Off) + public static ServicePingMessage GetFinPingMessage(GracefulShutdownMode mode = GracefulShutdownMode.Off) + { + return mode switch { - return mode switch - { - GracefulShutdownMode.WaitForClientsClose => ShutdownFinKeepAlive, - GracefulShutdownMode.MigrateClients => ShutdownFinMigratable, - _ => ShutdownFin, - }; - } + GracefulShutdownMode.WaitForClientsClose => ShutdownFinKeepAlive, + GracefulShutdownMode.MigrateClients => ShutdownFinMigratable, + _ => ShutdownFin, + }; + } - public static ServicePingMessage GetFinAckPingMessage() => ShutdownFinAck; + public static ServicePingMessage GetFinAckPingMessage() => ShutdownFinAck; - public static ServicePingMessage GetServersPingMessage() => ServersTag; + public static ServicePingMessage GetServersPingMessage() => ServersTag; - // for test - public static bool IsFin(this ServiceMessage serviceMessage) => - serviceMessage is ServicePingMessage ping && TryGetValue(ping, ShutdownKey, out var value) && (value switch - { - ShutdownFinValue => true, - ShutdownFinMigratableValue => true, - ShutdownFinKeepAliveValue => true, - _ => false, - }); + // for test + public static bool IsFin(this ServiceMessage serviceMessage) => + serviceMessage is ServicePingMessage ping && TryGetValue(ping, ShutdownKey, out var value) && (value switch + { + ShutdownFinValue => true, + ShutdownFinMigratableValue => true, + ShutdownFinKeepAliveValue => true, + _ => false, + }); - public static bool IsFinAck(this ServicePingMessage ping) => - TryGetValue(ping, ShutdownKey, out var value) && value == ShutdownFinAckValue; + public static bool IsFinAck(this ServicePingMessage ping) => + TryGetValue(ping, ShutdownKey, out var value) && value == ShutdownFinAckValue; - // for test - public static bool IsGetServers(this ServiceMessage serviceMessage) => - serviceMessage is ServicePingMessage ping && TryGetValue(ping, ServersKey, out _); + // for test + public static bool IsGetServers(this ServiceMessage serviceMessage) => + serviceMessage is ServicePingMessage ping && TryGetValue(ping, ServersKey, out _); - internal static bool TryGetValue(ServicePingMessage pingMessage, string key, out string value) + internal static bool TryGetValue(ServicePingMessage pingMessage, string key, out string value) + { + if (pingMessage == null) { - if (pingMessage == null) - { - value = null; - return false; - } + value = null; + return false; + } - for (int i = 0; i < pingMessage.Messages.Length - 1; i += 2) + for (int i = 0; i < pingMessage.Messages.Length - 1; i += 2) + { + if (pingMessage.Messages[i] == key) { - if (pingMessage.Messages[i] == key) - { - value = pingMessage.Messages[i + 1]; - return true; - } + value = pingMessage.Messages[i + 1]; + return true; } - - value = null; - return false; } + + value = null; + return false; } } \ No newline at end of file diff --git a/src/Microsoft.Azure.SignalR/HubHost/ServiceHubDispatcher.cs b/src/Microsoft.Azure.SignalR/HubHost/ServiceHubDispatcher.cs index ac5bdc777..f405226fc 100644 --- a/src/Microsoft.Azure.SignalR/HubHost/ServiceHubDispatcher.cs +++ b/src/Microsoft.Azure.SignalR/HubHost/ServiceHubDispatcher.cs @@ -17,250 +17,249 @@ using Microsoft.AspNetCore.Http.Connections; #endif -namespace Microsoft.Azure.SignalR +namespace Microsoft.Azure.SignalR; + +internal class ServiceHubDispatcher where THub : Hub { - internal class ServiceHubDispatcher where THub : Hub - { - private static readonly string Name = $"ServiceHubDispatcher<{typeof(THub).FullName}>"; - - private IHubContext Context { get; } - - private readonly ILoggerFactory _loggerFactory; - private readonly ILogger> _logger; - private readonly ServiceOptions _options; - private readonly IServiceEndpointManager _serviceEndpointManager; - private readonly IServiceConnectionManager _serviceConnectionManager; - private readonly IClientConnectionManager _clientConnectionManager; - private readonly IServiceProtocol _serviceProtocol; - private readonly IClientConnectionFactory _clientConnectionFactory; - private readonly IEndpointRouter _router; - private readonly string _hubName; - private readonly IServiceEventHandler _serviceEventHandler; - private readonly IClientInvocationManager _clientInvocationManager; - private readonly IHubProtocolResolver _hubProtocolResolver; + private static readonly string Name = $"ServiceHubDispatcher<{typeof(THub).FullName}>"; + + private IHubContext Context { get; } + + private readonly ILoggerFactory _loggerFactory; + private readonly ILogger> _logger; + private readonly ServiceOptions _options; + private readonly IServiceEndpointManager _serviceEndpointManager; + private readonly IServiceConnectionManager _serviceConnectionManager; + private readonly IClientConnectionManager _clientConnectionManager; + private readonly IServiceProtocol _serviceProtocol; + private readonly IClientConnectionFactory _clientConnectionFactory; + private readonly IEndpointRouter _router; + private readonly string _hubName; + private readonly IServiceEventHandler _serviceEventHandler; + private readonly IClientInvocationManager _clientInvocationManager; + private readonly IHubProtocolResolver _hubProtocolResolver; #if NET8_0_OR_GREATER - private readonly HttpConnectionDispatcherOptions _dispatcherOptions; + private readonly HttpConnectionDispatcherOptions _dispatcherOptions; #endif - protected readonly IServerNameProvider _nameProvider; - - public ServiceHubDispatcher( - IServiceProtocol serviceProtocol, - IHubContext context, - IServiceConnectionManager serviceConnectionManager, - IClientConnectionManager clientConnectionManager, - IServiceEndpointManager serviceEndpointManager, - IOptions options, - ILoggerFactory loggerFactory, - IEndpointRouter router, - IServerNameProvider nameProvider, - ServerLifetimeManager serverLifetimeManager, - IClientConnectionFactory clientConnectionFactory, - IClientInvocationManager clientInvocationManager, - IServiceEventHandler serviceEventHandler, - IHubProtocolResolver hubProtocolResolver + protected readonly IServerNameProvider _nameProvider; + + public ServiceHubDispatcher( + IServiceProtocol serviceProtocol, + IHubContext context, + IServiceConnectionManager serviceConnectionManager, + IClientConnectionManager clientConnectionManager, + IServiceEndpointManager serviceEndpointManager, + IOptions options, + ILoggerFactory loggerFactory, + IEndpointRouter router, + IServerNameProvider nameProvider, + ServerLifetimeManager serverLifetimeManager, + IClientConnectionFactory clientConnectionFactory, + IClientInvocationManager clientInvocationManager, + IServiceEventHandler serviceEventHandler, + IHubProtocolResolver hubProtocolResolver #if NET8_0_OR_GREATER - , - EndpointDataSource endpointDataSource + , + EndpointDataSource endpointDataSource #endif - ) - { - _serviceProtocol = serviceProtocol; - _serviceConnectionManager = serviceConnectionManager; - _clientConnectionManager = clientConnectionManager; - _serviceEndpointManager = serviceEndpointManager; - _options = options != null ? options.Value : throw new ArgumentNullException(nameof(options)); - - Context = context; - - _router = router ?? throw new ArgumentNullException(nameof(router)); - _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); - _logger = loggerFactory.CreateLogger>(); - _clientConnectionFactory = clientConnectionFactory; - _nameProvider = nameProvider; - _hubName = typeof(THub).Name; - _serviceEventHandler = serviceEventHandler; - _clientInvocationManager = clientInvocationManager; - - serverLifetimeManager?.Register(ShutdownAsync); - _hubProtocolResolver = hubProtocolResolver; + ) + { + _serviceProtocol = serviceProtocol; + _serviceConnectionManager = serviceConnectionManager; + _clientConnectionManager = clientConnectionManager; + _serviceEndpointManager = serviceEndpointManager; + _options = options != null ? options.Value : throw new ArgumentNullException(nameof(options)); + + Context = context; + + _router = router ?? throw new ArgumentNullException(nameof(router)); + _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + _logger = loggerFactory.CreateLogger>(); + _clientConnectionFactory = clientConnectionFactory; + _nameProvider = nameProvider; + _hubName = typeof(THub).Name; + _serviceEventHandler = serviceEventHandler; + _clientInvocationManager = clientInvocationManager; + + serverLifetimeManager?.Register(ShutdownAsync); + _hubProtocolResolver = hubProtocolResolver; #if NET8_0_OR_GREATER - _dispatcherOptions = GetDispatcherOptions(endpointDataSource, typeof(THub)); + _dispatcherOptions = GetDispatcherOptions(endpointDataSource, typeof(THub)); #endif - } + } #if NET8_0_OR_GREATER - private static HttpConnectionDispatcherOptions GetDispatcherOptions(EndpointDataSource source, Type hubType) + private static HttpConnectionDispatcherOptions GetDispatcherOptions(EndpointDataSource source, Type hubType) + { + if (source != null) { - if (source != null) + foreach (var endpoint in source.Endpoints) { - foreach (var endpoint in source.Endpoints) + var metaData = endpoint.Metadata; + if (metaData.GetMetadata()?.HubType == hubType) { - var metaData = endpoint.Metadata; - if (metaData.GetMetadata()?.HubType == hubType) + var options = metaData.GetMetadata(); + if (options != null) { - var options = metaData.GetMetadata(); - if (options != null) - { - return options; - } + return options; } } } - // It's not expected to go here in production environment. Return a value for test. - return new(); } + // It's not expected to go here in production environment. Return a value for test. + return new(); + } #endif - public void Start(ConnectionDelegate connectionDelegate, Action contextConfig = null) - { - // Create connections to Azure SignalR - var serviceConnection = GetServiceConnectionContainer(_hubName, connectionDelegate, contextConfig); + public void Start(ConnectionDelegate connectionDelegate, Action contextConfig = null) + { + // Create connections to Azure SignalR + var serviceConnection = GetServiceConnectionContainer(_hubName, connectionDelegate, contextConfig); - _serviceConnectionManager.SetServiceConnection(serviceConnection); + _serviceConnectionManager.SetServiceConnection(serviceConnection); - Log.StartingConnection(_logger, Name, _options.InitialHubServerConnectionCount); + Log.StartingConnection(_logger, Name, _options.InitialHubServerConnectionCount); - _ = _serviceConnectionManager.StartAsync(); - } + _ = _serviceConnectionManager.StartAsync(); + } - public async Task ShutdownAsync() + public async Task ShutdownAsync() + { + var options = _options.GracefulShutdown; + if (options.Mode == GracefulShutdownMode.Off) { - var options = _options.GracefulShutdown; - if (options.Mode == GracefulShutdownMode.Off) - { - return; - } - - try - { - var source = new CancellationTokenSource(_options.GracefulShutdown.Timeout); + return; + } - Log.SettingServerOffline(_logger, _hubName); + try + { + var source = new CancellationTokenSource(_options.GracefulShutdown.Timeout); - await Task.WhenAny( - _serviceConnectionManager.OfflineAsync(options.Mode), - Task.Delay(Timeout.InfiniteTimeSpan, source.Token) - ); + Log.SettingServerOffline(_logger, _hubName); - Log.TriggeringShutdownHooks(_logger, _hubName); + await Task.WhenAny( + _serviceConnectionManager.OfflineAsync(options.Mode), + Task.Delay(Timeout.InfiniteTimeSpan, source.Token) + ); - await Task.WhenAny( - options.OnShutdown(Context), - Task.Delay(Timeout.InfiniteTimeSpan, source.Token) - ); + Log.TriggeringShutdownHooks(_logger, _hubName); - Log.WaitingClientConnectionsToClose(_logger, _hubName); + await Task.WhenAny( + options.OnShutdown(Context), + Task.Delay(Timeout.InfiniteTimeSpan, source.Token) + ); - await Task.WhenAny( - _clientConnectionManager.WhenAllCompleted(), - Task.Delay(Timeout.InfiniteTimeSpan, source.Token) - ); - } - catch (OperationCanceledException) - { - Log.GracefulShutdownTimeoutExceeded(_logger, _hubName, Convert.ToInt32(_options.GracefulShutdown.Timeout.TotalMilliseconds)); - } + Log.WaitingClientConnectionsToClose(_logger, _hubName); - Log.StoppingServer(_logger, _hubName); - await _serviceConnectionManager.StopAsync(); + await Task.WhenAny( + _clientConnectionManager.WhenAllCompleted(), + Task.Delay(Timeout.InfiniteTimeSpan, source.Token) + ); } - - private IServiceConnectionContainer GetServiceConnectionContainer(string hub, ConnectionDelegate connectionDelegate, Action contextConfig = null) + catch (OperationCanceledException) { - var connectionFactory = new ConnectionFactory(_nameProvider, _loggerFactory); + Log.GracefulShutdownTimeoutExceeded(_logger, _hubName, Convert.ToInt32(_options.GracefulShutdown.Timeout.TotalMilliseconds)); + } - var serviceConnectionFactory = GetServiceConnectionFactory(connectionFactory, connectionDelegate, contextConfig); + Log.StoppingServer(_logger, _hubName); + await _serviceConnectionManager.StopAsync(); + } - var factory = new ServiceConnectionContainerFactory( - serviceConnectionFactory, - _serviceEndpointManager, - _router, - _options, - _loggerFactory, - _options.ServiceScaleTimeout - ); - return factory.Create(hub); - } + private IServiceConnectionContainer GetServiceConnectionContainer(string hub, ConnectionDelegate connectionDelegate, Action contextConfig = null) + { + var connectionFactory = new ConnectionFactory(_nameProvider, _loggerFactory); + + var serviceConnectionFactory = GetServiceConnectionFactory(connectionFactory, connectionDelegate, contextConfig); + + var factory = new ServiceConnectionContainerFactory( + serviceConnectionFactory, + _serviceEndpointManager, + _router, + _options, + _loggerFactory, + _options.ServiceScaleTimeout + ); + return factory.Create(hub); + } - internal virtual ServiceConnectionFactory GetServiceConnectionFactory( - ConnectionFactory connectionFactory, - ConnectionDelegate connectionDelegate, - Action contextConfig) + internal virtual ServiceConnectionFactory GetServiceConnectionFactory( + ConnectionFactory connectionFactory, + ConnectionDelegate connectionDelegate, + Action contextConfig) + { + return new ServiceConnectionFactory( + _serviceProtocol, + _clientConnectionManager, + connectionFactory, + _loggerFactory, + connectionDelegate, + _clientConnectionFactory, + _nameProvider, + _serviceEventHandler, + _clientInvocationManager, + _hubProtocolResolver) { - return new ServiceConnectionFactory( - _serviceProtocol, - _clientConnectionManager, - connectionFactory, - _loggerFactory, - connectionDelegate, - _clientConnectionFactory, - _nameProvider, - _serviceEventHandler, - _clientInvocationManager, - _hubProtocolResolver) - { - ConfigureContext = contextConfig, - ShutdownMode = _options.GracefulShutdown.Mode, - // todo: read per hub configuration from HttpConnectionDispatcherOptions.AllowStatefulReconnects for net 8. - AllowStatefulReconnects = _options.AllowStatefulReconnects ?? + ConfigureContext = contextConfig, + ShutdownMode = _options.GracefulShutdown.Mode, + // todo: read per hub configuration from HttpConnectionDispatcherOptions.AllowStatefulReconnects for net 8. + AllowStatefulReconnects = _options.AllowStatefulReconnects ?? #if NET8_0_OR_GREATER - _dispatcherOptions.AllowStatefulReconnects, + _dispatcherOptions.AllowStatefulReconnects, #else - false, + false, #endif - }; - } + }; + } - private static class Log - { - private static readonly Action _startingConnection = - LoggerMessage.Define(LogLevel.Debug, new EventId(1, "StartingConnection"), "Starting {name} with {connectionNumber} connections..."); + private static class Log + { + private static readonly Action _startingConnection = + LoggerMessage.Define(LogLevel.Debug, new EventId(1, "StartingConnection"), "Starting {name} with {connectionNumber} connections..."); - private static readonly Action _gracefulShutdownTimeoutExceeded = - LoggerMessage.Define(LogLevel.Warning, new EventId(2, "GracefulShutdownTimeoutExceeded"), "[{hubName}] Timeout({timeoutInMs}ms) reached, existing client connections will be dropped immediately."); + private static readonly Action _gracefulShutdownTimeoutExceeded = + LoggerMessage.Define(LogLevel.Warning, new EventId(2, "GracefulShutdownTimeoutExceeded"), "[{hubName}] Timeout({timeoutInMs}ms) reached, existing client connections will be dropped immediately."); - private static readonly Action _settingServerOffline = - LoggerMessage.Define(LogLevel.Information, new EventId(3, "SettingServerOffline"), "[{hubName}] Setting the hub server offline..."); + private static readonly Action _settingServerOffline = + LoggerMessage.Define(LogLevel.Information, new EventId(3, "SettingServerOffline"), "[{hubName}] Setting the hub server offline..."); - private static readonly Action _triggeringShutdownHooks = - LoggerMessage.Define(LogLevel.Information, new EventId(4, "TriggeringShutdownHooks"), "[{hubName}] Triggering shutdown hooks..."); + private static readonly Action _triggeringShutdownHooks = + LoggerMessage.Define(LogLevel.Information, new EventId(4, "TriggeringShutdownHooks"), "[{hubName}] Triggering shutdown hooks..."); - private static readonly Action _waitingClientConnectionsToClose = - LoggerMessage.Define(LogLevel.Information, new EventId(5, "WaitingClientConnectionsToClose"), "[{hubName}] Waiting client connections to close..."); + private static readonly Action _waitingClientConnectionsToClose = + LoggerMessage.Define(LogLevel.Information, new EventId(5, "WaitingClientConnectionsToClose"), "[{hubName}] Waiting client connections to close..."); - private static readonly Action _stoppingServer = - LoggerMessage.Define(LogLevel.Information, new EventId(6, "StoppingServer"), "[{hubName}] Stopping the hub server..."); + private static readonly Action _stoppingServer = + LoggerMessage.Define(LogLevel.Information, new EventId(6, "StoppingServer"), "[{hubName}] Stopping the hub server..."); - public static void StartingConnection(ILogger logger, string name, int connectionNumber) - { - _startingConnection(logger, name, connectionNumber, null); - } + public static void StartingConnection(ILogger logger, string name, int connectionNumber) + { + _startingConnection(logger, name, connectionNumber, null); + } - public static void GracefulShutdownTimeoutExceeded(ILogger logger, string hubName, int timeoutInMs) - { - _gracefulShutdownTimeoutExceeded(logger, hubName, timeoutInMs, null); - } + public static void GracefulShutdownTimeoutExceeded(ILogger logger, string hubName, int timeoutInMs) + { + _gracefulShutdownTimeoutExceeded(logger, hubName, timeoutInMs, null); + } - public static void SettingServerOffline(ILogger logger, string hubName) - { - _settingServerOffline(logger, hubName, null); - } + public static void SettingServerOffline(ILogger logger, string hubName) + { + _settingServerOffline(logger, hubName, null); + } - public static void TriggeringShutdownHooks(ILogger logger, string hubName) - { - _triggeringShutdownHooks(logger, hubName, null); - } + public static void TriggeringShutdownHooks(ILogger logger, string hubName) + { + _triggeringShutdownHooks(logger, hubName, null); + } - public static void WaitingClientConnectionsToClose(ILogger logger, string hubName) - { - _waitingClientConnectionsToClose(logger, hubName, null); - } + public static void WaitingClientConnectionsToClose(ILogger logger, string hubName) + { + _waitingClientConnectionsToClose(logger, hubName, null); + } - public static void StoppingServer(ILogger logger, string hubName) - { - _stoppingServer(logger, hubName, null); - } + public static void StoppingServer(ILogger logger, string hubName) + { + _stoppingServer(logger, hubName, null); } } }