public FdbDatabaseProvider(IOptions <FdbDatabaseProviderOptions> optionsAccessor)
 {
     Contract.NotNull(optionsAccessor, nameof(optionsAccessor));
     this.Options = optionsAccessor.Value;
     this.Root    = new FdbDirectorySubspaceLocation(this.Options.ConnectionOptions.Root ?? FdbPath.Root);
     this.DbTask  = Task.FromException <IFdbDatabase>(new InvalidOperationException("The database has not been initialized."));
 }
        public static async Task <IFdbDirectory> TryOpenCurrentDirectoryAsync(IFdbReadOnlyTransaction tr, FdbPath path)
        {
            var location = new FdbDirectorySubspaceLocation(path);

            if (location.Path.Count != 0)
            {
                return(await location.Resolve(tr));
            }
            else
            {
                return(location);
            }
        }
        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);
            }
        }