// runs once on the whole cluster
        private async Task RunAsync(CancellationToken cancellationToken)
        {
            _logger.LogDebug("Run heartbeat");

            var now         = DateTime.UtcNow;
            var connections = _clusterMembers.SnapshotConnections(true);

            // start one task per member
            // TODO: throttle?
            var tasks = connections
                        .Select(x => RunAsync(x, now, cancellationToken))
                        .ToList();

            await Task.WhenAll(tasks).CAF(); // may throw in case of cancellation
        }
        // runs once on the whole cluster
        private async Task RunAsync(CancellationToken cancellationToken)
        {
            _logger.LogDebug("Run heartbeat");

            var now         = DateTime.Now; // now, or utcNow, but *must* be same as what is used in socket connection base!
            var connections = _clusterMembers.SnapshotConnections(true);

            // start one task per member
            // TODO: throttle?
            var tasks = connections
                        .Select(x => RunAsync(x, now, cancellationToken))
                        .ToList();

            await Task.WhenAll(tasks).CfAwait(); // may throw in case of cancellation
        }
Exemple #3
0
        /// <summary>
        /// Installs a subscription on the cluster, i.e. on each member.
        /// </summary>
        /// <param name="subscription">The subscription.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that will complete when the subscription has been installed.</returns>
        public async Task InstallSubscriptionAsync(ClusterSubscription subscription, CancellationToken cancellationToken = default)
        {
            if (subscription == null)
            {
                throw new ArgumentNullException(nameof(subscription));
            }

            // capture active clients, and adds the subscription - atomically.
            List <MemberConnection> connections;

            lock (_clusterState.Mutex)
            {
                connections = _clusterMembers.SnapshotConnections(true);

                if (!_subscriptions.TryAdd(subscription.Id, subscription))
                {
                    throw new InvalidOperationException("A subscription with the same identifier already exists.");
                }
            }

            // from now on,
            // - if new clients are added, we won't deal with them here, but they will
            //   subscribe on their own since the subscription is now listed.
            // - if a captured client goes away while we install subscriptions, we
            //   will just ignore the associated errors and skip it entirely.

            // subscribe each captured client
            // TODO: could we install in parallel?
            // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
            foreach (var connection in connections)
            {
                // don't even try clients that became inactive
                if (!connection.Active)
                {
                    continue;
                }

                // this never throws
                var attempt = await InstallSubscriptionAsync(subscription, connection, cancellationToken).CfAwait();

                switch (attempt.Value)
                {
                case InstallResult.Success:
                case InstallResult.ClientNotActive:
                    continue;

                case InstallResult.SubscriptionNotActive:
                case InstallResult.ConfusedServer:
                    // not active: some other code must have
                    // - removed the subscriptions from _subscriptions
                    // - dealt with its existing clients
                    // nothing left to do here
                    throw new HazelcastException(attempt.Value == InstallResult.SubscriptionNotActive
                            ? "Failed to install the subscription because it was removed."
                            : "Failed to install the subscription because it was removed (and the server may be confused).", attempt.Exception);

                case InstallResult.Failed:
                    // failed: client is active but installing the subscription failed
                    // however, we might have installed it on other clients
                    await RemoveSubscriptionAsync(subscription, cancellationToken).CfAwait();

                    throw new HazelcastException("Failed to install subscription (see inner exception).", attempt.Exception);

                default:
                    throw new NotSupportedException();
                }
            }
        }