mirror of https://gitee.com/bigwinds/arangodb
restore _users collection at the very end only (#9899)
* restore _users collection at the very end only, because updating documents in _users may change the credentials for the arangorestore user itself, which may lead to all follow-up requests by arangorestore being rejected with "authorized"/"forbidden" * updated CHANGELOG
This commit is contained in:
parent
85814c05f1
commit
0fefe62d35
|
@ -6,6 +6,12 @@ v3.5.1 (XXXX-XX-XX)
|
||||||
|
|
||||||
* Drop collection action to timeout more quickly to stay on fast lane.
|
* Drop collection action to timeout more quickly to stay on fast lane.
|
||||||
|
|
||||||
|
* Make arangorestore restore data into the `_users` collection last. This is to
|
||||||
|
ensure that arangorestore does not overwrite the credentials of its invoker while
|
||||||
|
the restore is running, but only at the very end of the process.
|
||||||
|
This change also makes arangorestore restore the `_system` database last if it
|
||||||
|
is started with the `--all-databases` option.
|
||||||
|
|
||||||
* Check for duplicate server endpoints registered in the agency in sub-keys of
|
* Check for duplicate server endpoints registered in the agency in sub-keys of
|
||||||
`/Current/ServersRegistered`.
|
`/Current/ServersRegistered`.
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ arangodb::Result checkHttpResponse(arangodb::httpclient::SimpleHttpClient& clien
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Sort collections for proper recreation order
|
/// @brief Sort collections for proper recreation order
|
||||||
bool sortCollections(VPackBuilder const& l, VPackBuilder const& r) {
|
bool sortCollectionsForCreation(VPackBuilder const& l, VPackBuilder const& r) {
|
||||||
VPackSlice const left = l.slice().get("parameters");
|
VPackSlice const left = l.slice().get("parameters");
|
||||||
VPackSlice const right = r.slice().get("parameters");
|
VPackSlice const right = r.slice().get("parameters");
|
||||||
|
|
||||||
|
@ -898,24 +898,28 @@ arangodb::Result processInputDirectory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::sort(collections.begin(), collections.end(), ::sortCollections);
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<arangodb::RestoreFeature::JobData>> jobs(
|
// order collections so that prototypes for distributeShardsLike come first
|
||||||
collections.size());
|
std::sort(collections.begin(), collections.end(), ::sortCollectionsForCreation);
|
||||||
|
|
||||||
|
std::unique_ptr<arangodb::RestoreFeature::JobData> usersData;
|
||||||
|
std::vector<std::unique_ptr<arangodb::RestoreFeature::JobData>> jobs;
|
||||||
|
jobs.reserve(collections.size());
|
||||||
|
|
||||||
bool didModifyFoxxCollection = false;
|
bool didModifyFoxxCollection = false;
|
||||||
// Step 2: create collections
|
// Step 2: create collections
|
||||||
for (VPackBuilder const& b : collections) {
|
for (VPackBuilder const& b : collections) {
|
||||||
VPackSlice const collection = b.slice();
|
VPackSlice const collection = b.slice();
|
||||||
VPackSlice params = collection.get("parameters");
|
VPackSlice params = collection.get("parameters");
|
||||||
|
VPackSlice name = VPackSlice::emptyStringSlice();
|
||||||
if (params.isObject()) {
|
if (params.isObject()) {
|
||||||
params = params.get("name");
|
name = params.get("name");
|
||||||
// Only these two are relevant for FOXX.
|
// Only these two are relevant for FOXX.
|
||||||
if (params.isString() && (params.isEqualString("_apps") ||
|
if (name.isString() && (name.isEqualString("_apps") ||
|
||||||
params.isEqualString("_appbundles"))) {
|
name.isEqualString("_appbundles"))) {
|
||||||
didModifyFoxxCollection = true;
|
didModifyFoxxCollection = true;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
auto jobData =
|
auto jobData =
|
||||||
std::make_unique<arangodb::RestoreFeature::JobData>(directory, feature, options,
|
std::make_unique<arangodb::RestoreFeature::JobData>(directory, feature, options,
|
||||||
|
@ -928,11 +932,19 @@ arangodb::Result processInputDirectory(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stats.totalCollections++;
|
|
||||||
|
if (name.isString() && name.stringRef() == "_users") {
|
||||||
jobs.push_back(std::move(jobData));
|
// special treatment for _users collection - this must be the very last,
|
||||||
|
// and run isolated from all previous data loading operations - the
|
||||||
|
// reason is that loading into the users collection may change the
|
||||||
|
// credentials for the current arangorestore connection!
|
||||||
|
usersData = std::move(jobData);
|
||||||
|
} else {
|
||||||
|
stats.totalCollections++;
|
||||||
|
jobs.push_back(std::move(jobData));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4: fire up data transfer
|
// Step 4: fire up data transfer
|
||||||
for (auto& job : jobs) {
|
for (auto& job : jobs) {
|
||||||
if (!jobQueue.queueJob(std::move(job))) {
|
if (!jobQueue.queueJob(std::move(job))) {
|
||||||
|
@ -976,8 +988,8 @@ arangodb::Result processInputDirectory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// should instantly return
|
|
||||||
jobQueue.waitForIdle();
|
jobQueue.waitForIdle();
|
||||||
|
jobs.clear();
|
||||||
|
|
||||||
Result firstError = feature.getFirstError();
|
Result firstError = feature.getFirstError();
|
||||||
if (firstError.fail()) {
|
if (firstError.fail()) {
|
||||||
|
@ -989,10 +1001,10 @@ arangodb::Result processInputDirectory(
|
||||||
Result res = ::triggerFoxxHeal(httpClient);
|
Result res = ::triggerFoxxHeal(httpClient);
|
||||||
if (res.fail()) {
|
if (res.fail()) {
|
||||||
LOG_TOPIC("47cd7", WARN, Logger::RESTORE)
|
LOG_TOPIC("47cd7", WARN, Logger::RESTORE)
|
||||||
<< "Reloading of Foxx services failed. In the cluster Foxx "
|
<< "Reloading of Foxx services failed: " << res.errorMessage()
|
||||||
"services will be available eventually, On single servers send "
|
<< "- in the cluster Foxx services will be available eventually, On single servers send "
|
||||||
"a POST to '/_api/foxx/_local/heal' on the current database, "
|
<< "a POST to '/_api/foxx/_local/heal' on the current database, "
|
||||||
"with an empty body.";
|
<< "with an empty body.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1013,6 +1025,22 @@ arangodb::Result processInputDirectory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Last step: reload data into _users. Note: this can change the credentials
|
||||||
|
// of the arangorestore user itself
|
||||||
|
if (usersData) {
|
||||||
|
TRI_ASSERT(jobs.empty());
|
||||||
|
if (!jobQueue.queueJob(std::move(usersData))) {
|
||||||
|
return Result(TRI_ERROR_OUT_OF_MEMORY, "unable to queue restore job");
|
||||||
|
}
|
||||||
|
jobQueue.waitForIdle();
|
||||||
|
jobs.clear();
|
||||||
|
|
||||||
|
Result firstError = feature.getFirstError();
|
||||||
|
if (firstError.fail()) {
|
||||||
|
return firstError;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (std::exception const& ex) {
|
} catch (std::exception const& ex) {
|
||||||
return {TRI_ERROR_INTERNAL,
|
return {TRI_ERROR_INTERNAL,
|
||||||
std::string(
|
std::string(
|
||||||
|
@ -1357,12 +1385,16 @@ void RestoreFeature::start() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort by name, with _system first
|
// sort by name, with _system last
|
||||||
|
// this is necessary because in the system database there is the _users collection,
|
||||||
|
// and we have to process users last of all. otherwise we risk updating the
|
||||||
|
// credentials for the user which users the current arangorestore connection, and
|
||||||
|
// this will make subsequent arangorestore calls to the server fail with "unauthorized"
|
||||||
std::sort(databases.begin(), databases.end(), [](std::string const& lhs, std::string const& rhs) {
|
std::sort(databases.begin(), databases.end(), [](std::string const& lhs, std::string const& rhs) {
|
||||||
if (lhs == "_system" && rhs != "_system") {
|
if (lhs == "_system" && rhs != "_system") {
|
||||||
return true;
|
|
||||||
} else if (rhs == "_system" && lhs != "_system") {
|
|
||||||
return false;
|
return false;
|
||||||
|
} else if (rhs == "_system" && lhs != "_system") {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return lhs < rhs;
|
return lhs < rhs;
|
||||||
});
|
});
|
||||||
|
|
|
@ -321,7 +321,7 @@ inline void ClientTaskQueue<JobData>::waitForIdle() noexcept {
|
||||||
}
|
}
|
||||||
|
|
||||||
CONDITION_LOCKER(lock, _workersCondition);
|
CONDITION_LOCKER(lock, _workersCondition);
|
||||||
lock.wait(std::chrono::milliseconds(250));
|
lock.wait(std::chrono::milliseconds(100));
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue