public static async Task DumpSubspace(IFdbReadOnlyTransaction tr, IKeySubspace subspace)
        {
            Assert.That(tr, Is.Not.Null);
            Assert.That(subspace, Is.Not.Null);

            FdbTest.Log($"Dumping content of {subspace} at {subspace.GetPrefix():K}:");
            int count = 0;
            await tr
            .GetRange(KeyRange.StartsWith(subspace.GetPrefix()))
            .ForEachAsync((kvp) =>
            {
                var key = subspace.ExtractKey(kvp.Key, boundCheck: true);
                ++count;
                string keyDump;
                try
                {
                    // attempts decoding it as a tuple
                    keyDump = TuPack.Unpack(key).ToString() !;
                }
                catch (Exception)
                {
                    // not a tuple, dump as bytes
                    keyDump = "'" + key.ToString() + "'";
                }

                FdbTest.Log("- " + keyDump + " = " + kvp.Value.ToString());
            });

            if (count == 0)
            {
                FdbTest.Log("> empty !");
            }
            else
            {
                FdbTest.Log("> Found " + count + " values");
            }
        }
Example #2
0
        /// <summary>Delete a document from the collection</summary>
        /// <param name="trans"></param>
        /// <param name="ids"></param>
        public async Task DeleteMultipleAsync(IFdbTransaction trans, IEnumerable <TId> ids)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }
            if (ids == null)
            {
                throw new ArgumentNullException(nameof(ids));
            }

            var subspace = await this.Location.Resolve(trans);

            if (subspace == null)
            {
                throw new InvalidOperationException($"Location '{this.Location}' referenced by Document Collection Layer was not found.");
            }

            foreach (var id in ids)
            {
                var key = subspace.EncodePartial(id);
                trans.ClearRange(KeyRange.StartsWith(key));
            }
        }
        public static async Task DumpSubspace([NotNull] IFdbReadOnlyTransaction tr, [NotNull] IFdbSubspace subspace)
        {
            Assert.That(tr, Is.Not.Null);

            Console.WriteLine("Dumping content of subspace " + subspace.ToString() + " :");
            int count = 0;
            await tr
            .GetRange(KeyRange.StartsWith(subspace.Key))
            .ForEachAsync((kvp) =>
            {
                var key = subspace.ExtractKey(kvp.Key, boundCheck: true);
                ++count;
                string keyDump = null;
                try
                {
                    // attemps decoding it as a tuple
                    keyDump = key.ToTuple().ToString();
                }
                catch (Exception)
                {
                    // not a tuple, dump as bytes
                    keyDump = "'" + key.ToString() + "'";
                }

                Console.WriteLine("- " + keyDump + " = " + kvp.Value.ToString());
            });

            if (count == 0)
            {
                Console.WriteLine("> empty !");
            }
            else
            {
                Console.WriteLine("> Found " + count + " values");
            }
        }
        /// <summary>Find the DCs, machines and processes in the cluster</summary>
        public static async Task Topology(string[] path, IVarTuple extras, IFdbDatabase db, TextWriter log, CancellationToken ct)
        {
            var coords = await Fdb.System.GetCoordinatorsAsync(db, ct);

            log.WriteLine($"[Cluster] {coords.Id}");

            var servers = await db.QueryAsync(tr => tr
                                              .WithReadAccessToSystemKeys()
                                              .GetRange(KeyRange.StartsWith(Fdb.System.ServerList))
                                              .Select(kvp => new
            {
                // Offsets		Size	Type	Name		Description
                //    0			 2		Word	Version?	0100 (1.0 ?)
                //    2			 4		DWord	???			0x00 0x20 0xA2 0x00
                //    6			 2		Word	FDBMagic	0xDB 0x0F "FDB"
                //    8			16		Guid	NodeId		Unique Process ID
                //   24			16		Guid	Machine		"machine_id" field in foundationdb.conf (ends with 8x0 if manually specified)
                //   40			16		Guid	DataCenter	"datacenter_id" field in foundationdb.conf (ends with 8x0 if manually specified)
                //   56			 4		???		??			4 x 0
                //   60			12 x24	ARRAY[] ??			array of 12x the same 24-byte struct defined below

                // ...0			 4		DWord	IPAddress	01 00 00 7F => 127.0.0.1
                // ...4			 4		DWord	Port		94 11 00 00 -> 4500
                // ...8			 4		DWord	??			randomish, changes every reboot
                // ..12			 4		DWord	??			randomish, changes every reboot
                // ..16			 4		DWord	Size?		small L-E integer, usually between 0x20 and 0x40...
                // ..20			 4		DWord	??			randmoish, changes every reboot

                ProcessId    = kvp.Value.Substring(8, 16).ToHexaString(),
                MachineId    = kvp.Value.Substring(24, 16).ToHexaString(),
                DataCenterId = kvp.Value.Substring(40, 16).ToHexaString(),

                Parts = Enumerable.Range(0, 12).Select(i =>
                {
                    int p = 60 + 24 * i;
                    return(new
                    {
                        Address = new IPAddress(kvp.Value.Substring(p, 4).GetBytes().Reverse().ToArray()),
                        Port = kvp.Value.Substring(p + 4, 4).ToInt32(),
                        Unknown1 = kvp.Value.Substring(p + 8, 4).ToInt32(),
                        Unknown2 = kvp.Value.Substring(p + 12, 4).ToInt32(),
                        Unknown3 = kvp.Value.Substring(p + 16, 4).ToInt32(),
                        Unknown4 = kvp.Value.Substring(p + 20, 4).ToInt32(),
                    });
                }).ToList(),
                Raw = kvp.Value,
            }),
                                              ct
                                              );

            var numNodes    = servers.Select(s => s.ProcessId).Distinct().Count();
            var numMachines = servers.Select(s => s.MachineId).Distinct().Count();
            var numDCs      = servers.Select(s => s.DataCenterId).Distinct().Count();

            var dcs = servers.GroupBy(x => x.DataCenterId).ToArray();

            for (int dcIndex = 0; dcIndex < dcs.Length; dcIndex++)
            {
                var  dc     = dcs[dcIndex];
                bool lastDc = dcIndex == dcs.Length - 1;

                string dcId = dc.Key.EndsWith("0000000000000000") ? dc.Key.Substring(0, 16) : dc.Key;
                log.WriteLine((lastDc ? "`- " : "|- ") + "[DataCenter] {0} (#{1})", dcId, dcIndex);

                var    machines = dc.GroupBy(x => x.MachineId).ToArray();
                string dcPrefix = lastDc ? "   " : "|  ";
                for (int machineIndex = 0; machineIndex < machines.Length; machineIndex++)
                {
                    var machine     = machines[machineIndex];
                    var lastMachine = machineIndex == machines.Length - 1;

                    string machineId = machine.Key.EndsWith("0000000000000000") ? machine.Key.Substring(0, 16) : machine.Key;
                    log.WriteLine(dcPrefix + (lastMachine ? "`- " : "|- ") + "[Machine] {0}, {1}", machine.First().Parts[0].Address, machineId);

                    var    procs         = machine.ToArray();
                    string machinePrefix = dcPrefix + (lastMachine ? "   " : "|  ");
                    for (int procIndex = 0; procIndex < procs.Length; procIndex++)
                    {
                        var  proc     = procs[procIndex];
                        bool lastProc = procIndex == procs.Length - 1;

                        log.WriteLine(machinePrefix + (lastProc ? "`- " : "|- ") + "[Process] {0}:{1}, {2}", proc.Parts[0].Address, proc.Parts[0].Port, proc.ProcessId);
                        //foreach (var part in proc.Parts)
                        //{
                        //	log.WriteLine(machinePrefix + "|  -> {0}, {1}, {2:X8}, {3:X8}, {4}, {5:X8}", part.Address, part.Port, part.Unknown1, part.Unknown2, part.Unknown3, part.Unknown4);
                        //}
                    }
                }
            }
            log.WriteLine();
            Program.StdOut(log, $"Found {numNodes:N0} process(es) on {numMachines:N0} machine(s) in {numDCs:N0} datacenter(s)", ConsoleColor.White);
            log.WriteLine();
        }
        public static async Task Map(string[] path, IVarTuple extras, IFdbDatabase db, TextWriter log, CancellationToken ct)
        {
            // we want to merge the map of shards, with the map of directories from the Directory Layer, and count for each directory how many shards intersect
            bool progress = log == Console.Out;

            var folder = await TryOpenCurrentDirectoryAsync(path, db, ct);

            if (folder == null)
            {
                Program.Error(log, "# Directory not found");
                return;
            }

            Program.StdOut(log, "Listing all shards...");

            // note: this may break in future versions of the DL! Maybe we need a custom API to get a flat list of all directories in a DL that span a specific range ?
            var span   = folder.DirectoryLayer.ContentSubspace.Keys.ToRange();
            var shards = await Fdb.System.GetChunksAsync(db, span, ct);

            int totalShards = shards.Count;

            Program.StdOut(log, $"> Found {totalShards} shard(s) in partition /{folder.DirectoryLayer.FullName}", ConsoleColor.Gray);

            Program.StdOut(log, "Listing all directories...");
            var map = new Dictionary <string, int>(StringComparer.Ordinal);

            void Account(string[] p, int c)
            {
                for (int i = 1; i <= p.Length; i++)
                {
                    var s = "/" + string.Join("/", p, 0, i);
                    map[s] = map.TryGetValue(s, out int x) ? (x + c) : c;
                }
            }

            var work = new Stack <IFdbDirectory>();

            work.Push(folder);

            var dirs = new List <IFdbDirectory>();
            int n    = 0;

            while (work.Count > 0)
            {
                var cur = work.Pop();
                // skip sub partitions

                var names = await cur.ListAsync(db, ct);

                foreach (var name in names)
                {
                    var sub = await cur.TryOpenAsync(db, name, ct);

                    if (sub != null)
                    {
                        var p = sub.FullName;
                        if (sub is FdbDirectoryPartition)
                        {
                            if (progress)
                            {
                                log.Write("\r");
                            }
                            Program.StdOut(log, $"! Skipping partition {sub.Name}     ", ConsoleColor.DarkRed);
                            n = 0;
                            continue;
                        }
                        if (progress)
                        {
                            log.Write($"\r/{p}{(p.Length > n ? String.Empty : new string(' ', n - p.Length))}");
                        }
                        n = p.Length;
                        work.Push(sub);
                        dirs.Add(sub);
                    }
                }
            }
            if (progress)
            {
                log.Write("\r" + new string(' ', n + 2) + "\r");
            }
            Program.StdOut(log, $"> Found {dirs.Count} sub-directories", ConsoleColor.Gray);

            log.WriteLine();
            Program.StdOut(log, "Estimating size of each directory...");
            int foundShards = 0;

            n = 0;
            int           max    = 0;
            IFdbDirectory bigBad = null;

            foreach (var dir in dirs)
            {
                if (progress)
                {
                    log.Write($"\r> {dir.Name}{(dir.Name.Length > n ? String.Empty : new string(' ', n - dir.Name.Length))}");
                }
                n = dir.Name.Length;

                var p   = dir.Path.ToArray();
                var key = ((KeySubspace)dir).GetPrefix();

                // verify that the subspace has at least one key inside
                var bounds = await db.ReadAsync(async (tr) =>
                {
                    var kvs = await Task.WhenAll(
                        tr.GetRange(KeyRange.StartsWith(key)).FirstOrDefaultAsync(),
                        tr.GetRange(KeyRange.StartsWith(key)).LastOrDefaultAsync()
                        );
                    return(new { Min = kvs[0].Key, Max = kvs[1].Key });
                }, ct);

                if (bounds.Min.HasValue)
                {                 // folder is not empty
                    shards = await Fdb.System.GetChunksAsync(db, KeyRange.StartsWith(key), ct);

                    //TODO: we still need to check if the first and last shard really intersect the subspace

                    // we need to check if the shards actually contain data
                    //Console.WriteLine("/{0} under {1} with {2} shard(s)", string.Join("/", p), FdbKey.Dump(key), shards.Count);
                    foundShards += shards.Count;
                    Account(p, shards.Count);
                    if (shards.Count > max)
                    {
                        max = shards.Count; bigBad = dir;
                    }
                }
                else
                {
                    Account(p, 0);
                }
            }
            if (progress)
            {
                log.Write("\r" + new string(' ', n + 2) + "\r");
            }
            Program.StdOut(log, $"> Found a total of {foundShards:N0} shard(s) in {dirs.Count:N0} folder(s)", ConsoleColor.Gray);
            log.WriteLine();

            Program.StdOut(log, "Shards %Total              Path");
            foreach (var kvp in map.OrderBy(x => x.Key))
            {
                Program.StdOut(log, $"{kvp.Value,6} {RobustHistogram.FormatHistoBar((double)kvp.Value / foundShards, 20),-20} {kvp.Key}", ConsoleColor.Gray);
            }
            log.WriteLine();

            if (bigBad != null)
            {
                Program.StdOut(log, $"Biggest folder is /{bigBad.FullName} with {max} shards ({100.0 * max / totalShards:N1}% total, {100.0 * max / foundShards:N1}% subtree)");
                log.WriteLine();
            }
        }
        public static async Task ClearRange(string[] path, IVarTuple extras, IFdbDatabase db, TextWriter log, CancellationToken ct)
        {
            if (path == null || path.Length == 0)
            {
                Program.Error(log, "Cannot directory list the content of Root Partition.");
                return;
            }

            var folder = await db.Directory.TryOpenAsync(db, path, ct : ct);

            if (folder == null)
            {
                Program.Error(log, "The directory does not exist anymore");
                return;
            }
            if (folder.Layer == FdbDirectoryPartition.LayerId)
            {
                Program.Error(log, "Cannot clear the content of a Directory Partition!");
                return;
            }

            if (extras.Count == 0)
            {
                Program.Error(log, "You must specify a key of range of keys!");
                return;
            }

            KeyRange range;

            if (extras.Get <string>(0) == "*")
            {             // clear all!
                range = folder.ToRange();
            }
            else
            {
                object from = extras[0];
                object to   = extras.Count > 1 ? extras[1] : null;

                if (to == null)
                {
                    range = KeyRange.StartsWith(MakeKey(folder, from));
                }
                else
                {
                    range = new KeyRange(
                        MakeKey(folder, from),
                        MakeKey(folder, to)
                        );
                }
            }

            Program.Comment(log, "Clearing range " + range.ToString());

            bool empty = await db.ReadWriteAsync(async tr =>
            {
                var any = await tr.GetRangeAsync(range, new FdbRangeOptions {
                    Limit = 1
                });
                if (any.Count == 0)
                {
                    return(true);
                }
                tr.ClearRange(folder.ToRange());
                return(false);
            }, ct);

            if (empty)
            {
                Program.StdOut(log, $"Directory {String.Join("/", path)} was already empty.", ConsoleColor.Cyan);
            }
            else
            {
                Program.Success(log, $"Cleared all content of Directory {String.Join("/", path)}.");
            }
        }
        public static async Task Sampling(string[] path, IVarTuple extras, IFdbDatabase db, TextWriter log, CancellationToken ct)
        {
            double ratio = 0.1d;
            bool   auto  = true;

            if (extras.Count > 0)
            {
                double x = extras.Get <double>(0);
                if (x > 0 && x <= 1)
                {
                    ratio = x;
                }
                auto = false;
            }

            var folder = await TryOpenCurrentDirectoryAsync(path, db, ct);

            KeyRange span;

            if (folder is FdbDirectorySubspace)
            {
                span = KeyRange.StartsWith((folder as FdbDirectorySubspace).Copy().GetPrefix());
                log.WriteLine($"Reading list of shards for /{String.Join("/", path)} under {FdbKey.Dump(span.Begin)} ...");
            }
            else
            {
                log.WriteLine("Reading list of shards for the whole cluster ...");
                span = KeyRange.All;
            }

            // dump keyServers
            var ranges = await Fdb.System.GetChunksAsync(db, span, ct);

            log.WriteLine($"> Found {ranges.Count:N0} shard(s)");

            // take a sample
            var samples = new List <KeyRange>();

            if (ranges.Count <= 32)
            {             // small enough to scan it all
                samples.AddRange(ranges);
                log.WriteLine($"Sampling all {samples.Count:N0} shards ...");
            }
            else
            {             // need to take a random subset
                var rnd = new Random();
                int sz  = Math.Max((int)Math.Ceiling(ratio * ranges.Count), 1);
                if (auto)
                {
                    if (sz > 100)
                    {
                        sz = 100;                               //SAFETY
                    }
                    if (sz < 32)
                    {
                        sz = Math.Max(sz, Math.Min(32, ranges.Count));
                    }
                }

                var population = new List <KeyRange>(ranges);
                for (int i = 0; i < sz; i++)
                {
                    int p = rnd.Next(population.Count);
                    samples.Add(population[p]);
                    population.RemoveAt(p);
                }
                log.WriteLine($"Sampling {samples.Count:N0} out of {ranges.Count:N0} shards ({(100.0 * samples.Count / ranges.Count):N1}%) ...");
            }

            log.WriteLine();
            const string FORMAT_STRING = "{0,9} ║{1,10}{6,6} {2,-29} ║{3,10}{7,7} {4,-37} ║{5,10}";
            const string SCALE_KEY     = "....--------========########M";
            const string SCALE_VAL     = "....--------========########@@@@@@@@M";

            log.WriteLine(FORMAT_STRING, "Count", "Keys", SCALE_KEY, "Values", SCALE_VAL, "Total", "med.", "med.");

            var rangeOptions = new FdbRangeOptions {
                Mode = FdbStreamingMode.WantAll
            };

            samples = samples.OrderBy(x => x.Begin).ToList();

            long globalSize  = 0;
            long globalCount = 0;
            int  workers     = 8;        // Math.Max(4, Environment.ProcessorCount);

            var sw    = Stopwatch.StartNew();
            var tasks = new List <Task>();
            int n     = samples.Count;

            while (samples.Count > 0)
            {
                while (tasks.Count < workers && samples.Count > 0)
                {
                    var range = samples[0];
                    samples.RemoveAt(0);
                    tasks.Add(Task.Run(async() =>
                    {
                        var kk = new RobustHistogram(RobustHistogram.TimeScale.Ticks);
                        var vv = new RobustHistogram(RobustHistogram.TimeScale.Ticks);

                        #region Method 1: get_range everything...

                        using (var tr = db.BeginTransaction(ct))
                        {
                            long keySize   = 0;
                            long valueSize = 0;
                            long count     = 0;

                            int iter          = 0;
                            var beginSelector = KeySelector.FirstGreaterOrEqual(range.Begin);
                            var endSelector   = KeySelector.FirstGreaterOrEqual(range.End);
                            while (true)
                            {
                                FdbRangeChunk data = default(FdbRangeChunk);
                                FdbException error = null;
                                try
                                {
                                    data = await tr.Snapshot.GetRangeAsync(
                                        beginSelector,
                                        endSelector,
                                        rangeOptions,
                                        iter
                                        ).ConfigureAwait(false);
                                }
                                catch (FdbException e)
                                {
                                    error = e;
                                }

                                if (error != null)
                                {
                                    await tr.OnErrorAsync(error.Code).ConfigureAwait(false);
                                    continue;
                                }

                                if (data.Count == 0)
                                {
                                    break;
                                }

                                count += data.Count;
                                foreach (var kvp in data)
                                {
                                    keySize   += kvp.Key.Count;
                                    valueSize += kvp.Value.Count;

                                    kk.Add(TimeSpan.FromTicks(kvp.Key.Count));
                                    vv.Add(TimeSpan.FromTicks(kvp.Value.Count));
                                }

                                if (!data.HasMore)
                                {
                                    break;
                                }

                                beginSelector = KeySelector.FirstGreaterThan(data.Last);
                                ++iter;
                            }

                            long totalSize = keySize + valueSize;
                            Interlocked.Add(ref globalSize, totalSize);
                            Interlocked.Add(ref globalCount, count);

                            lock (log)
                            {
                                log.WriteLine(FORMAT_STRING, count.ToString("N0"), FormatSize(keySize), kk.GetDistribution(begin: 1, end: 12000, fold: 2), FormatSize(valueSize), vv.GetDistribution(begin: 1, end: 120000, fold: 2), FormatSize(totalSize), FormatSize((int)Math.Ceiling(kk.Median)), FormatSize((int)Math.Ceiling(vv.Median)));
                            }
                        }
                        #endregion

                        #region Method 2: estimate the count using key selectors...

                        //long counter = await Fdb.System.EstimateCountAsync(db, range, ct);
                        //Console.WriteLine("COUNT = " + counter.ToString("N0"));

                        #endregion
                    }, ct));
                }

                var done = await Task.WhenAny(tasks);

                tasks.Remove(done);
            }

            await Task.WhenAll(tasks);

            sw.Stop();

            log.WriteLine();
            if (n != ranges.Count)
            {
                log.WriteLine($"Sampled {FormatSize(globalSize)} ({globalSize:N0} bytes) and {globalCount:N0} keys in {sw.Elapsed.TotalSeconds:N1} sec");
                log.WriteLine($"> Estimated total size is {FormatSize(globalSize * ranges.Count / n)}");
            }
            else
            {
                log.WriteLine($"Found {FormatSize(globalSize)} ({globalSize:N0} bytes) and {globalCount:N0} keys in {sw.Elapsed.TotalSeconds:N1} sec");
                // compare to the whole cluster
                ranges = await Fdb.System.GetChunksAsync(db, FdbKey.MinValue, FdbKey.MaxValue, ct);

                log.WriteLine($"> This directory contains ~{(100.0 * n / ranges.Count):N2}% of all data");
            }
            log.WriteLine();
        }
        private async Task RunAsync(IFdbDatabase db, IDynamicKeySubspace location, CancellationToken ct, Action done, int N, int K, int W)
        {
            if (db == null)
            {
                throw new ArgumentNullException(nameof(db));
            }

            StringBuilder sb = new StringBuilder();

            db = new FdbLoggedDatabase(db, false, false, (log) =>
            {
                sb.AppendLine(log.Log.GetTimingsReport(true));
                //Console.WriteLine(log.Log.GetTimingsReport(true));
            });
            try
            {
                var workerPool = new FdbWorkerPool(location);
                Console.WriteLine($"workerPool at {location.GetPrefix():P}");

                var workerSignal = new AsyncCancelableMutex(ct);
                var clientSignal = new AsyncCancelableMutex(ct);

                int taskCounter = 0;

                int msgSent     = 0;
                int msgReceived = 0;

                Func <FdbWorkerMessage, CancellationToken, Task> handler = async(msg, _ct) =>
                {
                    Interlocked.Increment(ref msgReceived);

                    //await Task.Delay(10 + Math.Abs(msg.Id.GetHashCode()) % 50);
                    await Task.Delay(10).ConfigureAwait(false);
                };

                Func <int, Task> worker = async(id) =>
                {
                    await workerSignal.Task.ConfigureAwait(false);

                    Console.WriteLine("Worker #" + id + " is starting");
                    try
                    {
                        await workerPool.RunWorkerAsync(db, handler, ct).ConfigureAwait(false);
                    }
                    finally
                    {
                        Console.WriteLine("Worker #" + id + " has stopped");
                    }
                };

                Func <int, Task> client = async(id) =>
                {
                    await clientSignal.Task.ConfigureAwait(false);

                    await Task.Delay(10).ConfigureAwait(false);

                    var rnd = new Random(id * 111);
                    for (int i = 0; i < N; i++)
                    {
                        var taskId   = Slice.FromString("T" + Interlocked.Increment(ref taskCounter));
                        var taskBody = Slice.FromString("Message " + (i + 1) + " of " + N + " from client #" + id);

                        await workerPool.ScheduleTaskAsync(db, taskId, taskBody, ct).ConfigureAwait(false);

                        Interlocked.Increment(ref msgSent);

                        //if (i > 0 && i % 10 == 0) Console.WriteLine("@@@ Client#" + id + " pushed " + (i + 1) + " / " + N + " messages");

                        switch (rnd.Next(5))
                        {
                        case 0: await Task.Delay(10).ConfigureAwait(false); break;

                        case 1: await Task.Delay(100).ConfigureAwait(false); break;

                        case 2: await Task.Delay(500).ConfigureAwait(false); break;
                        }
                    }
                    Console.WriteLine("@@@ Client#" + id + " has finished!");
                };

                Func <string, Task> dump = async(label) =>
                {
                    Console.WriteLine($"<dump label=\'{label}\' key=\'{location.GetPrefix():P}\'>");
                    using (var tr = db.BeginTransaction(ct))
                    {
                        await tr.Snapshot
                        .GetRange(KeyRange.StartsWith(location.GetPrefix()))
                        .ForEachAsync((kvp) =>
                        {
                            Console.WriteLine($" - {location.Keys.Unpack(kvp.Key)} = {kvp.Value:V}");
                        }).ConfigureAwait(false);
                    }
                    Console.WriteLine("</dump>");
                };

                var workers = Enumerable.Range(0, W).Select((i) => worker(i)).ToArray();
                var clients = Enumerable.Range(0, K).Select((i) => client(i)).ToArray();

                DateTime start       = DateTime.Now;
                DateTime last        = start;
                int      lastHandled = -1;
                using (var timer = new Timer((_) =>
                {
                    var now = DateTime.Now;
                    Console.WriteLine("@@@ T=" + now.Subtract(start) + ", sent: " + msgSent.ToString("N0") + ", recv: " + msgReceived.ToString("N0"));
                    Console.WriteLine("### Workers: " + workerPool.IdleWorkers + " / " + workerPool.ActiveWorkers + " (" + new string('#', workerPool.IdleWorkers) + new string('.', workerPool.ActiveWorkers - workerPool.IdleWorkers) + "), sent: " + workerPool.MessageScheduled.ToString("N0") + ", recv: " + workerPool.MessageReceived.ToString("N0") + ", delta: " + (workerPool.MessageScheduled - workerPool.MessageReceived).ToString("N0") + ", busy: " + workerPool.WorkerBusyTime + " (avg " + workerPool.WorkerAverageBusyDuration.TotalMilliseconds.ToString("N3") + " ms)");

                    if (now.Subtract(last).TotalSeconds >= 10)
                    {
                        //dump("timer").GetAwaiter().GetResult();
                        last = now;
                        if (lastHandled == msgReceived)
                        {                         // STALL ?
                            Console.WriteLine("STALL! ");
                            done();
                        }
                        lastHandled = msgReceived;
                    }

                    if (msgReceived >= K * N)
                    {
                        dump("complete").GetAwaiter().GetResult();
                        done();
                    }
                }, null, 1000, 1000))
                {
                    var sw = Stopwatch.StartNew();

                    // start the workers
                    workerSignal.Set(async: true);
                    await Task.Delay(500);

                    await dump("workers started");

                    // start the clients
                    clientSignal.Set(async: true);

                    await Task.WhenAll(clients);

                    Console.WriteLine("Clients completed after " + sw.Elapsed);

                    await Task.WhenAll(workers);

                    Console.WriteLine("Workers completed after " + sw.Elapsed);
                }
            }
            finally
            {
                Console.WriteLine("---------------------------------------------------------------------------");
                Console.WriteLine("Transaction logs:");
                Console.WriteLine();

                Console.WriteLine(sb.ToString());
            }
        }
        public async Task Run(IFdbDatabase db, TextWriter log, CancellationToken ct)
        {
            // estimate the number of machines...
            Console.WriteLine("# Detecting cluster topology...");
            var servers = await db.QueryAsync(tr => tr
                                              .WithReadAccessToSystemKeys()
                                              .GetRange(KeyRange.StartsWith(Fdb.System.ServerList))
                                              .Select(kvp => new
            {
                Node       = kvp.Value.Substring(8, 16).ToHexaString(),
                Machine    = kvp.Value.Substring(24, 16).ToHexaString(),
                DataCenter = kvp.Value.Substring(40, 16).ToHexaString()
            }),
                                              ct
                                              );

            var numNodes    = servers.Select(s => s.Node).Distinct().Count();
            var numMachines = servers.Select(s => s.Machine).Distinct().Count();
            var numDCs      = servers.Select(s => s.DataCenter).Distinct().Count();

            Console.WriteLine("# > Found " + numNodes + " process(es) on " + numMachines + " machine(s) in " + numDCs + " datacenter(s)");
            Console.WriteLine("# Reading list of shards...");
            // dump keyServers
            var ranges = await Fdb.System.GetChunksAsync(db, FdbKey.MinValue, FdbKey.MaxValue, ct);

            Console.WriteLine("# > Found " + ranges.Count + " shards:");

            // take a sample
            var rnd = new Random(1234);
            int sz  = Math.Max((int)Math.Ceiling(this.Ratio * ranges.Count), 1);

            if (sz > 500)
            {
                sz = 500;                       //SAFETY
            }
            if (sz < 50)
            {
                sz = Math.Max(sz, Math.Min(50, ranges.Count));
            }

            var samples = new List <KeyRange>();

            for (int i = 0; i < sz; i++)
            {
                int p = rnd.Next(ranges.Count);
                samples.Add(ranges[p]);
                ranges.RemoveAt(p);
            }

            Console.WriteLine("# Sampling " + sz + " out of " + ranges.Count + " shards (" + (100.0 * sz / ranges.Count).ToString("N1") + "%) ...");
            Console.WriteLine("{0,9}{1,10}{2,10}{3,10} : K+V size distribution", "Count", "Keys", "Values", "Total");

            var rangeOptions = new FdbRangeOptions {
                Mode = FdbStreamingMode.WantAll
            };

            samples = samples.OrderBy(x => x.Begin).ToList();

            long total   = 0;
            int  workers = Math.Min(numMachines, 8);

            var sw    = Stopwatch.StartNew();
            var tasks = new List <Task>();

            while (samples.Count > 0)
            {
                while (tasks.Count < workers && samples.Count > 0)
                {
                    var range = samples[0];
                    samples.RemoveAt(0);
                    tasks.Add(Task.Run(async() =>
                    {
                        var hh = new RobustHistogram(RobustHistogram.TimeScale.Ticks);

                        #region Method 1: get_range everything...

                        using (var tr = await db.BeginTransactionAsync(ct))
                        {
                            long keySize   = 0;
                            long valueSize = 0;
                            long count     = 0;

                            int iter          = 0;
                            var beginSelector = KeySelector.FirstGreaterOrEqual(range.Begin);
                            var endSelector   = KeySelector.FirstGreaterOrEqual(range.End);
                            while (true)
                            {
                                FdbRangeChunk data = default(FdbRangeChunk);
                                FdbException error = null;
                                try
                                {
                                    data = await tr.Snapshot.GetRangeAsync(
                                        beginSelector,
                                        endSelector,
                                        rangeOptions,
                                        iter
                                        ).ConfigureAwait(false);
                                }
                                catch (FdbException e)
                                {
                                    error = e;
                                }

                                if (error != null)
                                {
                                    await tr.OnErrorAsync(error.Code).ConfigureAwait(false);
                                    continue;
                                }

                                if (data.Count == 0)
                                {
                                    break;
                                }

                                count += data.Count;
                                foreach (var kvp in data)
                                {
                                    keySize   += kvp.Key.Count;
                                    valueSize += kvp.Value.Count;

                                    hh.Add(TimeSpan.FromTicks(kvp.Key.Count + kvp.Value.Count));
                                }

                                if (!data.HasMore)
                                {
                                    break;
                                }

                                beginSelector = KeySelector.FirstGreaterThan(data.Last);
                                ++iter;
                            }

                            long totalSize = keySize + valueSize;
                            Interlocked.Add(ref total, totalSize);

                            Console.WriteLine("{0,9}{1,10}{2,10}{3,10} : {4}", count.ToString("N0"), FormatSize(keySize), FormatSize(valueSize), FormatSize(totalSize), hh.GetDistribution(begin: 1, end: 10000, fold: 2));
                        }
                        #endregion

                        #region Method 2: estimate the count using key selectors...

                        //long counter = await Fdb.System.EstimateCountAsync(db, range, ct);
                        //Console.WriteLine("COUNT = " + counter.ToString("N0"));

                        #endregion
                    }, ct));
                }

                var done = await Task.WhenAny(tasks);

                tasks.Remove(done);
            }

            await Task.WhenAll(tasks);

            sw.Stop();

            Console.WriteLine("> Sampled " + FormatSize(total) + " (" + total.ToString("N0") + " bytes) in " + sw.Elapsed.TotalSeconds.ToString("N1") + " sec");
            Console.WriteLine("> Estimated total size is " + FormatSize(total * ranges.Count / sz));
        }
Example #10
0
        public async Task Test_Can_Log_A_Transaction()
        {
            const int N = 10;

            using (var db = await OpenTestPartitionAsync())
            {
                // get a tuple view of the directory
                var location = (await GetCleanDirectory(db, "Logging")).Keys;

                // note: ensure that all methods are JITed
                await db.ReadWriteAsync(async (tr) =>
                {
                    await tr.GetReadVersionAsync();
                    tr.Set(location.Encode("Warmup", 0), Slice.FromInt32(1));
                    tr.Clear(location.Encode("Warmup", 1));
                    await tr.GetAsync(location.Encode("Warmup", 2));
                    await tr.GetRange(KeyRange.StartsWith(location.Encode("Warmup", 3))).ToListAsync();
                    tr.ClearRange(location.Encode("Warmup", 4), location.Encode("Warmup", 5));
                }, this.Cancellation);

                await db.WriteAsync((tr) =>
                {
                    var rnd = new Random();
                    tr.Set(location.Encode("One"), Slice.FromString("111111"));
                    tr.Set(location.Encode("Two"), Slice.FromString("222222"));
                    for (int j = 0; j < 4; j++)
                    {
                        for (int i = 0; i < 100; i++)
                        {
                            tr.Set(location.Encode("Range", j, rnd.Next(1000)), Slice.Empty);
                        }
                    }
                    for (int j = 0; j < N; j++)
                    {
                        tr.Set(location.Encode("X", j), Slice.FromInt32(j));
                        tr.Set(location.Encode("Y", j), Slice.FromInt32(j));
                        tr.Set(location.Encode("Z", j), Slice.FromInt32(j));
                        tr.Set(location.Encode("W", j), Slice.FromInt32(j));
                    }
                }, this.Cancellation);

                bool first = true;
                Action <FdbLoggedTransaction> logHandler = (tr) =>
                {
                    if (first)
                    {
                        Log(tr.Log.GetCommandsReport());
                        first = false;
                    }

                    Log(tr.Log.GetTimingsReport(true));
                };

                // create a logged version of the database
                var logged = new FdbLoggedDatabase(db, false, false, logHandler);

                for (int k = 0; k < N; k++)
                {
                    Log("==== " + k + " ==== ");
                    Log();

                    await logged.ReadWriteAsync(async (tr) =>
                    {
                        Assert.That(tr, Is.InstanceOf <FdbLoggedTransaction>());

                        //tr.SetOption(FdbTransactionOption.CausalReadRisky);

                        long ver = await tr.GetReadVersionAsync().ConfigureAwait(false);

                        await tr.GetAsync(location.Encode("One")).ConfigureAwait(false);
                        await tr.GetAsync(location.Encode("NotFound")).ConfigureAwait(false);

                        tr.Set(location.Encode("Write"), Slice.FromString("abcdef" + k.ToString()));

                        //tr.Annotate("BEFORE");
                        //await Task.Delay(TimeSpan.FromMilliseconds(10));
                        //tr.Annotate("AFTER");

                        //await tr.Snapshot.GetAsync(location.Pack("Snap")).ConfigureAwait(false);

                        tr.Annotate("This is a comment");

                        //await tr.GetRangeAsync(KeySelector.LastLessOrEqual(location.Pack("A")), KeySelector.FirstGreaterThan(location.Pack("Z"))).ConfigureAwait(false);

                        await Task.WhenAll(
                            tr.GetRange(KeyRange.StartsWith(location.Encode("Range", 0))).ToListAsync(),
                            tr.GetRange(location.Encode("Range", 1, 0), location.Encode("Range", 1, 200)).ToListAsync(),
                            tr.GetRange(location.Encode("Range", 2, 400), location.Encode("Range", 2, 600)).ToListAsync(),
                            tr.GetRange(location.Encode("Range", 3, 800), location.Encode("Range", 3, 1000)).ToListAsync()
                            ).ConfigureAwait(false);

                        await tr.GetAsync(location.Encode("Two")).ConfigureAwait(false);

                        await tr.GetValuesAsync(Enumerable.Range(0, N).Select(x => location.Encode("X", x))).ConfigureAwait(false);

                        for (int i = 0; i < N; i++)
                        {
                            await tr.GetAsync(location.Encode("Z", i)).ConfigureAwait(false);
                        }

                        await Task.WhenAll(Enumerable.Range(0, N / 2).Select(x => tr.GetAsync(location.Encode("Y", x)))).ConfigureAwait(false);
                        await Task.WhenAll(Enumerable.Range(N / 2, N / 2).Select(x => tr.GetAsync(location.Encode("Y", x)))).ConfigureAwait(false);

                        await Task.WhenAll(
                            tr.GetAsync(location.Encode("W", 1)),
                            tr.GetAsync(location.Encode("W", 2)),
                            tr.GetAsync(location.Encode("W", 3))
                            ).ConfigureAwait(false);

                        tr.Set(location.Encode("Write2"), Slice.FromString("ghijkl" + k.ToString()));
                        tr.Clear(location.Encode("Clear", "0"));
                        tr.ClearRange(location.Encode("Clear", "A"), location.Encode("Clear", "Z"));

                        if (tr.Context.Retries == 0)
                        {
                            // make it fail
                            //throw new FdbException(FdbError.PastVersion, "fake timeout");
                        }
                    }, this.Cancellation);
                }
            }
        }
Example #11
0
		/// <summary>Remove all the values for a specific key</summary>
		/// <param name="trans"></param>
		/// <param name="key"></param>
		/// <returns></returns>
		public void Remove([NotNull] IFdbTransaction trans, TKey key)
		{
			if (trans == null) throw new ArgumentNullException(nameof(trans));

			trans.ClearRange(KeyRange.StartsWith(this.Subspace.Keys.EncodePartial(key)));
		}