예제 #1
0
 protected IFdbTuple ToRelativePath(IEnumerable <string> path)
 {
     return(ToRelativePath(path == null ? null :  FdbTuple.FromEnumerable <string>(path)));
 }
        private static async Task MainAsync(string[] args, CancellationToken cancel)
        {
            #region Options Parsing...

            string clusterFile = null;
            var    dbName      = "DB";
            var    partition   = new string[0];
            bool   showHelp    = false;
            int    timeout     = 30;
            int    maxRetries  = 10;
            string execCommand = null;

            var opts = new OptionSet()
            {
                {
                    "c|connfile=",
                    "The path of a file containing the connection string for the FoundationDB cluster.",
                    v => clusterFile = v
                },
                {
                    "p|partition=",
                    "The name of the database partition to open.",
                    v => partition = v.Trim().Split('/')
                },
                {
                    "t|timeout=",
                    "Default timeout (in seconds) for failed transactions.",
                    (int v) => timeout = v
                },
                {
                    "r|retries=",
                    "Default max retry count for failed transactions.",
                    (int v) => maxRetries = v
                },
                {
                    "exec=",
                    "Execute this command, and exits immediately.",
                    v => execCommand = v
                },
                {
                    "h|help",
                    "Show this help and exit.",
                    v => showHelp = v != null
                }
            };

            var extra = opts.Parse(args);

            if (showHelp)
            {
                //TODO!
                opts.WriteOptionDescriptions(Console.Out);
                return;
            }

            string startCommand = null;
            if (!string.IsNullOrEmpty(execCommand))
            {
                startCommand = execCommand;
            }
            else if (extra.Count > 0)
            {             // the remainder of the command line will be the first command to execute
                startCommand = String.Join(" ", extra);
            }

            #endregion

            bool stop = false;
            Db = null;
            try
            {
                Db = await ChangeDatabase(clusterFile, dbName, partition, cancel);

                Db.DefaultTimeout    = Math.Max(0, timeout) * 1000;
                Db.DefaultRetryLimit = Math.Max(0, maxRetries);

                Console.WriteLine("Using API v" + Fdb.ApiVersion + " (max " + Fdb.GetMaxApiVersion() + ")");
                Console.WriteLine("Cluster file: " + (clusterFile ?? "<default>"));
                Console.WriteLine();
                Console.WriteLine("FoundationDB Shell menu:");
                Console.WriteLine("\tdir\tShow the content of the current directory");
                Console.WriteLine("\ttree\tShow all the directories under the current directory");
                Console.WriteLine("\tsampling\tDisplay statistics on random shards from the database");
                Console.WriteLine("\tcoordinators\tShow the current coordinators for the cluster");
                Console.WriteLine("\tmem\tShow memory usage statistics");
                Console.WriteLine("\tgc\tTrigger garbage collection");
                Console.WriteLine("\tquit\tQuit");

                Console.WriteLine("Ready...");


                var le = new LineEditor("FDBShell");

                string[] cmds = new string[]
                {
                    "cd",
                    "coordinators",
                    "count",
                    "dir",
                    "exit",
                    "gc",
                    "help",
                    "layer",
                    "map",
                    "mem",
                    "mkdir",
                    "mv",
                    "partition",
                    "pwd",
                    "quit",
                    "ren",
                    "rmdir",
                    "sampling",
                    "shards",
                    "show",
                    "status",
                    "topology",
                    "tree",
                    "version",
                    "wide",
                };

                le.AutoCompleteEvent = (txt, pos) =>
                {
                    string[] res;
                    int      p = txt.IndexOf(' ');
                    if (p > 0)
                    {
                        string cmd = txt.Substring(0, p);
                        string arg = txt.Substring(p + 1);

                        if (cmd == "cd")
                        {                         // handle completion for directories
                            // txt: "cd foo" => prefix = "foo"
                            // txt: "cd foobar/b" => prefix = "b"

                            string path   = CurrentDirectoryPath;
                            string prefix = "";
                            string search = arg;
                            p = arg.LastIndexOf('/');
                            if (p > 0)
                            {
                                path   = Path.Combine(path, arg.Substring(0, p));
                                search = arg.Substring(p + 1);
                                prefix = arg.Substring(0, p + 1);
                            }

                            var subdirs = RunAsyncCommand((db, log, ct) => AutoCompleteDirectories(path, db, log, ct), cancel).GetAwaiter().GetResult();
                            if (!subdirs.HasValue || subdirs.Value == null)
                            {
                                return(new LineEditor.Completion(txt, null));
                            }

                            res = subdirs.Value
                                  .Where(s => s.StartsWith(search, StringComparison.Ordinal))
                                  .Select(s => (cmd + " " + prefix + s).Substring(txt.Length))
                                  .ToArray();
                            return(new LineEditor.Completion(txt, res));
                        }

                        // unknown command
                        return(new LineEditor.Completion(txt, null));
                    }

                    // list of commands
                    res = cmds
                          .Where(cmd => cmd.StartsWith(txt, StringComparison.OrdinalIgnoreCase))
                          .Select(cmd => cmd.Substring(txt.Length))
                          .ToArray();
                    return(new LineEditor.Completion(txt, res));
                };
                le.TabAtStartCompletes = true;

                string          prompt       = null;
                Action <string> updatePrompt = (path) => { prompt = String.Format("fdb:{0}> ", path); };
                updatePrompt(CurrentDirectoryPath);

                while (!stop)
                {
                    string s = startCommand != null ? startCommand : le.Edit(prompt, "");
                    startCommand = null;

                    if (s == null)
                    {
                        break;
                    }

                    var    tokens = s.Trim().Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    string cmd    = tokens.Length > 0 ? tokens[0] : String.Empty;
                    string prm    = tokens.Length > 1 ? tokens[1] : String.Empty;
                    var    extras = tokens.Length > 2 ? FdbTuple.FromEnumerable <string>(tokens.Skip(2)) : FdbTuple.Empty;

                    var trimmedCommand = cmd.Trim().ToLowerInvariant();
                    switch (trimmedCommand)
                    {
                    case "":
                    {
                        continue;
                    }

                    case "log":
                    {
                        LogCommand(prm, Console.Out);

                        break;
                    }

                    case "version":
                    {
                        await VersionCommand(prm, clusterFile, Console.Out, cancel);

                        break;
                    }

                    case "tree":
                    {
                        var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Tree(path, extras, db, log, ct), cancel);

                        break;
                    }

                    case "map":
                    {
                        var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Map(path, extras, db, log, ct), cancel);

                        break;
                    }

                    case "dir":
                    case "ls":
                    {
                        var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Dir(path, extras, BasicCommands.DirectoryBrowseOptions.Default, db, log, ct), cancel);

                        break;
                    }

                    case "ll":
                    {
                        var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Dir(path, extras, BasicCommands.DirectoryBrowseOptions.ShowCount, db, log, ct), cancel);

                        break;
                    }

                    case "count":
                    {
                        var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Count(path, extras, db, log, ct), cancel);

                        break;
                    }

                    case "show":
                    case "top":
                    {
                        var path = ParsePath(CurrentDirectoryPath);
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Show(path, extras, false, db, log, ct), cancel);

                        break;
                    }

                    case "last":
                    {
                        var path = ParsePath(CurrentDirectoryPath);
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Show(path, extras, true, db, log, ct), cancel);

                        break;
                    }

                    case "cd":
                    case "pwd":
                    {
                        if (!string.IsNullOrEmpty(prm))
                        {
                            var newPath = CombinePath(CurrentDirectoryPath, prm);
                            var res     = await RunAsyncCommand((db, log, ct) => BasicCommands.TryOpenCurrentDirectoryAsync(ParsePath(newPath), db, ct), cancel);

                            if (res == null)
                            {
                                Console.WriteLine("# Directory {0} does not exist!", newPath);
                                Console.Beep();
                            }
                            else
                            {
                                CurrentDirectoryPath = newPath;
                                updatePrompt(CurrentDirectoryPath);
                            }
                        }
                        else
                        {
                            var res = await RunAsyncCommand((db, log, ct) => BasicCommands.TryOpenCurrentDirectoryAsync(ParsePath(CurrentDirectoryPath), db, ct), cancel);

                            if (res.GetValueOrDefault() == null)
                            {
                                Console.WriteLine("# Directory {0} does not exist anymore", CurrentDirectoryPath);
                            }
                            else
                            {
                                Console.WriteLine("# {0}", res);
                            }
                        }
                        break;
                    }

                    case "mkdir":
                    case "md":
                    {                             // "mkdir DIRECTORYNAME"
                        if (!string.IsNullOrEmpty(prm))
                        {
                            var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                            await RunAsyncCommand((db, log, ct) => BasicCommands.CreateDirectory(path, extras, db, log, ct), cancel);
                        }
                        break;
                    }

                    case "rmdir":
                    {                             // "rmdir DIRECTORYNAME"
                        if (!string.IsNullOrEmpty(prm))
                        {
                            var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                            await RunAsyncCommand((db, log, ct) => BasicCommands.RemoveDirectory(path, extras, db, log, ct), cancel);
                        }
                        break;
                    }

                    case "mv":
                    case "ren":
                    {                             // "mv SOURCE DESTINATION"
                        var srcPath = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                        var dstPath = ParsePath(CombinePath(CurrentDirectoryPath, extras.Get <string>(0)));
                        await RunAsyncCommand((db, log, ct) => BasicCommands.MoveDirectory(srcPath, dstPath, extras.Substring(1), db, log, ct), cancel);

                        break;
                    }

                    case "layer":
                    {
                        if (string.IsNullOrEmpty(prm))
                        {                                 // displays the layer id of the current folder
                            var path = ParsePath(CurrentDirectoryPath);
                            await RunAsyncCommand((db, log, ct) => BasicCommands.ShowDirectoryLayer(path, extras, db, log, ct), cancel);
                        }
                        else
                        {                                 // change the layer id of the current folder
                            prm = prm.Trim();
                            // double or single quotes can be used to escape the value
                            if (prm.Length >= 2 && (prm.StartsWith("'") && prm.EndsWith("'")) || (prm.StartsWith("\"") && prm.EndsWith("\"")))
                            {
                                prm = prm.Substring(1, prm.Length - 2);
                            }
                            var path = ParsePath(CurrentDirectoryPath);
                            await RunAsyncCommand((db, log, ct) => BasicCommands.ChangeDirectoryLayer(path, prm, extras, db, log, ct), cancel);
                        }
                        break;
                    }

                    case "mkpart":
                    {                             // "mkpart PARTITIONNAME"
                        if (!string.IsNullOrEmpty(prm))
                        {
                            var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                            await RunAsyncCommand((db, log, ct) => BasicCommands.CreateDirectory(path, FdbTuple.Create(FdbDirectoryPartition.LayerId).Concat(extras), db, log, ct), cancel);
                        }

                        break;
                    }

                    case "topology":
                    {
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Topology(null, extras, db, log, ct), cancel);

                        break;
                    }

                    case "shards":
                    {
                        var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Shards(path, extras, db, log, ct), cancel);

                        break;
                    }

                    case "sampling":
                    {
                        var path = ParsePath(CombinePath(CurrentDirectoryPath, prm));
                        await RunAsyncCommand((db, log, ct) => BasicCommands.Sampling(path, extras, db, log, ct), cancel);

                        break;
                    }

                    case "coordinators":
                    {
                        await RunAsyncCommand((db, log, ct) => CoordinatorsCommand(db, log, ct), cancel);

                        break;
                    }

                    case "partition":
                    {
                        if (string.IsNullOrEmpty(prm))
                        {
                            Console.WriteLine("# Current partition is {0}", String.Join("/", partition));
                            //TODO: browse existing partitions ?
                            break;
                        }

                        var          newPartition = prm.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
                        IFdbDatabase newDb        = null;
                        try
                        {
                            newDb = await ChangeDatabase(clusterFile, dbName, newPartition, cancel);
                        }
                        catch (Exception)
                        {
                            if (newDb != null)
                            {
                                newDb.Dispose();
                            }
                            newDb = null;
                            throw;
                        }
                        finally
                        {
                            if (newDb != null)
                            {
                                if (Db != null)
                                {
                                    Db.Dispose(); Db = null;
                                }
                                Db        = newDb;
                                partition = newPartition;
                                Console.WriteLine("# Changed partition to {0}", partition);
                            }
                        }
                        break;
                    }

                    case "q":
                    case "x":
                    case "quit":
                    case "exit":
                    case "bye":
                    {
                        stop = true;
                        break;
                    }

                    case "gc":
                    {
                        long before = GC.GetTotalMemory(false);
                        Console.Write("Collecting garbage...");
                        GC.Collect();
                        GC.WaitForPendingFinalizers();
                        GC.Collect();
                        Console.WriteLine(" Done");
                        long after = GC.GetTotalMemory(false);
                        Console.WriteLine("- before = " + before.ToString("N0"));
                        Console.WriteLine("- after  = " + after.ToString("N0"));
                        Console.WriteLine("- delta  = " + (before - after).ToString("N0"));
                        break;
                    }

                    case "mem":
                    {
                        Console.WriteLine("Memory usage:");
                        Console.WriteLine("- Working Set  : " + PerfCounters.WorkingSet.NextValue().ToString("N0") + " (peak " + PerfCounters.WorkingSetPeak.NextValue().ToString("N0") + ")");
                        Console.WriteLine("- Virtual Bytes: " + PerfCounters.VirtualBytes.NextValue().ToString("N0") + " (peak " + PerfCounters.VirtualBytesPeak.NextValue().ToString("N0") + ")");
                        Console.WriteLine("- Private Bytes: " + PerfCounters.PrivateBytes.NextValue().ToString("N0"));
                        Console.WriteLine("- Managed Mem  : " + GC.GetTotalMemory(false).ToString("N0"));
                        Console.WriteLine("- BytesInAlHeap: " + PerfCounters.ClrBytesInAllHeaps.NextValue().ToString("N0"));
                        break;
                    }

                    case "wide":
                    {
                        Console.WindowWidth = 160;
                        break;
                    }

                    case "status":
                    case "wtf":
                    {
                        var result = await RunAsyncCommand((_, log, ct) => FdbCliCommands.RunFdbCliCommand("status details", null, clusterFile, log, ct), cancel);

                        if (result.HasFailed)
                        {
                            break;
                        }
                        if (result.Value.ExitCode != 0)
                        {
                            Console.WriteLine("# fdbcli exited with code {0}", result.Value.ExitCode);
                            Console.WriteLine("> StdErr:");
                            Console.WriteLine(result.Value.StdErr);
                            Console.WriteLine("> StdOut:");
                        }
                        Console.WriteLine(result.Value.StdOut);
                        break;
                    }

                    default:
                    {
                        Console.WriteLine(string.Format("Unknown command : '{0}'", trimmedCommand));
                        break;
                    }
                    }

                    if (!string.IsNullOrEmpty(execCommand))
                    {                     // only run one command, and then exit
                        break;
                    }
                }
            }
            finally
            {
                if (Db != null)
                {
                    Db.Dispose();
                }
            }
        }