From 2942a53a1d51b488d7e8818e180c25abb335f0a2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Feb 2018 15:55:14 -0800 Subject: [PATCH] Add recovery mode and full backup information to DS --- .../src/DomainContentBackupManager.cpp | 91 +++++++++++++++++-- .../src/DomainContentBackupManager.h | 14 +-- domain-server/src/DomainServer.cpp | 28 ++---- 3 files changed, 96 insertions(+), 37 deletions(-) diff --git a/domain-server/src/DomainContentBackupManager.cpp b/domain-server/src/DomainContentBackupManager.cpp index f56c41dacd..54ea7e23d5 100644 --- a/domain-server/src/DomainContentBackupManager.cpp +++ b/domain-server/src/DomainContentBackupManager.cpp @@ -147,7 +147,24 @@ bool DomainContentBackupManager::process() { if (sinceLastSave > intervalToCheck) { _lastCheck = now; - backup(); + if (_isRecovering) { + bool anyHandlerIsRecovering { false }; + for (auto& handler : _backupHandlers) { + bool handlerIsRecovering { false }; + float progress { 0.0f }; + //std::tie = handler->getRecoveryStatus(); + if (handlerIsRecovering) { + anyHandlerIsRecovering = true; + emit recoveryCompleted(); + break; + } + } + _isRecovering = anyHandlerIsRecovering; + } + + if (!_isRecovering) { + backup(); + } } } @@ -213,6 +230,13 @@ void DomainContentBackupManager::deleteBackup(MiniPromise::Promise promise, cons return; } + if (_isRecovering && backupName == _recoveryFilename) { + promise->resolve({ + { "success", false } + }); + return; + } + QDir backupDir { _backupDirectory }; QFile backupFile { backupDir.filePath(backupName) }; auto success = backupFile.remove(); @@ -222,6 +246,13 @@ void DomainContentBackupManager::deleteBackup(MiniPromise::Promise promise, cons } void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise, const QString& backupName) { + if (_isRecovering) { + promise->resolve({ + { "success", false } + }); + return; + }; + if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "recoverFromBackup", Q_ARG(MiniPromise::Promise, promise), Q_ARG(const QString&, backupName)); @@ -239,11 +270,12 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise, qWarning() << "Failed to unzip file: " << backupName; success = false; } else { + _isRecovering = true; for (auto& handler : _backupHandlers) { handler->recoverBackup(zip); } - qDebug() << "Successfully recovered from " << backupName; + qDebug() << "Successfully started recovering from " << backupName; success = true; } backupFile.close(); @@ -257,8 +289,11 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise, }); } -std::vector DomainContentBackupManager::getAllBackups() { - std::vector backups; +void DomainContentBackupManager::getAllBackupInformation(MiniPromise::Promise promise) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "getAllBackupInformation", Q_ARG(MiniPromise::Promise, promise)); + return; + } QDir backupDir { _backupDirectory }; auto matchingFiles = @@ -269,6 +304,8 @@ std::vector DomainContentBackupManager::getAllBackups() { QString dateTimeFormat = "(" + DATETIME_FORMAT_RE + ")"; QRegExp backupNameFormat { prefixFormat + nameFormat + "-" + dateTimeFormat + "\\.zip" }; + QVariantList backups; + for (const auto& fileInfo : matchingFiles) { auto fileName = fileInfo.fileName(); if (backupNameFormat.exactMatch(fileName)) { @@ -280,12 +317,50 @@ std::vector DomainContentBackupManager::getAllBackups() { continue; } - BackupItemInfo backup { fileInfo.fileName(), name, fileInfo.absoluteFilePath(), createdAt, type == MANUAL_BACKUP_PREFIX }; - backups.push_back(backup); + bool isAvailable { true }; + float availabilityProgress { 0.0f }; + for (auto& handler : _backupHandlers) { + bool handlerIsAvailable { false }; + float progress { 0.0f }; + //std::tie = handler->isAvailable(); + //isAvailable = isAvailable && !handlerIsAvailable); + //availabilityProgress += progress / _backupHandlers.size(); + } + + backups.push_back(QVariantMap({ + { "id", fileInfo.fileName() }, + { "name", name }, + { "createdAtMillis", createdAt.toMSecsSinceEpoch() }, + { "isAvailable", isAvailable }, + { "availabilityProgress", availabilityProgress }, + { "isManualBackup", type == MANUAL_BACKUP_PREFIX } + })); } } - return backups; + float recoveryProgress = 0.0f; + bool isRecovering = _isRecovering.load(); + if (_isRecovering) { + for (auto& handler : _backupHandlers) { + bool handlerIsRecovering { false }; + float progress { 0.0f }; + //std::tie = handler->getRecoveryStatus(); + recoveryProgress += progress / _backupHandlers.size(); + } + } + + QVariantMap status { + { "isRecovering", isRecovering }, + { "recoveringBackupId", _recoveryFilename }, + { "recoveryProgress", recoveryProgress } + }; + + QVariantMap info { + { "backups", backups }, + { "status", status } + }; + + promise->resolve(info); } void DomainContentBackupManager::removeOldBackupVersions(const BackupRule& rule) { @@ -433,6 +508,8 @@ void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise, return; } + qDebug() << "copyFilePath" << copyFilePath; + promise->resolve({ { "success", true }, { "backupFilePath", copyFilePath } diff --git a/domain-server/src/DomainContentBackupManager.h b/domain-server/src/DomainContentBackupManager.h index 9ec7eb9950..790dff0fb4 100644 --- a/domain-server/src/DomainContentBackupManager.h +++ b/domain-server/src/DomainContentBackupManager.h @@ -24,14 +24,6 @@ #include -struct BackupItemInfo { - QString id; - QString name; - QString absolutePath; - QDateTime createdAt; - bool isManualBackup; -}; - class DomainContentBackupManager : public GenericThread { Q_OBJECT public: @@ -52,13 +44,13 @@ public: bool debugTimestampNow = false); void addBackupHandler(BackupHandlerPointer handler); - std::vector getAllBackups(); void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist void replaceData(QByteArray data); public slots: + void getAllBackupInformation(MiniPromise::Promise promise); void createManualBackup(MiniPromise::Promise promise, const QString& name); void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName); void deleteBackup(MiniPromise::Promise promise, const QString& backupName); @@ -66,6 +58,7 @@ public slots: signals: void loadCompleted(); + void recoveryCompleted(); protected: /// Implements generic processing behavior for this thread. @@ -86,6 +79,9 @@ private: std::vector _backupHandlers; int _persistInterval { 0 }; + std::atomic _isRecovering { false }; + QString _recoveryFilename { }; + int64_t _lastCheck { 0 }; std::vector _backupRules; }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b91e12a9cf..fe145b341b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -307,10 +307,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _contentManager->initialize(true); - qDebug() << "Existing backups:"; - for (auto& backup : _contentManager->getAllBackups()) { - qDebug() << " Backup: " << backup.name << backup.createdAt; - } + connect(_contentManager.get(), &DomainContentBackupManager::recoveryCompleted, this, &DomainServer::restart); } void DomainServer::parseCommandLine() { @@ -2131,24 +2128,13 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url return true; } else if (url.path() == URI_API_BACKUPS) { - QJsonObject rootJSON; - QJsonArray backupsJSON; + auto deferred = makePromise("getAllBackupInformation"); + deferred->then([connection, JSON_MIME_TYPE](QString error, QVariantMap result) { + QJsonDocument docJSON(QJsonObject::fromVariantMap(result)); - auto backups = _contentManager->getAllBackups(); - - for (const auto& backup : backups) { - QJsonObject obj; - obj["id"] = backup.id; - obj["name"] = backup.name; - obj["createdAtMillis"] = backup.createdAt.toMSecsSinceEpoch(); - obj["isManualBackup"] = backup.isManualBackup; - backupsJSON.push_back(obj); - } - - rootJSON["backups"] = backupsJSON; - QJsonDocument docJSON(rootJSON); - - connection->respond(HTTPConnection::StatusCode200, docJSON.toJson(), JSON_MIME_TYPE.toUtf8()); + connection->respond(HTTPConnection::StatusCode200, docJSON.toJson(), JSON_MIME_TYPE.toUtf8()); + }); + _contentManager->getAllBackupInformation(deferred); return true; } else if (url.path().startsWith(URI_API_BACKUPS_ID)) { auto id = url.path().mid(QString(URI_API_BACKUPS_ID).length());