public async Task Test_Queue_Fast() { using (var db = await OpenTestPartitionAsync()) { var location = db.Root["queue"]; await CleanLocation(db, location); #if ENABLE_LOGGING var logged = db.Logged((tr) => Log(tr.Log.GetTimingsReport(true))); #else var logged = db; #endif var queue = new FdbQueue <int>(location); Log("Empty? " + queue.ReadAsync(logged, (tr, state) => state.EmptyAsync(tr), this.Cancellation)); Log("Push 10, 8, 6 in separate transactions"); await queue.WriteAsync(logged, (tr, state) => state.Push(tr, 10), this.Cancellation); await queue.WriteAsync(logged, (tr, state) => state.Push(tr, 8), this.Cancellation); await queue.WriteAsync(logged, (tr, state) => state.Push(tr, 6), this.Cancellation); #if DEBUG await DumpSubspace(db, location); #endif // Empty? bool empty = await queue.ReadAsync(logged, (tr, state) => state.EmptyAsync(tr), this.Cancellation); Log("Empty? " + empty); Assert.That(empty, Is.False); var item = await queue.ReadWriteAsync(logged, (tr, state) => state.PopAsync(tr), this.Cancellation); Log($"Pop item: {item}"); Assert.That(item.HasValue, Is.True); Assert.That(item.Value, Is.EqualTo(10)); item = await queue.ReadWriteAsync(logged, (tr, state) => state.PeekAsync(tr), this.Cancellation); Log($"Next item: {item}"); Assert.That(item.HasValue, Is.True); Assert.That(item.Value, Is.EqualTo(8)); #if DEBUG await DumpSubspace(db, location); #endif item = await queue.ReadWriteAsync(logged, (tr, state) => state.PopAsync(tr), this.Cancellation); Log($"Pop item: {item}"); Assert.That(item.HasValue, Is.True); Assert.That(item.Value, Is.EqualTo(8)); #if DEBUG await DumpSubspace(db, location); #endif item = await queue.ReadWriteAsync(logged, (tr, state) => state.PopAsync(tr), this.Cancellation); Log($"Pop item: {item}"); Assert.That(item.HasValue, Is.True); Assert.That(item.Value, Is.EqualTo(6)); #if DEBUG await DumpSubspace(db, location); #endif empty = await queue.ReadAsync(logged, (tr, state) => state.EmptyAsync(tr), this.Cancellation); Log("Empty? " + empty); Assert.That(empty, Is.True); Log("Push 5"); await queue.WriteAsync(logged, (tr, state) => state.Push(tr, 5), this.Cancellation); #if DEBUG await DumpSubspace(db, location); #endif Log("Clear Queue"); await queue.WriteAsync(logged, (tr, state) => state.Clear(tr), this.Cancellation); #if DEBUG await DumpSubspace(db, location); #endif empty = await queue.ReadAsync(logged, (tr, state) => state.EmptyAsync(tr), this.Cancellation); Log("Empty? " + empty); Assert.That(empty, Is.True); } }
private async Task RunMultiClientTest(IFdbDatabase db, FdbDirectorySubspaceLocation location, string desc, int K, int NUM, CancellationToken ct) { Log($"Starting {desc} test with {K} threads and {NUM} iterations"); await CleanLocation(db, location); var queue = new FdbQueue <string>(location); // use a CTS to ensure that everything will stop in case of problems... using (var go = new CancellationTokenSource(TimeSpan.FromSeconds(30))) { var tok = go.Token; var pushLock = new AsyncCancelableMutex(tok); var popLock = new AsyncCancelableMutex(tok); int pushCount = 0; int popCount = 0; int stalls = 0; var start = DateTime.UtcNow; var pushTreads = Enumerable.Range(0, K) .Select(async id => { int i = 0; try { // wait for the signal await pushLock.Task.ConfigureAwait(false); var res = new List <string>(NUM); for (; i < NUM; i++) { var item = $"{id}.{i}"; await queue.WriteAsync(db, (tr, state) => state.Push(tr, item), tok).ConfigureAwait(false); Interlocked.Increment(ref pushCount); res.Add(item); } Log($"PushThread[{id}] pushed {NUM:N0} items in {(DateTime.UtcNow - start).TotalSeconds:N1} sec"); return(res); } catch (Exception e) { Log($"PushThread[{id}] failed after {i} push and {(DateTime.UtcNow - start).TotalSeconds:N1} sec: {e}"); Assert.Fail($"PushThread[{id}] failed: {e.Message}"); throw; } }).ToArray(); var popThreads = Enumerable.Range(0, K) .Select(async id => { int i = 0; try { // make everyone wait a bit, to ensure that they all start roughly at the same time await popLock.Task.ConfigureAwait(false); var res = new List <string>(NUM); while (i < NUM) { var item = await queue.ReadWriteAsync(db, (tr, state) => state.PopAsync(tr), tok).ConfigureAwait(false); if (item.HasValue) { Interlocked.Increment(ref popCount); res.Add(item.Value); ++i; } else { Interlocked.Increment(ref stalls); await Task.Delay(10, this.Cancellation).ConfigureAwait(false); } } Log($"PopThread[{id}] popped {NUM:N0} items in {(DateTime.UtcNow - start).TotalSeconds:N1} sec"); return(res); } catch (Exception e) { Log($"PopThread[{id}] failed: {e}"); Assert.Fail($"PopThread[{id}] failed after {i} pops and {(DateTime.UtcNow - start).TotalSeconds:N1} sec: {e.Message}"); throw; } }).ToArray(); var sw = Stopwatch.StartNew(); pushLock.Set(async: true); await Task.Delay(50, this.Cancellation); popLock.Set(async: true); await Task.WhenAll(pushTreads); Log("Push threads are finished!"); await Task.WhenAll(popThreads); sw.Stop(); Log($"> Finished {desc} test in {sw.Elapsed.TotalSeconds} seconds"); Log($"> Pushed {pushCount}, Popped {popCount} and Stalled {stalls}"); var pushedItems = pushTreads.SelectMany(t => t.Result).ToList(); var poppedItems = popThreads.SelectMany(t => t.Result).ToList(); Assert.That(pushCount, Is.EqualTo(K * NUM)); Assert.That(popCount, Is.EqualTo(K * NUM)); // all pushed items should have been popped (with no duplicates) Assert.That(poppedItems, Is.EquivalentTo(pushedItems)); // the queue should be empty bool empty = await queue.ReadAsync(db, (tr, state) => state.EmptyAsync(tr), ct); Assert.That(empty, Is.True); } }