Exemplo n.º 1
0
 private IOperationResult ExportDatabaseInternal(DocumentsOperationContext context, SmugglerExporter exporter, Action <IOperationProgress> onProgress, OperationCancelToken token)
 {
     try
     {
         return(exporter.Export(context, ResponseBodyStream(), onProgress));
     }
     finally
     {
         token.Dispose();
     }
 }
Exemplo n.º 2
0
        public Task <IOperationResult> AddOperation(
            DocumentDatabase database,
            string description,
            OperationType operationType,
            Func <Action <IOperationProgress>, Task <IOperationResult> > taskFactory,
            long id,
            IOperationDetailedDescription detailedDescription = null,
            OperationCancelToken token = null)
        {
            var operationState = new OperationState
            {
                Status = OperationStatus.InProgress
            };

            var notification = new OperationStatusChange
            {
                OperationId = id,
                State       = operationState
            };

            var operationDescription = new OperationDescription
            {
                Description         = description,
                TaskType            = operationType,
                StartTime           = SystemTime.UtcNow,
                DetailedDescription = detailedDescription
            };

            var operation = new Operation
            {
                Database    = database,
                Id          = id,
                Description = operationDescription,
                Token       = token,
                State       = operationState
            };

            object locker = new object();

            Monitor.Enter(locker);
            try
            {
                operation.Task = Task.Run(() => taskFactory(ProgressNotification));
                operation.Task.ContinueWith(ContinuationFunction);
                _active.TryAdd(id, operation);

                if (token == null)
                {
                    return(operation.Task);
                }

                return(operation.Task.ContinueWith(t =>
                {
                    token.Dispose();
                    return t;
                }).Unwrap());
            }
            finally
            {
                Monitor.Exit(locker);
            }

            void ContinuationFunction(Task <IOperationResult> taskResult)
            {
                operationDescription.EndTime = SystemTime.UtcNow;
                operationState.Progress      = null;

                if (taskResult.IsCanceled)
                {
                    operationState.Result = null;
                    operationState.Status = OperationStatus.Canceled;
                }
                else if (taskResult.IsFaulted)
                {
                    var innerException = taskResult.Exception.ExtractSingleInnerException();

                    var isConflict = innerException is DocumentConflictException || innerException is ConcurrencyException;
                    var status     = isConflict ? HttpStatusCode.Conflict : HttpStatusCode.InternalServerError;

                    var shouldPersist = false;

                    switch (operationType)
                    {
                    case OperationType.DatabaseExport:
                    case OperationType.DatabaseImport:
                        shouldPersist = true;
                        break;
                    }

                    operationState.Result = new OperationExceptionResult(innerException, status, shouldPersist);
                    operationState.Status = OperationStatus.Faulted;
                }
                else
                {
                    operationState.Result = taskResult.Result;
                    operationState.Status = OperationStatus.Completed;
                }

                if (Monitor.TryEnter(locker) == false)
                {
                    // adding of operation still didn't finish, just exit
                    RaiseNotifications(notification, operation);
                    return;
                }

                try
                {
                    if (_active.TryGetValue(id, out Operation completed))
                    {
                        completed.SetCompleted();
                        // add to completed items before removing from active ones to ensure an operation status is accessible all the time
                        _completed.TryAdd(id, completed);
                        _active.TryRemove(id, out completed);
                    }

                    RaiseNotifications(notification, operation);
                }
                finally
                {
                    Monitor.Exit(locker);
                }
            }

            void ProgressNotification(IOperationProgress progress)
            {
                notification.State.Progress = progress;
                RaiseNotifications(notification, operation);
            }
        }
Exemplo n.º 3
0
 protected virtual void Dispose()
 {
     _operationCancelToken.Dispose();
 }
Exemplo n.º 4
0
        public async Task <IOperationResult> Execute(Action <IOperationProgress> onProgress)
        {
            var databaseName = _restoreConfiguration.DatabaseName;
            var result       = new RestoreResult
            {
                DataDirectory = _restoreConfiguration.DataDirectory
            };

            try
            {
                if (onProgress == null)
                {
                    onProgress = _ => { }
                }
                ;

                Stopwatch       sw = null;
                RestoreSettings restoreSettings = null;
                var             firstFile       = _filesToRestore[0];
                var             lastFile        = _filesToRestore.Last();

                var extension       = Path.GetExtension(firstFile);
                var snapshotRestore = false;
                if (extension == Constants.Documents.PeriodicBackup.SnapshotExtension)
                {
                    onProgress.Invoke(result.Progress);

                    snapshotRestore = true;
                    sw = Stopwatch.StartNew();
                    // restore the snapshot
                    restoreSettings = SnapshotRestore(firstFile,
                                                      _restoreConfiguration.DataDirectory,
                                                      onProgress,
                                                      result);

                    // removing the snapshot from the list of files
                    _filesToRestore.RemoveAt(0);
                }
                else
                {
                    result.SnapshotRestore.Skipped   = true;
                    result.SnapshotRestore.Processed = true;

                    onProgress.Invoke(result.Progress);
                }

                if (restoreSettings == null)
                {
                    restoreSettings = new RestoreSettings
                    {
                        DatabaseRecord = new DatabaseRecord(databaseName)
                        {
                            // we only have a smuggler restore
                            // use the encryption key to encrypt the database
                            Encrypted = _hasEncryptionKey
                        }
                    };

                    DatabaseHelper.Validate(databaseName, restoreSettings.DatabaseRecord, _serverStore.Configuration);
                }

                var databaseRecord = restoreSettings.DatabaseRecord;
                if (databaseRecord.Settings == null)
                {
                    databaseRecord.Settings = new Dictionary <string, string>();
                }

                var runInMemoryConfigurationKey = RavenConfiguration.GetKey(x => x.Core.RunInMemory);
                databaseRecord.Settings.Remove(runInMemoryConfigurationKey);
                if (_serverStore.Configuration.Core.RunInMemory)
                {
                    databaseRecord.Settings[runInMemoryConfigurationKey] = "false";
                }

                var dataDirectoryConfigurationKey = RavenConfiguration.GetKey(x => x.Core.DataDirectory);
                databaseRecord.Settings.Remove(dataDirectoryConfigurationKey); // removing because we want to restore to given location, not to serialized in backup one
                if (_restoringToDefaultDataDirectory == false)
                {
                    databaseRecord.Settings[dataDirectoryConfigurationKey] = _restoreConfiguration.DataDirectory;
                }

                if (_hasEncryptionKey)
                {
                    // save the encryption key so we'll be able to access the database
                    _serverStore.PutSecretKey(_restoreConfiguration.EncryptionKey,
                                              databaseName, overwrite: false);
                }

                var addToInitLog = new Action <string>(txt => // init log is not save in mem during RestoreBackup
                {
                    var msg = $"[RestoreBackup] {DateTime.UtcNow} :: Database '{databaseName}' : {txt}";
                    if (Logger.IsInfoEnabled)
                    {
                        Logger.Info(msg);
                    }
                });

                using (var database = new DocumentDatabase(databaseName,
                                                           new RavenConfiguration(databaseName, ResourceType.Database)
                {
                    Core =
                    {
                        DataDirectory = new PathSetting(_restoreConfiguration.DataDirectory),
                        RunInMemory   = false
                    }
                }, _serverStore, addToInitLog))
                {
                    // smuggler needs an existing document database to operate
                    var options = InitializeOptions.SkipLoadingDatabaseRecord;
                    if (snapshotRestore)
                    {
                        options |= InitializeOptions.GenerateNewDatabaseId;
                    }

                    database.Initialize(options);

                    if (snapshotRestore)
                    {
                        result.SnapshotRestore.Processed = true;

                        var summary = database.GetDatabaseSummary();
                        result.Documents.ReadCount             += summary.DocumentsCount;
                        result.Documents.Attachments.ReadCount += summary.AttachmentsCount;
                        result.Counters.ReadCount          += summary.CountersCount;
                        result.RevisionDocuments.ReadCount += summary.RevisionsCount;
                        result.Conflicts.ReadCount         += summary.ConflictsCount;
                        result.Indexes.ReadCount           += databaseRecord.GetIndexesCount();
                        result.AddInfo($"Successfully restored {result.SnapshotRestore.ReadCount} " +
                                       $"files during snapshot restore, took: {sw.ElapsedMilliseconds:#,#;;0}ms");
                        onProgress.Invoke(result.Progress);
                    }
                    using (database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                    {
                        SmugglerRestore(_restoreConfiguration.BackupLocation, database, context, databaseRecord, onProgress, result);

                        result.DatabaseRecord.Processed    = true;
                        result.Documents.Processed         = true;
                        result.RevisionDocuments.Processed = true;
                        result.Conflicts.Processed         = true;
                        result.Indexes.Processed           = true;
                        result.Counters.Processed          = true;
                        onProgress.Invoke(result.Progress);

                        databaseRecord.Topology = new DatabaseTopology();
                        // restoring to the current node only
                        databaseRecord.Topology.Members.Add(_nodeTag);
                        databaseRecord.Disabled = true; // we are currently restoring, shouldn't try to access it
                        _serverStore.EnsureNotPassive();

                        DisableOngoingTasksIfNeeded(databaseRecord);

                        var(index, _) = await _serverStore.WriteDatabaseRecordAsync(databaseName, databaseRecord, null, restoreSettings.DatabaseValues, isRestore : true);

                        await _serverStore.Cluster.WaitForIndexNotification(index);

                        // restore identities & cmpXchg values
                        RestoreFromLastFile(onProgress, database, lastFile, context, result);
                    }
                }

                // after the db for restore is done, we can safely set the db status to active
                databaseRecord          = _serverStore.LoadDatabaseRecord(databaseName, out _);
                databaseRecord.Disabled = false;

                var(updateIndex, _) = await _serverStore.WriteDatabaseRecordAsync(databaseName, databaseRecord, null);

                await _serverStore.Cluster.WaitForIndexNotification(updateIndex);

                return(result);
            }
            catch (Exception e)
            {
                if (Logger.IsOperationsEnabled)
                {
                    Logger.Operations("Failed to restore database", e);
                }

                var alert = AlertRaised.Create(
                    _restoreConfiguration.DatabaseName,
                    "Failed to restore database",
                    $"Could not restore database named {_restoreConfiguration.DatabaseName}",
                    AlertType.RestoreError,
                    NotificationSeverity.Error,
                    details: new ExceptionDetails(e));
                _serverStore.NotificationCenter.Add(alert);

                if (_serverStore.LoadDatabaseRecord(_restoreConfiguration.DatabaseName, out var _) == null)
                {
                    // delete any files that we already created during the restore
                    IOExtensions.DeleteDirectory(_restoreConfiguration.DataDirectory);
                }
                else
                {
                    var deleteResult = await _serverStore.DeleteDatabaseAsync(_restoreConfiguration.DatabaseName, true, new[] { _serverStore.NodeTag });

                    await _serverStore.Cluster.WaitForIndexNotification(deleteResult.Index);
                }

                result.AddError($"Error occurred during restore of database {databaseName}. Exception: {e.Message}");
                onProgress.Invoke(result.Progress);
                throw;
            }
            finally
            {
                _operationCancelToken.Dispose();
            }
        }
Exemplo n.º 5
0
        public async Task <IOperationResult> Execute(Action <IOperationProgress> onProgress)
        {
            try
            {
                if (onProgress == null)
                {
                    onProgress = _ => { }
                }
                ;

                var result = new RestoreResult
                {
                    DataDirectory = _restoreConfiguration.DataDirectory
                };

                Stopwatch       sw = null;
                RestoreSettings restoreSettings = null;
                var             firstFile       = _filesToRestore[0];
                var             extension       = Path.GetExtension(firstFile);
                var             snapshotRestore = false;
                if (extension == Constants.Documents.PeriodicBackup.SnapshotExtension)
                {
                    onProgress.Invoke(result.Progress);

                    snapshotRestore = true;
                    sw = Stopwatch.StartNew();
                    // restore the snapshot
                    restoreSettings = SnapshotRestore(firstFile,
                                                      _restoreConfiguration.DataDirectory,
                                                      onProgress,
                                                      result);

                    // removing the snapshot from the list of files
                    _filesToRestore.RemoveAt(0);
                }
                else
                {
                    result.SnapshotRestore.Skipped   = true;
                    result.SnapshotRestore.Processed = true;

                    onProgress.Invoke(result.Progress);
                }

                var databaseName = _restoreConfiguration.DatabaseName;
                if (restoreSettings == null)
                {
                    restoreSettings = new RestoreSettings
                    {
                        DatabaseRecord = new DatabaseRecord(databaseName)
                        {
                            // we only have a smuggler restore
                            // use the encryption key to encrypt the database
                            Encrypted = _hasEncryptionKey
                        }
                    };

                    DatabaseHelper.Validate(databaseName, restoreSettings.DatabaseRecord);
                }

                var databaseRecord = restoreSettings.DatabaseRecord;
                if (databaseRecord.Settings == null)
                {
                    databaseRecord.Settings = new Dictionary <string, string>();
                }

                databaseRecord.Settings[RavenConfiguration.GetKey(x => x.Core.RunInMemory)]   = "false";
                databaseRecord.Settings[RavenConfiguration.GetKey(x => x.Core.DataDirectory)] = _restoreConfiguration.DataDirectory;

                if (_hasEncryptionKey)
                {
                    // save the encryption key so we'll be able to access the database
                    _serverStore.PutSecretKey(_restoreConfiguration.EncryptionKey,
                                              databaseName, overwrite: false);
                }

                using (var database = new DocumentDatabase(databaseName,
                                                           new RavenConfiguration(databaseName, ResourceType.Database)
                {
                    Core =
                    {
                        DataDirectory = new PathSetting(_restoreConfiguration.DataDirectory),
                        RunInMemory   = false
                    }
                }, _serverStore))
                {
                    // smuggler needs an existing document database to operate
                    var options = InitializeOptions.SkipLoadingDatabaseRecord;
                    if (snapshotRestore)
                    {
                        options |= InitializeOptions.GenerateNewDatabaseId;
                    }

                    database.Initialize(options);

                    if (snapshotRestore)
                    {
                        result.SnapshotRestore.Processed = true;

                        var summary = database.GetDatabaseSummary();
                        result.Documents.ReadCount             += summary.DocumentsCount;
                        result.Documents.Attachments.ReadCount += summary.AttachmentsCount;
                        result.RevisionDocuments.ReadCount     += summary.RevisionsCount;
                        result.Indexes.ReadCount    += databaseRecord.GetIndexesCount();
                        result.Identities.ReadCount += restoreSettings.Identities.Count;
                        result.AddInfo($"Successfully restored {result.SnapshotRestore.ReadCount} " +
                                       $"files during snapshot restore, took: {sw.ElapsedMilliseconds:#,#;;0}ms");
                        onProgress.Invoke(result.Progress);
                    }

                    SmugglerRestore(_restoreConfiguration.BackupLocation, database, databaseRecord, restoreSettings, onProgress, result);
                }

                result.Documents.Processed         = true;
                result.RevisionDocuments.Processed = true;
                result.Indexes.Processed           = true;
                result.Identities.Processed        = true;
                onProgress.Invoke(result.Progress);

                databaseRecord.Topology = new DatabaseTopology();
                // restoring to the current node only
                databaseRecord.Topology.Members.Add(_nodeTag);

                await _serverStore.WriteDatabaseRecordAsync(databaseName, databaseRecord, null, restoreSettings.DatabaseValues, isRestore : true);

                var index = await WriteIdentitiesAsync(databaseName, restoreSettings.Identities);

                await _serverStore.Cluster.WaitForIndexNotification(index);

                return(result);
            }
            catch (OperationCanceledException)
            {
                // database shutdown
                throw;
            }
            catch (Exception e)
            {
                if (Logger.IsOperationsEnabled)
                {
                    Logger.Operations("Failed to restore database", e);
                }

                var alert = AlertRaised.Create(
                    "Failed to restore database",
                    $"Could not restore database named {_restoreConfiguration.DatabaseName}",
                    AlertType.RestoreError,
                    NotificationSeverity.Error,
                    details: new ExceptionDetails(e));
                _serverStore.NotificationCenter.Add(alert);

                // delete any files that we already created during the restore
                IOExtensions.DeleteDirectory(_restoreConfiguration.DataDirectory);
                throw;
            }
            finally
            {
                _operationCancelToken.Dispose();
            }
        }