Remove unused entity server backups

The domain server now handles entity backups so this code is no longer
used.

This also fixes a bug where, if the entity server went down while
writing the persist file, it would leave a lock file behind. When
starting up the next time it would try to recover from the last backup.
Because the entity server no longer gets any backup rules, and the way
it searches for backups, it would end up finding the lock file and
treating it as the most recent backup. This meant that the
models.json.gz file was replaced with an empty file.
This commit is contained in:
Ryan Huffman 2018-04-05 10:23:46 -07:00
parent bc3edbc7fc
commit 2143bae100
4 changed files with 16 additions and 356 deletions

View file

@ -1055,17 +1055,6 @@ void OctreeServer::readConfiguration() {
readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval);
qDebug() << "persistInterval=" << _persistInterval;
bool noBackup;
readOptionBool(QString("NoBackup"), settingsSectionObject, noBackup);
_wantBackup = !noBackup;
qDebug() << "wantBackup=" << _wantBackup;
if (!readOptionString("backupDirectoryPath", settingsSectionObject, _backupDirectoryPath)) {
_backupDirectoryPath = "";
}
qDebug() << "backupDirectoryPath=" << _backupDirectoryPath;
readOptionBool(QString("persistFileDownload"), settingsSectionObject, _persistFileDownload);
qDebug() << "persistFileDownload=" << _persistFileDownload;
@ -1288,38 +1277,10 @@ void OctreeServer::beginRunning(QByteArray replaceData) {
}
auto persistFileDirectory = QFileInfo(_persistAbsoluteFilePath).absolutePath();
if (_backupDirectoryPath.isEmpty()) {
// Use the persist file's directory to store backups
_backupDirectoryPath = persistFileDirectory;
} else {
// The backup directory has been set.
// If relative, make it relative to the entities directory in the application data directory
// If absolute, no resolution is necessary
QDir backupDirectory { _backupDirectoryPath };
QString absoluteBackupDirectory;
if (backupDirectory.isRelative()) {
absoluteBackupDirectory = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath);
absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath();
} else {
absoluteBackupDirectory = backupDirectory.absolutePath();
}
backupDirectory = QDir(absoluteBackupDirectory);
if (!backupDirectory.exists()) {
if (backupDirectory.mkpath(".")) {
qDebug() << "Created backup directory";
} else {
qDebug() << "ERROR creating backup directory, using persist file directory";
_backupDirectoryPath = persistFileDirectory;
}
} else {
_backupDirectoryPath = absoluteBackupDirectory;
}
}
qDebug() << "Backups will be stored in: " << _backupDirectoryPath;
// now set up PersistThread
_persistThread = new OctreePersistThread(_tree, _persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval,
_wantBackup, _settings, _debugTimestampNow, _persistAsFileType, replaceData);
_persistThread = new OctreePersistThread(_tree, _persistAbsoluteFilePath, _persistInterval, _debugTimestampNow,
_persistAsFileType, replaceData);
_persistThread->initialize(true);
}

View file

@ -190,7 +190,6 @@ protected:
QString _persistFilePath;
QString _persistAbsoluteFilePath;
QString _persistAsFileType;
QString _backupDirectoryPath;
int _packetsPerClientPerInterval;
int _packetsTotalPerInterval;
OctreePointer _tree; // this IS a reaveraging tree
@ -203,10 +202,7 @@ protected:
OctreePersistThread* _persistThread;
int _persistInterval;
bool _wantBackup;
bool _persistFileDownload;
QString _backupExtensionFormat;
int _backupInterval;
int _maxBackupVersions;
time_t _started;

View file

@ -37,25 +37,19 @@
const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, int persistInterval,
bool wantBackup, const QJsonObject& settings, bool debugTimestampNow,
QString persistAsFileType, const QByteArray& replacementData) :
OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, int persistInterval,
bool debugTimestampNow, QString persistAsFileType, const QByteArray& replacementData) :
_tree(tree),
_filename(filename),
_backupDirectory(backupDirectory),
_persistInterval(persistInterval),
_initialLoadComplete(false),
_replacementData(replacementData),
_loadTimeUSecs(0),
_lastCheck(0),
_wantBackup(wantBackup),
_debugTimestampNow(debugTimestampNow),
_lastTimeDebug(0),
_persistAsFileType(persistAsFileType)
{
parseSettings(settings);
// in case the persist filename has an extension that doesn't match the file type
QString sansExt = fileNameWithoutExtension(_filename, PERSIST_EXTENSIONS);
_filename = sansExt + "." + _persistAsFileType;
@ -70,72 +64,6 @@ QString OctreePersistThread::getPersistFileMimeType() const {
return "";
}
void OctreePersistThread::parseSettings(const QJsonObject& settings) {
if (settings["backups"].isArray()) {
const QJsonArray& backupRules = settings["backups"].toArray();
qCDebug(octree) << "BACKUP RULES:";
foreach (const QJsonValue& value, backupRules) {
QJsonObject obj = value.toObject();
int interval = 0;
int count = 0;
QJsonValue intervalVal = obj["backupInterval"];
if (intervalVal.isString()) {
interval = intervalVal.toString().toInt();
} else {
interval = intervalVal.toInt();
}
QJsonValue countVal = obj["maxBackupVersions"];
if (countVal.isString()) {
count = countVal.toString().toInt();
} else {
count = countVal.toInt();
}
qCDebug(octree) << " Name:" << obj["Name"].toString();
qCDebug(octree) << " format:" << obj["format"].toString();
qCDebug(octree) << " interval:" << interval;
qCDebug(octree) << " count:" << count;
BackupRule newRule = { obj["Name"].toString(), interval, obj["format"].toString(), count, 0};
newRule.lastBackup = getMostRecentBackupTimeInUsecs(obj["format"].toString());
if (newRule.lastBackup > 0) {
quint64 now = usecTimestampNow();
quint64 sinceLastBackup = now - newRule.lastBackup;
qCDebug(octree) << " lastBackup:" << qPrintable(formatUsecTime(sinceLastBackup)) << "ago";
} else {
qCDebug(octree) << " lastBackup: NEVER";
}
_backupRules << newRule;
}
} else {
qCDebug(octree) << "BACKUP RULES: NONE";
}
}
quint64 OctreePersistThread::getMostRecentBackupTimeInUsecs(const QString& format) {
quint64 mostRecentBackupInUsecs = 0;
QString mostRecentBackupFileName;
QDateTime mostRecentBackupTime;
bool recentBackup = getMostRecentBackup(format, mostRecentBackupFileName, mostRecentBackupTime);
if (recentBackup) {
mostRecentBackupInUsecs = mostRecentBackupTime.toMSecsSinceEpoch() * USECS_PER_MSEC;
}
return mostRecentBackupInUsecs;
}
void OctreePersistThread::replaceData(QByteArray data) {
backupCurrentFile();
@ -188,20 +116,7 @@ bool OctreePersistThread::process() {
_tree->withWriteLock([&] {
PerformanceWarning warn(true, "Loading Octree File", true);
// First check to make sure "lock" file doesn't exist. If it does exist, then
// our last save crashed during the save, and we want to load our most recent backup.
QString lockFileName = _filename + ".lock";
std::ifstream lockFile(qPrintable(lockFileName), std::ios::in | std::ios::binary | std::ios::ate);
if (lockFile.is_open()) {
qCDebug(octree) << "WARNING: Octree lock file detected at startup:" << lockFileName;
lockFile.close();
qCDebug(octree) << "Removing lock file:" << lockFileName;
remove(qPrintable(lockFileName));
qCDebug(octree) << "Lock file removed:" << lockFileName;
}
persistentFileRead = _tree->readFromFile(qPrintable(_filename.toLocal8Bit()));
persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData());
_tree->pruneTree();
});
@ -233,11 +148,6 @@ bool OctreePersistThread::process() {
// Since we just loaded the persistent file, we can consider ourselves as having "just checked" for persistance.
_lastCheck = usecTimestampNow(); // we just loaded, no need to save again
// This last persist time is not really used until the file is actually persisted. It is only
// used in formatting the backup filename in cases of non-rolling backup names. However, we don't
// want an uninitialized value for this, so we set it to the current time (startup of the server)
time(&_lastPersistTime);
if (_replacementData.isNull()) {
sendLatestEntityDataToDS();
@ -304,26 +214,21 @@ void OctreePersistThread::persist() {
qCDebug(octree) << "DONE pruning Octree before saving...";
});
qCDebug(octree) << "persist operation calling backup...";
backup(); // handle backup if requested
qCDebug(octree) << "persist operation DONE with backup...";
_tree->incrementPersistDataVersion();
// create our "lock" file to indicate we're saving.
QString lockFileName = _filename + ".lock";
std::ofstream lockFile(qPrintable(lockFileName), std::ios::out|std::ios::binary);
std::ofstream lockFile(lockFileName.toLocal8Bit().constData(), std::ios::out|std::ios::binary);
if(lockFile.is_open()) {
qCDebug(octree) << "saving Octree lock file created at:" << lockFileName;
_tree->writeToFile(qPrintable(_filename), NULL, _persistAsFileType);
time(&_lastPersistTime);
_tree->writeToFile(_filename.toLocal8Bit().constData(), NULL, _persistAsFileType);
_tree->clearDirtyBit(); // tree is clean after saving
qCDebug(octree) << "DONE saving Octree to file...";
lockFile.close();
qCDebug(octree) << "saving Octree lock file closed:" << lockFileName;
remove(qPrintable(lockFileName));
remove(lockFileName.toLocal8Bit().constData());
qCDebug(octree) << "saving Octree lock file removed:" << lockFileName;
}
@ -345,197 +250,3 @@ void OctreePersistThread::sendLatestEntityDataToDS() {
qCWarning(octree) << "Failed to persist octree to DS";
}
}
void OctreePersistThread::restoreFromMostRecentBackup() {
qCDebug(octree) << "Restoring from most recent backup...";
QString mostRecentBackupFileName;
QDateTime mostRecentBackupTime;
bool recentBackup = getMostRecentBackup(QString(""), mostRecentBackupFileName, mostRecentBackupTime);
// If we found a backup file, restore from that file.
if (recentBackup) {
qCDebug(octree) << "BEST backup file:" << mostRecentBackupFileName << " last modified:" << mostRecentBackupTime.toString();
qCDebug(octree) << "Removing old file:" << _filename;
remove(qPrintable(_filename));
qCDebug(octree) << "Restoring backup file " << mostRecentBackupFileName << "...";
bool result = QFile::copy(mostRecentBackupFileName, _filename);
if (result) {
qCDebug(octree) << "DONE restoring backup file " << mostRecentBackupFileName << "to" << _filename << "...";
} else {
qCDebug(octree) << "ERROR while restoring backup file " << mostRecentBackupFileName << "to" << _filename << "...";
perror("ERROR while restoring backup file");
}
} else {
qCDebug(octree) << "NO BEST backup file found.";
}
}
bool OctreePersistThread::getMostRecentBackup(const QString& format,
QString& mostRecentBackupFileName, QDateTime& mostRecentBackupTime) {
// Based on our backup file name, determine the path and file name pattern for backup files
QFileInfo persistFileInfo(_filename);
QString path = _backupDirectory;
QString fileNamePart = persistFileInfo.fileName();
QStringList filters;
if (format.isEmpty()) {
// Create a file filter that will find all backup files of this extension format
foreach(const BackupRule& rule, _backupRules) {
QString backupExtension = rule.extensionFormat;
backupExtension.replace(QRegExp("%."), "*");
QString backupFileNamePart = fileNamePart + backupExtension;
filters << backupFileNamePart;
}
} else {
QString backupExtension = format;
backupExtension.replace(QRegExp("%."), "*");
QString backupFileNamePart = fileNamePart + backupExtension;
filters << backupFileNamePart;
}
bool bestBackupFound = false;
QString bestBackupFile;
QDateTime bestBackupFileTime;
// Iterate over all of the backup files in the persist location
QDirIterator dirIterator(path, filters, QDir::Files|QDir::NoSymLinks, QDirIterator::NoIteratorFlags);
while(dirIterator.hasNext()) {
dirIterator.next();
QDateTime lastModified = dirIterator.fileInfo().lastModified();
// Based on last modified date, track the most recently modified file as the best backup
if (lastModified > bestBackupFileTime) {
bestBackupFound = true;
bestBackupFile = dirIterator.filePath();
bestBackupFileTime = lastModified;
}
}
// If we found a backup then return the results
if (bestBackupFound) {
mostRecentBackupFileName = bestBackupFile;
mostRecentBackupTime = bestBackupFileTime;
}
return bestBackupFound;
}
void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) {
if (rule.extensionFormat.contains("%N")) {
if (rule.maxBackupVersions > 0) {
qCDebug(octree) << "Rolling old backup versions for rule" << rule.name << "...";
QString backupFileName = _backupDirectory + "/" + QUrl(_filename).fileName();
// Delete maximum rolling file because rename() fails on Windows if target exists
QString backupMaxExtensionN = rule.extensionFormat;
backupMaxExtensionN.replace(QString("%N"), QString::number(rule.maxBackupVersions));
QString backupMaxFilenameN = backupFileName + backupMaxExtensionN;
QFile backupMaxFileN(backupMaxFilenameN);
if (backupMaxFileN.exists()) {
int result = remove(qPrintable(backupMaxFilenameN));
if (result != 0) {
qCDebug(octree) << "ERROR deleting old backup file " << backupMaxFilenameN;
}
}
for(int n = rule.maxBackupVersions - 1; n > 0; n--) {
QString backupExtensionN = rule.extensionFormat;
QString backupExtensionNplusOne = rule.extensionFormat;
backupExtensionN.replace(QString("%N"), QString::number(n));
backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1));
QString backupFilenameN = findMostRecentFileExtension(backupFileName, PERSIST_EXTENSIONS) + backupExtensionN;
QString backupFilenameNplusOne = backupFileName + backupExtensionNplusOne;
QFile backupFileN(backupFilenameN);
if (backupFileN.exists()) {
qCDebug(octree) << "rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
int result = rename(qPrintable(backupFilenameN), qPrintable(backupFilenameNplusOne));
if (result == 0) {
qCDebug(octree) << "DONE rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
} else {
qCDebug(octree) << "ERROR in rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
perror("ERROR in rolling backup file");
}
}
}
qCDebug(octree) << "Done rolling old backup versions...";
} else {
qCDebug(octree) << "Rolling backups for rule" << rule.name << "."
<< " Max Rolled Backup Versions less than 1 [" << rule.maxBackupVersions << "]."
<< " No need to roll backups...";
}
}
}
void OctreePersistThread::backup() {
qCDebug(octree) << "backup operation wantBackup:" << _wantBackup;
if (_wantBackup) {
quint64 now = usecTimestampNow();
for(int i = 0; i < _backupRules.count(); i++) {
BackupRule& rule = _backupRules[i];
quint64 sinceLastBackup = now - rule.lastBackup;
quint64 SECS_TO_USECS = 1000 * 1000;
quint64 intervalToBackup = rule.interval * SECS_TO_USECS;
qCDebug(octree) << "Checking [" << rule.name << "] - Time since last backup [" << sinceLastBackup << "] " <<
"compared to backup interval [" << intervalToBackup << "]...";
if (sinceLastBackup > intervalToBackup) {
qCDebug(octree) << "Time since last backup [" << sinceLastBackup << "] for rule [" << rule.name
<< "] exceeds backup interval [" << intervalToBackup << "] doing backup now...";
struct tm* localTime = localtime(&_lastPersistTime);
QString backupFileName = _backupDirectory + "/" + QUrl(_filename).fileName();
// check to see if they asked for version rolling format
if (rule.extensionFormat.contains("%N")) {
rollOldBackupVersions(rule); // rename all the old backup files accordingly
QString backupExtension = rule.extensionFormat;
backupExtension.replace(QString("%N"), QString("1"));
backupFileName += backupExtension;
} else {
char backupExtension[256];
strftime(backupExtension, sizeof(backupExtension), qPrintable(rule.extensionFormat), localTime);
backupFileName += backupExtension;
}
if (rule.maxBackupVersions > 0) {
QFile persistFile(_filename);
if (persistFile.exists()) {
qCDebug(octree) << "backing up persist file " << _filename << "to" << backupFileName << "...";
bool result = QFile::copy(_filename, backupFileName);
if (result) {
qCDebug(octree) << "DONE backing up persist file...";
rule.lastBackup = now; // only record successful backup in this case.
} else {
qCDebug(octree) << "ERROR in backing up persist file...";
perror("ERROR in backing up persist file");
}
} else {
qCDebug(octree) << "persist file " << _filename << " does not exist. " <<
"nothing to backup for this rule ["<< rule.name << "]...";
}
} else {
qCDebug(octree) << "This backup rule" << rule.name
<< " has Max Rolled Backup Versions less than 1 [" << rule.maxBackupVersions << "]."
<< " There are no backups to be done...";
}
} else {
qCDebug(octree) << "Backup not needed for this rule ["<< rule.name << "]...";
}
}
}
}

View file

@ -32,20 +32,22 @@ public:
static const int DEFAULT_PERSIST_INTERVAL;
OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory,
int persistInterval = DEFAULT_PERSIST_INTERVAL, bool wantBackup = false,
const QJsonObject& settings = QJsonObject(), bool debugTimestampNow = false,
QString persistAsFileType = "json.gz", const QByteArray& replacementData = QByteArray());
OctreePersistThread(OctreePointer tree,
const QString& filename,
int persistInterval = DEFAULT_PERSIST_INTERVAL,
bool debugTimestampNow = false,
QString persistAsFileType = "json.gz",
const QByteArray& replacementData = QByteArray());
bool isInitialLoadComplete() const { return _initialLoadComplete; }
quint64 getLoadElapsedTime() const { return _loadTimeUSecs; }
void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist
QString getPersistFilename() const { return _filename; }
QString getPersistFileMimeType() const;
QByteArray getPersistFileContents() const;
void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist
signals:
void loadCompleted();
@ -54,12 +56,6 @@ protected:
virtual bool process() override;
void persist();
void backup();
void rollOldBackupVersions(const BackupRule& rule);
void restoreFromMostRecentBackup();
bool getMostRecentBackup(const QString& format, QString& mostRecentBackupFileName, QDateTime& mostRecentBackupTime);
quint64 getMostRecentBackupTimeInUsecs(const QString& format);
void parseSettings(const QJsonObject& settings);
bool backupCurrentFile();
void replaceData(QByteArray data);
@ -68,17 +64,13 @@ protected:
private:
OctreePointer _tree;
QString _filename;
QString _backupDirectory;
int _persistInterval;
bool _initialLoadComplete;
QByteArray _replacementData;
quint64 _loadTimeUSecs;
time_t _lastPersistTime;
quint64 _lastCheck;
bool _wantBackup;
QVector<BackupRule> _backupRules;
bool _debugTimestampNow;
quint64 _lastTimeDebug;