public void TestEquality()
        {
            var connectionA = new ZooKeeperConnectionInfo(
                "cs",
                TimeSpan.FromSeconds(1),
                TimeSpan.FromSeconds(2),
                new EquatableReadOnlyList <ZooKeeperAuthInfo>(new[] { new ZooKeeperAuthInfo("s", new EquatableReadOnlyList <byte>(new byte[] { 10 })) })
                );
            var connectionB = new ZooKeeperConnectionInfo(
                "cs",
                TimeSpan.FromSeconds(1),
                TimeSpan.FromSeconds(2),
                new EquatableReadOnlyList <ZooKeeperAuthInfo>(new[] { new ZooKeeperAuthInfo("s", new EquatableReadOnlyList <byte>(new byte[] { 10 })) })
                );
            var connectionC = connectionA with {
                AuthInfo = new EquatableReadOnlyList <ZooKeeperAuthInfo>(new[]
                {
                    new ZooKeeperAuthInfo("s", new EquatableReadOnlyList <byte>(new byte[] { 10 })),
                    new ZooKeeperAuthInfo("s2", new EquatableReadOnlyList <byte>(new byte[] { 11 })),
                })
            };

            Assert.IsTrue(connectionA == connectionB);
            connectionA.GetHashCode().ShouldEqual(connectionB.GetHashCode());
            Assert.IsFalse(connectionA == connectionC);
            Assert.AreNotEqual(connectionA.GetHashCode(), connectionC.GetHashCode());
        }
    }
Пример #2
0
            private void ReleaseEntry(ZooKeeperConnectionInfo connectionInfo, ConnectionEntry entry, bool remove)
            {
                bool shouldDispose;

                lock (this.PoolLock)
                {
                    if (remove)
                    {
                        this.Connections.As <ICollection <KeyValuePair <ZooKeeperConnectionInfo, ConnectionEntry> > >()
                        .Remove(new KeyValuePair <ZooKeeperConnectionInfo, ConnectionEntry>(connectionInfo, entry));
                    }

                    shouldDispose = --entry.UserCount == 0;
                    // this guarantee is upheld by the fact that we include the timeout watcher task as a "user"
                    Invariant.Require(
                        !shouldDispose || !(this.Connections.TryGetValue(connectionInfo, out var registeredEntry) && registeredEntry == entry),
                        "If we're disposing then the entry must be removed from the pool"
                        );
                }

                if (shouldDispose)
                {
                    // kick off connection disposal in the background to
                    // avoid blocking/throwing and to handle the case where the task hasn't yet completed
                    entry.ConnectionTask.ContinueWith(
                        t => { var ignored = t.Result.DisposeAsync(); },
                        TaskContinuationOptions.OnlyOnRanToCompletion
                        );
                }
            }
Пример #3
0
            private async Task <InternalConnection> InternalConnectAsync(ZooKeeperConnectionInfo connectionInfo)
            {
                var watcher   = new ConnectionWatcher(connectionInfo.SessionTimeout);
                var zooKeeper = new ZooKeeper(
                    connectstring: connectionInfo.ConnectionString,
                    sessionTimeout: connectionInfo.SessionTimeout.InMilliseconds,
                    watcher: watcher
                    );

                using var timeoutSource       = new CancellationTokenSource(connectionInfo.ConnectTimeout.TimeSpan);
                using var timeoutRegistration = timeoutSource.Token.Register(
                          () => watcher.TaskCompletionSource.TrySetException(new TimeoutException($"Timed out connecting to ZooKeeper after {connectionInfo.ConnectTimeout.InMilliseconds}ms"))
                          );

                foreach (var authInfo in connectionInfo.AuthInfo)
                {
                    zooKeeper.addAuthInfo(authInfo.Scheme, authInfo.Auth.ToArray());
                }

                try
                {
                    await watcher.TaskCompletionSource.Task.ConfigureAwait(false);

                    return(new InternalConnection(zooKeeper, watcher));
                }
                catch
                {
                    // on failure, clean up the instance we created
                    try { await zooKeeper.closeAsync().ConfigureAwait(false); }
                    finally { watcher.Dispose(); }
                    throw;
                }
            }
Пример #4
0
            public Task <ZooKeeperConnection> ConnectAsync(ZooKeeperConnectionInfo connectionInfo, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();

                lock (this.PoolLock)
                {
                    // if we have an entry in the pool, use that
                    if (this.Connections.TryGetValue(connectionInfo, out var entry))
                    {
                        ++entry.UserCount;
                        return(ToResultAsync(entry));
                    }

                    // create a new connection
                    var newConnectionTask = this.InternalConnectAsync(connectionInfo);
                    var newEntry          = new ConnectionEntry(newConnectionTask)
                    {
                        // 2 because we have both the current request and the timeout task we're about to create
                        UserCount = 2
                    };
                    this.Connections.Add(connectionInfo, newEntry);
                    newConnectionTask.ContinueWith(OnConnectionTaskCompleted);
                    return(ToResultAsync(newEntry));

                    void OnConnectionTaskCompleted(Task <InternalConnection> internalConnectionTask)
                    {
                        // if we never connected, just release our hold on the task
                        if (internalConnectionTask.Status != TaskStatus.RanToCompletion)
                        {
                            this.ReleaseEntry(connectionInfo, newEntry, remove: true);
                        }
                        // Otherwise, wait until either max age has elapsed or the connection is lost to release our hold. This
                        // ensures both that the connection won't be removed from the pool prematurely as well as that it will be
                        // eventually removed even if it stops being used
                        else
                        {
                            Task.Delay(this._maxAge.TimeSpan, internalConnectionTask.Result.ConnectionLostToken)
                            .ContinueWith(_ => this.ReleaseEntry(connectionInfo, newEntry, remove: true));
                        }
                    }
                }

                async Task <ZooKeeperConnection> ToResultAsync(ConnectionEntry entry)
                {
                    try
                    {
                        var internalConnection = await entry.ConnectionTask.ConfigureAwait(false);

                        return(new ZooKeeperConnection(internalConnection, releaseToPool: () => this.ReleaseEntry(connectionInfo, entry, remove: false)));
                    }
                    catch
                    {
                        // if we fail to construct a connection, still release our hold on the entry to allow cleanup
                        this.ReleaseEntry(connectionInfo, entry, remove: false);
                        throw;
                    }
                }
            }
Пример #5
0
        public ZooKeeperSynchronizationHelper(
            ZooKeeperPath path,
            bool assumePathExists,
            string connectionString,
            Action <ZooKeeperDistributedSynchronizationOptionsBuilder>?optionsBuilder,
            bool setAcquiredMarker = false)
        {
            this.Path = path;
            this._assumePathExists = assumePathExists;
            var options = ZooKeeperDistributedSynchronizationOptionsBuilder.GetOptions(optionsBuilder);

            this._connectionInfo = new ZooKeeperConnectionInfo(
                connectionString ?? throw new ArgumentNullException(nameof(connectionString)),
                ConnectTimeout: options.ConnectTimeout,
                SessionTimeout: options.SessionTimeout,
                AuthInfo: options.AuthInfo
                );
            this._acl = options.Acl;
            this._setAcquiredMarker = setAcquiredMarker;
        }