Skip to content

Commit 59caa96

Browse files
authored
Merge pull request #440 from etschannen/release-5.2
backup created large transactions when erasing log ranges
2 parents e659dc7 + e82985a commit 59caa96

File tree

5 files changed

+96
-135
lines changed

5 files changed

+96
-135
lines changed

documentation/sphinx/source/release-notes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Release Notes
88
Fixes
99
-----
1010

11+
* Backup would attempt to clear too many ranges in a single transaction when erasing log ranges. `(PR #440) https://github.com/apple/foundationdb/pull/440`_
1112
* A read-only transaction using the ``READ_LOCK_AWARE`` option would fail if committed. `(PR #437) https://github.com/apple/foundationdb/pull/437`_
1213

1314
5.2.2

fdbclient/BackupAgent.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ bool copyParameter(Reference<Task> source, Reference<Task> dest, Key key);
423423
Version getVersionFromString(std::string const& value);
424424
Standalone<VectorRef<KeyRangeRef>> getLogRanges(Version beginVersion, Version endVersion, Key destUidValue, int blockSize = CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE);
425425
Standalone<VectorRef<KeyRangeRef>> getApplyRanges(Version beginVersion, Version endVersion, Key backupUid);
426-
Future<Void> eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional<Version> beginVersion = Optional<Version>(), Optional<Version> endVersion = Optional<Version>(), bool checkBackupUid = false, Version backupUid = 0);
426+
Future<Void> eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional<Version> endVersion = Optional<Version>(), bool checkBackupUid = false, Version backupUid = 0);
427427
Key getApplyKey( Version version, Key backupUid );
428428
std::pair<uint64_t, uint32_t> decodeBKMutationLogKey(Key key);
429429
Standalone<VectorRef<MutationRef>> decodeBackupLogValue(StringRef value);

fdbclient/BackupAgentBase.actor.cpp

Lines changed: 82 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -624,146 +624,112 @@ ACTOR Future<Void> applyMutations(Database cx, Key uid, Key addPrefix, Key remov
624624
}
625625
}
626626

627-
ACTOR Future<Void> _clearLogRanges(Reference<ReadYourWritesTransaction> tr, bool clearVersionHistory, Key logUidValue, Key destUidValue, Version beginVersion, Version endVersion) {
627+
ACTOR static Future<Void> _eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional<Version> endVersion, bool checkBackupUid, Version backupUid) {
628628
state Key backupLatestVersionsPath = destUidValue.withPrefix(backupLatestVersionsPrefix);
629629
state Key backupLatestVersionsKey = logUidValue.withPrefix(backupLatestVersionsPath);
630-
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
631-
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
632-
633-
state Standalone<RangeResultRef> backupVersions = wait(tr->getRange(KeyRangeRef(backupLatestVersionsPath, strinc(backupLatestVersionsPath)), CLIENT_KNOBS->TOO_MANY));
634-
635-
// Make sure version history key does exist and lower the beginVersion if needed
636-
bool foundSelf = false;
637-
for (auto backupVersion : backupVersions) {
638-
Key currLogUidValue = backupVersion.key.removePrefix(backupLatestVersionsPrefix).removePrefix(destUidValue);
639-
640-
if (currLogUidValue == logUidValue) {
641-
foundSelf = true;
642-
beginVersion = std::min(beginVersion, BinaryReader::fromStringRef<Version>(backupVersion.value, Unversioned()));
643-
}
644-
}
645630

646-
// Do not clear anything if version history key cannot be found
647-
if (!foundSelf) {
631+
if (!destUidValue.size()) {
648632
return Void();
649633
}
650634

651-
Version nextSmallestVersion = endVersion;
652-
bool clearLogRangesRequired = true;
653-
654-
// More than one backup/DR with the same range
655-
if (backupVersions.size() > 1) {
656-
for (auto backupVersion : backupVersions) {
657-
Key currLogUidValue = backupVersion.key.removePrefix(backupLatestVersionsPrefix).removePrefix(destUidValue);
658-
Version currVersion = BinaryReader::fromStringRef<Version>(backupVersion.value, Unversioned());
635+
state Reference<ReadYourWritesTransaction> tr(new ReadYourWritesTransaction(cx));
636+
loop{
637+
try {
638+
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
639+
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
659640

660-
if (currLogUidValue == logUidValue) {
661-
continue;
662-
} else if (currVersion > beginVersion) {
663-
nextSmallestVersion = std::min(currVersion, nextSmallestVersion);
664-
} else {
665-
// If we can find a version less than or equal to beginVersion, clearing log ranges is not required
666-
clearLogRangesRequired = false;
667-
break;
641+
if (checkBackupUid) {
642+
Subspace sourceStates = Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keySourceStates).get(logUidValue);
643+
Optional<Value> v = wait( tr->get( sourceStates.pack(DatabaseBackupAgent::keyFolderId) ) );
644+
if(v.present() && BinaryReader::fromStringRef<Version>(v.get(), Unversioned()) > backupUid)
645+
return Void();
668646
}
669-
}
670-
}
671-
672-
if (clearVersionHistory && backupVersions.size() == 1) {
673-
// Clear version history
674-
tr->clear(prefixRange(backupLatestVersionsPath));
675647

676-
// Clear everything under blog/[destUid]
677-
tr->clear(prefixRange(destUidValue.withPrefix(backupLogKeys.begin)));
648+
state Standalone<RangeResultRef> backupVersions = wait(tr->getRange(KeyRangeRef(backupLatestVersionsPath, strinc(backupLatestVersionsPath)), CLIENT_KNOBS->TOO_MANY));
678649

679-
// Disable committing mutations into blog
680-
tr->clear(prefixRange(destUidValue.withPrefix(logRangesRange.begin)));
681-
} else {
682-
if (clearVersionHistory) {
683-
// Clear current backup version history
684-
tr->clear(backupLatestVersionsKey);
685-
} else {
686-
// Update current backup latest version
687-
tr->set(backupLatestVersionsKey, BinaryWriter::toValue<Version>(endVersion, Unversioned()));
688-
}
650+
// Make sure version history key does exist and lower the beginVersion if needed
651+
state Version currBeginVersion = invalidVersion;
652+
for (auto backupVersion : backupVersions) {
653+
Key currLogUidValue = backupVersion.key.removePrefix(backupLatestVersionsPrefix).removePrefix(destUidValue);
689654

690-
// Clear log ranges if needed
691-
if (clearLogRangesRequired) {
692-
Standalone<VectorRef<KeyRangeRef>> ranges = getLogRanges(beginVersion, nextSmallestVersion, destUidValue);
693-
for (auto& range : ranges) {
694-
tr->clear(range);
655+
if (currLogUidValue == logUidValue) {
656+
currBeginVersion = BinaryReader::fromStringRef<Version>(backupVersion.value, Unversioned());
657+
break;
658+
}
695659
}
696-
}
697-
}
698-
699-
return Void();
700-
}
701660

702-
// The difference between beginVersion and endVersion should not be too large
703-
Future<Void> clearLogRanges(Reference<ReadYourWritesTransaction> tr, bool clearVersionHistory, Key logUidValue, Key destUidValue, Version beginVersion, Version endVersion) {
704-
return _clearLogRanges(tr, clearVersionHistory, logUidValue, destUidValue, beginVersion, endVersion);
705-
}
661+
// Do not clear anything if version history key cannot be found
662+
if (currBeginVersion == invalidVersion) {
663+
return Void();
664+
}
706665

707-
ACTOR static Future<Void> _eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional<Version> beginVersion, Optional<Version> endVersion, bool checkBackupUid, Version backupUid) {
708-
if ((beginVersion.present() && endVersion.present() && endVersion.get() <= beginVersion.get()) || !destUidValue.size())
709-
return Void();
666+
state Version currEndVersion = currBeginVersion + CLIENT_KNOBS->CLEAR_LOG_RANGE_COUNT * CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE;
667+
if(endVersion.present()) {
668+
currEndVersion = std::min(currEndVersion, endVersion.get());
669+
}
710670

711-
state Version currBeginVersion;
712-
state Version endVersionValue;
713-
state Version currEndVersion;
714-
state bool clearVersionHistory;
715-
716-
ASSERT(beginVersion.present() == endVersion.present());
717-
if (beginVersion.present()) {
718-
currBeginVersion = beginVersion.get();
719-
endVersionValue = endVersion.get();
720-
clearVersionHistory = false;
721-
} else {
722-
// If beginVersion and endVersion are not presented, it means backup is done and we need to clear version history.
723-
// Set currBeginVersion to INTMAX_MAX and it will be set to the correct version in clearLogRanges().
724-
// Set endVersionValue to INTMAX_MAX since we need to clear log ranges up to next smallest version.
725-
currBeginVersion = endVersionValue = currEndVersion = INTMAX_MAX;
726-
clearVersionHistory = true;
727-
}
671+
state Version nextSmallestVersion = currEndVersion;
672+
bool clearLogRangesRequired = true;
673+
674+
// More than one backup/DR with the same range
675+
if (backupVersions.size() > 1) {
676+
for (auto backupVersion : backupVersions) {
677+
Key currLogUidValue = backupVersion.key.removePrefix(backupLatestVersionsPrefix).removePrefix(destUidValue);
678+
Version currVersion = BinaryReader::fromStringRef<Version>(backupVersion.value, Unversioned());
679+
680+
if (currLogUidValue == logUidValue) {
681+
continue;
682+
} else if (currVersion > currBeginVersion) {
683+
nextSmallestVersion = std::min(currVersion, nextSmallestVersion);
684+
} else {
685+
// If we can find a version less than or equal to beginVersion, clearing log ranges is not required
686+
clearLogRangesRequired = false;
687+
break;
688+
}
689+
}
690+
}
728691

692+
if (!endVersion.present() && backupVersions.size() == 1) {
693+
// Clear version history
694+
tr->clear(prefixRange(backupLatestVersionsPath));
729695

730-
while (currBeginVersion < endVersionValue || clearVersionHistory) {
731-
state Reference<ReadYourWritesTransaction> tr(new ReadYourWritesTransaction(cx));
732-
733-
loop{
734-
try {
735-
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
736-
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
737-
738-
if (checkBackupUid) {
739-
Subspace sourceStates = Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keySourceStates).get(logUidValue);
740-
Optional<Value> v = wait( tr->get( sourceStates.pack(DatabaseBackupAgent::keyFolderId) ) );
741-
if(v.present() && BinaryReader::fromStringRef<Version>(v.get(), Unversioned()) > backupUid)
742-
return Void();
743-
}
696+
// Clear everything under blog/[destUid]
697+
tr->clear(prefixRange(destUidValue.withPrefix(backupLogKeys.begin)));
744698

745-
if (!clearVersionHistory) {
746-
currEndVersion = std::min(currBeginVersion + CLIENT_KNOBS->CLEAR_LOG_RANGE_COUNT * CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE, endVersionValue);
699+
// Disable committing mutations into blog
700+
tr->clear(prefixRange(destUidValue.withPrefix(logRangesRange.begin)));
701+
} else {
702+
if (!endVersion.present() && currEndVersion >= nextSmallestVersion) {
703+
// Clear current backup version history
704+
tr->clear(backupLatestVersionsKey);
705+
} else {
706+
// Update current backup latest version
707+
tr->set(backupLatestVersionsKey, BinaryWriter::toValue<Version>(currEndVersion, Unversioned()));
747708
}
748709

749-
Void _ = wait(clearLogRanges(tr, clearVersionHistory, logUidValue, destUidValue, currBeginVersion, currEndVersion));
750-
Void _ = wait(tr->commit());
751-
752-
if (clearVersionHistory) {
753-
return Void();
710+
// Clear log ranges if needed
711+
if (clearLogRangesRequired) {
712+
Standalone<VectorRef<KeyRangeRef>> ranges = getLogRanges(currBeginVersion, nextSmallestVersion, destUidValue);
713+
for (auto& range : ranges) {
714+
tr->clear(range);
715+
}
754716
}
717+
}
718+
Void _ = wait(tr->commit());
755719

756-
currBeginVersion = currEndVersion;
757-
break;
758-
} catch (Error &e) {
759-
Void _ = wait(tr->onError(e));
720+
if (!endVersion.present() && (backupVersions.size() == 1 || currEndVersion >= nextSmallestVersion)) {
721+
return Void();
760722
}
723+
if(endVersion.present() && currEndVersion == endVersion.get()) {
724+
return Void();
725+
}
726+
tr->reset();
727+
} catch (Error &e) {
728+
Void _ = wait(tr->onError(e));
761729
}
762730
}
763-
764-
return Void();
765731
}
766732

767-
Future<Void> eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional<Version> beginVersion, Optional<Version> endVersion, bool checkBackupUid, Version backupUid) {
768-
return _eraseLogData(cx, logUidValue, destUidValue, beginVersion, endVersion, checkBackupUid, backupUid);
769-
}
733+
Future<Void> eraseLogData(Database cx, Key logUidValue, Key destUidValue, Optional<Version> endVersion, bool checkBackupUid, Version backupUid) {
734+
return _eraseLogData(cx, logUidValue, destUidValue, endVersion, checkBackupUid, backupUid);
735+
}

fdbclient/DatabaseBackupAgent.actor.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -476,21 +476,20 @@ namespace dbBackup {
476476

477477
Void _ = wait(checkTaskVersion(cx, task, EraseLogRangeTaskFunc::name, EraseLogRangeTaskFunc::version));
478478

479-
Version beginVersion = BinaryReader::fromStringRef<Version>(task->params[DatabaseBackupAgent::keyBeginVersion], Unversioned());
480479
Version endVersion = BinaryReader::fromStringRef<Version>(task->params[DatabaseBackupAgent::keyEndVersion], Unversioned());
481480

482-
Void _ = wait(eraseLogData(taskBucket->src, task->params[BackupAgentBase::keyConfigLogUid], task->params[BackupAgentBase::destUid], Optional<Version>(beginVersion), Optional<Version>(endVersion), true, BinaryReader::fromStringRef<Version>(task->params[BackupAgentBase::keyFolderId], Unversioned())));
481+
Void _ = wait(eraseLogData(taskBucket->src, task->params[BackupAgentBase::keyConfigLogUid], task->params[BackupAgentBase::destUid], Optional<Version>(endVersion), true, BinaryReader::fromStringRef<Version>(task->params[BackupAgentBase::keyFolderId], Unversioned())));
483482

484483
return Void();
485484
}
486485

487-
ACTOR static Future<Key> addTask(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket, Reference<Task> parentTask, Version beginVersion, Version endVersion, TaskCompletionKey completionKey, Reference<TaskFuture> waitFor = Reference<TaskFuture>()) {
486+
ACTOR static Future<Key> addTask(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket, Reference<Task> parentTask, Version endVersion, TaskCompletionKey completionKey, Reference<TaskFuture> waitFor = Reference<TaskFuture>()) {
488487
Key doneKey = wait(completionKey.get(tr, taskBucket));
489488
Reference<Task> task(new Task(EraseLogRangeTaskFunc::name, EraseLogRangeTaskFunc::version, doneKey, 1));
490489

491490
copyDefaultParameters(parentTask, task);
492491

493-
task->params[DatabaseBackupAgent::keyBeginVersion] = BinaryWriter::toValue(beginVersion, Unversioned());
492+
task->params[DatabaseBackupAgent::keyBeginVersion] = BinaryWriter::toValue(1, Unversioned()); //FIXME: remove in 6.X, only needed for 5.2 backward compatibility
494493
task->params[DatabaseBackupAgent::keyEndVersion] = BinaryWriter::toValue(endVersion, Unversioned());
495494

496495
if (!waitFor) {
@@ -749,7 +748,7 @@ namespace dbBackup {
749748

750749
// Do not erase at the first time
751750
if (prevBeginVersion > 0) {
752-
addTaskVector.push_back(EraseLogRangeTaskFunc::addTask(tr, taskBucket, task, prevBeginVersion, beginVersion, TaskCompletionKey::joinWith(allPartsDone)));
751+
addTaskVector.push_back(EraseLogRangeTaskFunc::addTask(tr, taskBucket, task, beginVersion, TaskCompletionKey::joinWith(allPartsDone)));
753752
}
754753

755754
Void _ = wait(waitForAll(addTaskVector) && taskBucket->finish(tr, task));
@@ -856,7 +855,7 @@ namespace dbBackup {
856855
}
857856

858857
Version backupUid = BinaryReader::fromStringRef<Version>(task->params[BackupAgentBase::keyFolderId], Unversioned());
859-
Void _ = wait(eraseLogData(taskBucket->src, logUidValue, destUidValue, Optional<Version>(), Optional<Version>(), true, backupUid));
858+
Void _ = wait(eraseLogData(taskBucket->src, logUidValue, destUidValue, Optional<Version>(), true, backupUid));
860859

861860
return Void();
862861
}
@@ -952,7 +951,7 @@ namespace dbBackup {
952951
}
953952

954953
if (prevBeginVersion > 0) {
955-
addTaskVector.push_back(EraseLogRangeTaskFunc::addTask(tr, taskBucket, task, prevBeginVersion, beginVersion, TaskCompletionKey::joinWith(allPartsDone)));
954+
addTaskVector.push_back(EraseLogRangeTaskFunc::addTask(tr, taskBucket, task, beginVersion, TaskCompletionKey::joinWith(allPartsDone)));
956955
}
957956

958957
Void _ = wait(waitForAll(addTaskVector) && taskBucket->finish(tr, task));

fdbclient/FileBackupAgent.actor.cpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,30 +1911,25 @@ namespace fileBackup {
19111911
state Reference<FlowLock> lock(new FlowLock(CLIENT_KNOBS->BACKUP_LOCK_BYTES));
19121912
Void _ = wait(checkTaskVersion(cx, task, EraseLogRangeTaskFunc::name, EraseLogRangeTaskFunc::version));
19131913

1914-
state Version beginVersion = Params.beginVersion().get(task);
19151914
state Version endVersion = Params.endVersion().get(task);
19161915
state Key destUidValue = Params.destUidValue().get(task);
19171916

19181917
state BackupConfig config(task);
19191918
state Key logUidValue = config.getUidAsKey();
19201919

1921-
if (beginVersion == 0) {
1922-
Void _ = wait(eraseLogData(cx, logUidValue, destUidValue));
1923-
} else {
1924-
Void _ = wait(eraseLogData(cx, logUidValue, destUidValue, Optional<Version>(beginVersion), Optional<Version>(endVersion)));
1925-
}
1920+
Void _ = wait(eraseLogData(cx, logUidValue, destUidValue, endVersion != 0 ? Optional<Version>(endVersion) : Optional<Version>()));
19261921

19271922
return Void();
19281923
}
19291924

1930-
ACTOR static Future<Key> addTask(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket, UID logUid, TaskCompletionKey completionKey, Key destUidValue, Version beginVersion = 0, Version endVersion = 0, Reference<TaskFuture> waitFor = Reference<TaskFuture>()) {
1925+
ACTOR static Future<Key> addTask(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket, UID logUid, TaskCompletionKey completionKey, Key destUidValue, Version endVersion = 0, Reference<TaskFuture> waitFor = Reference<TaskFuture>()) {
19311926
Key key = wait(addBackupTask(EraseLogRangeTaskFunc::name,
19321927
EraseLogRangeTaskFunc::version,
19331928
tr, taskBucket, completionKey,
19341929
BackupConfig(logUid),
19351930
waitFor,
19361931
[=](Reference<Task> task) {
1937-
Params.beginVersion().set(task, beginVersion);
1932+
Params.beginVersion().set(task, 1); //FIXME: remove in 6.X, only needed for 5.2 backward compatibility
19381933
Params.endVersion().set(task, endVersion);
19391934
Params.destUidValue().set(task, destUidValue);
19401935
},
@@ -2039,7 +2034,7 @@ namespace fileBackup {
20392034
// Do not erase at the first time
20402035
if (prevBeginVersion > 0) {
20412036
state Key destUidValue = wait(config.destUidValue().getOrThrow(tr));
2042-
Key _ = wait(EraseLogRangeTaskFunc::addTask(tr, taskBucket, config.getUid(), TaskCompletionKey::joinWith(logDispatchBatchFuture), destUidValue, prevBeginVersion, beginVersion));
2037+
Key _ = wait(EraseLogRangeTaskFunc::addTask(tr, taskBucket, config.getUid(), TaskCompletionKey::joinWith(logDispatchBatchFuture), destUidValue, beginVersion));
20432038
}
20442039

20452040
Void _ = wait(taskBucket->finish(tr, task));
@@ -3481,8 +3476,8 @@ class FileBackupAgentImpl {
34813476
tr->set(destUidLookupPath, destUidValue);
34823477
}
34833478
}
3484-
Version initVersion = 1;
3485-
tr->set(config.getUidAsKey().withPrefix(destUidValue).withPrefix(backupLatestVersionsPrefix), BinaryWriter::toValue<Version>(initVersion, Unversioned()));
3479+
3480+
tr->set(config.getUidAsKey().withPrefix(destUidValue).withPrefix(backupLatestVersionsPrefix), BinaryWriter::toValue<Version>(tr->getReadVersion().get(), Unversioned()));
34863481
config.destUidValue().set(tr, destUidValue);
34873482

34883483
// Point the tag to this new uid

0 commit comments

Comments
 (0)