Esempio n. 1
0
 /// <summary>
 /// Constructs a new lock based on the provided <paramref name="path"/>, <paramref name="connectionString"/>, and <paramref name="options"/>.
 ///
 /// If <paramref name="assumePathExists"/> is specified, then the node will not be created as part of acquiring nor will it be
 /// deleted after releasing (defaults to false).
 /// </summary>
 public ZooKeeperDistributedReaderWriterLock(
     ZooKeeperPath path,
     string connectionString,
     bool assumePathExists = false,
     Action <ZooKeeperDistributedSynchronizationOptionsBuilder>?options = null)
     : this(path, assumePathExists : assumePathExists, connectionString, options)
 {
     if (path == default)
     {
         throw new ArgumentNullException(nameof(path));
     }
     if (path == ZooKeeperPath.Root)
     {
         throw new ArgumentException("Cannot be the root", nameof(path));
     }
 }
        public ZooKeeperNodeHandle(ZooKeeperConnection connection, string nodePath, bool shouldDeleteParent)
        {
            this._connection         = connection;
            this._nodePath           = new ZooKeeperPath(nodePath);
            this._shouldDeleteParent = shouldDeleteParent;

            this._handleLostState = new Lazy <HandleLostState>(() =>
            {
                var handleLostSource    = CancellationTokenSource.CreateLinkedTokenSource(this._connection.ConnectionLostToken);
                var handleLostToken     = handleLostSource.Token; // grab this now before the source is disposed
                var disposalSource      = new CancellationTokenSource();
                var disposalSourceToken = disposalSource.Token;
                var monitoringTask      = Task.Run(async() =>
                {
                    try
                    {
                        while (true)
                        {
                            var result = await WaitForNotExistsOrChangedAsync(
                                this._connection,
                                this._nodePath.ToString(),
                                timeoutToken: disposalSource.Token
                                ).ConfigureAwait(false);
                            switch (result)
                            {
                            case false:     // disposalSource triggered
                                return;

                            case true:     // node no longer exists
                                handleLostSource.Cancel();
                                return;

                            default:     // something changed
                                break;   // continue looping
                            }
                        }
                    }
                    finally
                    {
                        handleLostSource.Dispose();
                    }
                });
                return(new HandleLostState(handleLostToken, disposalSource, monitoringTask));
            });
        }
Esempio n. 3
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;
        }
 /// <summary>
 /// Constructs a provider which uses <paramref name="connectionString"/> and <paramref name="options"/>. Lock and semaphore nodes will be created
 /// in <paramref name="directoryPath"/>.
 /// </summary>
 public ZooKeeperDistributedSynchronizationProvider(ZooKeeperPath directoryPath, string connectionString, Action <ZooKeeperDistributedSynchronizationOptionsBuilder>?options = null)
 {
     this._directoryPath    = directoryPath != default ? directoryPath : throw new ArgumentNullException(nameof(directoryPath));
     this._connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
     this._options          = options;
 }
Esempio n. 5
0
 /// <summary>
 /// Constructs a new semaphore based on the provided <paramref name="directoryPath"/>, <paramref name="name"/>, <paramref name="connectionString"/>, and <paramref name="options"/>.
 ///
 /// The semaphore's path will be a parent node of <paramref name="directoryPath"/>. If <paramref name="name"/> is not a valid node name, it will be transformed to ensure
 /// validity.
 /// </summary>
 public ZooKeeperDistributedSemaphore(ZooKeeperPath directoryPath, string name, int maxCount, string connectionString, Action <ZooKeeperDistributedSynchronizationOptionsBuilder>?options = null)
     : this(
         (directoryPath == default ? throw new ArgumentNullException(nameof(directoryPath)) : directoryPath).GetChildNodePathWithSafeName(name),
Esempio n. 6
0
        public static async Task <string> CreateEphemeralSequentialNode(
            this ZooKeeperConnection connection,
            ZooKeeperPath directory,
            string namePrefix,
            IEnumerable <ACL> aclEnumerable,
            bool ensureDirectoryExists)
        {
            // If we are in charge of ensuring the directory, this algorithm loops until either we EnsureDirectory fails or we hit an error other than the directory
            // not existing. This supports concurrent node creation and directory creation as well as node creation and directory deletion.

            var acl = aclEnumerable.ToList();

            while (true)
            {
                var createdDirectories = ensureDirectoryExists
                    ? await EnsureDirectoryAndGetCreatedPathsAsync().ConfigureAwait(false)
                    : Array.Empty <ZooKeeperPath>();

                try
                {
                    return(await connection.ZooKeeper.createAsync($"{directory}{ZooKeeperPath.Separator}{namePrefix}", data : Array.Empty <byte>(), acl, CreateMode.EPHEMERAL_SEQUENTIAL).ConfigureAwait(false));
                }
                catch (KeeperException.NoNodeException ex)
                {
                    // If we're not ensuring the directory, rethrow a more helpful error message. Otherwise,
                    // swallow the error and go around the loop again
                    if (!ensureDirectoryExists)
                    {
                        throw new InvalidOperationException($"Node '{directory}' does not exist", ex);
                    }
                }
                catch
                {
                    // on an unhandled error, clean up any directories we created
                    await TryCleanUpCreatedDirectoriesAsync(createdDirectories).ConfigureAwait(false);

                    throw;
                }
            }

            async Task <IReadOnlyList <ZooKeeperPath> > EnsureDirectoryAndGetCreatedPathsAsync()
            {
                // This algorithm loops until either the directory exists or our creation attempt fails with something other
                // than NoNodeException or NodeExistsException. This supports concurrent directory creation as well as concurrent
                // creation and deletion via optimistic concurrency

                var toCreate = new Stack <ZooKeeperPath>();

                toCreate.Push(directory);
                List <ZooKeeperPath>?created = null;

                do
                {
                    var directoryToCreate = toCreate.Peek();
                    if (directoryToCreate == ZooKeeperPath.Root)
                    {
                        throw new InvalidOperationException($"Received {typeof(KeeperException.NoNodeException)} when creating child node of directory '{ZooKeeperPath.Root}'");
                    }

                    try
                    {
                        await connection.ZooKeeper.createAsync(directoryToCreate.ToString(), data : Array.Empty <byte>(), acl, CreateMode.PERSISTENT).ConfigureAwait(false);

                        toCreate.Pop();
                        (created ??= new List <ZooKeeperPath>()).Add(directoryToCreate);
                    }
                    catch (KeeperException.NodeExistsException) // someone else created it
                    {
                        toCreate.Pop();
                    }
                    catch (KeeperException.NoNodeException) // parent needs to be created
                    {
                        toCreate.Push(directoryToCreate.GetDirectory() !.Value);
                    }
                    catch
                    {
                        // on an unhandled failure, attempt to clean up our work
                        if (created != null)
                        {
                            await TryCleanUpCreatedDirectoriesAsync(created).ConfigureAwait(false);
                        }

                        throw;
                    }
                }while (toCreate.Count != 0);

                return(created ?? (IReadOnlyList <ZooKeeperPath>)Array.Empty <ZooKeeperPath>());
            }

            async Task TryCleanUpCreatedDirectoriesAsync(IReadOnlyList <ZooKeeperPath> createdDirectories)
            {
                try
                {
                    // delete in reverse order of creation
                    for (var i = createdDirectories.Count - 1; i >= 0; --i)
                    {
                        await connection.ZooKeeper.deleteAsync(createdDirectories[i].ToString()).ConfigureAwait(false);
                    }
                }
                catch
                {
                    // swallow errors, since there's a good chance this cleanup fails the same way that the creation did
                }
            }
        }