Ejemplo n.º 1
0
        public async Task <HttpResponseMessage> Restore()
        {
            if (EnsureSystemDatabase() == false)
            {
                return(GetMessageWithString("Restore is only possible from the system database", HttpStatusCode.BadRequest));
            }

            var restoreStatus = new RestoreStatus {
                State = RestoreStatusState.Running, Messages = new List <string>()
            };

            var restoreRequest = await ReadJsonObjectAsync <DatabaseRestoreRequest>();

            DatabaseDocument databaseDocument = null;

            var databaseDocumentPath = MaintenanceActions.FindDatabaseDocument(restoreRequest.BackupLocation);

            if (File.Exists(databaseDocumentPath))
            {
                var databaseDocumentText = File.ReadAllText(databaseDocumentPath);
                databaseDocument = RavenJObject.Parse(databaseDocumentText).JsonDeserialization <DatabaseDocument>();
            }

            var databaseName = !string.IsNullOrWhiteSpace(restoreRequest.DatabaseName) ? restoreRequest.DatabaseName
                                                                   : databaseDocument == null ? null : databaseDocument.Id;

            if (string.IsNullOrWhiteSpace(databaseName))
            {
                var errorMessage = (databaseDocument == null || String.IsNullOrWhiteSpace(databaseDocument.Id))
                                                                ? "Database.Document file is invalid - database name was not found and not supplied in the request (Id property is missing or null). This is probably a bug - should never happen."
                                                                : "A database name must be supplied if the restore location does not contain a valid Database.Document file";

                restoreStatus.Messages.Add(errorMessage);
                DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null, RavenJObject.FromObject(new { restoreStatus }), new RavenJObject(), null);

                return(GetMessageWithString(errorMessage, HttpStatusCode.BadRequest));
            }

            if (databaseName == Constants.SystemDatabase)
            {
                return(GetMessageWithString("Cannot do an online restore for the <system> database", HttpStatusCode.BadRequest));
            }

            var existingDatabase = Database.Documents.GetDocumentMetadata("Raven/Databases/" + databaseName, null);

            if (existingDatabase != null)
            {
                return(GetMessageWithString("Cannot do an online restore for an existing database, delete the database " + databaseName + " and restore again.", HttpStatusCode.BadRequest));
            }

            var ravenConfiguration = new RavenConfiguration
            {
                DatabaseName     = databaseName,
                IsTenantDatabase = true
            };

            if (databaseDocument != null)
            {
                foreach (var setting in databaseDocument.Settings)
                {
                    ravenConfiguration.Settings[setting.Key] = setting.Value;
                }
            }

            if (File.Exists(Path.Combine(restoreRequest.BackupLocation, BackupMethods.Filename)))
            {
                ravenConfiguration.DefaultStorageTypeName = typeof(Raven.Storage.Voron.TransactionalStorage).AssemblyQualifiedName;
            }
            else if (Directory.Exists(Path.Combine(restoreRequest.BackupLocation, "new")))
            {
                ravenConfiguration.DefaultStorageTypeName = typeof(Raven.Storage.Esent.TransactionalStorage).AssemblyQualifiedName;
            }

            ravenConfiguration.CustomizeValuesForDatabaseTenant(databaseName);
            ravenConfiguration.Initialize();

            string documentDataDir;

            ravenConfiguration.DataDirectory = ResolveTenantDataDirectory(restoreRequest.DatabaseLocation, databaseName, out documentDataDir);
            restoreRequest.DatabaseLocation  = ravenConfiguration.DataDirectory;

            string anotherRestoreResourceName;

            if (IsAnotherRestoreInProgress(out anotherRestoreResourceName))
            {
                if (restoreRequest.RestoreStartTimeout.HasValue)
                {
                    try
                    {
                        using (var cts = new CancellationTokenSource())
                        {
                            cts.CancelAfter(TimeSpan.FromSeconds(restoreRequest.RestoreStartTimeout.Value));
                            var token = cts.Token;
                            do
                            {
                                await Task.Delay(500, token);
                            }while (IsAnotherRestoreInProgress(out anotherRestoreResourceName));
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        return(GetMessageWithString(string.Format("Another restore is still in progress (resource name = {0}). Waited {1} seconds for other restore to complete.", anotherRestoreResourceName, restoreRequest.RestoreStartTimeout.Value), HttpStatusCode.ServiceUnavailable));
                    }
                }
                else
                {
                    return(GetMessageWithString(string.Format("Another restore is in progress (resource name = {0})", anotherRestoreResourceName), HttpStatusCode.ServiceUnavailable));
                }
            }
            Database.Documents.Put(RestoreInProgress.RavenRestoreInProgressDocumentKey, null, RavenJObject.FromObject(new RestoreInProgress
            {
                Resource = databaseName
            }), new RavenJObject(), null);

            DatabasesLandlord.SystemDatabase.Documents.Delete(RestoreStatus.RavenRestoreStatusDocumentKey, null, null);

            bool defrag;

            if (bool.TryParse(GetQueryStringValue("defrag"), out defrag))
            {
                restoreRequest.Defrag = defrag;
            }

            var task = Task.Factory.StartNew(() =>
            {
                try
                {
                    MaintenanceActions.Restore(ravenConfiguration, restoreRequest,
                                               msg =>
                    {
                        restoreStatus.Messages.Add(msg);
                        DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null,
                                                                       RavenJObject.FromObject(restoreStatus), new RavenJObject(), null);
                    });

                    if (databaseDocument == null)
                    {
                        return;
                    }

                    databaseDocument.Settings[Constants.RavenDataDir] = documentDataDir;
                    if (restoreRequest.IndexesLocation != null)
                    {
                        databaseDocument.Settings[Constants.RavenIndexPath] = restoreRequest.IndexesLocation;
                    }
                    if (restoreRequest.JournalsLocation != null)
                    {
                        databaseDocument.Settings[Constants.RavenTxJournalPath] = restoreRequest.JournalsLocation;
                    }

                    bool replicationBundleRemoved = false;
                    if (restoreRequest.DisableReplicationDestinations)
                    {
                        replicationBundleRemoved = TryRemoveReplicationBundle(databaseDocument);
                    }

                    databaseDocument.Id = databaseName;
                    DatabasesLandlord.Protect(databaseDocument);
                    DatabasesLandlord
                    .SystemDatabase
                    .Documents
                    .Put("Raven/Databases/" + databaseName, null, RavenJObject.FromObject(databaseDocument), new RavenJObject(), null);

                    restoreStatus.Messages.Add("The new database was created");
                    restoreStatus.State = RestoreStatusState.Completed;
                    DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null,
                                                                   RavenJObject.FromObject(restoreStatus), new RavenJObject(), null);

                    if (restoreRequest.GenerateNewDatabaseId)
                    {
                        GenerateNewDatabaseId(databaseName);
                    }

                    if (replicationBundleRemoved)
                    {
                        AddReplicationBundleAndDisableReplicationDestinations(databaseName);
                    }
                }
                catch (Exception e)
                {
                    restoreStatus.State = RestoreStatusState.Faulted;
                    restoreStatus.Messages.Add("Unable to restore database " + e.Message);
                    DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null,
                                                                   RavenJObject.FromObject(restoreStatus), new RavenJObject(), null);
                    throw;
                }
                finally
                {
                    Database.Documents.Delete(RestoreInProgress.RavenRestoreInProgressDocumentKey, null, null);
                }
            }, TaskCreationOptions.LongRunning);

            long id;

            Database.Tasks.AddTask(task, new TaskBasedOperationState(task), new TaskActions.PendingTaskDescription
            {
                StartTime = SystemTime.UtcNow,
                TaskType  = TaskActions.PendingTaskType.RestoreDatabase,
                Payload   = "Restoring database " + databaseName + " from " + restoreRequest.BackupLocation
            }, out id);


            return(GetMessageWithObject(new
            {
                OperationId = id
            }));
        }