예제 #1
0
        private static void CreateUploadTaskIfNeeded <S, T>(
            S settings,
            List <Task> tasks,
            string backupPath,
            bool isFullBackup,
            Func <S, FileStream, UploadProgress, Task> uploadToServer,
            ref T uploadStatus,
            string backupDestination)
            where S : BackupSettings
            where T : CloudUploadStatus
        {
            if (PeriodicBackupConfiguration.CanBackupUsing(settings) == false)
            {
                return;
            }

            if (uploadStatus == null)
            {
                uploadStatus = (T)Activator.CreateInstance(typeof(T));
            }

            var localUploadStatus = uploadStatus;

            tasks.Add(Task.Run(async() =>
            {
                using (localUploadStatus.UpdateStats(isFullBackup))
                    using (var fileStream = File.OpenRead(backupPath))
                    {
                        var uploadProgress = localUploadStatus.UploadProgress;
                        uploadProgress.ChangeState(UploadState.PendingUpload);
                        uploadProgress.SetTotal(fileStream.Length);

                        try
                        {
                            await uploadToServer(settings, fileStream, uploadProgress);
                        }
                        catch (OperationCanceledException e)
                        {
                            // shutting down
                            localUploadStatus.Exception = e;
                        }
                        catch (Exception e)
                        {
                            localUploadStatus.Exception = e;
                            throw new InvalidOperationException($"Failed to backup to {backupDestination}", e);
                        }
                        finally
                        {
                            uploadProgress.ChangeState(UploadState.Done);
                        }
                    }
            }));
        }
예제 #2
0
        private void CreateUploadTaskIfNeeded <S, T>(S settings, Action <S, FileStream, Progress> uploadToServer, T uploadStatus, string targetName)
            where S : BackupSettings
            where T : CloudUploadStatus
        {
            if (PeriodicBackupConfiguration.CanBackupUsing(settings) == false)
            {
                return;
            }

            Debug.Assert(uploadStatus != null);

            var localUploadStatus = uploadStatus;
            var thread            = PoolOfThreads.GlobalRavenThreadPool.LongRunning(_ =>
            {
                try
                {
                    Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
                    NativeMemory.EnsureRegistered();

                    using (localUploadStatus.UpdateStats(_isFullBackup))
                        using (var fileStream = File.OpenRead(_settings.BackupPath))
                        {
                            var uploadProgress = localUploadStatus.UploadProgress;
                            try
                            {
                                localUploadStatus.Skipped = false;
                                uploadProgress.ChangeState(UploadState.PendingUpload);
                                uploadProgress.SetTotal(fileStream.Length);

                                AddInfo($"Starting the upload of backup file to {targetName}.");

                                var bytesPutsPerSec = new MeterMetric();

                                long lastUploadedInBytes = 0;
                                var totalToUpload        = new Size(uploadProgress.TotalInBytes, SizeUnit.Bytes).ToString();
                                var sw       = Stopwatch.StartNew();
                                var progress = new Progress(uploadProgress)
                                {
                                    OnUploadProgress = () =>
                                    {
                                        if (sw.ElapsedMilliseconds <= 1000)
                                        {
                                            return;
                                        }

                                        var totalUploadedInBytes = uploadProgress.UploadedInBytes;
                                        bytesPutsPerSec.MarkSingleThreaded(totalUploadedInBytes - lastUploadedInBytes);
                                        lastUploadedInBytes            = totalUploadedInBytes;
                                        var uploaded                   = new Size(totalUploadedInBytes, SizeUnit.Bytes);
                                        uploadProgress.BytesPutsPerSec = bytesPutsPerSec.MeanRate;
                                        AddInfo($"Uploaded: {uploaded} / {totalToUpload}");
                                        sw.Restart();
                                    }
                                };

                                uploadToServer(settings, fileStream, progress);

                                AddInfo($"Total uploaded: {totalToUpload}, took: {MsToHumanReadableString(uploadProgress.UploadTimeInMs)}");
                            }
                            finally
                            {
                                uploadProgress.ChangeState(UploadState.Done);
                            }
                        }
                }
                catch (Exception e)
                {
                    var extracted       = e.ExtractSingleInnerException();
                    var error           = $"Failed to upload the backup file to {targetName}.";
                    Exception exception = null;
                    if (extracted is OperationCanceledException)
                    {
                        // shutting down or HttpClient timeout
                        exception = TaskCancelToken.Token.IsCancellationRequested ? extracted : new TimeoutException(error, e);
                    }

                    localUploadStatus.Exception = (exception ?? e).ToString();
                    _exceptions.Add(exception ?? new InvalidOperationException(error, e));
                }
            }, null, $"Upload backup file of database '{_settings.DatabaseName}' to {targetName} (task: '{_settings.TaskName}')");

            _threads.Add(thread);
        }
예제 #3
0
        private void CreateUploadTaskIfNeeded <S, T>(
            S settings,
            List <Task> tasks,
            string backupPath,
            bool isFullBackup,
            Func <S, FileStream, Progress, Task> uploadToServer,
            T uploadStatus,
            Action <IOperationProgress> onProgress)
            where S : BackupSettings
            where T : CloudUploadStatus
        {
            if (PeriodicBackupConfiguration.CanBackupUsing(settings) == false)
            {
                return;
            }

            Debug.Assert(uploadStatus != null);

            var localUploadStatus = uploadStatus;

            tasks.Add(Task.Run(async() =>
            {
                using (localUploadStatus.UpdateStats(isFullBackup))
                    using (var fileStream = File.OpenRead(backupPath))
                    {
                        var uploadProgress        = localUploadStatus.UploadProgress;
                        localUploadStatus.Skipped = false;
                        uploadProgress.ChangeState(UploadState.PendingUpload);
                        uploadProgress.SetTotal(fileStream.Length);

                        AddInfo($"Starting {uploadStatus.GetType().AssemblyQualifiedName}", onProgress);

                        try
                        {
                            var bytesPutsPerSec = new MeterMetric();

                            long lastUploadedInBytes = 0;
                            var totalToUpload        = new Sparrow.Size(uploadProgress.TotalInBytes, SizeUnit.Bytes).ToString();
                            var sw       = Stopwatch.StartNew();
                            var progress = new Progress(uploadProgress)
                            {
                                OnUploadProgress = () =>
                                {
                                    if (sw.ElapsedMilliseconds <= 1000)
                                    {
                                        return;
                                    }

                                    var totalUploadedInBytes = uploadProgress.UploadedInBytes;
                                    bytesPutsPerSec.MarkSingleThreaded(totalUploadedInBytes - lastUploadedInBytes);
                                    lastUploadedInBytes            = totalUploadedInBytes;
                                    var uploaded                   = new Sparrow.Size(totalUploadedInBytes, SizeUnit.Bytes);
                                    uploadProgress.BytesPutsPerSec = bytesPutsPerSec.MeanRate;
                                    AddInfo($"Uploaded: {uploaded} / {totalToUpload}", onProgress);
                                    sw.Restart();
                                }
                            };

                            await uploadToServer(settings, fileStream, progress);

                            AddInfo($"Total uploaded: {totalToUpload}, " +
                                    $"took: {MsToHumanReadableString(uploadProgress.UploadTimeInMs)}", onProgress);
                        }
                        catch (OperationCanceledException e)
                        {
                            // shutting down
                            localUploadStatus.Exception = e.ToString();
                            throw;
                        }
                        catch (Exception e)
                        {
                            localUploadStatus.Exception = e.ToString();
                            throw new InvalidOperationException($"Failed to backup to {uploadStatus.GetType().FullName}", e);
                        }
                        finally
                        {
                            uploadProgress.ChangeState(UploadState.Done);
                        }
                    }
            }));
        }
예제 #4
0
        private async Task RunPeriodicBackup(
            PeriodicBackupConfiguration configuration,
            PeriodicBackupStatus status,
            bool isFullBackup)
        {
            var backupStarted = SystemTime.UtcNow;
            var totalSw       = Stopwatch.StartNew();

            status.BackupType = configuration.BackupType;

            try
            {
                using (_database.DocumentsStorage.ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                    using (var tx = context.OpenReadTransaction())
                    {
                        var backupToLocalFolder = PeriodicBackupConfiguration.CanBackupUsing(configuration.LocalSettings);
                        var now = SystemTime.UtcNow.ToString(DateTimeFormat, CultureInfo.InvariantCulture);

                        if (status.LocalBackup == null)
                        {
                            status.LocalBackup = new LocalBackup();
                        }

                        PathSetting backupDirectory;

                        string folderName;
                        // check if we need to do a new full backup
                        if (isFullBackup ||
                            status.LastFullBackup == null ||                 // no full backup was previously performed
                            status.NodeTag != _serverStore.NodeTag ||        // last backup was performed by a different node
                            status.BackupType != configuration.BackupType || // backup type has changed
                            status.LastEtag == null ||                       // last document etag wasn't updated
                            backupToLocalFolder && DirectoryContainsFullBackupOrSnapshot(status.LocalBackup.BackupDirectory, configuration.BackupType) == false)
                        // the local folder has a missing full backup
                        {
                            isFullBackup = true;

                            folderName = $"{now}.ravendb-{_database.Name}-{_serverStore.NodeTag}-{configuration.BackupType.ToString().ToLower()}";

                            backupDirectory = backupToLocalFolder ?
                                              new PathSetting(configuration.LocalSettings.FolderPath).Combine(folderName) :
                                              _tempBackupPath;

                            if (Directory.Exists(backupDirectory.FullPath) == false)
                            {
                                Directory.CreateDirectory(backupDirectory.FullPath);
                            }

                            status.LocalBackup.TempFolderUsed  = backupToLocalFolder == false;
                            status.LocalBackup.BackupDirectory = backupToLocalFolder ? backupDirectory.FullPath : null;
                        }
                        else
                        {
                            backupDirectory = backupToLocalFolder ? new PathSetting(status.LocalBackup.BackupDirectory) : _tempBackupPath;
                            folderName      = status.FolderName;
                        }

                        if (_logger.IsInfoEnabled)
                        {
                            var fullBackupText = "a " + (configuration.BackupType == BackupType.Backup ? "full backup" : "snapshot");
                            _logger.Info($"Creating {(isFullBackup ? fullBackupText : "an incremental backup")}");
                        }

                        if (isFullBackup == false)
                        {
                            // no-op if nothing has changed
                            var currentLastEtag = DocumentsStorage.ReadLastEtag(tx.InnerTransaction);
                            if (currentLastEtag == status.LastEtag)
                            {
                                if (_logger.IsInfoEnabled)
                                {
                                    _logger.Info("Skipping incremental backup because " +
                                                 $"last etag ({currentLastEtag}) hasn't changed since last backup");
                                }

                                status.DurationInMs          = totalSw.ElapsedMilliseconds;
                                status.LastIncrementalBackup = backupStarted;

                                return;
                            }
                        }

                        var startDocumentEtag = isFullBackup == false ? status.LastEtag : null;
                        var fileName          = GetFileName(isFullBackup, backupDirectory.FullPath, now, configuration.BackupType, out string backupFilePath);
                        var lastEtag          = CreateLocalBackupOrSnapshot(configuration,
                                                                            isFullBackup, status, backupFilePath, startDocumentEtag, context, tx);

                        try
                        {
                            await UploadToServer(configuration, status, backupFilePath, folderName, fileName, isFullBackup);
                        }
                        finally
                        {
                            // if user did not specify local folder we delete temporary file
                            if (backupToLocalFolder == false)
                            {
                                IOExtensions.DeleteFile(backupFilePath);
                            }
                        }

                        status.LastEtag   = lastEtag;
                        status.FolderName = folderName;
                    }

                totalSw.Stop();

                if (_logger.IsInfoEnabled)
                {
                    var fullBackupText = "a " + (configuration.BackupType == BackupType.Backup ? " full backup" : " snapshot");
                    _logger.Info($"Successfully created {(isFullBackup ? fullBackupText : "an incremental backup")} " +
                                 $"in {totalSw.ElapsedMilliseconds:#,#;;0} ms");
                }
            }
            catch (OperationCanceledException)
            {
                // shutting down, probably
            }
            catch (ObjectDisposedException)
            {
                // shutting down, probably
            }
            catch (Exception e)
            {
                const string message = "Error when performing periodic backup";

                if (_logger.IsOperationsEnabled)
                {
                    _logger.Operations(message, e);
                }

                _database.NotificationCenter.Add(AlertRaised.Create("Periodic Backup",
                                                                    message,
                                                                    AlertType.PeriodicBackup,
                                                                    NotificationSeverity.Error,
                                                                    details: new ExceptionDetails(e)));
            }
            finally
            {
                // whether we succeded or not,
                // we need to update the last backup time to avoid
                // starting a new backup right after this one
                if (isFullBackup)
                {
                    status.LastFullBackup = backupStarted;
                }
                else
                {
                    status.LastIncrementalBackup = backupStarted;
                }

                status.NodeTag      = _serverStore.NodeTag;
                status.DurationInMs = totalSw.ElapsedMilliseconds;
                status.Version++;

                // save the backup status
                await WriteStatus(status);
            }
        }
예제 #5
0
        private long CreateBackupTask(PeriodicBackup periodicBackup, bool isFullBackup)
        {
            using (periodicBackup.UpdateBackupTask())
            {
                try
                {
                    if (periodicBackup.Disposed)
                    {
                        throw new InvalidOperationException("Backup task was already disposed");
                    }

                    if (periodicBackup.RunningTask != null)
                    {
                        return(periodicBackup.RunningBackupTaskId ?? -1);
                    }

                    var backupStatus        = periodicBackup.BackupStatus = GetBackupStatus(periodicBackup.Configuration.TaskId, periodicBackup.BackupStatus);
                    var backupToLocalFolder = PeriodicBackupConfiguration.CanBackupUsing(periodicBackup.Configuration.LocalSettings);

                    // check if we need to do a new full backup
                    if (backupStatus.LastFullBackup == null ||                                // no full backup was previously performed
                        backupStatus.NodeTag != _serverStore.NodeTag ||                       // last backup was performed by a different node
                        backupStatus.BackupType != periodicBackup.Configuration.BackupType || // backup type has changed
                        backupStatus.LastEtag == null ||                                      // last document etag wasn't updated
                        backupToLocalFolder && BackupTask.DirectoryContainsBackupFiles(backupStatus.LocalBackup.BackupDirectory, IsFullBackupOrSnapshot) == false)
                    // the local folder already includes a full backup or snapshot
                    {
                        isFullBackup = true;
                    }

                    var operationId    = _database.Operations.GetNextOperationId();
                    var backupTypeText = GetBackupTypeText(isFullBackup, periodicBackup.Configuration.BackupType);

                    periodicBackup.StartTime = SystemTime.UtcNow;
                    var backupTask = new BackupTask(
                        _serverStore,
                        _database,
                        periodicBackup,
                        isFullBackup,
                        backupToLocalFolder,
                        operationId,
                        _tempBackupPath,
                        _logger,
                        _cancellationToken.Token);

                    periodicBackup.RunningBackupTaskId = operationId;
                    periodicBackup.CancelToken         = backupTask.TaskCancelToken;
                    var backupTaskName = $"{backupTypeText} backup task: '{periodicBackup.Configuration.Name}'";

                    var task = _database.Operations.AddOperation(
                        null,
                        backupTaskName,
                        Operations.Operations.OperationType.DatabaseBackup,
                        taskFactory: onProgress => Task.Run(async() =>
                    {
                        try
                        {
                            using (_database.PreventFromUnloading())
                            {
                                return(await backupTask.RunPeriodicBackup(onProgress));
                            }
                        }
                        finally
                        {
                            periodicBackup.RunningTask         = null;
                            periodicBackup.RunningBackupTaskId = null;
                            periodicBackup.CancelToken         = null;
                            periodicBackup.RunningBackupStatus = null;

                            if (periodicBackup.HasScheduledBackup() &&
                                _cancellationToken.IsCancellationRequested == false)
                            {
                                var newBackupTimer = GetTimer(periodicBackup.Configuration, periodicBackup.BackupStatus);
                                periodicBackup.UpdateTimer(newBackupTimer, discardIfDisabled: true);
                            }
                        }
                    }, backupTask.TaskCancelToken.Token),
                        id: operationId,
                        token: backupTask.TaskCancelToken);

                    periodicBackup.RunningTask = task;
                    task.ContinueWith(_ => backupTask.TaskCancelToken.Dispose());

                    return(operationId);
                }
                catch (Exception e)
                {
                    var message = $"Failed to start the backup task: '{periodicBackup.Configuration.Name}'";
                    if (_logger.IsOperationsEnabled)
                    {
                        _logger.Operations(message, e);
                    }

                    _database.NotificationCenter.Add(AlertRaised.Create(
                                                         _database.Name,
                                                         $"Periodic Backup task: '{periodicBackup.Configuration.Name}'",
                                                         message,
                                                         AlertType.PeriodicBackup,
                                                         NotificationSeverity.Error,
                                                         details: new ExceptionDetails(e)));

                    throw;
                }
            }
        }
예제 #6
0
        private async Task CanDeleteBackupsByDate(
            int backupAgeInSeconds,
            int numberOfBackupsToCreate,
            Action <PeriodicBackupConfiguration, string> modifyConfiguration,
            Func <string, Task <int> > getDirectoriesCount,
            int timeout, bool checkIncremental = false)
        {
            var minimumBackupAgeToKeep = TimeSpan.FromSeconds(backupAgeInSeconds);

            using (var store = GetDocumentStore())
            {
                var config = Backup.CreateBackupConfiguration(incrementalBackupFrequency: "30 3 L * ?", retentionPolicy: new RetentionPolicy
                {
                    MinimumBackupAgeToKeep = minimumBackupAgeToKeep
                });
                modifyConfiguration(config, store.Database);

                var backupTaskId = (await store.Maintenance.SendAsync(new UpdatePeriodicBackupOperation(config))).TaskId;

                var userId = "";
                for (var i = 0; i < numberOfBackupsToCreate; i++)
                {
                    using (var session = store.OpenAsyncSession())
                    {
                        var user = new User {
                            Name = "Grisha"
                        };
                        await session.StoreAsync(user);

                        userId = user.Id;
                        await session.SaveChangesAsync();
                    }

                    // create full backup
                    var etagForFullBackup = store.Maintenance.Send(new GetStatisticsOperation()).LastDocEtag;
                    await Backup.RunBackupAndReturnStatusAsync(Server, backupTaskId, store, isFullBackup : true, expectedEtag : etagForFullBackup, timeout : timeout);

                    using (var session = store.OpenAsyncSession())
                    {
                        var user = await session.LoadAsync <User>(userId);

                        user.Age = 33;
                        await session.SaveChangesAsync();
                    }

                    // create incremental backup
                    var etagForIncBackup = store.Maintenance.Send(new GetStatisticsOperation()).LastDocEtag;
                    Assert.NotEqual(etagForFullBackup, etagForIncBackup);
                    await Backup.RunBackupAndReturnStatusAsync(Server, backupTaskId, store, isFullBackup : false, expectedEtag : etagForIncBackup, timeout : timeout);
                }

                await Task.Delay(minimumBackupAgeToKeep + TimeSpan.FromSeconds(3));

                if (checkIncremental)
                {
                    using (var session = store.OpenAsyncSession())
                    {
                        var user = await session.LoadAsync <User>(userId);

                        user.Name = "Egor";
                        user.Age  = 322;
                        await session.SaveChangesAsync();
                    }

                    // create incremental backup with retention policy
                    var etagForIncBackup = store.Maintenance.Send(new GetStatisticsOperation()).LastDocEtag;
                    await Backup.RunBackupAndReturnStatusAsync(Server, backupTaskId, store, isFullBackup : false, expectedEtag : etagForIncBackup, timeout : timeout);
                }

                var sp1 = Stopwatch.StartNew();
                using (var session = store.OpenAsyncSession())
                {
                    await session.StoreAsync(new User { Name = "Grisha" });

                    await session.SaveChangesAsync();
                }
                sp1.Stop();
                var sp2  = Stopwatch.StartNew();
                var etag = store.Maintenance.Send(new GetStatisticsOperation()).LastDocEtag;
                sp2.Stop();
                var sp3    = Stopwatch.StartNew();
                var status = await Backup.RunBackupAndReturnStatusAsync(Server, backupTaskId, store, isFullBackup : true, expectedEtag : etag, timeout : timeout);

                sp3.Stop();

                var directoriesCount = await getDirectoriesCount(store.Database);

                var expectedNumberOfDirectories = checkIncremental ? 2 : 1;
                Assert.True(expectedNumberOfDirectories == directoriesCount,
                            $"ExpectedNumberOfDirectories: {expectedNumberOfDirectories}, ActualNumberOfDirectories: {directoriesCount}, SaveChanges() duration: {sp1.Elapsed}, GetStatisticsOperation duration: {sp2.Elapsed}, RunBackupAndReturnStatusAsync duration: {sp3.Elapsed}," +
                            $" Backup duration: {status.DurationInMs}, LocalRetentionDurationInMs: {status.LocalRetentionDurationInMs}");

                if (PeriodicBackupConfiguration.CanBackupUsing(config.LocalSettings))
                {
                    Assert.NotNull(status.LocalRetentionDurationInMs);
                }
            }
        }
예제 #7
0
        private long CreateBackupTask(PeriodicBackup periodicBackup, bool isFullBackup, DateTime startTimeInUtc)
        {
            using (periodicBackup.UpdateBackupTask())
            {
                if (periodicBackup.Disposed)
                {
                    throw new InvalidOperationException("Backup task was already disposed");
                }

                if (periodicBackup.RunningTask != null)
                {
                    return(periodicBackup.RunningBackupTaskId ?? -1);
                }

                if (_serverStore.Server.CpuCreditsBalance.BackgroundTasksAlertRaised.IsRaised())
                {
                    throw new BackupDelayException(
                              $"Failed to start Backup Task: '{periodicBackup.Configuration.Name}'. " +
                              $"The task cannot run because the CPU credits allocated to this machine are nearing exhaustion.")
                          {
                              DelayPeriod = _serverStore.Configuration.Server.CpuCreditsExhaustionBackupDelay.AsTimeSpan
                          };
                }

                if (LowMemoryNotification.Instance.LowMemoryState)
                {
                    throw new BackupDelayException(
                              $"Failed to start Backup Task: '{periodicBackup.Configuration.Name}'. " +
                              $"The task cannot run because the server is in low memory state.")
                          {
                              DelayPeriod = _serverStore.Configuration.Backup.LowMemoryBackupDelay.AsTimeSpan
                          };
                }

                if (_serverStore.ConcurrentBackupsSemaphore.Wait(0) == false)
                {
                    throw new BackupDelayException(
                              $"Failed to start Backup Task: '{periodicBackup.Configuration.Name}'. " +
                              $"The task exceeds the maximum number of concurrent backup tasks configured. " +
                              $"Current value of Backup.MaxNumberOfConcurrentBackups is: {_serverStore.Configuration.Backup.MaxNumberOfConcurrentBackups:#,#;;0}")
                          {
                              DelayPeriod = TimeSpan.FromMinutes(1)
                          };
                }

                try
                {
                    var backupStatus        = periodicBackup.BackupStatus = GetBackupStatus(periodicBackup.Configuration.TaskId, periodicBackup.BackupStatus);
                    var backupToLocalFolder = PeriodicBackupConfiguration.CanBackupUsing(periodicBackup.Configuration.LocalSettings);

                    // check if we need to do a new full backup
                    if (backupStatus.LastFullBackup == null ||                                // no full backup was previously performed
                        backupStatus.NodeTag != _serverStore.NodeTag ||                       // last backup was performed by a different node
                        backupStatus.BackupType != periodicBackup.Configuration.BackupType || // backup type has changed
                        backupStatus.LastEtag == null ||                                      // last document etag wasn't updated
                        backupToLocalFolder && BackupTask.DirectoryContainsBackupFiles(backupStatus.LocalBackup.BackupDirectory, IsFullBackupOrSnapshot) == false)
                    // the local folder already includes a full backup or snapshot
                    {
                        isFullBackup = true;
                    }

                    var operationId    = _database.Operations.GetNextOperationId();
                    var backupTypeText = GetBackupTypeText(isFullBackup, periodicBackup.Configuration.BackupType);

                    periodicBackup.StartTimeInUtc = startTimeInUtc;
                    var backupTask = new BackupTask(
                        _serverStore,
                        _database,
                        periodicBackup,
                        isFullBackup,
                        backupToLocalFolder,
                        operationId,
                        _tempBackupPath,
                        _logger,
                        _cancellationToken.Token);

                    periodicBackup.RunningBackupTaskId = operationId;
                    periodicBackup.CancelToken         = backupTask.TaskCancelToken;
                    var backupTaskName = $"{backupTypeText} backup task: '{periodicBackup.Configuration.Name}'. Database: '{_database.Name}'";

                    var task = _database.Operations.AddOperation(
                        null,
                        backupTaskName,
                        Operations.Operations.OperationType.DatabaseBackup,
                        taskFactory: onProgress => StartBackupThread(periodicBackup, backupTask, onProgress),
                        id: operationId,
                        token: backupTask.TaskCancelToken);

                    periodicBackup.RunningTask = task;
                    task.ContinueWith(_ => backupTask.TaskCancelToken.Dispose());

                    return(operationId);
                }
                catch (Exception e)
                {
                    // releasing the semaphore because we failed to start the backup task
                    _serverStore.ConcurrentBackupsSemaphore.Release();

                    var message = $"Failed to start the backup task: '{periodicBackup.Configuration.Name}'";
                    if (_logger.IsOperationsEnabled)
                    {
                        _logger.Operations(message, e);
                    }

                    _database.NotificationCenter.Add(AlertRaised.Create(
                                                         _database.Name,
                                                         $"Periodic Backup task: '{periodicBackup.Configuration.Name}'",
                                                         message,
                                                         AlertType.PeriodicBackup,
                                                         NotificationSeverity.Error,
                                                         details: new ExceptionDetails(e)));

                    throw;
                }
            }
        }
예제 #8
0
        private long CreateBackupTask(PeriodicBackup periodicBackup, bool isFullBackup)
        {
            if (periodicBackup.UpdateBackupTaskSemaphore.Wait(0) == false)
            {
                return(periodicBackup.RunningBackupTaskId ?? -1);
            }

            try
            {
                if (periodicBackup.RunningTask != null)
                {
                    return(periodicBackup.RunningBackupTaskId ?? -1);
                }

                var backupStatus        = periodicBackup.BackupStatus = GetBackupStatus(periodicBackup.Configuration.TaskId, periodicBackup.BackupStatus);
                var backupToLocalFolder = PeriodicBackupConfiguration.CanBackupUsing(periodicBackup.Configuration.LocalSettings);

                // check if we need to do a new full backup
                if (backupStatus.LastFullBackup == null ||                                // no full backup was previously performed
                    backupStatus.NodeTag != _serverStore.NodeTag ||                       // last backup was performed by a different node
                    backupStatus.BackupType != periodicBackup.Configuration.BackupType || // backup type has changed
                    backupStatus.LastEtag == null ||                                      // last document etag wasn't updated
                    backupToLocalFolder && BackupTask.DirectoryContainsBackupFiles(backupStatus.LocalBackup.BackupDirectory, IsFullBackupOrSnapshot) == false)
                // the local folder already includes a full backup or snapshot
                {
                    isFullBackup = true;
                }

                var operationId    = _database.Operations.GetNextOperationId();
                var backupTypeText = GetBackupTypeText(isFullBackup, periodicBackup.Configuration.BackupType);

                periodicBackup.StartTime = SystemTime.UtcNow;
                var backupTask = new BackupTask(
                    _serverStore,
                    _database,
                    periodicBackup,
                    isFullBackup,
                    backupToLocalFolder,
                    operationId,
                    _tempBackupPath,
                    _logger,
                    _cancellationToken.Token);

                periodicBackup.RunningBackupTaskId = operationId;
                periodicBackup.CancelToken         = backupTask.TaskCancelToken;
                var backupTaskName = $"{backupTypeText} backup task: '{periodicBackup.Configuration.Name}'";

                var task = _database.Operations.AddOperation(
                    null,
                    backupTaskName,
                    Operations.Operations.OperationType.DatabaseBackup,
                    taskFactory: onProgress => Task.Run(async() =>
                {
                    try
                    {
                        return(await backupTask.RunPeriodicBackup(onProgress));
                    }
                    finally
                    {
                        periodicBackup.RunningTask         = null;
                        periodicBackup.RunningBackupTaskId = null;
                        periodicBackup.CancelToken         = null;
                        periodicBackup.RunningBackupStatus = null;

                        if (periodicBackup.HasScheduledBackup() &&
                            _cancellationToken.IsCancellationRequested == false)
                        {
                            var newBackupTimer = GetTimer(periodicBackup.Configuration, periodicBackup.BackupStatus);
                            periodicBackup.UpdateTimer(newBackupTimer, discardIfDisabled: true);
                        }
                    }
                }, backupTask.TaskCancelToken.Token),
                    id: operationId,
                    token: backupTask.TaskCancelToken);

                periodicBackup.RunningTask = task;
                task.ContinueWith(_ => backupTask.TaskCancelToken.Dispose());

                return(operationId);
            }
            finally
            {
                periodicBackup.UpdateBackupTaskSemaphore.Release();
            }
        }
예제 #9
0
        private long CreateBackupTask(PeriodicBackup periodicBackup, bool isFullBackup, DateTime startTimeInUtc)
        {
            using (periodicBackup.UpdateBackupTask())
            {
                if (periodicBackup.Disposed)
                {
                    throw new InvalidOperationException("Backup task was already disposed");
                }

                if (periodicBackup.RunningTask != null)
                {
                    return(periodicBackup.RunningBackupTaskId ?? -1);
                }

                if (_serverStore.Server.CpuCreditsBalance.BackgroundTasksAlertRaised.IsRaised())
                {
                    throw new BackupDelayException(
                              $"Failed to start Backup Task: '{periodicBackup.Configuration.Name}'. " +
                              $"The task cannot run because the CPU credits allocated to this machine are nearing exhaustion.")
                          {
                              DelayPeriod = _serverStore.Configuration.Server.CpuCreditsExhaustionBackupDelay.AsTimeSpan
                          };
                }

                if (LowMemoryNotification.Instance.LowMemoryState)
                {
                    throw new BackupDelayException(
                              $"Failed to start Backup Task: '{periodicBackup.Configuration.Name}'. " +
                              $"The task cannot run because the server is in low memory state.")
                          {
                              DelayPeriod = _serverStore.Configuration.Backup.LowMemoryBackupDelay.AsTimeSpan
                          };
                }

                if (LowMemoryNotification.Instance.DirtyMemoryState.IsHighDirty)
                {
                    throw new BackupDelayException(
                              $"Failed to start Backup Task: '{periodicBackup.Configuration.Name}'. " +
                              $"The task cannot run because the server is in high dirty memory state.")
                          {
                              DelayPeriod = _serverStore.Configuration.Backup.LowMemoryBackupDelay.AsTimeSpan
                          };
                }

                _serverStore.ConcurrentBackupsCounter.StartBackup(periodicBackup.Configuration.Name);

                try
                {
                    var backupStatus        = periodicBackup.BackupStatus = GetBackupStatus(periodicBackup.Configuration.TaskId, periodicBackup.BackupStatus);
                    var backupToLocalFolder = PeriodicBackupConfiguration.CanBackupUsing(periodicBackup.Configuration.LocalSettings);

                    // check if we need to do a new full backup
                    if (backupStatus.LastFullBackup == null ||                                // no full backup was previously performed
                        backupStatus.NodeTag != _serverStore.NodeTag ||                       // last backup was performed by a different node
                        backupStatus.BackupType != periodicBackup.Configuration.BackupType || // backup type has changed
                        backupStatus.LastEtag == null ||                                      // last document etag wasn't updated
                        backupToLocalFolder && BackupTask.DirectoryContainsBackupFiles(backupStatus.LocalBackup.BackupDirectory, IsFullBackupOrSnapshot) == false)
                    // the local folder already includes a full backup or snapshot
                    {
                        isFullBackup = true;
                    }

                    var operationId    = _database.Operations.GetNextOperationId();
                    var backupTypeText = GetBackupTypeText(isFullBackup, periodicBackup.Configuration.BackupType);

                    periodicBackup.StartTimeInUtc = startTimeInUtc;
                    var backupTask = new BackupTask(
                        _serverStore,
                        _database,
                        periodicBackup,
                        isFullBackup,
                        backupToLocalFolder,
                        operationId,
                        _tempBackupPath,
                        _logger,
                        _cancellationToken.Token);

                    periodicBackup.RunningBackupTaskId = operationId;
                    periodicBackup.CancelToken         = backupTask.TaskCancelToken;
                    var backupTaskName = $"{backupTypeText} backup task: '{periodicBackup.Configuration.Name}'. Database: '{_database.Name}'";

                    var task = _database.Operations.AddOperation(
                        null,
                        backupTaskName,
                        Operations.Operations.OperationType.DatabaseBackup,
                        taskFactory: onProgress => StartBackupThread(periodicBackup, backupTask, onProgress),
                        id: operationId,
                        token: backupTask.TaskCancelToken);

                    periodicBackup.RunningTask = task;
                    task.ContinueWith(_ => backupTask.TaskCancelToken.Dispose());

                    return(operationId);
                }
                catch (Exception e)
                {
                    // we failed to START the backup, need to update the status anyway
                    // in order to reschedule the next full/incremental backup
                    periodicBackup.BackupStatus.Version++;
                    periodicBackup.BackupStatus.Error = new Error
                    {
                        Exception = e.ToString(),
                        At        = DateTime.UtcNow
                    };

                    if (isFullBackup)
                    {
                        periodicBackup.BackupStatus.LastFullBackupInternal = startTimeInUtc;
                    }
                    else
                    {
                        periodicBackup.BackupStatus.LastIncrementalBackupInternal = startTimeInUtc;
                    }

                    BackupTask.SaveBackupStatus(periodicBackup.BackupStatus, _database, _logger);
                    ScheduleNextBackup(periodicBackup);

                    var message = $"Failed to start the backup task: '{periodicBackup.Configuration.Name}'";
                    if (_logger.IsOperationsEnabled)
                    {
                        _logger.Operations(message, e);
                    }

                    _database.NotificationCenter.Add(AlertRaised.Create(
                                                         _database.Name,
                                                         message,
                                                         "The next backup will be rescheduled",
                                                         AlertType.PeriodicBackup,
                                                         NotificationSeverity.Error,
                                                         details: new ExceptionDetails(e)));

                    throw;
                }
            }
        }