/// <summary> /// Tests create scenario using either batch or multi /// </summary> /// <param name="batch">true if using batch, false if using multi</param> /// <param name="operationType">Type of the operation.</param> /// <param name="jobState">the job state</param> /// <returns> /// Request per second /// </returns> protected async Task <double> TestBatchOrMultiCreate(bool batch, Test.Helpers.OperationType operationType, JobState jobState) { var name = batch ? "Batch" : "Multi"; Random rnd = new Random(); // number of large trees will be a random number between (20, 50) this.LargeTreeRoots = Enumerable.Range(0, rnd.Next(20, 50)).Select(x => Guid.NewGuid()).ToList(); return(await this.TestFlowAsync( $"{name}(Create) node perf test", operationType, async (client, cancellationToken, threadId) => { var clock = Stopwatch.StartNew(); var rootName = $"{this.RootNodeName}_{name}"; while (!cancellationToken.IsCancellationRequested) { var ops = new List <Op>(this.BatchOpCount); var totalSize = 0; int smallTreeNodeCount = rnd.Next(this.BatchOpCount / 2); int bigTreeNodeCount = this.BatchOpCount - smallTreeNodeCount; var smallSubtreeId = Guid.NewGuid(); while (smallTreeNodeCount-- > 0) { var data = Helpers.MakeRandomData(rnd, rnd.Next(this.MinDataSize, this.MaxDataSize)); totalSize += data.Length; ops.Add(Op.Create($"/{rootName}/vnet{smallSubtreeId}/mappings/v4ca/{Guid.NewGuid()}", data, null, CreateMode.PersistentAllowPathCreation)); } while (bigTreeNodeCount-- > 0) { var data = Helpers.MakeRandomData(rnd, rnd.Next(this.MinDataSize, this.MaxDataSize)); totalSize += data.Length; int idx = rnd.Next(this.LargeTreeRoots.Count); ops.Add(Op.Create($"/{rootName}/vnet{this.LargeTreeRoots[idx]}/mappings/v4ca/{Guid.NewGuid()}", data, null, CreateMode.PersistentAllowPathCreation)); } try { var startTime = clock.Elapsed; if (batch) { await client.Batch(ops).ConfigureAwait(false); } else { await client.Multi(ops).ConfigureAwait(false); } var duration = clock.Elapsed - startTime; MdmHelper.LogOperationDuration((long)duration.TotalMilliseconds, operationType); this.AddTotalDataSize(totalSize); this.IncrementTotalDataCount(ops.Count); } catch (Exception ex) { this.IncrementTotalFailures(); this.Log($"Failed to call {name}: {ex.Message}"); } } }, jobState, this.TestCaseSeconds)); }
/// <summary> /// Main test workflow /// </summary> /// <param name="testTitle">Title of the test case</param> /// <param name="operationType">the operation type</param> /// <param name="workload">Workload in each thread</param> /// <param name="jobState">the job state</param> /// <param name="durationInSeconds">How long the test should run</param> /// <returns>Number of operations per second</returns> protected async Task <double> TestFlowAsync( string testTitle, Test.Helpers.OperationType operationType, Func <IRingMasterRequestHandler, CancellationToken, int, Task> workload, JobState jobState, int durationInSeconds) { this.ResetCounts(); var cancellation = new CancellationTokenSource(); this.Log($"Starting test {testTitle} in {this.threadCount} threads"); var lastCount = Interlocked.Read(ref this.totalDataCount); var lastSize = Interlocked.Read(ref this.totalDataSize); var threads = Helpers.StartMultipleThreads( this.threadCount, (object n) => workload(this.clients[(int)n], cancellation.Token, (int)n).GetAwaiter().GetResult()); var initialCount = lastCount; var initialSize = lastSize; var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < durationInSeconds; i++) { await Task.Delay(TimeSpan.FromSeconds(1)); long size = Interlocked.Read(ref this.totalDataSize); long delta = size - lastSize; long count = Interlocked.Read(ref this.totalDataCount); long deltaCount = count - lastCount; this.Log($"{DateTime.Now} - {deltaCount} - {delta}"); jobState.Status = $"processed data count: {count}, failures: {Interlocked.Read(ref this.totalFailures)}. Queued node count: {this.QueuedNodes.Count()}"; this.processedDataCounts.Add(deltaCount); lastSize = size; lastCount = count; } stopwatch.Stop(); var processedCount = Interlocked.Read(ref this.totalDataCount) - initialCount; var processedSize = Interlocked.Read(ref this.totalDataSize) - initialSize; var rate = processedCount / stopwatch.Elapsed.TotalSeconds; Helper.LogAndSetJobStatus(this.Log, jobState, $"Stopping test {testTitle}. Data processed {processedSize} bytes in {processedCount} ops. rate: {rate:G4} /sec, Failures = {this.totalFailures}"); MdmHelper.LogBytesProcessed(processedSize, operationType); cancellation.Cancel(); foreach (var thread in threads) { thread.Join(); } this.Log($"Stopped {testTitle}."); return(rate); }