Exemple #1
0
 public Recovery(VoronRecoveryConfiguration config)
 {
     _datafile                    = config.PathToDataFile;
     _output                      = config.OutputFileName;
     _pageSize                    = config.PageSizeInKb * Constants.Size.Kilobyte;
     _initialContextSize          = config.InitialContextSizeInMB * Constants.Size.Megabyte;
     _initialContextLongLivedSize = config.InitialContextLongLivedSizeInKB * Constants.Size.Kilobyte;
     _option                      = StorageEnvironmentOptions.ForPath(config.DataFileDirectory, null, Path.Combine(config.DataFileDirectory, "Journal"), null, null);
     _copyOnWrite                 = !config.DisableCopyOnWriteMode;
     // by default CopyOnWriteMode will be true
     _option.CopyOnWriteMode = _copyOnWrite;
     _progressIntervalInSec  = config.ProgressIntervalInSec;
     _previouslyWrittenDocs  = new Dictionary <string, long>();
 }
Exemple #2
0
 public Recovery(VoronRecoveryConfiguration config)
 {
     _datafile = config.PathToDataFile;
     _output   = config.OutputFileName;
     _pageSize = config.PageSizeInKb * Constants.Size.Kilobyte;
     _numberOfFieldsInDocumentTable = config.NumberOfFiledsInDocumentTable;
     _initialContextSize            = config.InitialContextSizeInMB * Constants.Size.Megabyte;
     _initialContextLongLivedSize   = config.InitialContextLongLivedSizeInKB * Constants.Size.Kilobyte;
     _option      = StorageEnvironmentOptions.ForPath(config.DataFileDirectory);
     _copyOnWrite = !config.DisableCopyOnWriteMode;
     // by default CopyOnWriteMode will be true
     //i'm setting CopyOnWriteMode this was because we want to keep it internal.
     _option.GetType().GetProperty("CopyOnWriteMode", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(_option, _copyOnWrite);
     _progressIntervalInSeconds = config.ProgressIntervalInSeconds;
 }
Exemple #3
0
        private static void ConfigureRecoveryCommand()
        {
            _app.Command("recover", cmd =>
            {
                cmd.ExtendedHelpText = cmd.Description = "Recovering a database into recovery.ravendump.";
                cmd.HelpOption(HelpOptionString);

                var dataFileDirectoryArg = cmd.Argument("DataFileDirectory", "The database directory which contains the data file");
                var recoverDirectoryArg  = cmd.Argument("RecoverDirectory", "The directory to recover the recovery.ravendump file");

                var outputFileNameArg                  = cmd.Option("--OutputFileName", "Will overwrite the default file name () with your own file name (under output directory).", CommandOptionType.SingleValue);
                var pageSizeInKbArg                    = cmd.Option("--PageSizeInKB", "Will set the recovery tool to work with page sizes other than 4kb.", CommandOptionType.SingleValue);
                var initialContextSizeInMbArg          = cmd.Option("--InitialContextSizeInMB", "Will set the recovery tool to use a context of the provided size in MB.", CommandOptionType.SingleValue);
                var initialContextLongLivedSizeInKbArg = cmd.Option("--InitialContextLongLivedSizeInKB", "Will set the recovery tool to use a long lived context size of the provided size in KB.", CommandOptionType.SingleValue);
                var progressIntervalInSecArg           = cmd.Option("--ProgressIntervalInSec", "Will set the recovery tool refresh to console rate interval in seconds.", CommandOptionType.SingleValue);
                var disableCopyOnWriteModeArg          = cmd.Option("--DisableCopyOnWriteMode", "Default is false.", CommandOptionType.SingleValue);
                var ignoreInvalidJournalErrorsArg      = cmd.Option("--IgnoreInvalidJournalErrors", "Default is false.", CommandOptionType.SingleValue);
                var ignoreDataIntegrityErrorsOfAlreadySyncedTransactionsArg = cmd.Option("--IgnoreInvalidDataErrorsOfAlreadySyncedTransactions", "Default is false.", CommandOptionType.SingleValue);


                var loggingModeArg = cmd.Option("--LoggingMode", "Logging mode: Operations or Information.", CommandOptionType.SingleValue);

                var masterKey = cmd.Option("--MasterKey", "Encryption key: base64 string of the encryption master key", CommandOptionType.SingleValue);

                cmd.OnExecute(() =>
                {
                    VoronRecoveryConfiguration config = new VoronRecoveryConfiguration
                    {
                        DataFileDirectory = dataFileDirectoryArg.Value,
                    };

                    if (string.IsNullOrWhiteSpace(config.DataFileDirectory) ||
                        Directory.Exists(config.DataFileDirectory) == false ||
                        File.Exists(Path.Combine(config.DataFileDirectory, DatafileName)) == false)
                    {
                        return(ExitWithError($"Missing {nameof(config.DataFileDirectory)} argument", cmd));
                    }
                    config.PathToDataFile = Path.Combine(config.DataFileDirectory, DatafileName);

                    var recoverDirectory = recoverDirectoryArg.Value;
                    if (string.IsNullOrWhiteSpace(recoverDirectory))
                    {
                        return(ExitWithError("Missing RecoverDirectory argument", cmd));
                    }

                    config.OutputFileName = Path.Combine(recoverDirectory, outputFileNameArg.HasValue() ? outputFileNameArg.Value() : RecoveryFileName);
                    try
                    {
                        if (!Directory.Exists(recoverDirectory))
                        {
                            Directory.CreateDirectory(recoverDirectory);
                        }
                        File.WriteAllText(config.OutputFileName, "I have write permission!");
                        File.Delete(config.OutputFileName);
                    }
                    catch
                    {
                        return(ExitWithError($"Cannot write to the output directory ({recoverDirectory}). " +
                                             "Permissions issue?", cmd));
                    }

                    if (pageSizeInKbArg.HasValue())
                    {
                        if (int.TryParse(pageSizeInKbArg.Value(), out var pageSize) == false ||
                            pageSize < 1)
                        {
                            return(ExitWithError($"{nameof(config.PageSizeInKB)} argument value ({pageSize}) is invalid", cmd));
                        }
                        config.PageSizeInKB = pageSize;
                    }

                    if (initialContextSizeInMbArg.HasValue())
                    {
                        if (int.TryParse(initialContextSizeInMbArg.Value(), out var contextSize) == false ||
                            contextSize < 1)
                        {
                            return(ExitWithError($"{nameof(config.InitialContextSizeInMB)} argument value ({contextSize}) is invalid", cmd));
                        }
                        config.InitialContextSizeInMB = contextSize;
                    }

                    if (initialContextLongLivedSizeInKbArg.HasValue())
                    {
                        if (int.TryParse(initialContextLongLivedSizeInKbArg.Value(), out var longLivedContextSize) == false ||
                            longLivedContextSize < 1)
                        {
                            return(ExitWithError($"{nameof(config.InitialContextLongLivedSizeInKB)} argument value ({longLivedContextSize}) is invalid", cmd));
                        }
                        config.InitialContextLongLivedSizeInKB = longLivedContextSize;
                    }

                    if (progressIntervalInSecArg.HasValue())
                    {
                        if (int.TryParse(progressIntervalInSecArg.Value(), out var refreshRate) == false ||
                            refreshRate < 1)
                        {
                            return(ExitWithError($"{nameof(config.ProgressIntervalInSec)} argument value ({refreshRate}) is invalid", cmd));
                        }
                        config.ProgressIntervalInSec = refreshRate;
                    }

                    if (disableCopyOnWriteModeArg.HasValue())
                    {
                        var value = disableCopyOnWriteModeArg.Value();
                        if (bool.TryParse(value, out var disableCopyOnWriteMode) == false)
                        {
                            return(ExitWithError($"{nameof(config.DisableCopyOnWriteMode)} argument value ({value}) is invalid", cmd));
                        }
                        config.DisableCopyOnWriteMode = disableCopyOnWriteMode;
                    }

                    if (ignoreInvalidJournalErrorsArg.HasValue())
                    {
                        var value = ignoreInvalidJournalErrorsArg.Value();
                        if (bool.TryParse(value, out var ignoreInvalidJournalErrors) == false)
                        {
                            return(ExitWithError($"{nameof(config.IgnoreInvalidJournalErrors)} argument value ({value}) is invalid", cmd));
                        }

                        config.IgnoreInvalidJournalErrors = ignoreInvalidJournalErrors;
                    }

                    if (ignoreDataIntegrityErrorsOfAlreadySyncedTransactionsArg.HasValue())
                    {
                        var value = ignoreDataIntegrityErrorsOfAlreadySyncedTransactionsArg.Value();
                        if (bool.TryParse(value, out var ignoreDataIntegrityErrorsOfAlreadySyncedTransactions) == false)
                        {
                            return(ExitWithError($"{nameof(config.IgnoreDataIntegrityErrorsOfAlreadySyncedTransactions)} argument value ({value}) is invalid", cmd));
                        }

                        config.IgnoreDataIntegrityErrorsOfAlreadySyncedTransactions = ignoreDataIntegrityErrorsOfAlreadySyncedTransactions;
                    }

                    if (loggingModeArg.HasValue())
                    {
                        var value = loggingModeArg.Value();
                        if (Enum.TryParse(value, out LogMode mode) == false)
                        {
                            return(ExitWithError($"{nameof(config.LoggingMode)} argument value ({value}) is invalid", cmd));
                        }
                        config.LoggingMode = mode;
                    }

                    if (masterKey.HasValue())
                    {
                        var value = masterKey.Value();
                        byte[] key;
                        try
                        {
                            key = Convert.FromBase64String(value);
                        }
                        catch
                        {
                            return(ExitWithError($"{nameof(config.MasterKey)} argument value ({value}) is not a valid base64 string", cmd));
                        }

                        config.MasterKey = key;
                    }
                    using (var recovery = new Recovery(config))
                    {
                        var cts = new CancellationTokenSource();
                        Console.WriteLine("Press 'q' to quit the recovery process");
                        var cancellationTask = Task.Factory.StartNew(() =>
                        {
                            while (Console.Read() != 'q')
                            {
                            }

                            cts.Cancel();
                            //The reason i do an exit here is because if we are in the middle of journal recovery
                            //we can't cancel it and it may take a long time.
                            //That said i'm still going to give it a while to do a proper exit
                            Task.Delay(5000).ContinueWith(_ => { Environment.Exit(1); });
                        }, cts.Token);

                        try
                        {
                            recovery.Execute(Console.Out, cts.Token);
                        }
                        catch (Exception e)
                        {
                            if (e.Data["ReturnCode"] is int returnCode)
                            {
                                return(returnCode);
                            }

                            return(ExitWithError(e.Message, _app));
                        }

                        cts.Cancel();
                    }

                    return(0);
                });
            });
        }
Exemple #4
0
        public static VoronRecoveryArgsProcessStatus ProcessArgs(string[] args, out VoronRecoveryConfiguration config)
        {
            config = null;
            if (args.Length < 2)
            {
                return(VoronRecoveryArgsProcessStatus.NotEnoughArguments);
            }
            if (!Directory.Exists(args[0]) || !File.Exists(Path.Combine(args[0], DatafileName)))
            {
                return(VoronRecoveryArgsProcessStatus.MissingDataFile);
            }
            try
            {
                if (!Directory.Exists(args[1]))
                {
                    Directory.CreateDirectory(args[1]);
                }
                var testFile = Path.Combine(args[1], RecoveryFileName);
                File.WriteAllText(testFile, "I have write permission!");
                File.Delete(testFile);
            }
            catch
            {
                return(VoronRecoveryArgsProcessStatus.CantWriteToOutputDirectory);
            }
            config = new VoronRecoveryConfiguration()
            {
                DataFileDirectory = args[0],
                PathToDataFile    = Path.Combine(args[0], DatafileName),
                OutputFileName    = Path.Combine(args[1], RecoveryFileName)
            };
            if (args.Length > 2 && args.Length % 2 != 0)
            {
                return(VoronRecoveryArgsProcessStatus.WrongNumberOfArgs);
            }
            for (var i = 2; i < args.Length; i += 2)
            {
                switch (args[i])
                {
                case "-OutputFileName":
                    config.OutputFileName = Path.Combine(args[1], args[i + 1]);
                    break;

                case "-PageSizeInKB":
                    int pageSize;
                    if (int.TryParse(args[i + 1], out pageSize) == false || pageSize < 1)
                    {
                        return(VoronRecoveryArgsProcessStatus.InvalidPageSize);
                    }
                    config.PageSizeInKb = pageSize;
                    break;

                case "-InitialContextSizeInMB":
                    int contextSize;
                    if (int.TryParse(args[i + 1], out contextSize) == false || contextSize < 1)
                    {
                        return(VoronRecoveryArgsProcessStatus.InvalidContextSize);
                    }
                    config.InitialContextSizeInMB = contextSize;
                    break;

                case "-InitialContextLongLivedSizeInKB":
                    int longLivedContextSize;
                    if (int.TryParse(args[i + 1], out longLivedContextSize) == false || longLivedContextSize < 1)
                    {
                        return(VoronRecoveryArgsProcessStatus.InvalidLongLivedContextSize);
                    }
                    config.InitialContextLongLivedSizeInKB = longLivedContextSize;
                    break;

                case "-RefreshRateInSeconds":
                    int refreshRate;
                    if (int.TryParse(args[i + 1], out refreshRate) == false || refreshRate < 1)
                    {
                        return(VoronRecoveryArgsProcessStatus.InvalidRefreshRate);
                    }
                    config.ProgressIntervalInSec = refreshRate;
                    break;

                case "-DisableCopyOnWriteMode":
                    bool disableCopyOnWriteMode;
                    if (bool.TryParse(args[i + 1], out disableCopyOnWriteMode) == false)
                    {
                        return(VoronRecoveryArgsProcessStatus.BadArg);
                    }
                    config.DisableCopyOnWriteMode = disableCopyOnWriteMode;
                    break;

                case "-LogMode":
                    LogMode mode;
                    if (Enum.TryParse(args[i + 1], out mode) == false)
                    {
                        return(VoronRecoveryArgsProcessStatus.BadArg);
                    }
                    config.LoggingMode = mode;
                    break;

                default:
                    return(VoronRecoveryArgsProcessStatus.BadArg);
                }
            }
            return(VoronRecoveryArgsProcessStatus.Success);
        }
Exemple #5
0
        public static void Main(string[] args)
        {
            VoronRecoveryConfiguration config;

            switch (VoronRecoveryConfiguration.ProcessArgs(args, out config))
            {
            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.Success:
                break;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.NotEnoughArguments:
                PrintUsage();
                return;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.MissingDataFile:
                PrintUsage();
                return;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.CantWriteToOutputDirectory:
                PrintUsage();
                return;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.WrongNumberOfArgs:
                Console.WriteLine($"The given amount of args doesn't dived by 2 like expected.{Environment.NewLine}");
                goto default;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.InvalidPageSize:
                Console.WriteLine($"Page size should be a positive number.{Environment.NewLine}");
                goto default;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.InvalidTableValueCount:
                Console.WriteLine($"Table value count should be a positive number.{Environment.NewLine}");
                goto default;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.InvalidContextSize:
                Console.WriteLine($"Context size should be a positive number.{Environment.NewLine}");
                goto default;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.InvalidLongLivedContextSize:
                Console.WriteLine($"Long lived size should be a positive number.{Environment.NewLine}");
                goto default;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.InvalidRefreshRate:
                Console.WriteLine($"Refresh rate should be a positive number.{Environment.NewLine}");
                goto default;

            case VoronRecoveryConfiguration.VoronRecoveryArgsProcessStatus.BadArg:
                Console.WriteLine($"Unexpected argument provided.{Environment.NewLine}");
                goto default;

            default:
                PrintUsage();
                return;
            }
            var recovery = new Recovery(config);
            var cts      = new CancellationTokenSource();

            Console.WriteLine("Press 'q' to quit the recovery process");
            var cancellationTask = Task.Factory.StartNew(() =>
            {
                while (Console.Read() != 'q')
                {
                }
                cts.Cancel();
                //The reason i do an exit here is because if we are in the middle of journal recovery
                //we can't cancel it and it may take a long time.
                //That said i'm still going to give it a while to do a proper exit
                Task.Delay(5000).ContinueWith(_ =>
                {
                    Environment.Exit(1);
                });
            }, cts.Token);

            recovery.Execute(cts.Token);
            cts.Cancel();
        }