Add recovery mode and full backup information to DS

This commit is contained in:
Ryan Huffman 2018-02-15 15:55:14 -08:00 committed by Atlante45
parent dd0b8a0c2f
commit 2942a53a1d
3 changed files with 96 additions and 37 deletions

View file

@ -147,7 +147,24 @@ bool DomainContentBackupManager::process() {
if (sinceLastSave > intervalToCheck) { if (sinceLastSave > intervalToCheck) {
_lastCheck = now; _lastCheck = now;
backup(); if (_isRecovering) {
bool anyHandlerIsRecovering { false };
for (auto& handler : _backupHandlers) {
bool handlerIsRecovering { false };
float progress { 0.0f };
//std::tie<handlerIsRecovering, progress> = 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; return;
} }
if (_isRecovering && backupName == _recoveryFilename) {
promise->resolve({
{ "success", false }
});
return;
}
QDir backupDir { _backupDirectory }; QDir backupDir { _backupDirectory };
QFile backupFile { backupDir.filePath(backupName) }; QFile backupFile { backupDir.filePath(backupName) };
auto success = backupFile.remove(); auto success = backupFile.remove();
@ -222,6 +246,13 @@ void DomainContentBackupManager::deleteBackup(MiniPromise::Promise promise, cons
} }
void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise, const QString& backupName) { void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise, const QString& backupName) {
if (_isRecovering) {
promise->resolve({
{ "success", false }
});
return;
};
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "recoverFromBackup", Q_ARG(MiniPromise::Promise, promise), QMetaObject::invokeMethod(this, "recoverFromBackup", Q_ARG(MiniPromise::Promise, promise),
Q_ARG(const QString&, backupName)); Q_ARG(const QString&, backupName));
@ -239,11 +270,12 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise,
qWarning() << "Failed to unzip file: " << backupName; qWarning() << "Failed to unzip file: " << backupName;
success = false; success = false;
} else { } else {
_isRecovering = true;
for (auto& handler : _backupHandlers) { for (auto& handler : _backupHandlers) {
handler->recoverBackup(zip); handler->recoverBackup(zip);
} }
qDebug() << "Successfully recovered from " << backupName; qDebug() << "Successfully started recovering from " << backupName;
success = true; success = true;
} }
backupFile.close(); backupFile.close();
@ -257,8 +289,11 @@ void DomainContentBackupManager::recoverFromBackup(MiniPromise::Promise promise,
}); });
} }
std::vector<BackupItemInfo> DomainContentBackupManager::getAllBackups() { void DomainContentBackupManager::getAllBackupInformation(MiniPromise::Promise promise) {
std::vector<BackupItemInfo> backups; if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "getAllBackupInformation", Q_ARG(MiniPromise::Promise, promise));
return;
}
QDir backupDir { _backupDirectory }; QDir backupDir { _backupDirectory };
auto matchingFiles = auto matchingFiles =
@ -269,6 +304,8 @@ std::vector<BackupItemInfo> DomainContentBackupManager::getAllBackups() {
QString dateTimeFormat = "(" + DATETIME_FORMAT_RE + ")"; QString dateTimeFormat = "(" + DATETIME_FORMAT_RE + ")";
QRegExp backupNameFormat { prefixFormat + nameFormat + "-" + dateTimeFormat + "\\.zip" }; QRegExp backupNameFormat { prefixFormat + nameFormat + "-" + dateTimeFormat + "\\.zip" };
QVariantList backups;
for (const auto& fileInfo : matchingFiles) { for (const auto& fileInfo : matchingFiles) {
auto fileName = fileInfo.fileName(); auto fileName = fileInfo.fileName();
if (backupNameFormat.exactMatch(fileName)) { if (backupNameFormat.exactMatch(fileName)) {
@ -280,12 +317,50 @@ std::vector<BackupItemInfo> DomainContentBackupManager::getAllBackups() {
continue; continue;
} }
BackupItemInfo backup { fileInfo.fileName(), name, fileInfo.absoluteFilePath(), createdAt, type == MANUAL_BACKUP_PREFIX }; bool isAvailable { true };
backups.push_back(backup); float availabilityProgress { 0.0f };
for (auto& handler : _backupHandlers) {
bool handlerIsAvailable { false };
float progress { 0.0f };
//std::tie<handlerIsAvailable, progress> = 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<handlerIsRecovering, progress> = 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) { void DomainContentBackupManager::removeOldBackupVersions(const BackupRule& rule) {
@ -433,6 +508,8 @@ void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise,
return; return;
} }
qDebug() << "copyFilePath" << copyFilePath;
promise->resolve({ promise->resolve({
{ "success", true }, { "success", true },
{ "backupFilePath", copyFilePath } { "backupFilePath", copyFilePath }

View file

@ -24,14 +24,6 @@
#include <shared/MiniPromises.h> #include <shared/MiniPromises.h>
struct BackupItemInfo {
QString id;
QString name;
QString absolutePath;
QDateTime createdAt;
bool isManualBackup;
};
class DomainContentBackupManager : public GenericThread { class DomainContentBackupManager : public GenericThread {
Q_OBJECT Q_OBJECT
public: public:
@ -52,13 +44,13 @@ public:
bool debugTimestampNow = false); bool debugTimestampNow = false);
void addBackupHandler(BackupHandlerPointer handler); void addBackupHandler(BackupHandlerPointer handler);
std::vector<BackupItemInfo> getAllBackups();
void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist
void replaceData(QByteArray data); void replaceData(QByteArray data);
public slots: public slots:
void getAllBackupInformation(MiniPromise::Promise promise);
void createManualBackup(MiniPromise::Promise promise, const QString& name); void createManualBackup(MiniPromise::Promise promise, const QString& name);
void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName); void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName);
void deleteBackup(MiniPromise::Promise promise, const QString& backupName); void deleteBackup(MiniPromise::Promise promise, const QString& backupName);
@ -66,6 +58,7 @@ public slots:
signals: signals:
void loadCompleted(); void loadCompleted();
void recoveryCompleted();
protected: protected:
/// Implements generic processing behavior for this thread. /// Implements generic processing behavior for this thread.
@ -86,6 +79,9 @@ private:
std::vector<BackupHandlerPointer> _backupHandlers; std::vector<BackupHandlerPointer> _backupHandlers;
int _persistInterval { 0 }; int _persistInterval { 0 };
std::atomic<bool> _isRecovering { false };
QString _recoveryFilename { };
int64_t _lastCheck { 0 }; int64_t _lastCheck { 0 };
std::vector<BackupRule> _backupRules; std::vector<BackupRule> _backupRules;
}; };

View file

@ -307,10 +307,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_contentManager->initialize(true); _contentManager->initialize(true);
qDebug() << "Existing backups:"; connect(_contentManager.get(), &DomainContentBackupManager::recoveryCompleted, this, &DomainServer::restart);
for (auto& backup : _contentManager->getAllBackups()) {
qDebug() << " Backup: " << backup.name << backup.createdAt;
}
} }
void DomainServer::parseCommandLine() { void DomainServer::parseCommandLine() {
@ -2131,24 +2128,13 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
return true; return true;
} else if (url.path() == URI_API_BACKUPS) { } else if (url.path() == URI_API_BACKUPS) {
QJsonObject rootJSON; auto deferred = makePromise("getAllBackupInformation");
QJsonArray backupsJSON; deferred->then([connection, JSON_MIME_TYPE](QString error, QVariantMap result) {
QJsonDocument docJSON(QJsonObject::fromVariantMap(result));
auto backups = _contentManager->getAllBackups(); connection->respond(HTTPConnection::StatusCode200, docJSON.toJson(), JSON_MIME_TYPE.toUtf8());
});
for (const auto& backup : backups) { _contentManager->getAllBackupInformation(deferred);
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());
return true; return true;
} else if (url.path().startsWith(URI_API_BACKUPS_ID)) { } else if (url.path().startsWith(URI_API_BACKUPS_ID)) {
auto id = url.path().mid(QString(URI_API_BACKUPS_ID).length()); auto id = url.path().mid(QString(URI_API_BACKUPS_ID).length());