public void TestRingMasterGetFullSubtree() { ResetCounts(); queuedNodes = new ConcurrentQueue <string>(); var cancellation = new CancellationTokenSource(); Task.Run(async() => { var startFrom = string.Empty; while (!cancellation.IsCancellationRequested) { var children = await helperClient.GetChildren($"/{rootNodeName}", null, $">:{MaxChildrenCount}:{startFrom}"); foreach (var child in children) { queuedNodes.Enqueue($"/{rootNodeName}/{child}/mappings/v4ca"); startFrom = child; } if (children.Count < MaxChildrenCount) { break; } } }); var rate = TestFlowAsync( "Get full sub-tree perf test", OperationType.GetFullSubtree, GetFullSubtreeThread, testCaseSeconds * 4) .GetAwaiter().GetResult(); cancellation.Cancel(); log($"get full subtree rate: {rate:G4} /sec"); }
/// <summary> /// Enumerates the children of the node at the given path without blocking. /// </summary> /// <param name="ringMaster">Interface to ringmaster</param> /// <param name="path">Node path</param> /// <param name="maxChildrenPerRequest">Maximum number of children to retrieve with each GetChildren request</param> /// <param name="action">Action to execute for each child</param> /// <returns>A <see cref="Task"/> that tracks execution of this method</returns> /// <remarks>This method issues multiple GetChildren requests to the given <paramref name="ringMaster"/> if required to enumerate /// all the children of the given node. It will invoke the given action for each child</remarks> public static async Task ForEachChild(this IRingMasterRequestHandler ringMaster, string path, int maxChildrenPerRequest, Action <string> action) { string startingChildName = string.Empty; while (true) { IReadOnlyList <string> children = await ringMaster.GetChildren( path, watcher : null, retrievalCondition : string.Format(">:{0}:{1}", maxChildrenPerRequest, startingChildName)); foreach (var child in children) { startingChildName = child; action(child); } if (children.Count < maxChildrenPerRequest) { break; } } }
/// <summary> /// Recursively deletes all the nodes under the given path. /// </summary> /// <param name="ringMaster">Interface to RingMaster</param> /// <param name="path">Path to recursively delete</param> /// <param name="cancellationToken">Token to be observed for cancellation signal</param> /// <returns>A <see cref="Task"/> that resolves to the number of nodes deleted</returns> public async Task <int> Delete(IRingMasterRequestHandler ringMaster, string path, CancellationToken cancellationToken) { var recursiveDeleteTimer = Stopwatch.StartNew(); var pendingNodes = new Stack <NodeState>(); var deleteOperations = new List <Op>(); this.deletedCount = 0; try { pendingNodes.Push(new NodeState(path)); while (pendingNodes.Count > 0) { cancellationToken.ThrowIfCancellationRequested(); var currentNode = pendingNodes.Pop(); if (!currentNode.AllChildrenProcessed) { IReadOnlyList <string> children = await ringMaster.GetChildren( currentNode.Path, watcher : null, retrievalCondition : string.Format(">:{0}:{1}", this.MaxChildrenEnumerationCount, currentNode.StartingChildName)); pendingNodes.Push(new NodeState { Path = currentNode.Path, StartingChildName = children.Count > 0 ? children[children.Count - 1] : string.Empty, AllChildrenProcessed = children.Count < this.MaxChildrenEnumerationCount, }); foreach (var child in children) { string childFullPath = (currentNode.Path == "/") ? $"/{child}" : $"{currentNode.Path}/{child}"; pendingNodes.Push(new NodeState(childFullPath)); } } else { this.instrumentation?.DeleteQueued(this.deletedCount, currentNode.Path); deleteOperations.Add(Op.Delete(currentNode.Path, version: -1)); } if (deleteOperations.Count >= this.MaxDeleteBatchLength) { await this.DeleteMulti(ringMaster, deleteOperations); deleteOperations.Clear(); } } if (deleteOperations.Count > 0) { await this.DeleteMulti(ringMaster, deleteOperations); } this.instrumentation?.RecursiveDeleteSucceeded(this.deletedCount, recursiveDeleteTimer.Elapsed); return(this.deletedCount); } catch { this.instrumentation?.RecursiveDeleteFailed(this.deletedCount, recursiveDeleteTimer.Elapsed); throw; } }