Example #1
0
        public void UnregisteredAcksCantBeTriggered()
        {
            var ackHandler = new AckHandler(completeAcksOnTimeout: false,
                                            ackThreshold: TimeSpan.Zero,
                                            ackInterval: TimeSpan.Zero);

            Assert.False(ackHandler.TriggerAck("foo"));
        }
Example #2
0
        public void UnregisteredAcksCantBeTriggered()
        {
            var ackHandler = new AckHandler(cancelAcksOnTimeout: false,
                                            ackThreshold: TimeSpan.Zero,
                                            ackInterval: TimeSpan.Zero);

            Assert.False(ackHandler.TriggerAck("foo"));
        }
        /// <summary>
        /// Create server specific channel in order to send an ack to a single server
        /// </summary>
        /// <param name="server"></param>
        private void SubscribeToAckChannel(IRedisServer server)
        {
            var ackChannel = _channels.Ack(server.ServerName);

            server.Subscriber.Subscribe(ackChannel, (_, data) =>
            {
                var ackId = _protocol.ReadAck((byte[])data);
                _ackHandler.TriggerAck(ackId);
            });
        }
Example #4
0
        public void TriggeredAcksAreCompleted()
        {
            var ackHandler = new AckHandler(completeAcksOnTimeout: false,
                                            ackThreshold: TimeSpan.Zero,
                                            ackInterval: TimeSpan.Zero);

            Task task = ackHandler.CreateAck("foo");

            Assert.True(ackHandler.TriggerAck("foo"));
            Assert.True(task.IsCompleted);
        }
Example #5
0
        public void TriggeredAcksAreCompleted()
        {
            var ackHandler = new AckHandler(cancelAcksOnTimeout: false,
                                            ackThreshold: TimeSpan.Zero,
                                            ackInterval: TimeSpan.Zero);

            Task task = ackHandler.CreateAck("foo");

            Assert.True(ackHandler.TriggerAck("foo"));
            Assert.True(task.IsCompleted);
        }
        public RedisHubLifetimeManager(ILogger <RedisHubLifetimeManager <THub> > logger,
                                       IOptions <RedisOptions> options)
        {
            _logger     = logger;
            _options    = options.Value;
            _ackHandler = new AckHandler();

            var writer = new LoggerTextWriter(logger);

            _logger.LogInformation("Connecting to redis endpoints: {endpoints}", string.Join(", ", options.Value.Options.EndPoints.Select(e => EndPointCollection.ToString(e))));
            _redisServerConnection = _options.Connect(writer);
            if (_redisServerConnection.IsConnected)
            {
                _logger.LogInformation("Connected to redis");
            }
            else
            {
                // TODO: We could support reconnecting, like old SignalR does.
                throw new InvalidOperationException("Connection to redis failed.");
            }
            _bus = _redisServerConnection.GetSubscriber();

            var previousBroadcastTask = Task.CompletedTask;

            var channelName = _channelNamePrefix;

            _logger.LogInformation("Subscribing to channel: {channel}", channelName);
            _bus.Subscribe(channelName, async(c, data) =>
            {
                await previousBroadcastTask;

                _logger.LogTrace("Received message from redis channel {channel}", channelName);

                var message = DeserializeMessage <HubMessage>(data);

                // TODO: This isn't going to work when we allow JsonSerializer customization or add Protobuf
                var tasks = new List <Task>(_connections.Count);

                foreach (var connection in _connections)
                {
                    tasks.Add(WriteAsync(connection, message));
                }

                previousBroadcastTask = Task.WhenAll(tasks);
            });

            var allExceptTask = Task.CompletedTask;

            channelName = _channelNamePrefix + ".AllExcept";
            _logger.LogInformation("Subscribing to channel: {channel}", channelName);
            _bus.Subscribe(channelName, async(c, data) =>
            {
                await allExceptTask;

                _logger.LogTrace("Received message from redis channel {channel}", channelName);

                var message     = DeserializeMessage <RedisExcludeClientsMessage>(data);
                var excludedIds = message.ExcludedIds;

                // TODO: This isn't going to work when we allow JsonSerializer customization or add Protobuf

                var tasks = new List <Task>(_connections.Count);

                foreach (var connection in _connections)
                {
                    if (!excludedIds.Contains(connection.ConnectionId))
                    {
                        tasks.Add(WriteAsync(connection, message));
                    }
                }

                allExceptTask = Task.WhenAll(tasks);
            });

            channelName = _channelNamePrefix + ".internal.group";
            _bus.Subscribe(channelName, async(c, data) =>
            {
                var groupMessage = DeserializeMessage <GroupMessage>(data);

                if (groupMessage.Action == GroupAction.Remove)
                {
                    if (!await RemoveGroupAsyncCore(groupMessage.ConnectionId, groupMessage.Group))
                    {
                        // user not on this server
                        return;
                    }
                }

                if (groupMessage.Action == GroupAction.Add)
                {
                    if (!await AddGroupAsyncCore(groupMessage.ConnectionId, groupMessage.Group))
                    {
                        // user not on this server
                        return;
                    }
                }

                // Sending ack to server that sent the original add/remove
                await PublishAsync($"{_channelNamePrefix}.internal.{groupMessage.Server}", new GroupMessage
                {
                    Action       = GroupAction.Ack,
                    ConnectionId = groupMessage.ConnectionId,
                    Group        = groupMessage.Group,
                    Id           = groupMessage.Id
                });
            });

            // Create server specific channel in order to send an ack to a single server
            var serverChannel = $"{_channelNamePrefix}.internal.{_serverName}";

            _bus.Subscribe(serverChannel, (c, data) =>
            {
                var groupMessage = DeserializeMessage <GroupMessage>(data);

                if (groupMessage.Action == GroupAction.Ack)
                {
                    _ackHandler.TriggerAck(groupMessage.Id);
                }
            });
        }