public virtual void CleanConnection(string connectionId)
        {
            Log.SignalR(LogTag.TextChatHub_CleaningConnection, new { connectionId });

            // Remove Message Queue for this connection. Queues are created only when needed, so they might not always exists
            if (MessageQueues.ContainsKey(connectionId))
            {
                MessageQueues[connectionId].Reset();
                MessageQueues.Remove(connectionId);
            }

            // Remove all connections to rooms
            RoomsConnections.RemoveConnectionFromAllRooms(connectionId);

            // Remove User's Connection, and leave chat if this was the last connection
            if (UsersConnections.ContainsValue(connectionId))
            {
                UserId userId = UsersConnections.GetFromValue(connectionId);
                UsersConnections.Remove(connectionId);
                if (!UsersConnections.ContainsKey(userId))
                {
                    ChatCtrl.LeaveChat(userId);
                }
            }
            else
            {
                Log.Info(LogTag.TextChatHub_AlreadyCleaned);
            }

            // Also remove LastSeen Info for this connection
            if (LastSeenConnections.ContainsKey(connectionId))
            {
                LastSeenConnections.Remove(connectionId);
            }
        }
        // Get the partner's connections related to a specific private room, along with the active connections to the room
        private IEnumerable <ConnectionId> GetPartnerAndActiveConnections(RoomId roomId, UserId thisUserId, ConnectionId exceptConnection = null)
        {
            var roomConnIds = RoomsConnections.GetConnections(roomId, exceptConnection);
            var userConnIds = GetPartnerConnections(roomId, thisUserId);

            return(roomConnIds.Union(userConnIds).Where(c => c != exceptConnection));
        }
        //========== Background Methods ======================================================================================

        private void HeartbeatCheck()
        {
            try {             // Try catch needed because errors are otherwised silenced, somehow :-(
                // Remove dead connections (Ping not received timely)
                foreach (var kvp in LastSeenConnections.ToList())
                {
                    if (kvp.Value < DateTime.Now.AddSeconds(-CleanZombieAfter))
                    {
                        var connectionId = kvp.Key;
                        Log.SignalR(LogTag.CleaningZombieConnection, new { connectionId });
                        CleanConnection(connectionId);
                    }
                }

                // Remove inactive members from "crowded" rooms
                var chatState = ChatCtrl.GetState();
                foreach (var roomKvp in chatState.Rooms)
                {
                    if (roomKvp.Key.IsMonoLang() && roomKvp.Value.Count > BootAboveUserCount)
                    {
                        var mostInactiveUserKvp = roomKvp.Value.Aggregate((l, r) => l.Value.LastActive < r.Value.LastActive ? l : r);
                        if (mostInactiveUserKvp.Value.LastActive < DateTime.Now.AddMinutes(-BootOutOfRoomAfter))
                        {
                            Log.SignalR(LogTag.KickSlackerOutOfRoom, new { roomId = roomKvp.Key, userId = mostInactiveUserKvp.Key });
                            // Get the list of connections that have to leave the room
                            var userConnectionIds = UsersConnections.GetFromKey(mostInactiveUserKvp.Key)
                                                    .Where(connId => RoomsConnections.HasConnection(roomKvp.Key, connId))
                                                    .Select(connId => connId.ToString()).ToList();
                            Clients.Clients(userConnectionIds).LeaveRoom(roomKvp.Key);
                        }
                    }
                }

                // Check for inactive members
                foreach (var chatUser in chatState.AllUsers.Values)
                {
                    if (chatUser.LastActivity < DateTime.Now.AddMinutes(-UserIdleAfter))
                    {
                        ChatCtrl.SetIdleStatus(chatUser.Id);
                        Log.SignalR(LogTag.SetChatUserIdle, new { userId = chatUser.Id });
                    }
                }

                Log.SignalR(LogTag.HubHeartbeatCheckCompleted);
            } catch (Exception e) {
                Log.Error(LogTag.HeartbeatCheckError, e);
            }
        }