Renamed backup state enums for clarity, added backup state names. Changed Epochs to EpochSeconds in backup JSON along with some other renaming/moving of fields, and added information about snapshot dispatch. Changed timestamp format for input/output in all backup/restore contexts to be a fully qualified time with timezone offset. Added information about the last snapshot dispatch to backup config and status (not yet populated).

This commit is contained in:
Stephen Atherton 2019-03-10 16:00:01 -07:00
parent 8bdb21a04e
commit 023bbb566f
5 changed files with 117 additions and 55 deletions

View File

@ -840,7 +840,7 @@ static void printBackupUsage(bool devhelp) {
" File containing blob credentials in JSON format. Can be specified multiple times for multiple files. See below for more details.\n");
printf(" --expire_before_timestamp DATETIME\n"
" Datetime cutoff for expire operations. Requires a cluster file and will use version/timestamp metadata\n"
" in the database to obtain a cutoff version very close to the timestamp given in YYYY-MM-DD.HH:MI:SS format (UTC).\n");
" in the database to obtain a cutoff version very close to the timestamp given in %s.\n", BackupAgentBase::timeFormat().c_str());
printf(" --expire_before_version VERSION\n"
" Version cutoff for expire operations. Deletes data files containing no data at or after VERSION.\n");
printf(" --delete_before_days NUM_DAYS\n"
@ -913,7 +913,7 @@ static void printRestoreUsage(bool devhelp ) {
printf(TLS_HELP);
#endif
printf(" -v DBVERSION The version at which the database will be restored.\n");
printf(" --timestamp Instead of a numeric version, use this to specify a timestamp in YYYY-MM-DD.HH:MI:SS format (UTC)\n");
printf(" --timestamp Instead of a numeric version, use this to specify a timestamp in %s\n", BackupAgentBase::timeFormat().c_str());
printf(" and it will be converted to a version from that time using metadata in orig_cluster_file.\n");
printf(" --orig_cluster_file CONNFILE\n");
printf(" The cluster file for the original database from which the backup was created. The original database\n");
@ -1252,8 +1252,8 @@ ACTOR Future<std::string> getLayerStatus(Reference<ReadYourWritesTransaction> tr
tagRoot.create("current_status") = statusText;
tagRoot.create("last_restorable_version") = tagLastRestorableVersions[j].get();
tagRoot.create("last_restorable_seconds_behind") = last_restorable_seconds_behind;
tagRoot.create("running_backup") = (status == BackupAgentBase::STATE_DIFFERENTIAL || status == BackupAgentBase::STATE_BACKUP);
tagRoot.create("running_backup_is_restorable") = (status == BackupAgentBase::STATE_DIFFERENTIAL);
tagRoot.create("running_backup") = (status == BackupAgentBase::STATE_RUNNING_DIFFERENTIAL || status == BackupAgentBase::STATE_RUNNING);
tagRoot.create("running_backup_is_restorable") = (status == BackupAgentBase::STATE_RUNNING_DIFFERENTIAL);
tagRoot.create("range_bytes_written") = tagRangeBytes[j].get();
tagRoot.create("mutation_log_bytes_written") = tagLogBytes[j].get();
tagRoot.create("mutation_stream_id") = backupTagUids[j].toString();
@ -1296,8 +1296,8 @@ ACTOR Future<std::string> getLayerStatus(Reference<ReadYourWritesTransaction> tr
BackupAgentBase::enumState status = (BackupAgentBase::enumState)backupStatus[i].get();
JSONDoc tagRoot = tagsRoot.create(tagName);
tagRoot.create("running_backup") = (status == BackupAgentBase::STATE_DIFFERENTIAL || status == BackupAgentBase::STATE_BACKUP);
tagRoot.create("running_backup_is_restorable") = (status == BackupAgentBase::STATE_DIFFERENTIAL);
tagRoot.create("running_backup") = (status == BackupAgentBase::STATE_RUNNING_DIFFERENTIAL || status == BackupAgentBase::STATE_RUNNING);
tagRoot.create("running_backup_is_restorable") = (status == BackupAgentBase::STATE_RUNNING_DIFFERENTIAL);
tagRoot.create("range_bytes_written") = tagRangeBytesDR[i].get();
tagRoot.create("mutation_log_bytes_written") = tagLogBytesDR[i].get();
tagRoot.create("mutation_stream_id") = drTagUids[i].toString();

View File

@ -44,17 +44,29 @@ public:
char buffer[128];
struct tm timeinfo;
getLocalTime(&curTime, &timeinfo);
strftime(buffer, 128, "%Y/%m/%d %H:%M:%S", &timeinfo);
strftime(buffer, 128, "%Y/%m/%d.%H:%M:%S%z", &timeinfo);
return buffer;
}
static std::string timeFormat() {
return "YYYY/MM/DD.HH:MI:SS[+/-]HHMM";
}
static int64_t parseTime(std::string timestamp) {
struct tm out;
if (strptime(timestamp.c_str(), "%Y/%m/%d.%H:%M:%S%z", &out) == nullptr) {
return -1;
}
return (int64_t) mktime(&out);
}
// Type of program being executed
enum enumActionResult {
RESULT_SUCCESSFUL = 0, RESULT_ERRORED = 1, RESULT_DUPLICATE = 2, RESULT_UNNEEDED = 3
};
enum enumState {
STATE_ERRORED = 0, STATE_SUBMITTED = 1, STATE_BACKUP = 2, STATE_DIFFERENTIAL = 3, STATE_COMPLETED = 4, STATE_NEVERRAN = 5, STATE_ABORTED = 6, STATE_PARTIALLY_ABORTED = 7
STATE_ERRORED = 0, STATE_SUBMITTED = 1, STATE_RUNNING = 2, STATE_RUNNING_DIFFERENTIAL = 3, STATE_COMPLETED = 4, STATE_NEVERRAN = 5, STATE_ABORTED = 6, STATE_PARTIALLY_ABORTED = 7
};
static const Key keyFolderId;
@ -100,11 +112,11 @@ public:
}
else if (!stateText.compare("has been started")) {
enState = STATE_BACKUP;
enState = STATE_RUNNING;
}
else if (!stateText.compare("is differential")) {
enState = STATE_DIFFERENTIAL;
enState = STATE_RUNNING_DIFFERENTIAL;
}
else if (!stateText.compare("has been completed")) {
@ -122,7 +134,7 @@ public:
return enState;
}
// Convert the status text to an enumerated value
// Convert the status enum to a text description
static const char* getStateText(enumState enState)
{
const char* stateText;
@ -138,10 +150,10 @@ public:
case STATE_SUBMITTED:
stateText = "has been submitted";
break;
case STATE_BACKUP:
case STATE_RUNNING:
stateText = "has been started";
break;
case STATE_DIFFERENTIAL:
case STATE_RUNNING_DIFFERENTIAL:
stateText = "is differential";
break;
case STATE_COMPLETED:
@ -161,6 +173,45 @@ public:
return stateText;
}
// Convert the status enum to a name
static const char* getStateName(enumState enState)
{
const char* s;
switch (enState)
{
case STATE_ERRORED:
s = "Errored";
break;
case STATE_NEVERRAN:
s = "NeverRan";
break;
case STATE_SUBMITTED:
s = "Submitted";
break;
case STATE_RUNNING:
s = "Running";
break;
case STATE_RUNNING_DIFFERENTIAL:
s = "RunningDifferentially";
break;
case STATE_COMPLETED:
s = "Completed";
break;
case STATE_ABORTED:
s = "Aborted";
break;
case STATE_PARTIALLY_ABORTED:
s = "Aborting";
break;
default:
s = "<undefined>";
break;
}
return s;
}
// Determine if the specified state is runnable
static bool isRunnable(enumState enState)
{
@ -169,8 +220,8 @@ public:
switch (enState)
{
case STATE_SUBMITTED:
case STATE_BACKUP:
case STATE_DIFFERENTIAL:
case STATE_RUNNING:
case STATE_RUNNING_DIFFERENTIAL:
case STATE_PARTIALLY_ABORTED:
isRunnable = true;
break;
@ -691,6 +742,14 @@ public:
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
KeyBackedBinaryValue<int64_t> snapshotDispatchLastShardsBehind() {
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
KeyBackedProperty<Version> snapshotDispatchLastVersion() {
return configSpace.pack(LiteralStringRef(__FUNCTION__));
}
Future<Void> initNewSnapshot(Reference<ReadYourWritesTransaction> tr, int64_t intervalSeconds = -1) {
BackupConfig &copy = *this; // Capture this by value instead of this ptr
@ -714,6 +773,8 @@ public:
copy.snapshotBeginVersion().set(tr, beginVersion.get());
copy.snapshotTargetEndVersion().set(tr, endVersion);
copy.snapshotRangeFileCount().set(tr, 0);
copy.snapshotDispatchLastVersion().clear(tr);
copy.snapshotDispatchLastShardsBehind().clear(tr);
return Void();
});

View File

@ -175,7 +175,7 @@ std::string BackupDescription::toJSON() const {
auto i = versionTimeMap.find(v);
if(i != versionTimeMap.end()) {
doc.setKey("Timestamp", BackupAgentBase::formatTime(i->second));
doc.setKey("Epochs", i->second);
doc.setKey("EpochSeconds", i->second);
}
}
else if(maxLogEnd.present()) {
@ -1628,20 +1628,11 @@ ACTOR Future<Version> timeKeeperVersionFromDatetime(std::string datetime, Databa
state KeyBackedMap<int64_t, Version> versionMap(timeKeeperPrefixRange.begin);
state Reference<ReadYourWritesTransaction> tr = Reference<ReadYourWritesTransaction>(new ReadYourWritesTransaction(db));
int year, month, day, hour, minute, second;
if (sscanf(datetime.c_str(), "%d-%d-%d.%d:%d:%d", &year, &month, &day, &hour, &minute, &second) != 6) {
fprintf(stderr, "ERROR: Incorrect date/time format.\n");
state int64_t time = BackupAgentBase::parseTime(datetime);
if(time < 0) {
fprintf(stderr, "ERROR: Incorrect date/time or format. Format is %s.\n", BackupAgentBase::timeFormat().c_str());
throw backup_error();
}
struct tm expDateTime = {0};
expDateTime.tm_year = year - 1900;
expDateTime.tm_mon = month - 1;
expDateTime.tm_mday = day;
expDateTime.tm_hour = hour;
expDateTime.tm_min = minute;
expDateTime.tm_sec = second;
expDateTime.tm_isdst = -1;
state int64_t time = (int64_t) mktime(&expDateTime);
loop {
try {

View File

@ -1376,7 +1376,7 @@ namespace dbBackup {
try {
tr.setOption(FDBTransactionOptions::LOCK_AWARE);
tr.addReadConflictRange(singleKeyRange(sourceStates.pack(DatabaseBackupAgent::keyStateStatus)));
tr.set(sourceStates.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_DIFFERENTIAL)));
tr.set(sourceStates.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_RUNNING_DIFFERENTIAL)));
Key versionKey = task->params[DatabaseBackupAgent::keyConfigLogUid].withPrefix(task->params[BackupAgentBase::destUid]).withPrefix(backupLatestVersionsPrefix);
Optional<Key> prevBeginVersion = wait(tr.get(versionKey));
@ -1418,7 +1418,7 @@ namespace dbBackup {
wait(success(FinishedFullBackupTaskFunc::addTask(tr, taskBucket, task, TaskCompletionKey::noSignal())));
}
else { // Start the writing of logs, if differential
tr->set(states.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_DIFFERENTIAL)));
tr->set(states.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_RUNNING_DIFFERENTIAL)));
allPartsDone = futureBucket->future(tr);
@ -1544,7 +1544,7 @@ namespace dbBackup {
srcTr2->set( Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keySourceTagName).pack(task->params[BackupAgentBase::keyTagName]), logUidValue );
srcTr2->set( sourceStates.pack(DatabaseBackupAgent::keyFolderId), task->params[DatabaseBackupAgent::keyFolderId] );
srcTr2->set( sourceStates.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_BACKUP)));
srcTr2->set( sourceStates.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_RUNNING)));
state Key destPath = destUidValue.withPrefix(backupLogKeys.begin);
// Start logging the mutations for the specified ranges of the tag
@ -1572,7 +1572,7 @@ namespace dbBackup {
tr->set(logUidValue.withPrefix(applyMutationsBeginRange.begin), BinaryWriter::toValue(beginVersion, Unversioned()));
tr->set(logUidValue.withPrefix(applyMutationsEndRange.begin), BinaryWriter::toValue(beginVersion, Unversioned()));
tr->set(states.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_BACKUP)));
tr->set(states.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_RUNNING)));
state Reference<TaskFuture> kvBackupRangeComplete = futureBucket->future(tr);
state Reference<TaskFuture> kvBackupComplete = futureBucket->future(tr);
@ -1776,7 +1776,7 @@ public:
}
// Break, if in differential mode (restorable) and stopWhenDone is not enabled
if ((!stopWhenDone) && (BackupAgentBase::STATE_DIFFERENTIAL == status)) {
if ((!stopWhenDone) && (BackupAgentBase::STATE_RUNNING_DIFFERENTIAL == status)) {
return status;
}
@ -1939,7 +1939,7 @@ public:
state int status = wait(backupAgent->getStateValue(dest, destlogUid));
TraceEvent("DBA_SwitchoverStart").detail("Status", status);
if (status != BackupAgentBase::STATE_DIFFERENTIAL && status != BackupAgentBase::STATE_COMPLETED) {
if (status != BackupAgentBase::STATE_RUNNING_DIFFERENTIAL && status != BackupAgentBase::STATE_COMPLETED) {
throw backup_duplicate();
}
@ -2296,10 +2296,10 @@ public:
case BackupAgentBase::STATE_SUBMITTED:
statusText += "The DR on tag `" + tagNameDisplay + "' is NOT a complete copy of the primary database (just started).\n";
break;
case BackupAgentBase::STATE_BACKUP:
case BackupAgentBase::STATE_RUNNING:
statusText += "The DR on tag `" + tagNameDisplay + "' is NOT a complete copy of the primary database.\n";
break;
case BackupAgentBase::STATE_DIFFERENTIAL:
case BackupAgentBase::STATE_RUNNING_DIFFERENTIAL:
statusText += "The DR on tag `" + tagNameDisplay + "' is a complete copy of the primary database.\n";
break;
case BackupAgentBase::STATE_COMPLETED:

View File

@ -2059,8 +2059,8 @@ namespace fileBackup {
}
// If the backup is restorable but the state is not differential then set state to differential
if(restorableVersion.present() && backupState != BackupAgentBase::STATE_DIFFERENTIAL)
config.stateEnum().set(tr, BackupAgentBase::STATE_DIFFERENTIAL);
if(restorableVersion.present() && backupState != BackupAgentBase::STATE_RUNNING_DIFFERENTIAL)
config.stateEnum().set(tr, BackupAgentBase::STATE_RUNNING_DIFFERENTIAL);
// If stopWhenDone is set and there is a restorable version, set the done future and do not create further tasks.
if(stopWhenDone && restorableVersion.present()) {
@ -2295,8 +2295,8 @@ namespace fileBackup {
}
// If the backup is restorable and the state isn't differential the set state to differential
if(restorableVersion.present() && backupState != BackupAgentBase::STATE_DIFFERENTIAL)
config.stateEnum().set(tr, BackupAgentBase::STATE_DIFFERENTIAL);
if(restorableVersion.present() && backupState != BackupAgentBase::STATE_RUNNING_DIFFERENTIAL)
config.stateEnum().set(tr, BackupAgentBase::STATE_RUNNING_DIFFERENTIAL);
// Unless we are to stop, start the next snapshot using the default interval
Reference<TaskFuture> snapshotDoneFuture = task->getDoneFuture(futureBucket);
@ -2376,7 +2376,7 @@ namespace fileBackup {
config.startMutationLogs(tr, backupRange, destUidValue);
}
config.stateEnum().set(tr, EBackupState::STATE_BACKUP);
config.stateEnum().set(tr, EBackupState::STATE_RUNNING);
state Reference<TaskFuture> backupFinished = futureBucket->future(tr);
@ -3474,7 +3474,7 @@ public:
// Break, if one of the following is true
// - no longer runnable
// - in differential mode (restorable) and stopWhenDone is not enabled
if( !FileBackupAgent::isRunnable(status) || ((!stopWhenDone) && (BackupAgentBase::STATE_DIFFERENTIAL == status) )) {
if( !FileBackupAgent::isRunnable(status) || ((!stopWhenDone) && (BackupAgentBase::STATE_RUNNING_DIFFERENTIAL == status) )) {
if(pContainer != nullptr) {
Reference<IBackupContainer> c = wait(config.backupContainer().getOrThrow(tr, false, backup_invalid_info()));
@ -3822,7 +3822,7 @@ public:
if(version.present()) {
doc.setKey("Version", version.get());
if(epochs.present()) {
doc.setKey("Epochs", epochs.get());
doc.setKey("EpochSeconds", epochs.get());
doc.setKey("Timestamp", timeStampToString(epochs));
}
}
@ -3865,8 +3865,10 @@ public:
state EBackupState backupState = wait(config.stateEnum().getD(tr, false, EBackupState::STATE_NEVERRAN));
JsonBuilderObject statusDoc;
statusDoc.setKey("Enum", (int)backupState);
statusDoc.setKey("Name", BackupAgentBase::getStateName(backupState));
statusDoc.setKey("Description", BackupAgentBase::getStateText(backupState));
statusDoc.setKey("Completed", backupState == BackupAgentBase::STATE_COMPLETED);
statusDoc.setKey("Running", BackupAgentBase::isRunnable(backupState));
doc.setKey("Status", statusDoc);
state Future<Void> done = Void();
@ -3881,19 +3883,17 @@ public:
doc.setKey("Restorable", latestRestorable.present());
if(latestRestorable.present() && backupState != BackupAgentBase::STATE_COMPLETED) {
if(latestRestorable.present()) {
JsonBuilderObject o = latestRestorable.toJSON();
o.setKey("LagSeconds", (recentReadVersion - latestRestorable.version.get()) / CLIENT_KNOBS->CORE_VERSIONSPERSECOND);
if(backupState != BackupAgentBase::STATE_COMPLETED) {
o.setKey("LagSeconds", (recentReadVersion - latestRestorable.version.get()) / CLIENT_KNOBS->CORE_VERSIONSPERSECOND);
}
doc.setKey("LatestRestorablePoint", o);
}
doc.setKey("DestinationURL", bc->getURL());
if(backupState == BackupAgentBase::STATE_COMPLETED) {
doc.setKey("Completed", latestRestorable.toJSON());
}
}
if(backupState == BackupAgentBase::STATE_DIFFERENTIAL || backupState == BackupAgentBase::STATE_BACKUP) {
if(backupState == BackupAgentBase::STATE_RUNNING_DIFFERENTIAL || backupState == BackupAgentBase::STATE_RUNNING) {
state int64_t snapshotInterval;
state int64_t logBytesWritten;
state int64_t rangeBytesWritten;
@ -3902,6 +3902,8 @@ public:
state TimestampedVersion snapshotTargetEnd;
state TimestampedVersion latestLogEnd;
state TimestampedVersion latestSnapshotEnd;
state TimestampedVersion snapshotLastDispatch;
state Optional<int64_t> snapshotLastDispatchShardsBehind;
wait( store(snapshotInterval, config.snapshotIntervalSeconds().getOrThrow(tr))
&& store(logBytesWritten, config.logBytesWritten().getD(tr))
@ -3911,6 +3913,8 @@ public:
&& store(snapshotTargetEnd, getTimestampedVersion(tr, config.snapshotTargetEndVersion().get(tr)))
&& store(latestLogEnd, getTimestampedVersion(tr, config.latestLogEndVersion().get(tr)))
&& store(latestSnapshotEnd, getTimestampedVersion(tr, config.latestSnapshotEndVersion().get(tr)))
&& store(snapshotLastDispatch, getTimestampedVersion(tr, config.snapshotDispatchLastVersion().get(tr)))
&& store(snapshotLastDispatchShardsBehind, config.snapshotDispatchLastShardsBehind().get(tr))
);
doc.setKey("StopAfterSnapshot", stopWhenDone);
@ -3941,6 +3945,12 @@ public:
double progress = (interval > 0) ? (100.0 * elapsed / interval) : 100;
snapshot.setKey("ExpectedProgress", progress);
}
JsonBuilderObject dispatchDoc = snapshotLastDispatch.toJSON();
if(snapshotLastDispatchShardsBehind.present()) {
dispatchDoc.setKey("ShardsBehind", snapshotLastDispatchShardsBehind.get());
}
snapshot.setKey("LastDispatch", dispatchDoc);
}
doc.setKey("CurrentSnapshot", snapshot);
@ -4010,11 +4020,11 @@ public:
case BackupAgentBase::STATE_SUBMITTED:
statusText += "The backup on tag `" + tagName + "' is in progress (just started) to " + bc->getURL() + ".\n";
break;
case BackupAgentBase::STATE_BACKUP:
case BackupAgentBase::STATE_RUNNING:
statusText += "The backup on tag `" + tagName + "' is in progress to " + bc->getURL() + ".\n";
snapshotProgress = true;
break;
case BackupAgentBase::STATE_DIFFERENTIAL:
case BackupAgentBase::STATE_RUNNING_DIFFERENTIAL:
statusText += "The backup on tag `" + tagName + "' is restorable but continuing to " + bc->getURL() + ".\n";
snapshotProgress = true;
break;
@ -4057,7 +4067,7 @@ public:
);
statusText += format("Snapshot interval is %lld seconds. ", snapshotInterval);
if(backupState == BackupAgentBase::STATE_DIFFERENTIAL)
if(backupState == BackupAgentBase::STATE_RUNNING_DIFFERENTIAL)
statusText += format("Current snapshot progress target is %3.2f%% (>100%% means the snapshot is supposed to be done)\n", 100.0 * (recentReadVersion - snapshotBeginVersion) / (snapshotTargetEndVersion - snapshotBeginVersion)) ;
else
statusText += "The initial snapshot is still running.\n";
@ -4202,7 +4212,7 @@ public:
backupConfig = BackupConfig(uidFlag.first);
state EBackupState status = wait(backupConfig.stateEnum().getOrThrow(ryw_tr));
if (status != BackupAgentBase::STATE_DIFFERENTIAL ) {
if (status != BackupAgentBase::STATE_RUNNING_DIFFERENTIAL ) {
throw backup_duplicate();
}