예제 #1
0
        public async Task PostExport()
        {
            DocumentsOperationContext context;

            using (ContextPool.AllocateOperationContext(out context))
                using (context.OpenReadTransaction())
                {
                    var operationId = GetIntValueQueryString("operationId", required: false);

                    var stream = TryGetRequestFormStream("DownloadOptions") ?? RequestBodyStream();

                    var blittableJson = await context.ReadForMemoryAsync(stream, "DownloadOptions");

                    var options = JsonDeserializationServer.DatabaseSmugglerOptions(blittableJson);

                    var exporter = new SmugglerExporter(Database, options);
                    var token    = CreateOperationToken();

                    var fileName = exporter.Options.FileName;
                    if (string.IsNullOrEmpty(fileName))
                    {
                        fileName = $"Dump of {context.DocumentDatabase.Name} {SystemTime.UtcNow.ToString("yyyy-MM-dd HH-mm", CultureInfo.InvariantCulture)}";
                    }

                    var contentDisposition = "attachment; filename=" + Uri.EscapeDataString(fileName) + ".ravendbdump";
                    HttpContext.Response.Headers["Content-Disposition"] = contentDisposition;

                    if (operationId.HasValue)
                    {
                        try
                        {
                            await
                            Database.Operations.AddOperation("Export database: " + Database.Name,
                                                             DatabaseOperations.PendingOperationType.DatabaseExport,
                                                             onProgress =>
                                                             Task.Run(() => ExportDatabaseInternal(context, exporter, onProgress, token),
                                                                      token.Token), operationId.Value, token);
                        }
                        catch (Exception)
                        {
                            HttpContext.Abort();
                        }
                    }
                    else
                    {
                        ExportDatabaseInternal(context, exporter, null, token);
                    }
                }
        }
예제 #2
0
        private void StreamToClient(Stream stream, ExportOptions options, Lazy <NameValueCollection> headers, IPrincipal user)
        {
            var old     = CurrentOperationContext.Headers.Value;
            var oldUser = CurrentOperationContext.User.Value;

            try
            {
                CurrentOperationContext.Headers.Value = headers;
                CurrentOperationContext.User.Value    = user;

                Database.TransactionalStorage.Batch(accessor =>
                {
                    var bufferStream = new BufferedStream(stream, 1024 * 64);

                    using (var cts = new CancellationTokenSource())
                        using (var timeout = cts.TimeoutAfter(DatabasesLandlord.SystemConfiguration.DatabaseOperationTimeout))
                            using (var streamWriter = new StreamWriter(bufferStream))
                                using (var writer = new JsonTextWriter(streamWriter))
                                {
                                    writer.WriteStartObject();
                                    writer.WritePropertyName("Results");
                                    writer.WriteStartArray();

                                    var exporter = new SmugglerExporter(Database, options);

                                    exporter.Export(item => WriteToStream(writer, item, timeout), cts.Token);

                                    writer.WriteEndArray();
                                    writer.WriteEndObject();
                                    writer.Flush();
                                    bufferStream.Flush();
                                }
                });
            }
            finally
            {
                CurrentOperationContext.Headers.Value = old;
                CurrentOperationContext.User.Value    = oldUser;
            }
        }
예제 #3
0
        private async Task RunPeriodicExport(bool fullExport)
        {
            if (_cancellationToken.IsCancellationRequested)
            {
                return;
            }

            try
            {
                DocumentsOperationContext context;
                using (_database.DocumentsStorage.ContextPool.AllocateOperationContext(out context))
                {
                    var sp = Stopwatch.StartNew();
                    using (var tx = context.OpenReadTransaction())
                    {
                        var exportDirectory = _configuration.LocalFolderName ?? Path.Combine(_database.Configuration.Core.DataDirectory, "PeriodicExport-Temp");
                        if (Directory.Exists(exportDirectory) == false)
                        {
                            Directory.CreateDirectory(exportDirectory);
                        }

                        var now = SystemTime.UtcNow.ToString("yyyy-MM-dd-HH-mm", CultureInfo.InvariantCulture);

                        if (_status.LastFullExportDirectory == null ||
                            IsDirectoryExistsOrContainsFiles() == false ||
                            fullExport)
                        {
                            fullExport = true;
                            _status.LastFullExportDirectory = Path.Combine(exportDirectory, $"{now}.ravendb-{_database.Name}-backup");
                            Directory.CreateDirectory(_status.LastFullExportDirectory);
                        }

                        if (_logger.IsInfoEnabled)
                        {
                            _logger.Info($"Exporting a {(fullExport ? "full" : "incremental")} export");
                        }

                        if (fullExport == false)
                        {
                            var currentLastEtag = DocumentsStorage.ReadLastEtag(tx.InnerTransaction);
                            // No-op if nothing has changed
                            if (currentLastEtag == _status.LastDocsEtag)
                            {
                                return;
                            }
                        }

                        var dataExporter = new SmugglerExporter(_database)
                        {
                            Options = new DatabaseSmugglerOptions
                            {
                                RevisionDocumentsLimit = _exportLimit
                            }
                        };

                        string exportFilePath;

                        string fileName;
                        if (fullExport)
                        {
                            // create filename for full export
                            fileName       = $"{now}.ravendb-full-export";
                            exportFilePath = Path.Combine(_status.LastFullExportDirectory, fileName);
                            if (File.Exists(exportFilePath))
                            {
                                var counter = 1;
                                while (true)
                                {
                                    fileName       = $"{now} - {counter}.${Constants.PeriodicExport.FullExportExtension}";
                                    exportFilePath = Path.Combine(_status.LastFullExportDirectory, fileName);

                                    if (File.Exists(exportFilePath) == false)
                                    {
                                        break;
                                    }
                                    counter++;
                                }
                            }
                        }
                        else
                        {
                            // create filename for incremental export
                            fileName       = $"{now}-0.${Constants.PeriodicExport.IncrementalExportExtension}";
                            exportFilePath = Path.Combine(_status.LastFullExportDirectory, fileName);
                            if (File.Exists(exportFilePath))
                            {
                                var counter = 1;
                                while (true)
                                {
                                    fileName       = $"{now}-{counter}.${Constants.PeriodicExport.IncrementalExportExtension}";
                                    exportFilePath = Path.Combine(_status.LastFullExportDirectory, fileName);

                                    if (File.Exists(exportFilePath) == false)
                                    {
                                        break;
                                    }
                                    counter++;
                                }
                            }

                            dataExporter.StartDocsEtag = _status.LastDocsEtag;
                            if (dataExporter.StartDocsEtag == null)
                            {
                                IncrementalExport.ReadLastEtagsFromFile(_status.LastFullExportDirectory, context, dataExporter);
                            }
                        }

                        var exportResult = dataExporter.Export(context, exportFilePath);

                        if (fullExport == false)
                        {
                            // No-op if nothing has changed
                            if (exportResult.LastDocsEtag == _status.LastDocsEtag)
                            {
                                if (_logger.IsInfoEnabled)
                                {
                                    _logger.Info("Periodic export returned prematurely, nothing has changed since last export");
                                }
                                return;
                            }
                        }

                        try
                        {
                            await UploadToServer(exportFilePath, fileName, fullExport);
                        }
                        finally
                        {
                            // if user did not specify local folder we delete temporary file.
                            if (string.IsNullOrEmpty(_configuration.LocalFolderName))
                            {
                                IOExtensions.DeleteFile(exportFilePath);
                            }
                        }

                        _status.LastDocsEtag = exportResult.LastDocsEtag;
                        if (fullExport)
                        {
                            _status.LastFullExportAt = SystemTime.UtcNow;
                        }
                        else
                        {
                            _status.LastExportAt = SystemTime.UtcNow;
                        }

                        WriteStatus();
                    }
                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info($"Successfully exported {(fullExport ? "full" : "incremental")} export in {sp.ElapsedMilliseconds:#,#;;0} ms.");
                    }

                    _exportLimit = null;
                }
            }
            catch (OperationCanceledException)
            {
                // shutting down, probably
            }
            catch (ObjectDisposedException)
            {
                // shutting down, probably
            }
            catch (Exception e)
            {
                _exportLimit = 100;
                if (_logger.IsOperationsEnabled)
                {
                    _logger.Operations("Error when performing periodic export", e);
                }
                _database.Alerts.AddAlert(new Alert
                {
                    Type      = AlertType.PeriodicExport,
                    Message   = "Error in Periodic Export",
                    CreatedAt = SystemTime.UtcNow,
                    Severity  = AlertSeverity.Error,
                    Content   = new ExceptionAlertContent
                    {
                        Message   = e.Message,
                        Exception = e.ToString()
                    }
                });
            }
        }
예제 #4
0
 private IOperationResult ExportDatabaseInternal(DocumentsOperationContext context, SmugglerExporter exporter, Action <IOperationProgress> onProgress, OperationCancelToken token)
 {
     try
     {
         return(exporter.Export(context, ResponseBodyStream(), onProgress));
     }
     finally
     {
         token.Dispose();
     }
 }
예제 #5
0
        public static void ReadLastEtagsFromFile(string exportDirectory, DocumentsOperationContext context, SmugglerExporter smugglerExporter)
        {
            var etagFileLocation = Path.Combine(exportDirectory, IncrementalExportStateFile);

            if (File.Exists(etagFileLocation) == false)
            {
                return;
            }

            using (var fileStream = new FileStream(etagFileLocation, FileMode.Open))
            {
                var reader = context.ReadForMemory(fileStream, IncrementalExportStateFile);
                reader.TryGet("LastDocsEtag", out smugglerExporter.StartDocsEtag);
            }
        }