public async Task RunAsync() { if (currentTask != null) { throw new DomainException("Another backup process is already running."); } if (state.Value.Jobs.Count >= MaxBackups) { throw new DomainException($"You cannot have more than {MaxBackups} backups."); } var job = new BackupStateJob { Id = Guid.NewGuid(), Started = clock.GetCurrentInstant(), Status = JobStatus.Started }; currentTask = new CancellationTokenSource(); currentJob = job; state.Value.Jobs.Insert(0, job); await state.WriteAsync(); Process(job, currentTask.Token); }
private async Task CleanupArchiveAsync(BackupStateJob job) { try { await backupArchiveLocation.DeleteArchiveAsync(job.Id); } catch (Exception ex) { log.LogError(ex, w => w .WriteProperty("action", "deleteArchive") .WriteProperty("status", "failed") .WriteProperty("backupId", job.Id.ToString())); } }
private async Task CleanupBackupAsync(BackupStateJob job) { try { await assetStore.DeleteAsync(job.Id.ToString(), 0, null); } catch (Exception ex) { log.LogError(ex, w => w .WriteProperty("action", "deleteBackup") .WriteProperty("status", "failed") .WriteProperty("backupId", job.Id.ToString())); } }
public async Task RunAsync() { if (currentTask != null) { throw new DomainException("Another backup process is already running."); } if (state.Jobs.Count >= MaxBackups) { throw new DomainException($"You cannot have more than {MaxBackups} backups."); } var job = new BackupStateJob { Id = Guid.NewGuid(), Started = clock.GetCurrentInstant() }; currentTask = new CancellationTokenSource(); currentJob = job; var lastTimestamp = job.Started; state.Jobs.Insert(0, job); await WriteAsync(); try { using (var stream = await backupArchiveLocation.OpenStreamAsync(job.Id)) { using (var writer = new EventStreamWriter(stream)) { await eventStore.QueryAsync(async @event => { var eventData = @event.Data; if (eventData.Type == "AssetCreatedEvent" || eventData.Type == "AssetUpdatedEvent") { var parsedEvent = eventDataFormatter.Parse(eventData); var assetVersion = 0L; var assetId = Guid.Empty; if (parsedEvent.Payload is AssetCreated assetCreated) { assetId = assetCreated.AssetId; assetVersion = assetCreated.FileVersion; } if (parsedEvent.Payload is AssetUpdated asetUpdated) { assetId = asetUpdated.AssetId; assetVersion = asetUpdated.FileVersion; } await writer.WriteEventAsync(eventData, async attachmentStream => { await assetStore.DownloadAsync(assetId.ToString(), assetVersion, null, attachmentStream); }); job.HandledAssets++; } else { await writer.WriteEventAsync(eventData); } job.HandledEvents++; var now = clock.GetCurrentInstant(); if ((now - lastTimestamp) >= UpdateDuration) { lastTimestamp = now; await WriteAsync(); } }, SquidexHeaders.AppId, appId.ToString(), null, currentTask.Token); } stream.Position = 0; currentTask.Token.ThrowIfCancellationRequested(); await assetStore.UploadAsync(job.Id.ToString(), 0, null, stream, currentTask.Token); } } catch (Exception ex) { log.LogError(ex, w => w .WriteProperty("action", "makeBackup") .WriteProperty("status", "failed") .WriteProperty("backupId", job.Id.ToString())); job.IsFailed = true; } finally { await CleanupArchiveAsync(job); job.Stopped = clock.GetCurrentInstant(); await WriteAsync(); currentTask = null; currentJob = null; } }
private async Task ProcessAsync(BackupStateJob job, CancellationToken ct) { var jobId = job.Id.ToString(); var handlers = CreateHandlers(); var lastTimestamp = job.Started; try { using (var stream = await backupArchiveLocation.OpenStreamAsync(jobId)) { using (var writer = new BackupWriter(serializer, stream, true)) { await eventStore.QueryAsync(async storedEvent => { var @event = eventDataFormatter.Parse(storedEvent.Data); writer.WriteEvent(storedEvent); foreach (var handler in handlers) { await handler.BackupEventAsync(@event, Key, writer); } job.HandledEvents = writer.WrittenEvents; job.HandledAssets = writer.WrittenAttachments; lastTimestamp = await WritePeriodically(lastTimestamp); }, SquidexHeaders.AppId, Key.ToString(), null, ct); foreach (var handler in handlers) { await handler.BackupAsync(Key, writer); } foreach (var handler in handlers) { await handler.CompleteBackupAsync(Key, writer); } } stream.Position = 0; ct.ThrowIfCancellationRequested(); await assetStore.UploadAsync(jobId, 0, null, stream, false, ct); } job.Status = JobStatus.Completed; } catch (Exception ex) { log.LogError(ex, jobId, (ctx, w) => w .WriteProperty("action", "makeBackup") .WriteProperty("status", "failed") .WriteProperty("backupId", ctx)); job.Status = JobStatus.Failed; } finally { await Safe.DeleteAsync(backupArchiveLocation, jobId, log); job.Stopped = clock.GetCurrentInstant(); await state.WriteAsync(); currentTask = null; currentJob = null; } }
private void Process(BackupStateJob job, CancellationToken ct) { ProcessAsync(job, ct).Forget(); }
public async Task RunAsync() { if (currentTask != null) { throw new DomainException("Another backup process is already running."); } if (state.Jobs.Count >= MaxBackups) { throw new DomainException($"You cannot have more than {MaxBackups} backups."); } var job = new BackupStateJob { Id = Guid.NewGuid(), Started = clock.GetCurrentInstant(), Status = JobStatus.Started }; currentTask = new CancellationTokenSource(); currentJob = job; var lastTimestamp = job.Started; state.Jobs.Insert(0, job); await WriteAsync(); try { using (var stream = await backupArchiveLocation.OpenStreamAsync(job.Id)) { using (var writer = new BackupWriter(serializer, stream, true)) { await eventStore.QueryAsync(async storedEvent => { var @event = eventDataFormatter.Parse(storedEvent.Data); writer.WriteEvent(storedEvent); foreach (var handler in handlers) { await handler.BackupEventAsync(@event, appId, writer); } job.HandledEvents = writer.WrittenEvents; job.HandledAssets = writer.WrittenAttachments; lastTimestamp = await WritePeriodically(lastTimestamp); }, SquidexHeaders.AppId, appId.ToString(), null, currentTask.Token); foreach (var handler in handlers) { await handler.BackupAsync(appId, writer); } foreach (var handler in handlers) { await handler.CompleteBackupAsync(appId, writer); } } stream.Position = 0; currentTask.Token.ThrowIfCancellationRequested(); await assetStore.UploadAsync(job.Id.ToString(), 0, null, stream, currentTask.Token); } job.Status = JobStatus.Completed; } catch (Exception ex) { log.LogError(ex, w => w .WriteProperty("action", "makeBackup") .WriteProperty("status", "failed") .WriteProperty("backupId", job.Id.ToString())); job.Status = JobStatus.Failed; } finally { await Safe.DeleteAsync(backupArchiveLocation, job.Id, log); job.Stopped = clock.GetCurrentInstant(); await WriteAsync(); currentTask = null; currentJob = null; } }
public async Task StartNewAsync() { if (currentTask != null) { throw new DomainException("Another backup process is already running."); } if (state.Jobs.Count >= MaxBackups) { throw new DomainException($"You cannot have more than {MaxBackups} backups."); } var job = new BackupStateJob { Id = Guid.NewGuid(), Started = clock.GetCurrentInstant() }; currentTask = new CancellationTokenSource(); currentJob = job; state.Jobs.Add(job); await WriteAsync(); try { using (var stream = await backupArchiveLocation.OpenStreamAsync(job.Id)) { using (var writer = new EventStreamWriter(stream)) { await eventStore.QueryAsync(async @event => { var eventData = @event.Data; if (eventData.Type == nameof(AssetCreated) || eventData.Type == nameof(AssetUpdated)) { var parsedEvent = eventDataFormatter.Parse(eventData); var assetVersion = 0L; var assetId = Guid.Empty; if (parsedEvent.Payload is AssetCreated assetCreated) { assetId = assetCreated.AssetId; assetVersion = assetCreated.FileVersion; } if (parsedEvent.Payload is AssetUpdated asetUpdated) { assetId = asetUpdated.AssetId; assetVersion = asetUpdated.FileVersion; } await writer.WriteEventAsync(eventData, async attachmentStream => { await assetStore.DownloadAsync(assetId.ToString(), assetVersion, null, attachmentStream); }); } else { await writer.WriteEventAsync(eventData); } }, "AppId", appId, null, currentTask.Token); } stream.Position = 0; currentTask.Token.ThrowIfCancellationRequested(); await assetStore.UploadAsync(job.Id.ToString(), 0, null, stream); currentTask.Token.ThrowIfCancellationRequested(); } } catch { job.Failed = true; } finally { job.Stopped = clock.GetCurrentInstant(); await WriteAsync(); currentTask = null; currentJob = null; } }
private async Task CleanupAsync(BackupStateJob job) { await backupArchiveLocation.DeleteArchiveAsync(job.Id); }