2020-04-09 05:50:55 +08:00
|
|
|
/*
|
|
|
|
* SpecialKeySpace.actor.cpp
|
|
|
|
*
|
|
|
|
* This source file is part of the FoundationDB open source project
|
|
|
|
*
|
2022-03-22 04:36:23 +08:00
|
|
|
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
2020-04-09 05:50:55 +08:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2021-02-18 17:27:14 +08:00
|
|
|
#include "boost/lexical_cast.hpp"
|
2021-02-20 06:29:08 +08:00
|
|
|
#include "boost/algorithm/string.hpp"
|
2021-02-05 16:55:34 +08:00
|
|
|
|
2021-06-04 06:10:04 +08:00
|
|
|
#include <time.h>
|
|
|
|
#include <msgpack.hpp>
|
|
|
|
|
|
|
|
#include <exception>
|
|
|
|
|
|
|
|
#include "fdbclient/ActorLineageProfiler.h"
|
2021-10-11 11:44:56 +08:00
|
|
|
#include "fdbclient/ClusterConnectionMemoryRecord.h"
|
2022-02-20 07:09:55 +08:00
|
|
|
#include "fdbclient/FDBOptions.g.h"
|
2021-06-04 06:10:04 +08:00
|
|
|
#include "fdbclient/Knobs.h"
|
|
|
|
#include "fdbclient/ProcessInterface.h"
|
2021-02-13 10:55:01 +08:00
|
|
|
#include "fdbclient/GlobalConfig.actor.h"
|
2020-03-04 10:35:24 +08:00
|
|
|
#include "fdbclient/SpecialKeySpace.actor.h"
|
2021-02-20 04:22:00 +08:00
|
|
|
#include "flow/Arena.h"
|
2020-03-04 10:35:24 +08:00
|
|
|
#include "flow/UnitTest.h"
|
2020-07-07 02:02:48 +08:00
|
|
|
#include "fdbclient/ManagementAPI.actor.h"
|
|
|
|
#include "fdbclient/StatusClient.h"
|
2020-03-04 10:35:24 +08:00
|
|
|
#include "flow/actorcompiler.h" // This must be the last #include.
|
|
|
|
|
2020-12-04 06:06:11 +08:00
|
|
|
namespace {
|
|
|
|
const std::string kTracingTransactionIdKey = "transaction_id";
|
|
|
|
const std::string kTracingTokenKey = "token";
|
2021-01-30 03:45:52 +08:00
|
|
|
|
|
|
|
static bool isAlphaNumeric(const std::string& key) {
|
|
|
|
// [A-Za-z0-9_]+
|
|
|
|
if (!key.size())
|
|
|
|
return false;
|
|
|
|
for (const char& c : key) {
|
|
|
|
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2020-12-04 06:06:11 +08:00
|
|
|
}
|
2021-01-30 03:45:52 +08:00
|
|
|
} // namespace
|
2020-12-04 06:06:11 +08:00
|
|
|
|
2020-06-30 06:37:46 +08:00
|
|
|
std::unordered_map<SpecialKeySpace::MODULE, KeyRange> SpecialKeySpace::moduleToBoundary = {
|
2020-05-13 17:28:04 +08:00
|
|
|
{ SpecialKeySpace::MODULE::TRANSACTION,
|
|
|
|
KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction0")) },
|
|
|
|
{ SpecialKeySpace::MODULE::WORKERINTERFACE,
|
|
|
|
KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"), LiteralStringRef("\xff\xff/worker_interfaces0")) },
|
|
|
|
{ SpecialKeySpace::MODULE::STATUSJSON, singleKeyRange(LiteralStringRef("\xff\xff/status/json")) },
|
|
|
|
{ SpecialKeySpace::MODULE::CONNECTIONSTRING, singleKeyRange(LiteralStringRef("\xff\xff/connection_string")) },
|
2020-05-19 01:38:23 +08:00
|
|
|
{ SpecialKeySpace::MODULE::CLUSTERFILEPATH, singleKeyRange(LiteralStringRef("\xff\xff/cluster_file_path")) },
|
2020-05-19 05:23:17 +08:00
|
|
|
{ SpecialKeySpace::MODULE::METRICS,
|
2020-06-15 13:29:44 +08:00
|
|
|
KeyRangeRef(LiteralStringRef("\xff\xff/metrics/"), LiteralStringRef("\xff\xff/metrics0")) },
|
2020-07-18 06:16:28 +08:00
|
|
|
{ SpecialKeySpace::MODULE::MANAGEMENT,
|
2020-07-31 17:18:49 +08:00
|
|
|
KeyRangeRef(LiteralStringRef("\xff\xff/management/"), LiteralStringRef("\xff\xff/management0")) },
|
2020-08-14 16:01:08 +08:00
|
|
|
{ SpecialKeySpace::MODULE::ERRORMSG, singleKeyRange(LiteralStringRef("\xff\xff/error_message")) },
|
|
|
|
{ SpecialKeySpace::MODULE::CONFIGURATION,
|
2020-12-01 06:57:17 +08:00
|
|
|
KeyRangeRef(LiteralStringRef("\xff\xff/configuration/"), LiteralStringRef("\xff\xff/configuration0")) },
|
2021-02-13 10:55:01 +08:00
|
|
|
{ SpecialKeySpace::MODULE::GLOBALCONFIG,
|
|
|
|
KeyRangeRef(LiteralStringRef("\xff\xff/global_config/"), LiteralStringRef("\xff\xff/global_config0")) },
|
2020-12-01 06:57:17 +08:00
|
|
|
{ SpecialKeySpace::MODULE::TRACING,
|
2021-06-04 06:10:04 +08:00
|
|
|
KeyRangeRef(LiteralStringRef("\xff\xff/tracing/"), LiteralStringRef("\xff\xff/tracing0")) },
|
|
|
|
{ SpecialKeySpace::MODULE::ACTORLINEAGE,
|
|
|
|
KeyRangeRef(LiteralStringRef("\xff\xff/actor_lineage/"), LiteralStringRef("\xff\xff/actor_lineage0")) },
|
|
|
|
{ SpecialKeySpace::MODULE::ACTOR_PROFILER_CONF,
|
|
|
|
KeyRangeRef(LiteralStringRef("\xff\xff/actor_profiler_conf/"),
|
2022-07-07 16:12:49 +08:00
|
|
|
LiteralStringRef("\xff\xff/actor_profiler_conf0")) },
|
|
|
|
{ SpecialKeySpace::MODULE::CLUSTERID, singleKeyRange(LiteralStringRef("\xff\xff/cluster_id")) },
|
2020-05-13 17:28:04 +08:00
|
|
|
};
|
|
|
|
|
2020-07-18 02:56:27 +08:00
|
|
|
std::unordered_map<std::string, KeyRange> SpecialKeySpace::managementApiCommandToRange = {
|
2020-07-18 06:16:28 +08:00
|
|
|
{ "exclude",
|
|
|
|
KeyRangeRef(LiteralStringRef("excluded/"), LiteralStringRef("excluded0"))
|
|
|
|
.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
|
|
|
{ "failed",
|
|
|
|
KeyRangeRef(LiteralStringRef("failed/"), LiteralStringRef("failed0"))
|
2020-10-09 05:23:02 +08:00
|
|
|
.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
2021-05-19 14:48:04 +08:00
|
|
|
{ "excludedlocality",
|
2021-06-10 08:04:05 +08:00
|
|
|
KeyRangeRef(LiteralStringRef("excluded_locality/"), LiteralStringRef("excluded_locality0"))
|
2021-05-19 14:48:04 +08:00
|
|
|
.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
|
|
|
{ "failedlocality",
|
2021-06-10 08:04:05 +08:00
|
|
|
KeyRangeRef(LiteralStringRef("failed_locality/"), LiteralStringRef("failed_locality0"))
|
2021-05-19 14:48:04 +08:00
|
|
|
.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
2020-10-23 02:35:39 +08:00
|
|
|
{ "lock", singleKeyRange(LiteralStringRef("db_locked")).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
2020-10-23 02:08:54 +08:00
|
|
|
{ "consistencycheck",
|
|
|
|
singleKeyRange(LiteralStringRef("consistency_check_suspended"))
|
2021-01-30 03:45:52 +08:00
|
|
|
.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
|
|
|
{ "coordinators",
|
|
|
|
KeyRangeRef(LiteralStringRef("coordinators/"), LiteralStringRef("coordinators0"))
|
2021-03-12 04:53:46 +08:00
|
|
|
.withPrefix(moduleToBoundary[MODULE::CONFIGURATION].begin) },
|
|
|
|
{ "advanceversion",
|
|
|
|
singleKeyRange(LiteralStringRef("min_required_commit_version"))
|
|
|
|
.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
Add fdbcli command to read/write version epoch (#6480)
* Initialize cluster version at wall-clock time
Previously, new clusters would begin at version 0. After this change,
clusters will initialize at a version matching wall-clock time. Instead
of using the Unix epoch (or Windows epoch), FDB clusters will use a new
epoch, defaulting to January 1, 2010, 01:00:00+00:00. In the future,
this base epoch will be modifiable through fdbcli, allowing
administrators to advance the cluster version.
Basing the version off of time allows different FDB clusters to share
data without running into version issues.
* Send version epoch to master
* Cleanup
* Update fdbserver/storageserver.actor.cpp
Co-authored-by: A.J. Beamon <aj.beamon@snowflake.com>
* Jump directly to expected version if possible
* Fix initial version issue on storage servers
* Add random recovery offset to start version in simulation
* Type fixes
* Disable reference time by default
Enable on a cluster using the fdbcli command `versionepoch add 0`.
* Use correct recoveryTransactionVersion when recovering
* Allow version epoch to be adjusted forwards (to decrease the version)
* Set version epoch in simulation
* Add quiet database check to ensure small version offset
* Fix initial version issue on storage servers
* Disable reference time by default
Enable on a cluster using the fdbcli command `versionepoch add 0`.
* Add fdbcli command to read/write version epoch
* Cause recovery when version epoch is set
* Handle optional version epoch key
* Add ability to clear the version epoch
This causes version advancement to revert to the old methodology whereas
versions attempt to advance by about a million versions per second,
instead of trying to match the clock.
* Update transaction access
* Modify version epoch to use microseconds instead of seconds
* Modify fdbcli version target API
Move commands from `versionepoch` to `targetversion` top level command.
* Add fdbcli tests for
* Temporarily disable targetversion cli tests
* Fix version epoch fetch issue
* Fix Arena issue
* Reduce max version jump in simulation to 1,000,000
* Rework fdbcli API
It now requires two commands to fully switch a cluster to using the
version epoch. First, enable the version epoch with `versionepoch
enable` or `versionepoch set <versionepoch>`. At this point, versions
will be given out at a faster or slower rate in an attempt to reach the
expected version. Then, run `versionepoch commit` to perform a one time
jump to the expected version. This is essentially irreversible.
* Temporarily disable old targetversion tests
* Cleanup
* Move version epoch buggify to sequencer
This will cause some issues with the QuietDatabase check for the version
offset - namely, it won't do anything, since the version epoch is not
being written to the txnStateStore in simulation. This will get fixed in
the future.
Co-authored-by: A.J. Beamon <aj.beamon@snowflake.com>
2022-04-09 03:33:19 +08:00
|
|
|
{ "versionepoch",
|
|
|
|
singleKeyRange(LiteralStringRef("version_epoch")).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
2021-03-12 04:53:46 +08:00
|
|
|
{ "profile",
|
|
|
|
KeyRangeRef(LiteralStringRef("profiling/"), LiteralStringRef("profiling0"))
|
2021-03-27 03:24:45 +08:00
|
|
|
.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
|
|
|
{ "maintenance",
|
|
|
|
KeyRangeRef(LiteralStringRef("maintenance/"), LiteralStringRef("maintenance0"))
|
|
|
|
.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
|
|
|
{ "datadistribution",
|
|
|
|
KeyRangeRef(LiteralStringRef("data_distribution/"), LiteralStringRef("data_distribution0"))
|
2022-03-07 14:16:42 +08:00
|
|
|
.withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
2022-07-01 06:03:37 +08:00
|
|
|
{ "tenant", KeyRangeRef("tenant/"_sr, "tenant0"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) },
|
|
|
|
{ "tenantmap",
|
|
|
|
KeyRangeRef("tenant_map/"_sr, "tenant_map0"_sr).withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin) }
|
2020-07-15 01:31:19 +08:00
|
|
|
};
|
|
|
|
|
2021-06-04 06:10:04 +08:00
|
|
|
std::unordered_map<std::string, KeyRange> SpecialKeySpace::actorLineageApiCommandToRange = {
|
|
|
|
{ "state",
|
|
|
|
KeyRangeRef(LiteralStringRef("state/"), LiteralStringRef("state0"))
|
|
|
|
.withPrefix(moduleToBoundary[MODULE::ACTORLINEAGE].begin) },
|
|
|
|
{ "time",
|
|
|
|
KeyRangeRef(LiteralStringRef("time/"), LiteralStringRef("time0"))
|
|
|
|
.withPrefix(moduleToBoundary[MODULE::ACTORLINEAGE].begin) }
|
|
|
|
};
|
|
|
|
|
2021-06-05 06:23:04 +08:00
|
|
|
std::set<std::string> SpecialKeySpace::options = { "excluded/force",
|
|
|
|
"failed/force",
|
2021-06-17 15:27:26 +08:00
|
|
|
"excluded_locality/force",
|
2022-08-16 02:05:07 +08:00
|
|
|
"failed_locality/force",
|
|
|
|
"worker_interfaces/verify" };
|
2020-07-18 03:32:42 +08:00
|
|
|
|
2020-12-05 03:21:39 +08:00
|
|
|
std::set<std::string> SpecialKeySpace::tracingOptions = { kTracingTransactionIdKey, kTracingTokenKey };
|
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult rywGetRange(ReadYourWritesTransaction* ryw, const KeyRangeRef& kr, const RangeResult& res);
|
2020-09-09 02:08:48 +08:00
|
|
|
|
2020-05-12 15:42:43 +08:00
|
|
|
// This function will move the given KeySelector as far as possible to the standard form:
|
2020-03-04 10:35:24 +08:00
|
|
|
// orEqual == false && offset == 1 (Standard form)
|
2020-05-12 15:42:43 +08:00
|
|
|
// If the corresponding key is not in the underlying key range, it will move over the range
|
2020-06-16 06:31:55 +08:00
|
|
|
// The cache object is used to cache the first read result from the rpc call during the key resolution,
|
|
|
|
// then when we need to do key resolution or result filtering,
|
|
|
|
// we, instead of rpc call, read from this cache object have consistent results
|
2020-06-16 03:47:27 +08:00
|
|
|
ACTOR Future<Void> moveKeySelectorOverRangeActor(const SpecialKeyRangeReadImpl* skrImpl,
|
|
|
|
ReadYourWritesTransaction* ryw,
|
2020-06-12 03:22:19 +08:00
|
|
|
KeySelector* ks,
|
2022-08-03 03:04:40 +08:00
|
|
|
KeyRangeMap<Optional<RangeResult>>* cache) {
|
2022-03-07 13:52:39 +08:00
|
|
|
// should be removed before calling
|
|
|
|
ASSERT(!ks->orEqual);
|
|
|
|
|
|
|
|
// never being called if KeySelector is already normalized
|
|
|
|
ASSERT(ks->offset != 1);
|
2020-03-04 10:35:24 +08:00
|
|
|
|
2020-05-12 15:42:43 +08:00
|
|
|
state Key startKey(skrImpl->getKeyRange().begin);
|
|
|
|
state Key endKey(skrImpl->getKeyRange().end);
|
2021-05-04 04:14:16 +08:00
|
|
|
state RangeResult result;
|
2020-03-04 10:35:24 +08:00
|
|
|
|
|
|
|
if (ks->offset < 1) {
|
|
|
|
// less than the given key
|
2020-05-15 17:04:15 +08:00
|
|
|
if (skrImpl->getKeyRange().contains(ks->getKey()))
|
|
|
|
endKey = ks->getKey();
|
2020-03-04 10:35:24 +08:00
|
|
|
} else {
|
|
|
|
// greater than the given key
|
2020-05-12 15:42:43 +08:00
|
|
|
if (skrImpl->getKeyRange().contains(ks->getKey()))
|
|
|
|
startKey = ks->getKey();
|
2020-03-04 10:35:24 +08:00
|
|
|
}
|
2022-03-07 13:52:39 +08:00
|
|
|
|
|
|
|
// Note : startKey never equals endKey here
|
|
|
|
ASSERT(startKey < endKey);
|
2020-03-04 10:35:24 +08:00
|
|
|
|
2022-05-26 04:23:43 +08:00
|
|
|
DisabledTraceEvent(SevDebug, "NormalizeKeySelector")
|
2020-03-04 10:35:24 +08:00
|
|
|
.detail("OriginalKey", ks->getKey())
|
|
|
|
.detail("OriginalOffset", ks->offset)
|
2020-05-12 15:42:43 +08:00
|
|
|
.detail("SpecialKeyRangeStart", skrImpl->getKeyRange().begin)
|
|
|
|
.detail("SpecialKeyRangeEnd", skrImpl->getKeyRange().end);
|
2020-03-04 10:35:24 +08:00
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
GetRangeLimits limitsHint(ks->offset >= 1 ? ks->offset : 1 - ks->offset);
|
|
|
|
|
2020-06-12 03:22:19 +08:00
|
|
|
if (skrImpl->isAsync()) {
|
|
|
|
const SpecialKeyRangeAsyncImpl* ptr = dynamic_cast<const SpecialKeyRangeAsyncImpl*>(skrImpl);
|
2022-03-07 13:52:39 +08:00
|
|
|
RangeResult result_ = wait(ptr->getRange(ryw, KeyRangeRef(startKey, endKey), limitsHint, cache));
|
2020-06-12 03:22:19 +08:00
|
|
|
result = result_;
|
|
|
|
} else {
|
2022-03-07 13:52:39 +08:00
|
|
|
RangeResult result_ = wait(skrImpl->getRange(ryw, KeyRangeRef(startKey, endKey), limitsHint));
|
2020-06-12 03:22:19 +08:00
|
|
|
result = result_;
|
|
|
|
}
|
|
|
|
|
2020-03-24 14:55:56 +08:00
|
|
|
if (result.size() == 0) {
|
2020-05-15 08:30:48 +08:00
|
|
|
TraceEvent(SevDebug, "ZeroElementsIntheRange").detail("Start", startKey).detail("End", endKey);
|
2020-03-24 14:55:56 +08:00
|
|
|
return Void();
|
|
|
|
}
|
2020-04-01 00:33:25 +08:00
|
|
|
// Note : KeySelector::setKey has byte limit according to the knobs, customize it if needed
|
2020-03-04 10:35:24 +08:00
|
|
|
if (ks->offset < 1) {
|
|
|
|
if (result.size() >= 1 - ks->offset) {
|
|
|
|
ks->setKey(KeyRef(ks->arena(), result[result.size() - (1 - ks->offset)].key));
|
|
|
|
ks->offset = 1;
|
|
|
|
} else {
|
|
|
|
ks->setKey(KeyRef(ks->arena(), result[0].key));
|
|
|
|
ks->offset += result.size();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (result.size() >= ks->offset) {
|
|
|
|
ks->setKey(KeyRef(ks->arena(), result[ks->offset - 1].key));
|
|
|
|
ks->offset = 1;
|
|
|
|
} else {
|
2022-03-07 13:52:39 +08:00
|
|
|
// TODO : the keyAfter will just return if key == \xff\xff
|
|
|
|
ks->setKey(KeyRef(ks->arena(), keyAfter(result[result.size() - 1].key)));
|
2020-03-04 10:35:24 +08:00
|
|
|
ks->offset -= result.size();
|
|
|
|
}
|
|
|
|
}
|
2022-05-26 04:23:43 +08:00
|
|
|
DisabledTraceEvent(SevDebug, "NormalizeKeySelector")
|
2020-03-04 10:35:24 +08:00
|
|
|
.detail("NormalizedKey", ks->getKey())
|
|
|
|
.detail("NormalizedOffset", ks->offset)
|
2020-05-12 15:42:43 +08:00
|
|
|
.detail("SpecialKeyRangeStart", skrImpl->getKeyRange().begin)
|
|
|
|
.detail("SpecialKeyRangeEnd", skrImpl->getKeyRange().end);
|
2020-03-04 10:35:24 +08:00
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
2020-05-12 15:42:43 +08:00
|
|
|
// This function will normalize the given KeySelector to a standard KeySelector:
|
|
|
|
// orEqual == false && offset == 1 (Standard form)
|
|
|
|
// If the corresponding key is outside the whole space, it will move to the begin or the end
|
|
|
|
// It does have overhead here since we query all keys twice in the worst case.
|
|
|
|
// However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard
|
|
|
|
// to maintain; Thus, separate each part to make the code easy to understand and more compact
|
2020-06-18 02:28:52 +08:00
|
|
|
// Boundary is the range of the legal key space, which, by default is the range of the module
|
2020-06-18 03:47:54 +08:00
|
|
|
// And (\xff\xff, \xff\xff\xff) if SPECIAL_KEY_SPACE_RELAXED is turned on
|
2020-05-21 04:55:07 +08:00
|
|
|
ACTOR Future<Void> normalizeKeySelectorActor(SpecialKeySpace* sks,
|
|
|
|
ReadYourWritesTransaction* ryw,
|
|
|
|
KeySelector* ks,
|
2020-06-18 01:33:52 +08:00
|
|
|
KeyRangeRef boundary,
|
|
|
|
int* actualOffset,
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult* result,
|
2022-08-03 03:04:40 +08:00
|
|
|
KeyRangeMap<Optional<RangeResult>>* cache) {
|
2020-12-05 06:58:40 +08:00
|
|
|
// If offset < 1, where we need to move left, iter points to the range containing at least one smaller key
|
2020-12-07 06:04:16 +08:00
|
|
|
// (It's a wasting of time to walk through the range whose begin key is same as ks->key)
|
2020-12-05 06:58:40 +08:00
|
|
|
// (rangeContainingKeyBefore itself handles the case where ks->key == Key())
|
|
|
|
// Otherwise, we only need to move right if offset > 1, iter points to the range containing the key
|
|
|
|
// Since boundary.end is always a key in the RangeMap, it is always safe to move right
|
2020-07-15 01:36:47 +08:00
|
|
|
state RangeMap<Key, SpecialKeyRangeReadImpl*, KeyRangeRef>::iterator iter =
|
2020-06-16 03:47:27 +08:00
|
|
|
ks->offset < 1 ? sks->getReadImpls().rangeContainingKeyBefore(ks->getKey())
|
|
|
|
: sks->getReadImpls().rangeContaining(ks->getKey());
|
2020-12-05 06:58:40 +08:00
|
|
|
while ((ks->offset < 1 && iter->begin() >= boundary.begin) || (ks->offset > 1 && iter->begin() < boundary.end)) {
|
2020-05-12 15:42:43 +08:00
|
|
|
if (iter->value() != nullptr) {
|
2020-06-12 03:22:19 +08:00
|
|
|
wait(moveKeySelectorOverRangeActor(iter->value(), ryw, ks, cache));
|
2020-05-12 15:42:43 +08:00
|
|
|
}
|
2020-12-05 06:58:40 +08:00
|
|
|
// Check if we can still move the iterator left
|
|
|
|
if (ks->offset < 1) {
|
2020-12-08 06:26:11 +08:00
|
|
|
if (iter == sks->getReadImpls().ranges().begin()) {
|
2020-12-05 06:58:40 +08:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
--iter;
|
|
|
|
}
|
|
|
|
} else if (ks->offset > 1) {
|
|
|
|
// Always safe to move right
|
|
|
|
++iter;
|
|
|
|
}
|
2020-05-12 15:42:43 +08:00
|
|
|
}
|
|
|
|
*actualOffset = ks->offset;
|
|
|
|
|
|
|
|
if (!ks->isFirstGreaterOrEqual()) {
|
2020-10-16 04:55:31 +08:00
|
|
|
TraceEvent(SevDebug, "ReadToBoundary")
|
2020-05-12 15:42:43 +08:00
|
|
|
.detail("TerminateKey", ks->getKey())
|
|
|
|
.detail("TerminateOffset", ks->offset);
|
2021-01-30 03:45:52 +08:00
|
|
|
// If still not normalized after moving to the boundary,
|
2020-12-05 06:58:40 +08:00
|
|
|
// let key selector clamp up to the boundary
|
|
|
|
if (ks->offset < 1) {
|
2020-05-12 15:42:43 +08:00
|
|
|
result->readToBegin = true;
|
2020-12-05 06:58:40 +08:00
|
|
|
ks->setKey(boundary.begin);
|
2021-01-30 03:45:52 +08:00
|
|
|
} else {
|
2020-05-12 15:42:43 +08:00
|
|
|
result->readThroughEnd = true;
|
2020-12-05 06:58:40 +08:00
|
|
|
ks->setKey(boundary.end);
|
|
|
|
}
|
2020-05-12 15:42:43 +08:00
|
|
|
ks->offset = 1;
|
|
|
|
}
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
2020-07-07 05:02:22 +08:00
|
|
|
SpecialKeySpace::SpecialKeySpace(KeyRef spaceStartKey, KeyRef spaceEndKey, bool testOnly)
|
2021-07-25 02:20:51 +08:00
|
|
|
: readImpls(nullptr, spaceEndKey),
|
|
|
|
modules(testOnly ? SpecialKeySpace::MODULE::TESTONLY : SpecialKeySpace::MODULE::UNKNOWN, spaceEndKey),
|
|
|
|
writeImpls(nullptr, spaceEndKey), range(KeyRangeRef(spaceStartKey, spaceEndKey)) {
|
2020-07-07 05:02:22 +08:00
|
|
|
// Default begin of KeyRangeMap is Key(), insert the range to update start key
|
|
|
|
readImpls.insert(range, nullptr);
|
|
|
|
writeImpls.insert(range, nullptr);
|
2022-03-07 13:52:39 +08:00
|
|
|
if (!testOnly) {
|
|
|
|
// testOnly is used in the correctness workload
|
|
|
|
modulesBoundaryInit();
|
|
|
|
}
|
2020-07-07 05:02:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void SpecialKeySpace::modulesBoundaryInit() {
|
|
|
|
for (const auto& pair : moduleToBoundary) {
|
|
|
|
ASSERT(range.contains(pair.second));
|
|
|
|
// Make sure the module is not overlapping with any registered read modules
|
|
|
|
// Note: same like ranges, one module's end cannot be another module's start, relax the condition if needed
|
|
|
|
ASSERT(modules.rangeContaining(pair.second.begin) == modules.rangeContaining(pair.second.end) &&
|
|
|
|
modules[pair.second.begin] == SpecialKeySpace::MODULE::UNKNOWN);
|
|
|
|
modules.insert(pair.second, pair.first);
|
|
|
|
// Note: Due to underlying implementation, the insertion here is important to make cross_module_read being
|
|
|
|
// handled correctly
|
|
|
|
readImpls.insert(pair.second, nullptr);
|
|
|
|
writeImpls.insert(pair.second, nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR Future<RangeResult> SpecialKeySpace::checkRYWValid(SpecialKeySpace* sks,
|
|
|
|
ReadYourWritesTransaction* ryw,
|
|
|
|
KeySelector begin,
|
|
|
|
KeySelector end,
|
|
|
|
GetRangeLimits limits,
|
2021-07-03 12:41:50 +08:00
|
|
|
Reverse reverse) {
|
2020-06-18 02:13:55 +08:00
|
|
|
ASSERT(ryw);
|
2020-05-21 04:55:07 +08:00
|
|
|
choose {
|
2021-05-04 04:14:16 +08:00
|
|
|
when(RangeResult result =
|
2020-05-21 04:55:07 +08:00
|
|
|
wait(SpecialKeySpace::getRangeAggregationActor(sks, ryw, begin, end, limits, reverse))) {
|
2020-06-18 01:33:52 +08:00
|
|
|
return result;
|
2020-05-15 14:49:57 +08:00
|
|
|
}
|
2020-06-18 02:13:55 +08:00
|
|
|
when(wait(ryw->resetFuture())) { throw internal_error(); }
|
2020-05-06 04:07:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR Future<RangeResult> SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks,
|
|
|
|
ReadYourWritesTransaction* ryw,
|
|
|
|
KeySelector begin,
|
|
|
|
KeySelector end,
|
|
|
|
GetRangeLimits limits,
|
2021-07-03 12:41:50 +08:00
|
|
|
Reverse reverse) {
|
2020-03-04 10:35:24 +08:00
|
|
|
// This function handles ranges which cover more than one keyrange and aggregates all results
|
|
|
|
// KeySelector, GetRangeLimits and reverse are all handled here
|
2021-05-04 04:14:16 +08:00
|
|
|
state RangeResult result;
|
|
|
|
state RangeResult pairs;
|
2020-07-15 01:36:47 +08:00
|
|
|
state RangeMap<Key, SpecialKeyRangeReadImpl*, KeyRangeRef>::iterator iter;
|
2020-03-31 14:27:09 +08:00
|
|
|
state int actualBeginOffset;
|
|
|
|
state int actualEndOffset;
|
2020-06-18 01:33:52 +08:00
|
|
|
state KeyRangeRef moduleBoundary;
|
2022-08-03 03:04:40 +08:00
|
|
|
// used to cache results from potential first async read
|
|
|
|
// the current implementation will read the whole range result to save in the cache
|
|
|
|
state KeyRangeMap<Optional<RangeResult>> cache(Optional<RangeResult>(), specialKeys.end);
|
2020-04-03 15:26:11 +08:00
|
|
|
|
2020-06-18 03:47:54 +08:00
|
|
|
if (ryw->specialKeySpaceRelaxed()) {
|
|
|
|
moduleBoundary = sks->range;
|
|
|
|
} else {
|
2020-06-18 01:33:52 +08:00
|
|
|
auto beginIter = sks->getModules().rangeContaining(begin.getKey());
|
2020-06-18 03:47:54 +08:00
|
|
|
if (beginIter->begin() <= end.getKey() && end.getKey() <= beginIter->end()) {
|
2020-06-18 01:33:52 +08:00
|
|
|
if (beginIter->value() == SpecialKeySpace::MODULE::UNKNOWN)
|
|
|
|
throw special_keys_no_module_found();
|
|
|
|
else
|
|
|
|
moduleBoundary = beginIter->range();
|
|
|
|
} else {
|
|
|
|
TraceEvent(SevInfo, "SpecialKeyCrossModuleRead")
|
2022-03-02 00:57:01 +08:00
|
|
|
.detail("Begin", begin)
|
|
|
|
.detail("End", end)
|
2020-06-18 01:33:52 +08:00
|
|
|
.detail("BoundaryBegin", beginIter->begin())
|
|
|
|
.detail("BoundaryEnd", beginIter->end());
|
|
|
|
throw special_keys_cross_module_read();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wait(normalizeKeySelectorActor(sks, ryw, &begin, moduleBoundary, &actualBeginOffset, &result, &cache));
|
|
|
|
wait(normalizeKeySelectorActor(sks, ryw, &end, moduleBoundary, &actualEndOffset, &result, &cache));
|
2020-03-31 14:27:09 +08:00
|
|
|
// Handle all corner cases like what RYW does
|
2020-03-04 10:35:24 +08:00
|
|
|
// return if range inverted
|
2020-03-31 14:27:09 +08:00
|
|
|
if (actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
|
2022-07-20 04:15:51 +08:00
|
|
|
CODE_PROBE(true, "inverted range");
|
2020-06-18 01:33:52 +08:00
|
|
|
return RangeResultRef(false, false);
|
2020-03-31 14:27:09 +08:00
|
|
|
}
|
2020-03-31 16:36:07 +08:00
|
|
|
// If touches begin or end, return with readToBegin and readThroughEnd flags
|
2020-06-18 01:33:52 +08:00
|
|
|
if (begin.getKey() == moduleBoundary.end || end.getKey() == moduleBoundary.begin) {
|
2022-07-20 04:15:51 +08:00
|
|
|
CODE_PROBE(true, "query touches begin or end");
|
2020-06-18 01:33:52 +08:00
|
|
|
return result;
|
2020-03-04 10:35:24 +08:00
|
|
|
}
|
2020-06-16 03:47:27 +08:00
|
|
|
state RangeMap<Key, SpecialKeyRangeReadImpl*, KeyRangeRef>::Ranges ranges =
|
2020-06-15 14:11:57 +08:00
|
|
|
sks->getReadImpls().intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey()));
|
2020-03-04 10:35:24 +08:00
|
|
|
// TODO : workaround to write this two together to make the code compact
|
|
|
|
// The issue here is boost::iterator_range<> doest not provide rbegin(), rend()
|
|
|
|
iter = reverse ? ranges.end() : ranges.begin();
|
|
|
|
if (reverse) {
|
|
|
|
while (iter != ranges.begin()) {
|
|
|
|
--iter;
|
|
|
|
if (iter->value() == nullptr)
|
|
|
|
continue;
|
|
|
|
KeyRangeRef kr = iter->range();
|
|
|
|
KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin;
|
|
|
|
KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end;
|
2022-08-03 03:04:40 +08:00
|
|
|
if (iter->value()->isAsync() && cache.rangeContaining(keyStart).value().present()) {
|
2020-06-12 03:22:19 +08:00
|
|
|
const SpecialKeyRangeAsyncImpl* ptr = dynamic_cast<const SpecialKeyRangeAsyncImpl*>(iter->value());
|
2022-03-07 13:52:39 +08:00
|
|
|
RangeResult pairs_ = wait(ptr->getRange(ryw, KeyRangeRef(keyStart, keyEnd), limits, &cache));
|
2020-06-12 03:22:19 +08:00
|
|
|
pairs = pairs_;
|
|
|
|
} else {
|
2022-03-07 13:52:39 +08:00
|
|
|
RangeResult pairs_ = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd), limits));
|
2020-06-12 03:22:19 +08:00
|
|
|
pairs = pairs_;
|
|
|
|
}
|
2020-04-07 15:08:47 +08:00
|
|
|
result.arena().dependsOn(pairs.arena());
|
2020-03-04 10:35:24 +08:00
|
|
|
// limits handler
|
|
|
|
for (int i = pairs.size() - 1; i >= 0; --i) {
|
2020-07-31 09:58:09 +08:00
|
|
|
ASSERT(iter->range().contains(pairs[i].key));
|
2020-04-09 03:43:25 +08:00
|
|
|
result.push_back(result.arena(), pairs[i]);
|
2020-04-09 04:38:12 +08:00
|
|
|
// Note : behavior here is even the last k-v pair makes total bytes larger than specified, it's still
|
|
|
|
// returned. In other words, the total size of the returned value (less the last entry) will be less
|
|
|
|
// than byteLimit
|
2020-04-09 03:43:25 +08:00
|
|
|
limits.decrement(pairs[i]);
|
2020-03-31 14:27:09 +08:00
|
|
|
if (limits.isReached()) {
|
|
|
|
result.more = true;
|
|
|
|
result.readToBegin = false;
|
2020-06-18 01:33:52 +08:00
|
|
|
return result;
|
2020-03-31 14:27:09 +08:00
|
|
|
};
|
2020-03-04 10:35:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (iter = ranges.begin(); iter != ranges.end(); ++iter) {
|
|
|
|
if (iter->value() == nullptr)
|
|
|
|
continue;
|
|
|
|
KeyRangeRef kr = iter->range();
|
|
|
|
KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin;
|
|
|
|
KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end;
|
2022-08-03 03:04:40 +08:00
|
|
|
if (iter->value()->isAsync() && cache.rangeContaining(keyStart).value().present()) {
|
2020-06-12 03:22:19 +08:00
|
|
|
const SpecialKeyRangeAsyncImpl* ptr = dynamic_cast<const SpecialKeyRangeAsyncImpl*>(iter->value());
|
2022-03-07 13:52:39 +08:00
|
|
|
RangeResult pairs_ = wait(ptr->getRange(ryw, KeyRangeRef(keyStart, keyEnd), limits, &cache));
|
2020-06-12 03:22:19 +08:00
|
|
|
pairs = pairs_;
|
|
|
|
} else {
|
2022-03-07 13:52:39 +08:00
|
|
|
RangeResult pairs_ = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd), limits));
|
2020-06-12 03:22:19 +08:00
|
|
|
pairs = pairs_;
|
|
|
|
}
|
2020-04-07 15:08:47 +08:00
|
|
|
result.arena().dependsOn(pairs.arena());
|
2020-03-04 10:35:24 +08:00
|
|
|
// limits handler
|
2020-03-31 14:27:09 +08:00
|
|
|
for (int i = 0; i < pairs.size(); ++i) {
|
2020-07-31 09:58:09 +08:00
|
|
|
ASSERT(iter->range().contains(pairs[i].key));
|
2020-04-09 03:43:25 +08:00
|
|
|
result.push_back(result.arena(), pairs[i]);
|
2020-04-09 04:38:12 +08:00
|
|
|
// Note : behavior here is even the last k-v pair makes total bytes larger than specified, it's still
|
|
|
|
// returned. In other words, the total size of the returned value (less the last entry) will be less
|
|
|
|
// than byteLimit
|
2020-04-09 03:43:25 +08:00
|
|
|
limits.decrement(pairs[i]);
|
2020-03-31 14:27:09 +08:00
|
|
|
if (limits.isReached()) {
|
|
|
|
result.more = true;
|
|
|
|
result.readThroughEnd = false;
|
2020-06-18 01:33:52 +08:00
|
|
|
return result;
|
2020-03-31 14:27:09 +08:00
|
|
|
};
|
2020-03-04 10:35:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-18 01:33:52 +08:00
|
|
|
return result;
|
2020-03-04 10:35:24 +08:00
|
|
|
}
|
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
Future<RangeResult> SpecialKeySpace::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeySelector begin,
|
|
|
|
KeySelector end,
|
|
|
|
GetRangeLimits limits,
|
2021-07-03 12:41:50 +08:00
|
|
|
Reverse reverse) {
|
2020-03-04 10:35:24 +08:00
|
|
|
// validate limits here
|
|
|
|
if (!limits.isValid())
|
|
|
|
return range_limits_invalid();
|
|
|
|
if (limits.isReached()) {
|
2022-07-20 04:15:51 +08:00
|
|
|
CODE_PROBE(true, "read limit 0");
|
2021-05-04 04:14:16 +08:00
|
|
|
return RangeResult();
|
2020-03-04 10:35:24 +08:00
|
|
|
}
|
2020-03-31 16:44:02 +08:00
|
|
|
// make sure orEqual == false
|
|
|
|
begin.removeOrEqual(begin.arena());
|
|
|
|
end.removeOrEqual(end.arena());
|
2020-04-07 13:09:17 +08:00
|
|
|
|
2020-05-15 17:04:15 +08:00
|
|
|
if (begin.offset >= end.offset && begin.getKey() >= end.getKey()) {
|
2022-07-20 04:15:51 +08:00
|
|
|
CODE_PROBE(true, "range inverted");
|
2021-05-04 04:14:16 +08:00
|
|
|
return RangeResult();
|
2020-05-15 14:49:57 +08:00
|
|
|
}
|
2020-05-15 08:30:48 +08:00
|
|
|
|
2020-06-18 02:13:55 +08:00
|
|
|
return checkRYWValid(this, ryw, begin, end, limits, reverse);
|
2020-03-04 10:35:24 +08:00
|
|
|
}
|
|
|
|
|
2020-05-21 04:55:07 +08:00
|
|
|
ACTOR Future<Optional<Value>> SpecialKeySpace::getActor(SpecialKeySpace* sks,
|
|
|
|
ReadYourWritesTransaction* ryw,
|
2020-04-09 04:38:12 +08:00
|
|
|
KeyRef key) {
|
2020-03-04 10:35:24 +08:00
|
|
|
// use getRange to workaround this
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result = wait(sks->getRange(ryw,
|
|
|
|
KeySelector(firstGreaterOrEqual(key)),
|
|
|
|
KeySelector(firstGreaterOrEqual(keyAfter(key))),
|
|
|
|
GetRangeLimits(CLIENT_KNOBS->TOO_MANY),
|
2021-07-17 15:11:40 +08:00
|
|
|
Reverse::False));
|
2020-03-04 10:35:24 +08:00
|
|
|
ASSERT(result.size() <= 1);
|
|
|
|
if (result.size()) {
|
|
|
|
return Optional<Value>(result[0].value);
|
|
|
|
} else {
|
|
|
|
return Optional<Value>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-21 04:55:07 +08:00
|
|
|
Future<Optional<Value>> SpecialKeySpace::get(ReadYourWritesTransaction* ryw, const Key& key) {
|
2020-04-09 04:38:12 +08:00
|
|
|
return getActor(this, ryw, key);
|
2020-03-04 10:35:24 +08:00
|
|
|
}
|
|
|
|
|
2020-07-07 05:02:22 +08:00
|
|
|
void SpecialKeySpace::set(ReadYourWritesTransaction* ryw, const KeyRef& key, const ValueRef& value) {
|
2020-07-16 07:15:43 +08:00
|
|
|
if (!ryw->specialKeySpaceChangeConfiguration())
|
|
|
|
throw special_keys_write_disabled();
|
2020-07-07 05:02:22 +08:00
|
|
|
auto impl = writeImpls[key];
|
2020-07-18 03:36:50 +08:00
|
|
|
if (impl == nullptr) {
|
|
|
|
TraceEvent(SevDebug, "SpecialKeySpaceNoWriteModuleFound")
|
2020-07-18 06:16:28 +08:00
|
|
|
.detail("Key", key.toString())
|
|
|
|
.detail("Value", value.toString());
|
2020-07-18 03:36:50 +08:00
|
|
|
throw special_keys_no_write_module_found();
|
|
|
|
}
|
2020-07-07 05:02:22 +08:00
|
|
|
return impl->set(ryw, key, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpecialKeySpace::clear(ReadYourWritesTransaction* ryw, const KeyRangeRef& range) {
|
2020-07-16 07:15:43 +08:00
|
|
|
if (!ryw->specialKeySpaceChangeConfiguration())
|
|
|
|
throw special_keys_write_disabled();
|
2020-07-07 05:02:22 +08:00
|
|
|
if (range.empty())
|
|
|
|
return;
|
|
|
|
auto begin = writeImpls[range.begin];
|
2020-07-15 02:56:40 +08:00
|
|
|
auto end = writeImpls.rangeContainingKeyBefore(range.end)->value();
|
|
|
|
if (begin != end) {
|
2021-08-08 15:03:25 +08:00
|
|
|
TraceEvent(SevDebug, "SpecialKeySpaceCrossModuleClear").detail("Range", range);
|
2020-07-07 05:02:22 +08:00
|
|
|
throw special_keys_cross_module_clear(); // ban cross module clear
|
2020-07-16 07:15:43 +08:00
|
|
|
} else if (begin == nullptr) {
|
2021-08-08 15:03:25 +08:00
|
|
|
TraceEvent(SevDebug, "SpecialKeySpaceNoWriteModuleFound").detail("Range", range);
|
2020-07-07 05:02:22 +08:00
|
|
|
throw special_keys_no_write_module_found();
|
2020-07-15 02:56:40 +08:00
|
|
|
}
|
2020-07-07 05:02:22 +08:00
|
|
|
return begin->clear(ryw, range);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpecialKeySpace::clear(ReadYourWritesTransaction* ryw, const KeyRef& key) {
|
2020-07-16 07:15:43 +08:00
|
|
|
if (!ryw->specialKeySpaceChangeConfiguration())
|
|
|
|
throw special_keys_write_disabled();
|
2020-07-07 05:02:22 +08:00
|
|
|
auto impl = writeImpls[key];
|
|
|
|
if (impl == nullptr)
|
|
|
|
throw special_keys_no_write_module_found();
|
|
|
|
return impl->clear(ryw, key);
|
|
|
|
}
|
|
|
|
|
2020-10-23 09:25:51 +08:00
|
|
|
bool validateSnakeCaseNaming(const KeyRef& k) {
|
|
|
|
KeyRef key(k);
|
|
|
|
// Remove prefix \xff\xff
|
|
|
|
ASSERT(key.startsWith(specialKeys.begin));
|
|
|
|
key = key.removePrefix(specialKeys.begin);
|
|
|
|
// Suffix can be \xff\xff or \x00 in single key range
|
|
|
|
if (key.endsWith(specialKeys.begin))
|
|
|
|
key = key.removeSuffix(specialKeys.end);
|
|
|
|
else if (key.endsWith(LiteralStringRef("\x00")))
|
|
|
|
key = key.removeSuffix(LiteralStringRef("\x00"));
|
|
|
|
for (const char& c : key.toString()) {
|
|
|
|
// only small letters, numbers, '/', '_' is allowed
|
|
|
|
ASSERT((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '/' || c == '_');
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-28 03:29:03 +08:00
|
|
|
void SpecialKeySpace::registerKeyRange(SpecialKeySpace::MODULE module,
|
|
|
|
SpecialKeySpace::IMPLTYPE type,
|
|
|
|
const KeyRangeRef& kr,
|
|
|
|
SpecialKeyRangeReadImpl* impl) {
|
2022-04-19 08:52:17 +08:00
|
|
|
// Not allowed to register an empty range
|
|
|
|
ASSERT(!kr.empty());
|
2020-07-07 05:02:22 +08:00
|
|
|
// module boundary check
|
2020-10-23 09:25:51 +08:00
|
|
|
if (module == SpecialKeySpace::MODULE::TESTONLY) {
|
2020-07-07 07:42:42 +08:00
|
|
|
ASSERT(normalKeys.contains(kr));
|
2020-10-23 09:27:58 +08:00
|
|
|
} else {
|
2020-07-07 05:02:22 +08:00
|
|
|
ASSERT(moduleToBoundary.at(module).contains(kr));
|
2022-03-07 13:52:39 +08:00
|
|
|
// validate keys follow snake case naming style
|
|
|
|
ASSERT(validateSnakeCaseNaming(kr.begin) && validateSnakeCaseNaming(kr.end));
|
2020-10-23 09:25:51 +08:00
|
|
|
}
|
2020-07-07 05:02:22 +08:00
|
|
|
// make sure the registered range is not overlapping with existing ones
|
|
|
|
// Note: kr.end should not be the same as another range's begin, although it should work even they are the same
|
|
|
|
for (auto iter = readImpls.rangeContaining(kr.begin); true; ++iter) {
|
|
|
|
ASSERT(iter->value() == nullptr);
|
2022-03-07 13:52:39 +08:00
|
|
|
if (iter == readImpls.rangeContaining(kr.end)) {
|
|
|
|
// Note: relax the condition that the end can be another range's start, if needed
|
|
|
|
break;
|
|
|
|
}
|
2020-07-07 05:02:22 +08:00
|
|
|
}
|
|
|
|
readImpls.insert(kr, impl);
|
|
|
|
// if rw, it means the module can do both read and write
|
2020-07-28 03:29:03 +08:00
|
|
|
if (type == SpecialKeySpace::IMPLTYPE::READWRITE) {
|
2020-07-07 05:02:22 +08:00
|
|
|
// since write impls are always subset of read impls,
|
|
|
|
// no need to check overlapped registration
|
|
|
|
auto rwImpl = dynamic_cast<SpecialKeyRangeRWImpl*>(impl);
|
|
|
|
ASSERT(rwImpl);
|
|
|
|
writeImpls.insert(kr, rwImpl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-18 06:16:28 +08:00
|
|
|
Key SpecialKeySpace::decode(const KeyRef& key) {
|
|
|
|
auto impl = writeImpls[key];
|
|
|
|
ASSERT(impl != nullptr);
|
|
|
|
return impl->decode(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyRange SpecialKeySpace::decode(const KeyRangeRef& kr) {
|
|
|
|
// Only allow to decode key range in the same underlying impl range
|
|
|
|
auto begin = writeImpls.rangeContaining(kr.begin);
|
|
|
|
ASSERT(begin->value() != nullptr);
|
|
|
|
auto end = writeImpls.rangeContainingKeyBefore(kr.end);
|
|
|
|
ASSERT(begin == end);
|
|
|
|
return KeyRangeRef(begin->value()->decode(kr.begin), begin->value()->decode(kr.end));
|
|
|
|
}
|
|
|
|
|
2020-06-24 02:21:03 +08:00
|
|
|
ACTOR Future<Void> commitActor(SpecialKeySpace* sks, ReadYourWritesTransaction* ryw) {
|
2020-06-28 03:22:32 +08:00
|
|
|
state RangeMap<Key, std::pair<bool, Optional<Value>>, KeyRangeRef>::Ranges ranges =
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().containedRanges(specialKeys);
|
2020-07-15 01:36:47 +08:00
|
|
|
state RangeMap<Key, std::pair<bool, Optional<Value>>, KeyRangeRef>::iterator iter = ranges.begin();
|
2021-05-04 08:22:34 +08:00
|
|
|
state std::vector<SpecialKeyRangeRWImpl*> writeModulePtrs;
|
2021-05-04 23:24:31 +08:00
|
|
|
std::unordered_set<SpecialKeyRangeRWImpl*> deduplicate;
|
2020-06-24 02:21:03 +08:00
|
|
|
while (iter != ranges.end()) {
|
|
|
|
std::pair<bool, Optional<Value>> entry = iter->value();
|
|
|
|
if (entry.first) {
|
2020-07-17 09:15:35 +08:00
|
|
|
auto modulePtr = sks->getRWImpls().rangeContaining(iter->begin())->value();
|
2021-05-04 23:24:31 +08:00
|
|
|
auto [_, inserted] = deduplicate.insert(modulePtr);
|
|
|
|
if (inserted) {
|
|
|
|
writeModulePtrs.push_back(modulePtr);
|
|
|
|
}
|
2020-06-24 02:21:03 +08:00
|
|
|
}
|
2020-07-07 02:02:48 +08:00
|
|
|
++iter;
|
2020-06-24 02:21:03 +08:00
|
|
|
}
|
2021-05-04 08:22:34 +08:00
|
|
|
state std::vector<SpecialKeyRangeRWImpl*>::const_iterator it;
|
2020-06-28 03:22:32 +08:00
|
|
|
for (it = writeModulePtrs.begin(); it != writeModulePtrs.end(); ++it) {
|
|
|
|
Optional<std::string> msg = wait((*it)->commit(ryw));
|
|
|
|
if (msg.present()) {
|
|
|
|
ryw->setSpecialKeySpaceErrorMsg(msg.get());
|
2022-03-07 13:52:39 +08:00
|
|
|
TraceEvent(SevDebug, "SpecialKeySpaceManagementAPIError")
|
2020-07-17 09:15:35 +08:00
|
|
|
.detail("Reason", msg.get())
|
|
|
|
.detail("Range", (*it)->getKeyRange().toString());
|
2020-07-31 09:58:09 +08:00
|
|
|
throw special_keys_api_failure();
|
2020-06-28 03:22:32 +08:00
|
|
|
}
|
|
|
|
}
|
2020-06-24 02:21:03 +08:00
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> SpecialKeySpace::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
return commitActor(this, ryw);
|
|
|
|
}
|
|
|
|
|
2022-08-03 03:04:40 +08:00
|
|
|
// For SKSCTestRWImpl and SKSCTestAsyncReadImpl
|
|
|
|
Future<RangeResult> SKSCTestGetRangeBase(ReadYourWritesTransaction* ryw, KeyRangeRef kr, GetRangeLimits limitsHint) {
|
2020-09-09 02:08:48 +08:00
|
|
|
auto resultFuture = ryw->getRange(kr, CLIENT_KNOBS->TOO_MANY);
|
|
|
|
// all keys are written to RYW, since GRV is set, the read should happen locally
|
|
|
|
ASSERT(resultFuture.isReady());
|
|
|
|
auto result = resultFuture.getValue();
|
|
|
|
ASSERT(!result.more && result.size() < CLIENT_KNOBS->TOO_MANY);
|
|
|
|
auto kvs = resultFuture.getValue();
|
|
|
|
return rywGetRange(ryw, kr, kvs);
|
|
|
|
}
|
|
|
|
|
2022-08-03 03:04:40 +08:00
|
|
|
SKSCTestRWImpl::SKSCTestRWImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
|
|
|
Future<RangeResult> SKSCTestRWImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
|
|
|
ASSERT(range.contains(kr));
|
|
|
|
return SKSCTestGetRangeBase(ryw, kr, limitsHint);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> SKSCTestRWImpl::commit(ReadYourWritesTransaction* ryw) {
|
2020-09-09 02:08:48 +08:00
|
|
|
ASSERT(false);
|
|
|
|
return Optional<std::string>();
|
|
|
|
}
|
|
|
|
|
2022-08-03 03:04:40 +08:00
|
|
|
SKSCTestAsyncReadImpl::SKSCTestAsyncReadImpl(KeyRangeRef kr) : SpecialKeyRangeAsyncImpl(kr) {}
|
|
|
|
|
|
|
|
Future<RangeResult> SKSCTestAsyncReadImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
|
|
|
ASSERT(range.contains(kr));
|
|
|
|
return SKSCTestGetRangeBase(ryw, kr, limitsHint);
|
|
|
|
}
|
|
|
|
|
2020-06-16 03:47:27 +08:00
|
|
|
ReadConflictRangeImpl::ReadConflictRangeImpl(KeyRangeRef kr) : SpecialKeyRangeReadImpl(kr) {}
|
2020-04-29 00:00:06 +08:00
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR static Future<RangeResult> getReadConflictRangeImpl(ReadYourWritesTransaction* ryw, KeyRange kr) {
|
2020-04-29 03:44:34 +08:00
|
|
|
wait(ryw->pendingReads());
|
2020-04-30 05:43:37 +08:00
|
|
|
return ryw->getReadConflictRangeIntersecting(kr);
|
2020-04-29 00:00:06 +08:00
|
|
|
}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ReadConflictRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2020-04-29 00:00:06 +08:00
|
|
|
return getReadConflictRangeImpl(ryw, kr);
|
|
|
|
}
|
|
|
|
|
2020-06-16 03:47:27 +08:00
|
|
|
WriteConflictRangeImpl::WriteConflictRangeImpl(KeyRangeRef kr) : SpecialKeyRangeReadImpl(kr) {}
|
2020-04-29 01:34:10 +08:00
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> WriteConflictRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2020-04-30 05:43:37 +08:00
|
|
|
return ryw->getWriteConflictRangeIntersecting(kr);
|
2020-04-29 01:34:10 +08:00
|
|
|
}
|
|
|
|
|
2020-06-16 03:47:27 +08:00
|
|
|
ConflictingKeysImpl::ConflictingKeysImpl(KeyRangeRef kr) : SpecialKeyRangeReadImpl(kr) {}
|
2020-04-07 04:23:41 +08:00
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ConflictingKeysImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2021-12-18 03:57:39 +08:00
|
|
|
if (ryw->getTransactionState()->conflictingKeys) {
|
|
|
|
auto krMapPtr = ryw->getTransactionState()->conflictingKeys.get();
|
2020-04-07 04:38:18 +08:00
|
|
|
auto beginIter = krMapPtr->rangeContaining(kr.begin);
|
|
|
|
auto endIter = krMapPtr->rangeContaining(kr.end);
|
2022-07-22 05:42:08 +08:00
|
|
|
|
|
|
|
if (!kr.contains(beginIter->begin()) && beginIter != endIter)
|
|
|
|
++beginIter;
|
2020-04-07 04:23:41 +08:00
|
|
|
for (auto it = beginIter; it != endIter; ++it) {
|
2020-07-25 07:31:16 +08:00
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(it->begin(), it->value()));
|
2020-04-07 04:23:41 +08:00
|
|
|
}
|
2022-07-22 05:42:08 +08:00
|
|
|
if (kr.contains(endIter->begin()))
|
2020-07-25 07:31:16 +08:00
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(endIter->begin(), endIter->value()));
|
2020-04-07 04:23:41 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR Future<RangeResult> ddMetricsGetRangeActor(ReadYourWritesTransaction* ryw, KeyRangeRef kr) {
|
2020-10-20 09:16:09 +08:00
|
|
|
loop {
|
|
|
|
try {
|
|
|
|
auto keys = kr.removePrefix(ddStatsRange.begin);
|
|
|
|
Standalone<VectorRef<DDMetricsRef>> resultWithoutPrefix = wait(
|
|
|
|
waitDataDistributionMetricsList(ryw->getDatabase(), keys, CLIENT_KNOBS->STORAGE_METRICS_SHARD_LIMIT));
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-10-20 09:16:09 +08:00
|
|
|
for (const auto& ddMetricsRef : resultWithoutPrefix) {
|
|
|
|
// each begin key is the previous end key, thus we only encode the begin key in the result
|
|
|
|
KeyRef beginKey = ddMetricsRef.beginKey.withPrefix(ddStatsRange.begin, result.arena());
|
|
|
|
// Use json string encoded in utf-8 to encode the values, easy for adding more fields in the future
|
|
|
|
json_spirit::mObject statsObj;
|
|
|
|
statsObj["shard_bytes"] = ddMetricsRef.shardBytes;
|
|
|
|
std::string statsString =
|
|
|
|
json_spirit::write_string(json_spirit::mValue(statsObj), json_spirit::Output_options::raw_utf8);
|
|
|
|
ValueRef bytes(result.arena(), statsString);
|
|
|
|
result.push_back(result.arena(), KeyValueRef(beginKey, bytes));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
} catch (Error& e) {
|
2020-10-22 07:58:23 +08:00
|
|
|
state Error err(e);
|
2020-11-20 05:17:38 +08:00
|
|
|
if (e.code() == error_code_dd_not_found) {
|
2020-10-20 09:16:09 +08:00
|
|
|
TraceEvent(SevWarnAlways, "DataDistributorNotPresent")
|
|
|
|
.detail("Operation", "DDMetricsReqestThroughSpecialKeys");
|
2020-10-22 07:58:23 +08:00
|
|
|
wait(delayJittered(FLOW_KNOBS->PREVENT_FAST_SPIN_DELAY));
|
2020-10-20 09:16:09 +08:00
|
|
|
continue;
|
|
|
|
}
|
2020-10-22 07:58:23 +08:00
|
|
|
throw err;
|
2020-05-19 01:38:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-12 03:22:19 +08:00
|
|
|
DDStatsRangeImpl::DDStatsRangeImpl(KeyRangeRef kr) : SpecialKeyRangeAsyncImpl(kr) {}
|
2020-05-19 01:38:23 +08:00
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> DDStatsRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2020-06-12 03:22:19 +08:00
|
|
|
return ddMetricsGetRangeActor(ryw, kr);
|
2020-05-19 01:38:23 +08:00
|
|
|
}
|
2020-06-15 13:29:44 +08:00
|
|
|
|
2020-07-18 03:32:42 +08:00
|
|
|
Key SpecialKeySpace::getManagementApiCommandOptionSpecialKey(const std::string& command, const std::string& option) {
|
2020-07-18 06:16:28 +08:00
|
|
|
Key prefix = LiteralStringRef("options/").withPrefix(moduleToBoundary[MODULE::MANAGEMENT].begin);
|
2020-07-18 03:32:42 +08:00
|
|
|
auto pair = command + "/" + option;
|
|
|
|
ASSERT(options.find(pair) != options.end());
|
|
|
|
return prefix.withSuffix(pair);
|
|
|
|
}
|
2020-07-17 09:15:35 +08:00
|
|
|
|
|
|
|
ManagementCommandsOptionsImpl::ManagementCommandsOptionsImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ManagementCommandsOptionsImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-07-17 09:15:35 +08:00
|
|
|
// Since we only have limit number of options, a brute force loop here is enough
|
2020-07-29 02:39:29 +08:00
|
|
|
for (const auto& option : SpecialKeySpace::getManagementApiOptionsSet()) {
|
2020-07-17 09:15:35 +08:00
|
|
|
auto key = getKeyRange().begin.withSuffix(option);
|
|
|
|
// ignore all invalid keys
|
|
|
|
auto r = ryw->getSpecialKeySpaceWriteMap()[key];
|
2020-08-06 04:18:39 +08:00
|
|
|
if (kr.contains(key) && r.first && r.second.present()) {
|
2020-07-17 09:15:35 +08:00
|
|
|
result.push_back(result.arena(), KeyValueRef(key, ValueRef()));
|
2020-08-06 04:18:39 +08:00
|
|
|
result.arena().dependsOn(key.arena());
|
|
|
|
}
|
2020-07-17 09:15:35 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ManagementCommandsOptionsImpl::set(ReadYourWritesTransaction* ryw, const KeyRef& key, const ValueRef& value) {
|
|
|
|
std::string option = key.removePrefix(getKeyRange().begin).toString();
|
|
|
|
// ignore all invalid keys
|
2020-07-29 02:39:29 +08:00
|
|
|
if (SpecialKeySpace::getManagementApiOptionsSet().find(option) !=
|
|
|
|
SpecialKeySpace::getManagementApiOptionsSet().end()) {
|
2020-07-18 03:32:42 +08:00
|
|
|
TraceEvent(SevDebug, "ManagementApiOption").detail("Option", option).detail("Key", key);
|
2020-07-17 09:15:35 +08:00
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(key, std::make_pair(true, Optional<Value>(value)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ManagementCommandsOptionsImpl::clear(ReadYourWritesTransaction* ryw, const KeyRangeRef& range) {
|
2020-07-29 04:32:31 +08:00
|
|
|
ryw->getSpecialKeySpaceWriteMap().rawErase(range);
|
2020-07-17 09:15:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ManagementCommandsOptionsImpl::clear(ReadYourWritesTransaction* ryw, const KeyRef& key) {
|
|
|
|
std::string option = key.removePrefix(getKeyRange().begin).toString();
|
|
|
|
// ignore all invalid keys
|
2020-07-29 02:39:29 +08:00
|
|
|
if (SpecialKeySpace::getManagementApiOptionsSet().find(option) !=
|
|
|
|
SpecialKeySpace::getManagementApiOptionsSet().end()) {
|
2020-07-17 09:15:35 +08:00
|
|
|
ryw->getSpecialKeySpaceWriteMap().rawErase(singleKeyRange(key));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> ManagementCommandsOptionsImpl::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
// Nothing to do, keys should be used by other impls' commit callback
|
|
|
|
return Optional<std::string>();
|
|
|
|
}
|
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult rywGetRange(ReadYourWritesTransaction* ryw, const KeyRangeRef& kr, const RangeResult& res) {
|
2020-09-05 05:54:32 +08:00
|
|
|
// "res" is the read result regardless of your writes, if ryw disabled, return immediately
|
|
|
|
if (ryw->readYourWritesDisabled())
|
|
|
|
return res;
|
|
|
|
// If ryw enabled, we update it with writes from the transaction
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-09-05 05:54:32 +08:00
|
|
|
RangeMap<Key, std::pair<bool, Optional<Value>>, KeyRangeRef>::Ranges ranges =
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().containedRanges(kr);
|
|
|
|
RangeMap<Key, std::pair<bool, Optional<Value>>, KeyRangeRef>::iterator iter = ranges.begin();
|
|
|
|
auto iter2 = res.begin();
|
|
|
|
result.arena().dependsOn(res.arena());
|
|
|
|
while (iter != ranges.end() || iter2 != res.end()) {
|
|
|
|
if (iter == ranges.end()) {
|
|
|
|
result.push_back(result.arena(), KeyValueRef(iter2->key, iter2->value));
|
|
|
|
++iter2;
|
|
|
|
} else if (iter2 == res.end()) {
|
|
|
|
// insert if it is a set entry
|
|
|
|
std::pair<bool, Optional<Value>> entry = iter->value();
|
|
|
|
if (entry.first && entry.second.present()) {
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(iter->begin(), entry.second.get()));
|
2020-06-24 02:21:03 +08:00
|
|
|
}
|
2020-09-05 05:54:32 +08:00
|
|
|
++iter;
|
|
|
|
} else if (iter->range().contains(iter2->key)) {
|
2020-06-28 03:22:32 +08:00
|
|
|
std::pair<bool, Optional<Value>> entry = iter->value();
|
2020-09-05 05:54:32 +08:00
|
|
|
// if this is a valid range either for set or clear, move iter2 outside the range
|
2020-06-24 02:21:03 +08:00
|
|
|
if (entry.first) {
|
2020-09-05 05:54:32 +08:00
|
|
|
// insert if this is a set entry
|
|
|
|
if (entry.second.present())
|
2020-07-31 17:18:49 +08:00
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(iter->begin(), entry.second.get()));
|
2020-09-05 05:54:32 +08:00
|
|
|
// move iter2 outside the range
|
|
|
|
while (iter2 != res.end() && iter->range().contains(iter2->key))
|
|
|
|
++iter2;
|
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
} else if (iter->begin() > iter2->key) {
|
|
|
|
result.push_back(result.arena(), KeyValueRef(iter2->key, iter2->value));
|
|
|
|
++iter2;
|
|
|
|
} else if (iter->end() <= iter2->key) {
|
|
|
|
// insert if it is a set entry
|
|
|
|
std::pair<bool, Optional<Value>> entry = iter->value();
|
|
|
|
if (entry.first && entry.second.present()) {
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(iter->begin(), entry.second.get()));
|
2020-06-24 02:21:03 +08:00
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
}
|
2020-06-15 13:29:44 +08:00
|
|
|
return result;
|
|
|
|
}
|
2020-06-15 13:39:20 +08:00
|
|
|
|
2020-09-05 05:54:32 +08:00
|
|
|
// read from those readwrite modules in which special keys have one-to-one mapping with real persisted keys
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR Future<RangeResult> rwModuleWithMappingGetRangeActor(ReadYourWritesTransaction* ryw,
|
|
|
|
const SpecialKeyRangeRWImpl* impl,
|
|
|
|
KeyRangeRef kr) {
|
|
|
|
RangeResult resultWithoutPrefix =
|
2020-09-05 05:54:32 +08:00
|
|
|
wait(ryw->getTransaction().getRange(ryw->getDatabase()->specialKeySpace->decode(kr), CLIENT_KNOBS->TOO_MANY));
|
|
|
|
ASSERT(!resultWithoutPrefix.more && resultWithoutPrefix.size() < CLIENT_KNOBS->TOO_MANY);
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-09-05 05:54:32 +08:00
|
|
|
for (const KeyValueRef& kv : resultWithoutPrefix)
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(impl->encode(kv.key), kv.value));
|
|
|
|
return rywGetRange(ryw, kr, result);
|
|
|
|
}
|
|
|
|
|
2020-06-24 02:21:03 +08:00
|
|
|
ExcludeServersRangeImpl::ExcludeServersRangeImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
2020-06-15 13:39:20 +08:00
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ExcludeServersRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2020-09-05 05:54:32 +08:00
|
|
|
return rwModuleWithMappingGetRangeActor(ryw, this, kr);
|
2020-06-24 02:21:03 +08:00
|
|
|
}
|
|
|
|
|
2020-09-05 06:21:47 +08:00
|
|
|
void ExcludeServersRangeImpl::set(ReadYourWritesTransaction* ryw, const KeyRef& key, const ValueRef& value) {
|
|
|
|
// ignore value
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(key, std::make_pair(true, Optional<Value>(ValueRef())));
|
|
|
|
}
|
|
|
|
|
2020-07-31 17:18:49 +08:00
|
|
|
Key ExcludeServersRangeImpl::decode(const KeyRef& key) const {
|
|
|
|
return key.removePrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin)
|
|
|
|
.withPrefix(LiteralStringRef("\xff/conf/"));
|
|
|
|
}
|
|
|
|
|
|
|
|
Key ExcludeServersRangeImpl::encode(const KeyRef& key) const {
|
|
|
|
return key.removePrefix(LiteralStringRef("\xff/conf/"))
|
|
|
|
.withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool parseNetWorkAddrFromKeys(ReadYourWritesTransaction* ryw,
|
|
|
|
bool failed,
|
|
|
|
std::vector<AddressExclusion>& addresses,
|
|
|
|
std::set<AddressExclusion>& exclusions,
|
|
|
|
Optional<std::string>& msg) {
|
2022-03-07 13:52:39 +08:00
|
|
|
KeyRangeRef range = failed ? SpecialKeySpace::getManagementApiCommandRange("failed")
|
|
|
|
: SpecialKeySpace::getManagementApiCommandRange("exclude");
|
2020-07-07 02:02:48 +08:00
|
|
|
auto ranges = ryw->getSpecialKeySpaceWriteMap().containedRanges(range);
|
|
|
|
auto iter = ranges.begin();
|
|
|
|
while (iter != ranges.end()) {
|
|
|
|
auto entry = iter->value();
|
|
|
|
// only check for exclude(set) operation, include(clear) are not checked
|
2020-08-21 05:41:17 +08:00
|
|
|
TraceEvent(SevDebug, "ParseNetworkAddress")
|
2020-07-07 02:02:48 +08:00
|
|
|
.detail("Valid", entry.first)
|
|
|
|
.detail("Set", entry.second.present())
|
|
|
|
.detail("Key", iter->begin().toString());
|
|
|
|
if (entry.first && entry.second.present()) {
|
|
|
|
Key address = iter->begin().removePrefix(range.begin);
|
|
|
|
auto a = AddressExclusion::parse(address);
|
|
|
|
if (!a.isValid()) {
|
|
|
|
std::string error = "ERROR: \'" + address.toString() + "\' is not a valid network endpoint address\n";
|
|
|
|
if (address.toString().find(":tls") != std::string::npos)
|
|
|
|
error += " Do not include the `:tls' suffix when naming a process\n";
|
2020-08-01 04:31:36 +08:00
|
|
|
msg = ManagementAPIError::toJsonString(
|
|
|
|
false, entry.second.present() ? (failed ? "exclude failed" : "exclude") : "include", error);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
addresses.push_back(a);
|
|
|
|
exclusions.insert(a);
|
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR Future<bool> checkExclusion(Database db,
|
|
|
|
std::vector<AddressExclusion>* addresses,
|
2020-07-16 07:15:43 +08:00
|
|
|
std::set<AddressExclusion>* exclusions,
|
|
|
|
bool markFailed,
|
|
|
|
Optional<std::string>* msg) {
|
2020-07-07 02:02:48 +08:00
|
|
|
|
|
|
|
if (markFailed) {
|
|
|
|
state bool safe;
|
|
|
|
try {
|
|
|
|
bool _safe = wait(checkSafeExclusions(db, *addresses));
|
|
|
|
safe = _safe;
|
|
|
|
} catch (Error& e) {
|
2020-07-31 13:15:22 +08:00
|
|
|
if (e.code() == error_code_actor_cancelled)
|
|
|
|
throw;
|
2020-07-07 02:02:48 +08:00
|
|
|
TraceEvent("CheckSafeExclusionsError").error(e);
|
|
|
|
safe = false;
|
|
|
|
}
|
|
|
|
if (!safe) {
|
2020-07-16 07:15:43 +08:00
|
|
|
std::string temp = "ERROR: It is unsafe to exclude the specified servers at this time.\n"
|
2020-07-17 09:15:35 +08:00
|
|
|
"Please check that this exclusion does not bring down an entire storage team.\n"
|
|
|
|
"Please also ensure that the exclusion will keep a majority of coordinators alive.\n"
|
2020-07-31 13:38:35 +08:00
|
|
|
"You may add more storage processes or coordinators to make the operation safe.\n"
|
2020-08-01 04:31:36 +08:00
|
|
|
"Call set(\"0xff0xff/management/failed/<ADDRESS...>\", ...) to exclude without "
|
2020-07-31 13:38:35 +08:00
|
|
|
"performing safety checks.\n";
|
2020-07-16 07:15:43 +08:00
|
|
|
*msg = ManagementAPIError::toJsonString(false, markFailed ? "exclude failed" : "exclude", temp);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
StatusObject status = wait(StatusClient::statusFetcher(db));
|
|
|
|
state std::string errorString =
|
|
|
|
"ERROR: Could not calculate the impact of this exclude on the total free space in the cluster.\n"
|
2020-07-31 13:38:35 +08:00
|
|
|
"Please try the exclude again in 30 seconds.\n"
|
2020-08-01 04:31:36 +08:00
|
|
|
"Call set(\"0xff0xff/management/options/exclude/force\", ...) first to exclude without checking free "
|
2020-07-31 13:38:35 +08:00
|
|
|
"space.\n";
|
2020-07-07 02:02:48 +08:00
|
|
|
|
|
|
|
StatusObjectReader statusObj(status);
|
|
|
|
|
|
|
|
StatusObjectReader statusObjCluster;
|
|
|
|
if (!statusObj.get("cluster", statusObjCluster)) {
|
2020-07-16 07:15:43 +08:00
|
|
|
*msg = ManagementAPIError::toJsonString(false, markFailed ? "exclude failed" : "exclude", errorString);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
StatusObjectReader processesMap;
|
|
|
|
if (!statusObjCluster.get("processes", processesMap)) {
|
2020-07-16 07:15:43 +08:00
|
|
|
*msg = ManagementAPIError::toJsonString(false, markFailed ? "exclude failed" : "exclude", errorString);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
state int ssTotalCount = 0;
|
|
|
|
state int ssExcludedCount = 0;
|
|
|
|
state double worstFreeSpaceRatio = 1.0;
|
|
|
|
try {
|
|
|
|
for (auto proc : processesMap.obj()) {
|
|
|
|
bool storageServer = false;
|
|
|
|
StatusArray rolesArray = proc.second.get_obj()["roles"].get_array();
|
|
|
|
for (StatusObjectReader role : rolesArray) {
|
|
|
|
if (role["role"].get_str() == "storage") {
|
|
|
|
storageServer = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Skip non-storage servers in free space calculation
|
|
|
|
if (!storageServer)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
StatusObjectReader process(proc.second);
|
|
|
|
std::string addrStr;
|
|
|
|
if (!process.get("address", addrStr)) {
|
2020-07-16 07:15:43 +08:00
|
|
|
*msg = ManagementAPIError::toJsonString(false, markFailed ? "exclude failed" : "exclude", errorString);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
NetworkAddress addr = NetworkAddress::parse(addrStr);
|
|
|
|
bool excluded =
|
|
|
|
(process.has("excluded") && process.last().get_bool()) || addressExcluded(*exclusions, addr);
|
|
|
|
ssTotalCount++;
|
|
|
|
if (excluded)
|
|
|
|
ssExcludedCount++;
|
|
|
|
|
|
|
|
if (!excluded) {
|
|
|
|
StatusObjectReader disk;
|
|
|
|
if (!process.get("disk", disk)) {
|
2020-07-17 09:15:35 +08:00
|
|
|
*msg =
|
|
|
|
ManagementAPIError::toJsonString(false, markFailed ? "exclude failed" : "exclude", errorString);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t total_bytes;
|
|
|
|
if (!disk.get("total_bytes", total_bytes)) {
|
2020-07-17 09:15:35 +08:00
|
|
|
*msg =
|
|
|
|
ManagementAPIError::toJsonString(false, markFailed ? "exclude failed" : "exclude", errorString);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t free_bytes;
|
|
|
|
if (!disk.get("free_bytes", free_bytes)) {
|
2020-07-17 09:15:35 +08:00
|
|
|
*msg =
|
|
|
|
ManagementAPIError::toJsonString(false, markFailed ? "exclude failed" : "exclude", errorString);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
worstFreeSpaceRatio = std::min(worstFreeSpaceRatio, double(free_bytes) / total_bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (...) // std::exception
|
|
|
|
{
|
2020-07-16 07:15:43 +08:00
|
|
|
*msg = ManagementAPIError::toJsonString(false, markFailed ? "exclude failed" : "exclude", errorString);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ssExcludedCount == ssTotalCount ||
|
|
|
|
(1 - worstFreeSpaceRatio) * ssTotalCount / (ssTotalCount - ssExcludedCount) > 0.9) {
|
2020-07-31 13:38:35 +08:00
|
|
|
std::string temp = "ERROR: This exclude may cause the total free space in the cluster to drop below 10%.\n"
|
2020-08-01 04:31:36 +08:00
|
|
|
"Call set(\"0xff0xff/management/options/exclude/force\", ...) first to exclude without "
|
2020-07-31 13:38:35 +08:00
|
|
|
"checking free space.\n";
|
2020-07-16 07:15:43 +08:00
|
|
|
*msg = ManagementAPIError::toJsonString(false, markFailed ? "exclude failed" : "exclude", temp);
|
2020-07-07 02:02:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void includeServers(ReadYourWritesTransaction* ryw) {
|
2020-07-16 07:15:43 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
|
|
|
|
ryw->setOption(FDBTransactionOptions::LOCK_AWARE);
|
|
|
|
ryw->setOption(FDBTransactionOptions::USE_PROVISIONAL_PROXIES);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2020-07-16 07:15:43 +08:00
|
|
|
// includeServers might be used in an emergency transaction, so make sure it is retry-self-conflicting and
|
|
|
|
// CAUSAL_WRITE_RISKY
|
|
|
|
ryw->setOption(FDBTransactionOptions::CAUSAL_WRITE_RISKY);
|
2020-07-07 02:02:48 +08:00
|
|
|
std::string versionKey = deterministicRandom()->randomUniqueID().toString();
|
2020-07-15 01:31:19 +08:00
|
|
|
// for exluded servers
|
2020-07-31 17:18:49 +08:00
|
|
|
auto ranges =
|
2022-03-07 13:52:39 +08:00
|
|
|
ryw->getSpecialKeySpaceWriteMap().containedRanges(SpecialKeySpace::getManagementApiCommandRange("exclude"));
|
2020-07-07 02:02:48 +08:00
|
|
|
auto iter = ranges.begin();
|
|
|
|
Transaction& tr = ryw->getTransaction();
|
|
|
|
while (iter != ranges.end()) {
|
|
|
|
auto entry = iter->value();
|
|
|
|
if (entry.first && !entry.second.present()) {
|
|
|
|
tr.addReadConflictRange(singleKeyRange(excludedServersVersionKey));
|
|
|
|
tr.set(excludedServersVersionKey, versionKey);
|
2020-07-18 06:16:28 +08:00
|
|
|
tr.clear(ryw->getDatabase()->specialKeySpace->decode(iter->range()));
|
2020-07-07 02:02:48 +08:00
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
}
|
2020-07-15 01:31:19 +08:00
|
|
|
// for failed servers
|
2022-03-07 13:52:39 +08:00
|
|
|
ranges = ryw->getSpecialKeySpaceWriteMap().containedRanges(SpecialKeySpace::getManagementApiCommandRange("failed"));
|
2020-07-15 01:31:19 +08:00
|
|
|
iter = ranges.begin();
|
|
|
|
while (iter != ranges.end()) {
|
|
|
|
auto entry = iter->value();
|
|
|
|
if (entry.first && !entry.second.present()) {
|
|
|
|
tr.addReadConflictRange(singleKeyRange(failedServersVersionKey));
|
|
|
|
tr.set(failedServersVersionKey, versionKey);
|
2020-07-18 06:16:28 +08:00
|
|
|
tr.clear(ryw->getDatabase()->specialKeySpace->decode(iter->range()));
|
2020-07-15 01:31:19 +08:00
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
}
|
2020-07-07 02:02:48 +08:00
|
|
|
}
|
|
|
|
|
2020-07-17 09:15:35 +08:00
|
|
|
ACTOR Future<Optional<std::string>> excludeCommitActor(ReadYourWritesTransaction* ryw, bool failed) {
|
2020-07-07 02:02:48 +08:00
|
|
|
// parse network addresses
|
|
|
|
state Optional<std::string> result;
|
|
|
|
state std::vector<AddressExclusion> addresses;
|
|
|
|
state std::set<AddressExclusion> exclusions;
|
2020-07-31 17:18:49 +08:00
|
|
|
if (!parseNetWorkAddrFromKeys(ryw, failed, addresses, exclusions, result))
|
|
|
|
return result;
|
2020-07-17 09:15:35 +08:00
|
|
|
// If force option is not set, we need to do safety check
|
2020-07-18 03:32:42 +08:00
|
|
|
auto force = ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandOptionSpecialKey(
|
2020-08-06 04:18:39 +08:00
|
|
|
failed ? "failed" : "excluded", "force")];
|
2020-07-17 09:15:35 +08:00
|
|
|
// only do safety check when we have servers to be excluded and the force option key is not set
|
|
|
|
if (addresses.size() && !(force.first && force.second.present())) {
|
|
|
|
bool safe = wait(checkExclusion(ryw->getDatabase(), &addresses, &exclusions, failed, &result));
|
|
|
|
if (!safe)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
excludeServers(ryw->getTransaction(), addresses, failed);
|
2020-07-07 02:02:48 +08:00
|
|
|
includeServers(ryw);
|
|
|
|
|
2020-06-28 03:22:32 +08:00
|
|
|
return result;
|
2020-06-15 13:39:20 +08:00
|
|
|
}
|
2020-07-07 02:02:48 +08:00
|
|
|
|
|
|
|
Future<Optional<std::string>> ExcludeServersRangeImpl::commit(ReadYourWritesTransaction* ryw) {
|
2020-07-17 09:15:35 +08:00
|
|
|
return excludeCommitActor(ryw, false);
|
2020-07-07 02:02:48 +08:00
|
|
|
}
|
2020-07-07 06:40:21 +08:00
|
|
|
|
|
|
|
FailedServersRangeImpl::FailedServersRangeImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> FailedServersRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2020-09-05 05:54:32 +08:00
|
|
|
return rwModuleWithMappingGetRangeActor(ryw, this, kr);
|
2020-07-07 06:40:21 +08:00
|
|
|
}
|
|
|
|
|
2020-09-05 06:21:47 +08:00
|
|
|
void FailedServersRangeImpl::set(ReadYourWritesTransaction* ryw, const KeyRef& key, const ValueRef& value) {
|
|
|
|
// ignore value
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(key, std::make_pair(true, Optional<Value>(ValueRef())));
|
|
|
|
}
|
|
|
|
|
2020-07-31 17:18:49 +08:00
|
|
|
Key FailedServersRangeImpl::decode(const KeyRef& key) const {
|
|
|
|
return key.removePrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin)
|
|
|
|
.withPrefix(LiteralStringRef("\xff/conf/"));
|
|
|
|
}
|
|
|
|
|
|
|
|
Key FailedServersRangeImpl::encode(const KeyRef& key) const {
|
|
|
|
return key.removePrefix(LiteralStringRef("\xff/conf/"))
|
|
|
|
.withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin);
|
|
|
|
}
|
|
|
|
|
2020-07-07 06:40:21 +08:00
|
|
|
Future<Optional<std::string>> FailedServersRangeImpl::commit(ReadYourWritesTransaction* ryw) {
|
2020-07-17 09:15:35 +08:00
|
|
|
return excludeCommitActor(ryw, true);
|
2020-07-14 04:35:25 +08:00
|
|
|
}
|
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR Future<RangeResult> ExclusionInProgressActor(ReadYourWritesTransaction* ryw, KeyRef prefix, KeyRangeRef kr) {
|
|
|
|
state RangeResult result;
|
2020-07-14 04:35:25 +08:00
|
|
|
state Transaction& tr = ryw->getTransaction();
|
2022-03-17 00:32:31 +08:00
|
|
|
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
|
2020-07-16 07:15:43 +08:00
|
|
|
tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); // necessary?
|
|
|
|
tr.setOption(FDBTransactionOptions::LOCK_AWARE);
|
2020-07-14 04:35:25 +08:00
|
|
|
|
|
|
|
state std::vector<AddressExclusion> excl = wait((getExcludedServers(&tr)));
|
2020-07-16 07:15:43 +08:00
|
|
|
state std::set<AddressExclusion> exclusions(excl.begin(), excl.end());
|
2020-07-14 04:35:25 +08:00
|
|
|
state std::set<NetworkAddress> inProgressExclusion;
|
2020-07-16 07:15:43 +08:00
|
|
|
// Just getting a consistent read version proves that a set of tlogs satisfying the exclusions has completed
|
|
|
|
// recovery Check that there aren't any storage servers with addresses violating the exclusions
|
2021-05-04 04:14:16 +08:00
|
|
|
state RangeResult serverList = wait(tr.getRange(serverListKeys, CLIENT_KNOBS->TOO_MANY));
|
2020-07-16 07:15:43 +08:00
|
|
|
ASSERT(!serverList.more && serverList.size() < CLIENT_KNOBS->TOO_MANY);
|
|
|
|
|
|
|
|
for (auto& s : serverList) {
|
|
|
|
auto addresses = decodeServerListValue(s.value).getKeyValues.getEndpoint().addresses;
|
|
|
|
if (addressExcluded(exclusions, addresses.address)) {
|
2020-07-14 04:35:25 +08:00
|
|
|
inProgressExclusion.insert(addresses.address);
|
|
|
|
}
|
2020-07-16 07:15:43 +08:00
|
|
|
if (addresses.secondaryAddress.present() && addressExcluded(exclusions, addresses.secondaryAddress.get())) {
|
2020-07-14 04:35:25 +08:00
|
|
|
inProgressExclusion.insert(addresses.secondaryAddress.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-16 07:15:43 +08:00
|
|
|
Optional<Standalone<StringRef>> value = wait(tr.get(logsKey));
|
2020-07-14 04:35:25 +08:00
|
|
|
ASSERT(value.present());
|
|
|
|
auto logs = decodeLogsValue(value.get());
|
2020-07-16 07:15:43 +08:00
|
|
|
for (auto const& log : logs.first) {
|
2020-07-14 04:35:25 +08:00
|
|
|
if (log.second == NetworkAddress() || addressExcluded(exclusions, log.second)) {
|
|
|
|
inProgressExclusion.insert(log.second);
|
|
|
|
}
|
|
|
|
}
|
2020-07-16 07:15:43 +08:00
|
|
|
for (auto const& log : logs.second) {
|
2020-07-14 04:35:25 +08:00
|
|
|
if (log.second == NetworkAddress() || addressExcluded(exclusions, log.second)) {
|
|
|
|
inProgressExclusion.insert(log.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-21 16:20:14 +08:00
|
|
|
// sort and remove :tls
|
|
|
|
std::set<std::string> inProgressAddresses;
|
2020-07-16 07:15:43 +08:00
|
|
|
for (auto const& address : inProgressExclusion) {
|
2020-08-21 16:20:14 +08:00
|
|
|
inProgressAddresses.insert(formatIpPort(address.ip, address.port));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto const& address : inProgressAddresses) {
|
|
|
|
Key addrKey = prefix.withSuffix(address);
|
2020-07-31 09:58:09 +08:00
|
|
|
if (kr.contains(addrKey)) {
|
|
|
|
result.push_back(result.arena(), KeyValueRef(addrKey, ValueRef()));
|
|
|
|
result.arena().dependsOn(addrKey.arena());
|
|
|
|
}
|
2020-07-14 04:35:25 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ExclusionInProgressRangeImpl::ExclusionInProgressRangeImpl(KeyRangeRef kr) : SpecialKeyRangeAsyncImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ExclusionInProgressRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2020-07-14 04:35:25 +08:00
|
|
|
return ExclusionInProgressActor(ryw, getKeyRange().begin, kr);
|
2020-07-16 08:30:45 +08:00
|
|
|
}
|
2020-08-14 16:01:08 +08:00
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR Future<RangeResult> getProcessClassActor(ReadYourWritesTransaction* ryw, KeyRef prefix, KeyRangeRef kr) {
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-09-17 08:42:34 +08:00
|
|
|
std::vector<ProcessData> _workers = wait(getWorkers(&ryw->getTransaction()));
|
2020-08-21 04:50:35 +08:00
|
|
|
auto workers = _workers; // strip const
|
2020-08-26 09:18:32 +08:00
|
|
|
// Note : the sort by string is anti intuition, ex. 1.1.1.1:11 < 1.1.1.1:5
|
2020-08-21 15:56:12 +08:00
|
|
|
std::sort(workers.begin(), workers.end(), [](const ProcessData& lhs, const ProcessData& rhs) {
|
|
|
|
return formatIpPort(lhs.address.ip, lhs.address.port) < formatIpPort(rhs.address.ip, rhs.address.port);
|
|
|
|
});
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-08-21 04:50:35 +08:00
|
|
|
for (auto& w : workers) {
|
|
|
|
// exclude :tls in keys even the network addresss is TLS
|
2020-09-03 04:38:24 +08:00
|
|
|
KeyRef k(prefix.withSuffix(formatIpPort(w.address.ip, w.address.port), result.arena()));
|
2020-08-21 04:50:35 +08:00
|
|
|
if (kr.contains(k)) {
|
2020-09-03 04:38:24 +08:00
|
|
|
ValueRef v(result.arena(), w.processClass.toString());
|
2020-08-21 04:50:35 +08:00
|
|
|
result.push_back(result.arena(), KeyValueRef(k, v));
|
2020-08-14 16:01:08 +08:00
|
|
|
}
|
|
|
|
}
|
2020-09-05 05:54:32 +08:00
|
|
|
return rywGetRange(ryw, kr, result);
|
2020-08-14 16:01:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR Future<Optional<std::string>> processClassCommitActor(ReadYourWritesTransaction* ryw, KeyRangeRef range) {
|
2020-08-20 08:54:38 +08:00
|
|
|
// enable related options
|
|
|
|
ryw->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
|
|
|
|
ryw->setOption(FDBTransactionOptions::LOCK_AWARE);
|
|
|
|
ryw->setOption(FDBTransactionOptions::USE_PROVISIONAL_PROXIES);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-09-17 08:42:34 +08:00
|
|
|
std::vector<ProcessData> workers = wait(
|
2020-08-20 08:54:38 +08:00
|
|
|
getWorkers(&ryw->getTransaction())); // make sure we use the Transaction object to avoid used_during_commit()
|
|
|
|
|
2020-08-14 16:01:08 +08:00
|
|
|
auto ranges = ryw->getSpecialKeySpaceWriteMap().containedRanges(range);
|
|
|
|
auto iter = ranges.begin();
|
|
|
|
while (iter != ranges.end()) {
|
|
|
|
auto entry = iter->value();
|
2020-08-21 15:56:12 +08:00
|
|
|
// only loop through (set) operation, (clear) not exist
|
2020-08-14 16:01:08 +08:00
|
|
|
if (entry.first && entry.second.present()) {
|
2020-08-21 15:56:12 +08:00
|
|
|
// parse network address
|
2020-08-14 16:01:08 +08:00
|
|
|
Key address = iter->begin().removePrefix(range.begin);
|
2020-08-20 08:54:38 +08:00
|
|
|
AddressExclusion addr = AddressExclusion::parse(address);
|
2020-08-21 15:56:12 +08:00
|
|
|
// parse class type
|
2020-08-14 16:01:08 +08:00
|
|
|
ValueRef processClassType = entry.second.get();
|
|
|
|
ProcessClass processClass(processClassType.toString(), ProcessClass::DBSource);
|
2020-08-21 15:56:12 +08:00
|
|
|
// make sure we use the underlying Transaction object to avoid used_during_commit()
|
2020-08-20 08:54:38 +08:00
|
|
|
bool foundChange = false;
|
|
|
|
for (int i = 0; i < workers.size(); i++) {
|
|
|
|
if (addr.excludes(workers[i].address)) {
|
|
|
|
if (processClass.classType() != ProcessClass::InvalidClass)
|
|
|
|
ryw->getTransaction().set(processClassKeyFor(workers[i].locality.processId().get()),
|
|
|
|
processClassValue(processClass));
|
|
|
|
else
|
|
|
|
ryw->getTransaction().clear(processClassKeyFor(workers[i].locality.processId().get()));
|
|
|
|
foundChange = true;
|
|
|
|
}
|
2020-08-14 16:01:08 +08:00
|
|
|
}
|
2020-08-20 08:54:38 +08:00
|
|
|
if (foundChange)
|
|
|
|
ryw->getTransaction().set(processClassChangeKey, deterministicRandom()->randomUniqueID().toString());
|
2020-08-14 16:01:08 +08:00
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
}
|
2020-08-21 15:56:12 +08:00
|
|
|
return Optional<std::string>();
|
2020-08-14 16:01:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ProcessClassRangeImpl::ProcessClassRangeImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ProcessClassRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2020-08-14 16:01:08 +08:00
|
|
|
return getProcessClassActor(ryw, getKeyRange().begin, kr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> ProcessClassRangeImpl::commit(ReadYourWritesTransaction* ryw) {
|
2020-08-21 15:56:12 +08:00
|
|
|
// Validate network address and process class type
|
|
|
|
Optional<std::string> errorMsg;
|
|
|
|
auto ranges = ryw->getSpecialKeySpaceWriteMap().containedRanges(getKeyRange());
|
|
|
|
auto iter = ranges.begin();
|
|
|
|
while (iter != ranges.end()) {
|
|
|
|
auto entry = iter->value();
|
|
|
|
// only check for setclass(set) operation, (clear) are forbidden thus not exist
|
|
|
|
if (entry.first && entry.second.present()) {
|
|
|
|
// validate network address
|
|
|
|
Key address = iter->begin().removePrefix(range.begin);
|
|
|
|
AddressExclusion addr = AddressExclusion::parse(address);
|
|
|
|
if (!addr.isValid()) {
|
|
|
|
std::string error = "ERROR: \'" + address.toString() + "\' is not a valid network endpoint address\n";
|
|
|
|
if (address.toString().find(":tls") != std::string::npos)
|
|
|
|
error += " Do not include the `:tls' suffix when naming a process\n";
|
|
|
|
errorMsg = ManagementAPIError::toJsonString(false, "setclass", error);
|
|
|
|
return errorMsg;
|
|
|
|
}
|
|
|
|
// validate class type
|
|
|
|
ValueRef processClassType = entry.second.get();
|
|
|
|
ProcessClass processClass(processClassType.toString(), ProcessClass::DBSource);
|
|
|
|
if (processClass.classType() == ProcessClass::InvalidClass &&
|
|
|
|
processClassType != LiteralStringRef("default")) {
|
|
|
|
std::string error = "ERROR: \'" + processClassType.toString() + "\' is not a valid process class\n";
|
|
|
|
errorMsg = ManagementAPIError::toJsonString(false, "setclass", error);
|
|
|
|
return errorMsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
}
|
2020-08-14 16:01:08 +08:00
|
|
|
return processClassCommitActor(ryw, getKeyRange());
|
2020-08-20 08:54:38 +08:00
|
|
|
}
|
|
|
|
|
2020-10-09 05:23:02 +08:00
|
|
|
void throwSpecialKeyApiFailure(ReadYourWritesTransaction* ryw, std::string command, std::string message) {
|
|
|
|
auto msg = ManagementAPIError::toJsonString(false, command, message);
|
2020-08-21 05:38:07 +08:00
|
|
|
ryw->setSpecialKeySpaceErrorMsg(msg);
|
2020-08-20 08:54:38 +08:00
|
|
|
throw special_keys_api_failure();
|
|
|
|
}
|
|
|
|
|
2020-08-26 05:59:43 +08:00
|
|
|
void ProcessClassRangeImpl::clear(ReadYourWritesTransaction* ryw, const KeyRangeRef& range) {
|
2020-10-09 05:23:02 +08:00
|
|
|
return throwSpecialKeyApiFailure(ryw, "setclass", "Clear operation is meaningless thus forbidden for setclass");
|
2020-08-21 05:38:07 +08:00
|
|
|
}
|
|
|
|
|
2020-08-26 05:59:43 +08:00
|
|
|
void ProcessClassRangeImpl::clear(ReadYourWritesTransaction* ryw, const KeyRef& key) {
|
2020-10-09 05:23:02 +08:00
|
|
|
return throwSpecialKeyApiFailure(
|
|
|
|
ryw, "setclass", "Clear range operation is meaningless thus forbidden for setclass");
|
2020-08-21 05:51:41 +08:00
|
|
|
}
|
2020-08-26 09:18:32 +08:00
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR Future<RangeResult> getProcessClassSourceActor(ReadYourWritesTransaction* ryw, KeyRef prefix, KeyRangeRef kr) {
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-09-17 08:42:34 +08:00
|
|
|
std::vector<ProcessData> _workers = wait(getWorkers(&ryw->getTransaction()));
|
2020-08-26 09:18:32 +08:00
|
|
|
auto workers = _workers; // strip const
|
|
|
|
// Note : the sort by string is anti intuition, ex. 1.1.1.1:11 < 1.1.1.1:5
|
|
|
|
std::sort(workers.begin(), workers.end(), [](const ProcessData& lhs, const ProcessData& rhs) {
|
|
|
|
return formatIpPort(lhs.address.ip, lhs.address.port) < formatIpPort(rhs.address.ip, rhs.address.port);
|
|
|
|
});
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-08-26 09:18:32 +08:00
|
|
|
for (auto& w : workers) {
|
|
|
|
// exclude :tls in keys even the network addresss is TLS
|
|
|
|
Key k(prefix.withSuffix(formatIpPort(w.address.ip, w.address.port)));
|
|
|
|
if (kr.contains(k)) {
|
|
|
|
Value v(w.processClass.sourceString());
|
|
|
|
result.push_back(result.arena(), KeyValueRef(k, v));
|
|
|
|
result.arena().dependsOn(k.arena());
|
|
|
|
result.arena().dependsOn(v.arena());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ProcessClassSourceRangeImpl::ProcessClassSourceRangeImpl(KeyRangeRef kr) : SpecialKeyRangeReadImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ProcessClassSourceRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2020-08-26 09:18:32 +08:00
|
|
|
return getProcessClassSourceActor(ryw, getKeyRange().begin, kr);
|
2020-10-09 05:23:02 +08:00
|
|
|
}
|
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR Future<RangeResult> getLockedKeyActor(ReadYourWritesTransaction* ryw, KeyRangeRef kr) {
|
2022-01-29 02:55:02 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2020-10-09 05:23:02 +08:00
|
|
|
Optional<Value> val = wait(ryw->getTransaction().get(databaseLockedKey));
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-10-09 05:23:02 +08:00
|
|
|
if (val.present()) {
|
2021-09-04 03:15:56 +08:00
|
|
|
UID uid = UID::fromString(BinaryReader::fromStringRef<UID>(val.get().substr(10), Unversioned()).toString());
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(kr.begin, Value(uid.toString())));
|
2020-10-09 05:23:02 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
LockDatabaseImpl::LockDatabaseImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> LockDatabaseImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2020-10-23 06:36:10 +08:00
|
|
|
// single key range, the queried range should always be the same as the underlying range
|
2020-10-09 05:23:02 +08:00
|
|
|
ASSERT(kr == getKeyRange());
|
|
|
|
auto lockEntry = ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandPrefix("lock")];
|
|
|
|
if (!ryw->readYourWritesDisabled() && lockEntry.first) {
|
|
|
|
// ryw enabled and we have written to the special key
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-10-09 05:23:02 +08:00
|
|
|
if (lockEntry.second.present()) {
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(kr.begin, lockEntry.second.get()));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return getLockedKeyActor(ryw, kr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-04 03:15:56 +08:00
|
|
|
ACTOR Future<Optional<std::string>> lockDatabaseCommitActor(ReadYourWritesTransaction* ryw, UID uid) {
|
2020-10-09 05:23:02 +08:00
|
|
|
state Optional<std::string> msg;
|
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2020-10-09 05:23:02 +08:00
|
|
|
Optional<Value> val = wait(ryw->getTransaction().get(databaseLockedKey));
|
|
|
|
|
|
|
|
if (val.present() && BinaryReader::fromStringRef<UID>(val.get().substr(10), Unversioned()) != uid) {
|
|
|
|
// check database not locked
|
|
|
|
// if locked already, throw error
|
2021-09-10 06:59:43 +08:00
|
|
|
throw database_locked();
|
2020-10-09 05:23:02 +08:00
|
|
|
} else if (!val.present()) {
|
|
|
|
// lock database
|
|
|
|
ryw->getTransaction().atomicOp(databaseLockedKey,
|
|
|
|
BinaryWriter::toValue(uid, Unversioned())
|
|
|
|
.withPrefix(LiteralStringRef("0123456789"))
|
|
|
|
.withSuffix(LiteralStringRef("\x00\x00\x00\x00")),
|
|
|
|
MutationRef::SetVersionstampedValue);
|
|
|
|
ryw->getTransaction().addWriteConflictRange(normalKeys);
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR Future<Optional<std::string>> unlockDatabaseCommitActor(ReadYourWritesTransaction* ryw) {
|
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2020-10-09 05:23:02 +08:00
|
|
|
Optional<Value> val = wait(ryw->getTransaction().get(databaseLockedKey));
|
|
|
|
if (val.present()) {
|
|
|
|
ryw->getTransaction().clear(singleKeyRange(databaseLockedKey));
|
|
|
|
}
|
|
|
|
return Optional<std::string>();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> LockDatabaseImpl::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
auto lockId = ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandPrefix("lock")].second;
|
|
|
|
if (lockId.present()) {
|
2021-09-04 03:15:56 +08:00
|
|
|
std::string uidStr = lockId.get().toString();
|
|
|
|
UID uid;
|
|
|
|
try {
|
|
|
|
uid = UID::fromString(uidStr);
|
|
|
|
} catch (Error& e) {
|
|
|
|
return Optional<std::string>(
|
|
|
|
ManagementAPIError::toJsonString(false, "lock", "Invalid UID hex string: " + uidStr));
|
|
|
|
}
|
|
|
|
return lockDatabaseCommitActor(ryw, uid);
|
2020-10-09 05:23:02 +08:00
|
|
|
} else {
|
|
|
|
return unlockDatabaseCommitActor(ryw);
|
|
|
|
}
|
|
|
|
}
|
2020-10-23 02:08:54 +08:00
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR Future<RangeResult> getConsistencyCheckKeyActor(ReadYourWritesTransaction* ryw, KeyRangeRef kr) {
|
2022-01-29 02:55:02 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2020-10-23 02:08:54 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
|
|
|
|
Optional<Value> val = wait(ryw->getTransaction().get(fdbShouldConsistencyCheckBeSuspended));
|
|
|
|
bool ccSuspendSetting = val.present() ? BinaryReader::fromStringRef<bool>(val.get(), Unversioned()) : false;
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-10-23 02:08:54 +08:00
|
|
|
if (ccSuspendSetting) {
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(kr.begin, ValueRef()));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConsistencyCheckImpl::ConsistencyCheckImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ConsistencyCheckImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2020-10-23 06:36:10 +08:00
|
|
|
// single key range, the queried range should always be the same as the underlying range
|
2020-10-23 02:08:54 +08:00
|
|
|
ASSERT(kr == getKeyRange());
|
|
|
|
auto entry = ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandPrefix("consistencycheck")];
|
|
|
|
if (!ryw->readYourWritesDisabled() && entry.first) {
|
|
|
|
// ryw enabled and we have written to the special key
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-10-23 02:08:54 +08:00
|
|
|
if (entry.second.present()) {
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(kr.begin, entry.second.get()));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return getConsistencyCheckKeyActor(ryw, kr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> ConsistencyCheckImpl::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
auto entry =
|
|
|
|
ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandPrefix("consistencycheck")].second;
|
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
|
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2020-10-23 02:08:54 +08:00
|
|
|
ryw->getTransaction().set(fdbShouldConsistencyCheckBeSuspended,
|
|
|
|
BinaryWriter::toValue(entry.present(), Unversioned()));
|
|
|
|
return Optional<std::string>();
|
|
|
|
}
|
2020-12-01 06:57:17 +08:00
|
|
|
|
2021-02-13 10:55:01 +08:00
|
|
|
GlobalConfigImpl::GlobalConfigImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2021-03-20 08:37:01 +08:00
|
|
|
// Returns key-value pairs for each value stored in the global configuration
|
|
|
|
// framework within the range specified. The special-key-space getrange
|
|
|
|
// function should only be used for informational purposes. All values are
|
|
|
|
// returned as strings regardless of their true type.
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> GlobalConfigImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2021-03-17 08:20:25 +08:00
|
|
|
KeyRangeRef modified =
|
|
|
|
KeyRangeRef(kr.begin.removePrefix(getKeyRange().begin), kr.end.removePrefix(getKeyRange().begin));
|
2022-05-07 02:29:17 +08:00
|
|
|
std::map<KeyRef, Reference<ConfigValue>> values = ryw->getDatabase()->globalConfig->get(modified);
|
2021-02-25 10:29:53 +08:00
|
|
|
for (const auto& [key, config] : values) {
|
2021-02-20 06:00:07 +08:00
|
|
|
Key prefixedKey = key.withPrefix(getKeyRange().begin);
|
2021-03-20 04:28:03 +08:00
|
|
|
if (config.isValid() && config->value.has_value()) {
|
|
|
|
if (config->value.type() == typeid(StringRef)) {
|
2021-03-17 08:20:25 +08:00
|
|
|
result.push_back_deep(result.arena(),
|
2021-03-20 04:28:03 +08:00
|
|
|
KeyValueRef(prefixedKey, std::any_cast<StringRef>(config->value).toString()));
|
|
|
|
} else if (config->value.type() == typeid(int64_t)) {
|
2021-03-17 08:20:25 +08:00
|
|
|
result.push_back_deep(result.arena(),
|
2021-03-20 04:28:03 +08:00
|
|
|
KeyValueRef(prefixedKey, std::to_string(std::any_cast<int64_t>(config->value))));
|
2021-06-03 11:21:44 +08:00
|
|
|
} else if (config->value.type() == typeid(bool)) {
|
|
|
|
result.push_back_deep(result.arena(),
|
|
|
|
KeyValueRef(prefixedKey, std::to_string(std::any_cast<bool>(config->value))));
|
2021-03-20 04:28:03 +08:00
|
|
|
} else if (config->value.type() == typeid(float)) {
|
2021-03-17 08:20:25 +08:00
|
|
|
result.push_back_deep(result.arena(),
|
2021-03-20 04:28:03 +08:00
|
|
|
KeyValueRef(prefixedKey, std::to_string(std::any_cast<float>(config->value))));
|
|
|
|
} else if (config->value.type() == typeid(double)) {
|
2021-03-17 08:20:25 +08:00
|
|
|
result.push_back_deep(result.arena(),
|
2021-03-20 04:28:03 +08:00
|
|
|
KeyValueRef(prefixedKey, std::to_string(std::any_cast<double>(config->value))));
|
2021-02-20 06:00:07 +08:00
|
|
|
} else {
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
2021-02-13 10:55:01 +08:00
|
|
|
}
|
|
|
|
}
|
2021-02-20 06:00:07 +08:00
|
|
|
|
2021-02-13 10:55:01 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-03-20 08:37:01 +08:00
|
|
|
// Marks the key for insertion into global configuration.
|
2021-02-13 10:55:01 +08:00
|
|
|
void GlobalConfigImpl::set(ReadYourWritesTransaction* ryw, const KeyRef& key, const ValueRef& value) {
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(key, std::make_pair(true, Optional<Value>(value)));
|
|
|
|
}
|
|
|
|
|
2021-03-20 08:37:01 +08:00
|
|
|
// Writes global configuration changes to durable memory. Also writes the
|
|
|
|
// changes made in the transaction to a recent history set, and updates the
|
|
|
|
// latest version which the global configuration was updated at.
|
2021-04-14 03:50:18 +08:00
|
|
|
ACTOR Future<Optional<std::string>> globalConfigCommitActor(GlobalConfigImpl* globalConfig,
|
|
|
|
ReadYourWritesTransaction* ryw) {
|
2021-02-13 10:55:01 +08:00
|
|
|
state Transaction& tr = ryw->getTransaction();
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-02-13 10:55:01 +08:00
|
|
|
|
|
|
|
// History should only contain three most recent updates. If it currently
|
|
|
|
// has three items, remove the oldest to make room for a new item.
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult history = wait(tr.getRange(globalConfigHistoryKeys, CLIENT_KNOBS->TOO_MANY));
|
2021-02-13 10:55:01 +08:00
|
|
|
constexpr int kGlobalConfigMaxHistorySize = 3;
|
|
|
|
if (history.size() > kGlobalConfigMaxHistorySize - 1) {
|
2021-03-16 09:03:54 +08:00
|
|
|
for (int i = 0; i < history.size() - (kGlobalConfigMaxHistorySize - 1); ++i) {
|
|
|
|
tr.clear(history[i].key);
|
2021-02-13 10:55:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-28 03:45:24 +08:00
|
|
|
Standalone<VectorRef<KeyValueRef>> insertions;
|
|
|
|
Standalone<VectorRef<KeyRangeRef>> clears;
|
2021-02-13 10:55:01 +08:00
|
|
|
|
2021-03-16 09:03:54 +08:00
|
|
|
// Transform writes from the special-key-space (\xff\xff/global_config/) to
|
|
|
|
// the system key space (\xff/globalConfig/), and writes mutations to
|
|
|
|
// latest version history.
|
2021-02-13 10:55:01 +08:00
|
|
|
state RangeMap<Key, std::pair<bool, Optional<Value>>, KeyRangeRef>::Ranges ranges =
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().containedRanges(specialKeys);
|
|
|
|
state RangeMap<Key, std::pair<bool, Optional<Value>>, KeyRangeRef>::iterator iter = ranges.begin();
|
|
|
|
while (iter != ranges.end()) {
|
|
|
|
std::pair<bool, Optional<Value>> entry = iter->value();
|
|
|
|
if (entry.first) {
|
2021-03-24 07:22:39 +08:00
|
|
|
if (entry.second.present() && iter->begin().startsWith(globalConfig->getKeyRange().begin)) {
|
2021-02-25 03:49:25 +08:00
|
|
|
Key bareKey = iter->begin().removePrefix(globalConfig->getKeyRange().begin);
|
2022-04-28 03:45:24 +08:00
|
|
|
insertions.push_back_deep(insertions.arena(), KeyValueRef(bareKey, entry.second.get()));
|
2021-03-24 07:22:39 +08:00
|
|
|
} else if (!entry.second.present() && iter->range().begin.startsWith(globalConfig->getKeyRange().begin) &&
|
|
|
|
iter->range().end.startsWith(globalConfig->getKeyRange().begin)) {
|
2021-02-25 03:49:25 +08:00
|
|
|
KeyRef bareRangeBegin = iter->range().begin.removePrefix(globalConfig->getKeyRange().begin);
|
|
|
|
KeyRef bareRangeEnd = iter->range().end.removePrefix(globalConfig->getKeyRange().begin);
|
2022-04-28 03:45:24 +08:00
|
|
|
clears.push_back_deep(clears.arena(), KeyRangeRef(bareRangeBegin, bareRangeEnd));
|
2021-02-13 10:55:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
}
|
2022-04-28 03:45:24 +08:00
|
|
|
GlobalConfig::applyChanges(tr, insertions, clears);
|
2021-02-13 10:55:01 +08:00
|
|
|
|
|
|
|
return Optional<std::string>();
|
|
|
|
}
|
|
|
|
|
2021-03-20 08:37:01 +08:00
|
|
|
// Called when a transaction includes keys in the global configuration special-key-space range.
|
2021-02-13 10:55:01 +08:00
|
|
|
Future<Optional<std::string>> GlobalConfigImpl::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
return globalConfigCommitActor(this, ryw);
|
2020-12-04 06:06:11 +08:00
|
|
|
}
|
2020-12-01 06:57:17 +08:00
|
|
|
|
2021-03-20 08:37:01 +08:00
|
|
|
// Marks the range for deletion from global configuration.
|
2021-02-13 10:55:01 +08:00
|
|
|
void GlobalConfigImpl::clear(ReadYourWritesTransaction* ryw, const KeyRangeRef& range) {
|
2021-02-25 03:23:29 +08:00
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(range, std::make_pair(true, Optional<Value>()));
|
2021-02-13 10:55:01 +08:00
|
|
|
}
|
|
|
|
|
2021-03-20 08:37:01 +08:00
|
|
|
// Marks the key for deletion from global configuration.
|
2021-02-13 10:55:01 +08:00
|
|
|
void GlobalConfigImpl::clear(ReadYourWritesTransaction* ryw, const KeyRef& key) {
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(key, std::make_pair(true, Optional<Value>()));
|
2020-12-04 06:06:11 +08:00
|
|
|
}
|
2020-12-01 06:57:17 +08:00
|
|
|
|
2021-02-20 16:43:54 +08:00
|
|
|
TracingOptionsImpl::TracingOptionsImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> TracingOptionsImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2020-12-05 03:21:39 +08:00
|
|
|
for (const auto& option : SpecialKeySpace::getTracingOptions()) {
|
|
|
|
auto key = getKeyRange().begin.withSuffix(option);
|
|
|
|
if (!kr.contains(key)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key.endsWith(kTracingTransactionIdKey)) {
|
2021-01-30 03:45:52 +08:00
|
|
|
result.push_back_deep(result.arena(),
|
2022-05-03 03:56:51 +08:00
|
|
|
KeyValueRef(key, ryw->getTransactionState()->spanContext.traceID.toString()));
|
2020-12-05 03:21:39 +08:00
|
|
|
} else if (key.endsWith(kTracingTokenKey)) {
|
2021-01-30 03:45:52 +08:00
|
|
|
result.push_back_deep(result.arena(),
|
2022-05-03 03:56:51 +08:00
|
|
|
KeyValueRef(key, std::to_string(ryw->getTransactionState()->spanContext.spanID)));
|
2020-12-05 03:21:39 +08:00
|
|
|
}
|
2020-12-01 06:57:17 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TracingOptionsImpl::set(ReadYourWritesTransaction* ryw, const KeyRef& key, const ValueRef& value) {
|
2020-12-04 06:06:11 +08:00
|
|
|
if (ryw->getApproximateSize() > 0) {
|
2022-08-04 00:22:49 +08:00
|
|
|
ryw->setSpecialKeySpaceErrorMsg(
|
|
|
|
ManagementAPIError::toJsonString(false, "configure trace", "tracing options must be set first"));
|
2020-12-08 06:43:44 +08:00
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(key, std::make_pair(true, Optional<Value>()));
|
|
|
|
return;
|
2020-12-04 06:06:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (key.endsWith(kTracingTransactionIdKey)) {
|
2022-05-03 03:56:51 +08:00
|
|
|
ryw->setTransactionID(UID::fromString(value.toString()));
|
2020-12-04 06:06:11 +08:00
|
|
|
} else if (key.endsWith(kTracingTokenKey)) {
|
|
|
|
if (value.toString() == "true") {
|
|
|
|
ryw->setToken(deterministicRandom()->randomUInt64());
|
|
|
|
} else if (value.toString() == "false") {
|
|
|
|
ryw->setToken(0);
|
|
|
|
} else {
|
2022-08-04 00:22:49 +08:00
|
|
|
ryw->setSpecialKeySpaceErrorMsg(
|
|
|
|
ManagementAPIError::toJsonString(false, "configure trace token", "token must be set to true/false"));
|
2020-12-04 06:06:11 +08:00
|
|
|
throw special_keys_api_failure();
|
|
|
|
}
|
2020-12-01 06:57:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> TracingOptionsImpl::commit(ReadYourWritesTransaction* ryw) {
|
2020-12-08 06:43:44 +08:00
|
|
|
if (ryw->getSpecialKeySpaceWriteMap().size() > 0) {
|
|
|
|
throw special_keys_api_failure();
|
|
|
|
}
|
2020-12-01 06:57:17 +08:00
|
|
|
return Optional<std::string>();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TracingOptionsImpl::clear(ReadYourWritesTransaction* ryw, const KeyRangeRef& range) {
|
2022-08-04 00:22:49 +08:00
|
|
|
ryw->setSpecialKeySpaceErrorMsg(ManagementAPIError::toJsonString(false, "clear trace", "clear range disabled"));
|
2020-12-04 06:06:11 +08:00
|
|
|
throw special_keys_api_failure();
|
2020-12-01 06:57:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void TracingOptionsImpl::clear(ReadYourWritesTransaction* ryw, const KeyRef& key) {
|
2022-08-04 00:22:49 +08:00
|
|
|
ryw->setSpecialKeySpaceErrorMsg(ManagementAPIError::toJsonString(false, "clear trace", "clear disabled"));
|
2020-12-04 06:06:11 +08:00
|
|
|
throw special_keys_api_failure();
|
2020-12-01 06:57:17 +08:00
|
|
|
}
|
2021-01-30 03:45:52 +08:00
|
|
|
|
|
|
|
CoordinatorsImpl::CoordinatorsImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-04-28 12:54:13 +08:00
|
|
|
ACTOR Future<RangeResult> coordinatorsGetRangeActor(ReadYourWritesTransaction* ryw, KeyRef prefix, KeyRangeRef kr) {
|
|
|
|
state ClusterConnectionString cs = ryw->getDatabase()->getConnectionRecord()->getConnectionString();
|
|
|
|
state std::vector<NetworkAddress> coordinator_processes = wait(cs.tryResolveHostnames());
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2021-01-30 03:45:52 +08:00
|
|
|
Key cluster_decription_key = prefix.withSuffix(LiteralStringRef("cluster_description"));
|
|
|
|
if (kr.contains(cluster_decription_key)) {
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(cluster_decription_key, cs.clusterKeyName()));
|
|
|
|
}
|
|
|
|
// Note : the sort by string is anti intuition, ex. 1.1.1.1:11 < 1.1.1.1:5
|
|
|
|
// include :tls in keys if the network addresss is TLS
|
|
|
|
std::sort(coordinator_processes.begin(),
|
|
|
|
coordinator_processes.end(),
|
2021-02-05 16:55:34 +08:00
|
|
|
[](const NetworkAddress& lhs, const NetworkAddress& rhs) { return lhs.toString() < rhs.toString(); });
|
|
|
|
std::string processes_str;
|
2021-02-19 06:23:51 +08:00
|
|
|
for (const auto& w : coordinator_processes) {
|
2021-02-05 16:55:34 +08:00
|
|
|
if (processes_str.size())
|
|
|
|
processes_str += ",";
|
|
|
|
processes_str += w.toString();
|
|
|
|
}
|
|
|
|
Key processes_key = prefix.withSuffix(LiteralStringRef("processes"));
|
|
|
|
if (kr.contains(processes_key)) {
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(processes_key, Value(processes_str)));
|
2021-01-30 03:45:52 +08:00
|
|
|
}
|
|
|
|
return rywGetRange(ryw, kr, result);
|
|
|
|
}
|
|
|
|
|
2022-04-28 12:54:13 +08:00
|
|
|
Future<RangeResult> CoordinatorsImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
|
|
|
KeyRef prefix(getKeyRange().begin);
|
|
|
|
return coordinatorsGetRangeActor(ryw, prefix, kr);
|
|
|
|
}
|
|
|
|
|
2021-01-30 03:45:52 +08:00
|
|
|
ACTOR static Future<Optional<std::string>> coordinatorsCommitActor(ReadYourWritesTransaction* ryw, KeyRangeRef kr) {
|
2022-04-28 12:54:13 +08:00
|
|
|
state ClusterConnectionString conn; // We don't care about the Key here.
|
2022-02-25 08:34:30 +08:00
|
|
|
state std::vector<std::string> process_address_or_hostname_strs;
|
2021-01-30 03:45:52 +08:00
|
|
|
state Optional<std::string> msg;
|
2021-02-05 16:55:34 +08:00
|
|
|
state int index;
|
|
|
|
state bool parse_error = false;
|
2021-01-30 03:45:52 +08:00
|
|
|
|
2021-07-18 08:53:38 +08:00
|
|
|
// check update for coordinators
|
2021-02-05 16:55:34 +08:00
|
|
|
Key processes_key = LiteralStringRef("processes").withPrefix(kr.begin);
|
|
|
|
auto processes_entry = ryw->getSpecialKeySpaceWriteMap()[processes_key];
|
|
|
|
if (processes_entry.first) {
|
|
|
|
ASSERT(processes_entry.second.present()); // no clear should be seen here
|
|
|
|
auto processesStr = processes_entry.second.get().toString();
|
2022-02-25 08:34:30 +08:00
|
|
|
boost::split(process_address_or_hostname_strs, processesStr, [](char c) { return c == ','; });
|
|
|
|
if (!process_address_or_hostname_strs.size()) {
|
2021-02-05 16:55:34 +08:00
|
|
|
return ManagementAPIError::toJsonString(
|
|
|
|
false,
|
|
|
|
"coordinators",
|
|
|
|
"New coordinators\' processes are empty, please specify new processes\' network addresses with format "
|
2022-02-25 08:34:30 +08:00
|
|
|
"\"IP:PORT,IP:PORT,...,IP:PORT\" or \"HOSTNAME:PORT,HOSTNAME:PORT,...,HOSTNAME:PORT\"");
|
2021-02-05 16:55:34 +08:00
|
|
|
}
|
2022-02-25 08:34:30 +08:00
|
|
|
for (index = 0; index < process_address_or_hostname_strs.size(); index++) {
|
2021-02-05 16:55:34 +08:00
|
|
|
try {
|
2022-02-25 08:34:30 +08:00
|
|
|
if (Hostname::isHostname(process_address_or_hostname_strs[index])) {
|
|
|
|
conn.hostnames.push_back(Hostname::parse(process_address_or_hostname_strs[index]));
|
2022-02-25 08:14:06 +08:00
|
|
|
} else {
|
2022-02-25 08:34:30 +08:00
|
|
|
NetworkAddress a = NetworkAddress::parse(process_address_or_hostname_strs[index]);
|
|
|
|
if (!a.isValid()) {
|
|
|
|
parse_error = true;
|
|
|
|
} else {
|
|
|
|
conn.coords.push_back(a);
|
|
|
|
}
|
2022-02-25 08:14:06 +08:00
|
|
|
}
|
2021-02-05 16:55:34 +08:00
|
|
|
} catch (Error& e) {
|
2021-02-19 06:23:51 +08:00
|
|
|
TraceEvent(SevDebug, "SpecialKeysNetworkParseError").error(e);
|
2021-02-05 16:55:34 +08:00
|
|
|
parse_error = true;
|
|
|
|
}
|
2021-01-30 03:45:52 +08:00
|
|
|
|
2021-02-05 16:55:34 +08:00
|
|
|
if (parse_error) {
|
2022-02-25 08:34:30 +08:00
|
|
|
std::string error = "ERROR: \'" + process_address_or_hostname_strs[index] +
|
|
|
|
"\' is not a valid network endpoint address\n";
|
2021-02-05 16:55:34 +08:00
|
|
|
return ManagementAPIError::toJsonString(false, "coordinators", error);
|
2021-01-30 03:45:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-24 02:42:56 +08:00
|
|
|
std::string newName;
|
2021-01-30 03:45:52 +08:00
|
|
|
// check update for cluster_description
|
|
|
|
Key cluster_decription_key = LiteralStringRef("cluster_description").withPrefix(kr.begin);
|
|
|
|
auto entry = ryw->getSpecialKeySpaceWriteMap()[cluster_decription_key];
|
|
|
|
if (entry.first) {
|
|
|
|
// check valid description [a-zA-Z0-9_]+
|
|
|
|
if (entry.second.present() && isAlphaNumeric(entry.second.get().toString())) {
|
|
|
|
// do the name change
|
2022-05-24 02:42:56 +08:00
|
|
|
newName = entry.second.get().toString();
|
2021-01-30 03:45:52 +08:00
|
|
|
} else {
|
|
|
|
// throw the error
|
2022-04-28 12:54:13 +08:00
|
|
|
return ManagementAPIError::toJsonString(
|
|
|
|
false, "coordinators", "Cluster description must match [A-Za-z0-9_]+");
|
2021-01-30 03:45:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TraceEvent(SevDebug, "SKSChangeCoordinatorsStart")
|
2022-05-24 02:42:56 +08:00
|
|
|
.detail("NewConnectionString", conn.toString())
|
2021-01-30 03:45:52 +08:00
|
|
|
.detail("Description", entry.first ? entry.second.get().toString() : "");
|
|
|
|
|
2022-05-24 02:42:56 +08:00
|
|
|
Optional<CoordinatorsResult> r = wait(changeQuorumChecker(&ryw->getTransaction(), &conn, newName));
|
2021-01-30 03:45:52 +08:00
|
|
|
|
|
|
|
TraceEvent(SevDebug, "SKSChangeCoordinatorsFinish")
|
2021-02-17 05:06:25 +08:00
|
|
|
.detail("Result", r.present() ? static_cast<int>(r.get()) : -1); // -1 means success
|
2021-01-30 03:45:52 +08:00
|
|
|
if (r.present()) {
|
|
|
|
auto res = r.get();
|
2021-02-17 05:01:37 +08:00
|
|
|
bool retriable = false;
|
2021-09-14 06:54:36 +08:00
|
|
|
if (res == CoordinatorsResult::COORDINATOR_UNREACHABLE) {
|
2021-02-17 05:01:37 +08:00
|
|
|
retriable = true;
|
2021-01-30 03:45:52 +08:00
|
|
|
} else if (res == CoordinatorsResult::SUCCESS) {
|
|
|
|
TraceEvent(SevError, "SpecialKeysForCoordinators").detail("UnexpectedSuccessfulResult", "");
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
2021-09-14 06:54:36 +08:00
|
|
|
msg = ManagementAPIError::toJsonString(retriable, "coordinators", ManagementAPI::generateErrorMessage(res));
|
2021-01-30 03:45:52 +08:00
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> CoordinatorsImpl::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
return coordinatorsCommitActor(ryw, getKeyRange());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoordinatorsImpl::clear(ReadYourWritesTransaction* ryw, const KeyRangeRef& range) {
|
|
|
|
return throwSpecialKeyApiFailure(ryw, "coordinators", "Clear range is meaningless thus forbidden for coordinators");
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoordinatorsImpl::clear(ReadYourWritesTransaction* ryw, const KeyRef& key) {
|
|
|
|
return throwSpecialKeyApiFailure(
|
|
|
|
ryw, "coordinators", "Clear operation is meaningless thus forbidden for coordinators");
|
2021-01-30 10:20:09 +08:00
|
|
|
}
|
2021-02-17 15:55:58 +08:00
|
|
|
|
|
|
|
CoordinatorsAutoImpl::CoordinatorsAutoImpl(KeyRangeRef kr) : SpecialKeyRangeReadImpl(kr) {}
|
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR static Future<RangeResult> CoordinatorsAutoImplActor(ReadYourWritesTransaction* ryw, KeyRangeRef kr) {
|
|
|
|
state RangeResult res;
|
2021-02-17 15:55:58 +08:00
|
|
|
state std::string autoCoordinatorsKey;
|
|
|
|
state Transaction& tr = ryw->getTransaction();
|
|
|
|
|
|
|
|
tr.setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-02-17 15:55:58 +08:00
|
|
|
tr.setOption(FDBTransactionOptions::USE_PROVISIONAL_PROXIES);
|
|
|
|
tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
|
|
|
|
Optional<Value> currentKey = wait(tr.get(coordinatorsKey));
|
|
|
|
|
|
|
|
if (!currentKey.present()) {
|
|
|
|
ryw->setSpecialKeySpaceErrorMsg(
|
|
|
|
ManagementAPIError::toJsonString(false, "auto_coordinators", "The coordinator key does not exist"));
|
|
|
|
throw special_keys_api_failure();
|
|
|
|
}
|
|
|
|
state ClusterConnectionString old(currentKey.get().toString());
|
|
|
|
state CoordinatorsResult result = CoordinatorsResult::SUCCESS;
|
|
|
|
|
2022-04-28 12:54:13 +08:00
|
|
|
std::vector<NetworkAddress> oldCoordinators = wait(old.tryResolveHostnames());
|
2021-02-17 15:55:58 +08:00
|
|
|
std::vector<NetworkAddress> _desiredCoordinators = wait(autoQuorumChange()->getDesiredCoordinators(
|
2021-10-11 11:44:56 +08:00
|
|
|
&tr,
|
2022-04-28 12:54:13 +08:00
|
|
|
oldCoordinators,
|
2021-10-11 11:44:56 +08:00
|
|
|
Reference<ClusterConnectionMemoryRecord>(new ClusterConnectionMemoryRecord(old)),
|
|
|
|
result));
|
2021-02-17 15:55:58 +08:00
|
|
|
|
|
|
|
if (result == CoordinatorsResult::NOT_ENOUGH_MACHINES) {
|
|
|
|
// we could get not_enough_machines if we happen to see the database while the cluster controller is updating
|
|
|
|
// the worker list, so make sure it happens twice before returning a failure
|
|
|
|
ryw->setSpecialKeySpaceErrorMsg(ManagementAPIError::toJsonString(
|
2021-07-18 03:05:32 +08:00
|
|
|
true,
|
|
|
|
"auto_coordinators",
|
|
|
|
"Too few fdbserver machines to provide coordination at the current redundancy level"));
|
2021-02-17 15:55:58 +08:00
|
|
|
throw special_keys_api_failure();
|
|
|
|
}
|
|
|
|
|
2022-05-24 02:42:56 +08:00
|
|
|
if (result == CoordinatorsResult::SAME_NETWORK_ADDRESSES) {
|
|
|
|
for (const auto& host : old.hostnames) {
|
|
|
|
autoCoordinatorsKey += autoCoordinatorsKey.size() ? "," : "";
|
|
|
|
autoCoordinatorsKey += host.toString();
|
|
|
|
}
|
|
|
|
for (const auto& coord : old.coords) {
|
|
|
|
autoCoordinatorsKey += autoCoordinatorsKey.size() ? "," : "";
|
|
|
|
autoCoordinatorsKey += coord.toString();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (const auto& address : _desiredCoordinators) {
|
|
|
|
autoCoordinatorsKey += autoCoordinatorsKey.size() ? "," : "";
|
|
|
|
autoCoordinatorsKey += address.toString();
|
|
|
|
}
|
2021-02-17 15:55:58 +08:00
|
|
|
}
|
|
|
|
res.push_back_deep(res.arena(), KeyValueRef(kr.begin, Value(autoCoordinatorsKey)));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> CoordinatorsAutoImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-02-17 15:55:58 +08:00
|
|
|
// single key range, the queried range should always be the same as the underlying range
|
|
|
|
ASSERT(kr == getKeyRange());
|
|
|
|
return CoordinatorsAutoImplActor(ryw, kr);
|
|
|
|
}
|
2021-02-20 06:29:08 +08:00
|
|
|
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR static Future<RangeResult> getMinCommitVersionActor(ReadYourWritesTransaction* ryw, KeyRangeRef kr) {
|
2022-01-29 02:55:02 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-02-18 17:27:14 +08:00
|
|
|
Optional<Value> val = wait(ryw->getTransaction().get(minRequiredCommitVersionKey));
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2021-02-18 17:27:14 +08:00
|
|
|
if (val.present()) {
|
|
|
|
Version minRequiredCommitVersion = BinaryReader::fromStringRef<Version>(val.get(), Unversioned());
|
|
|
|
ValueRef version(result.arena(), boost::lexical_cast<std::string>(minRequiredCommitVersion));
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(kr.begin, version));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
AdvanceVersionImpl::AdvanceVersionImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> AdvanceVersionImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-02-18 17:27:14 +08:00
|
|
|
// single key range, the queried range should always be the same as the underlying range
|
|
|
|
ASSERT(kr == getKeyRange());
|
|
|
|
auto entry = ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandPrefix("advanceversion")];
|
|
|
|
if (!ryw->readYourWritesDisabled() && entry.first) {
|
|
|
|
// ryw enabled and we have written to the special key
|
2021-05-04 04:14:16 +08:00
|
|
|
RangeResult result;
|
2021-02-18 17:27:14 +08:00
|
|
|
if (entry.second.present()) {
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(kr.begin, entry.second.get()));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
return getMinCommitVersionActor(ryw, kr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR static Future<Optional<std::string>> advanceVersionCommitActor(ReadYourWritesTransaction* ryw, Version v) {
|
2022-04-30 03:32:49 +08:00
|
|
|
Optional<Standalone<StringRef>> versionEpochValue = wait(ryw->getTransaction().get(versionEpochKey));
|
|
|
|
if (versionEpochValue.present()) {
|
|
|
|
return ManagementAPIError::toJsonString(
|
|
|
|
false, "advanceversion", "Illegal to modify the version while the version epoch is enabled");
|
|
|
|
}
|
|
|
|
|
2022-03-12 03:02:35 +08:00
|
|
|
// Max version we can set for minRequiredCommitVersionKey,
|
|
|
|
// making sure the cluster can still be alive for 1000 years after the recovery
|
|
|
|
static const Version maxAllowedVerion =
|
|
|
|
std::numeric_limits<int64_t>::max() - 1 - CLIENT_KNOBS->VERSIONS_PER_SECOND * 3600 * 24 * 365 * 1000;
|
|
|
|
|
2021-02-18 17:27:14 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-02-26 05:02:56 +08:00
|
|
|
TraceEvent(SevDebug, "AdvanceVersion").detail("MaxAllowedVersion", maxAllowedVerion);
|
|
|
|
if (v > maxAllowedVerion) {
|
|
|
|
return ManagementAPIError::toJsonString(
|
2021-03-12 04:53:46 +08:00
|
|
|
false,
|
|
|
|
"advanceversion",
|
2021-02-26 05:02:56 +08:00
|
|
|
"The given version is larger than the maximum allowed value(2**63-1-version_per_second*3600*24*365*1000)");
|
|
|
|
}
|
2021-02-18 17:27:14 +08:00
|
|
|
Version rv = wait(ryw->getTransaction().getReadVersion());
|
|
|
|
if (rv <= v) {
|
|
|
|
ryw->getTransaction().set(minRequiredCommitVersionKey, BinaryWriter::toValue(v + 1, Unversioned()));
|
|
|
|
} else {
|
2021-03-12 04:53:46 +08:00
|
|
|
return ManagementAPIError::toJsonString(
|
|
|
|
false, "advanceversion", "Current read version is larger than the given version");
|
2021-02-18 17:27:14 +08:00
|
|
|
}
|
|
|
|
return Optional<std::string>();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> AdvanceVersionImpl::commit(ReadYourWritesTransaction* ryw) {
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-02-18 17:27:14 +08:00
|
|
|
auto minCommitVersion =
|
|
|
|
ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandPrefix("advanceversion")].second;
|
|
|
|
if (minCommitVersion.present()) {
|
|
|
|
try {
|
|
|
|
// Version is int64_t
|
|
|
|
Version v = boost::lexical_cast<int64_t>(minCommitVersion.get().toString());
|
|
|
|
return advanceVersionCommitActor(ryw, v);
|
|
|
|
} catch (boost::bad_lexical_cast& e) {
|
|
|
|
return Optional<std::string>(ManagementAPIError::toJsonString(
|
|
|
|
false, "advanceversion", "Invalid version(int64_t) argument: " + minCommitVersion.get().toString()));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ryw->getTransaction().clear(minRequiredCommitVersionKey);
|
|
|
|
}
|
|
|
|
return Optional<std::string>();
|
2021-02-20 04:22:00 +08:00
|
|
|
}
|
|
|
|
|
Add fdbcli command to read/write version epoch (#6480)
* Initialize cluster version at wall-clock time
Previously, new clusters would begin at version 0. After this change,
clusters will initialize at a version matching wall-clock time. Instead
of using the Unix epoch (or Windows epoch), FDB clusters will use a new
epoch, defaulting to January 1, 2010, 01:00:00+00:00. In the future,
this base epoch will be modifiable through fdbcli, allowing
administrators to advance the cluster version.
Basing the version off of time allows different FDB clusters to share
data without running into version issues.
* Send version epoch to master
* Cleanup
* Update fdbserver/storageserver.actor.cpp
Co-authored-by: A.J. Beamon <aj.beamon@snowflake.com>
* Jump directly to expected version if possible
* Fix initial version issue on storage servers
* Add random recovery offset to start version in simulation
* Type fixes
* Disable reference time by default
Enable on a cluster using the fdbcli command `versionepoch add 0`.
* Use correct recoveryTransactionVersion when recovering
* Allow version epoch to be adjusted forwards (to decrease the version)
* Set version epoch in simulation
* Add quiet database check to ensure small version offset
* Fix initial version issue on storage servers
* Disable reference time by default
Enable on a cluster using the fdbcli command `versionepoch add 0`.
* Add fdbcli command to read/write version epoch
* Cause recovery when version epoch is set
* Handle optional version epoch key
* Add ability to clear the version epoch
This causes version advancement to revert to the old methodology whereas
versions attempt to advance by about a million versions per second,
instead of trying to match the clock.
* Update transaction access
* Modify version epoch to use microseconds instead of seconds
* Modify fdbcli version target API
Move commands from `versionepoch` to `targetversion` top level command.
* Add fdbcli tests for
* Temporarily disable targetversion cli tests
* Fix version epoch fetch issue
* Fix Arena issue
* Reduce max version jump in simulation to 1,000,000
* Rework fdbcli API
It now requires two commands to fully switch a cluster to using the
version epoch. First, enable the version epoch with `versionepoch
enable` or `versionepoch set <versionepoch>`. At this point, versions
will be given out at a faster or slower rate in an attempt to reach the
expected version. Then, run `versionepoch commit` to perform a one time
jump to the expected version. This is essentially irreversible.
* Temporarily disable old targetversion tests
* Cleanup
* Move version epoch buggify to sequencer
This will cause some issues with the QuietDatabase check for the version
offset - namely, it won't do anything, since the version epoch is not
being written to the txnStateStore in simulation. This will get fixed in
the future.
Co-authored-by: A.J. Beamon <aj.beamon@snowflake.com>
2022-04-09 03:33:19 +08:00
|
|
|
ACTOR static Future<RangeResult> getVersionEpochActor(ReadYourWritesTransaction* ryw, KeyRangeRef kr) {
|
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
|
|
|
Optional<Value> val = wait(ryw->getTransaction().get(versionEpochKey));
|
|
|
|
RangeResult result;
|
|
|
|
if (val.present()) {
|
|
|
|
int64_t versionEpoch = BinaryReader::fromStringRef<int64_t>(val.get(), Unversioned());
|
|
|
|
ValueRef version(result.arena(), boost::lexical_cast<std::string>(versionEpoch));
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(kr.begin, version));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
VersionEpochImpl::VersionEpochImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
|
|
|
Future<RangeResult> VersionEpochImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
|
|
|
ASSERT(kr == getKeyRange());
|
|
|
|
return getVersionEpochActor(ryw, kr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> VersionEpochImpl::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
auto versionEpoch =
|
|
|
|
ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandPrefix("versionepoch")].second;
|
|
|
|
if (versionEpoch.present()) {
|
|
|
|
int64_t epoch = BinaryReader::fromStringRef<int64_t>(versionEpoch.get(), Unversioned());
|
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
|
|
|
ryw->getTransaction().set(versionEpochKey, BinaryWriter::toValue(epoch, Unversioned()));
|
|
|
|
} else {
|
|
|
|
ryw->getTransaction().clear(versionEpochKey);
|
|
|
|
}
|
|
|
|
return Optional<std::string>();
|
|
|
|
}
|
|
|
|
|
2021-02-20 04:22:00 +08:00
|
|
|
ClientProfilingImpl::ClientProfilingImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-05-24 04:02:17 +08:00
|
|
|
Future<RangeResult> ClientProfilingImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
|
|
|
KeyRef prefix = getKeyRange().begin;
|
|
|
|
RangeResult result = RangeResult();
|
2021-02-20 04:22:00 +08:00
|
|
|
// client_txn_sample_rate
|
2022-05-24 04:02:17 +08:00
|
|
|
Key sampleRateKey = LiteralStringRef("client_txn_sample_rate").withPrefix(prefix);
|
2022-01-27 08:04:40 +08:00
|
|
|
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2022-01-27 08:04:40 +08:00
|
|
|
|
2021-02-20 04:22:00 +08:00
|
|
|
if (kr.contains(sampleRateKey)) {
|
|
|
|
auto entry = ryw->getSpecialKeySpaceWriteMap()[sampleRateKey];
|
|
|
|
if (!ryw->readYourWritesDisabled() && entry.first) {
|
2022-03-07 13:52:39 +08:00
|
|
|
// clear is forbidden
|
|
|
|
ASSERT(entry.second.present());
|
2021-02-26 05:13:34 +08:00
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(sampleRateKey, entry.second.get()));
|
2021-02-20 04:22:00 +08:00
|
|
|
} else {
|
|
|
|
std::string sampleRateStr = "default";
|
2022-04-28 03:45:24 +08:00
|
|
|
const double sampleRateDbl = ryw->getDatabase()->globalConfig->get<double>(
|
|
|
|
fdbClientInfoTxnSampleRate, std::numeric_limits<double>::infinity());
|
|
|
|
if (!std::isinf(sampleRateDbl)) {
|
|
|
|
sampleRateStr = std::to_string(sampleRateDbl);
|
2021-02-20 04:22:00 +08:00
|
|
|
}
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(sampleRateKey, Value(sampleRateStr)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// client_txn_size_limit
|
2022-05-24 04:02:17 +08:00
|
|
|
Key txnSizeLimitKey = LiteralStringRef("client_txn_size_limit").withPrefix(prefix);
|
2021-02-20 04:22:00 +08:00
|
|
|
if (kr.contains(txnSizeLimitKey)) {
|
|
|
|
auto entry = ryw->getSpecialKeySpaceWriteMap()[txnSizeLimitKey];
|
|
|
|
if (!ryw->readYourWritesDisabled() && entry.first) {
|
2022-03-07 13:52:39 +08:00
|
|
|
// clear is forbidden
|
|
|
|
ASSERT(entry.second.present());
|
2021-02-26 05:13:34 +08:00
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(txnSizeLimitKey, entry.second.get()));
|
2021-02-20 04:22:00 +08:00
|
|
|
} else {
|
|
|
|
std::string sizeLimitStr = "default";
|
2022-04-28 03:45:24 +08:00
|
|
|
const int64_t sizeLimit = ryw->getDatabase()->globalConfig->get<int64_t>(fdbClientInfoTxnSizeLimit, -1);
|
|
|
|
if (sizeLimit != -1) {
|
|
|
|
sizeLimitStr = boost::lexical_cast<std::string>(sizeLimit);
|
2021-02-20 04:22:00 +08:00
|
|
|
}
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(txnSizeLimitKey, Value(sizeLimitStr)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> ClientProfilingImpl::commit(ReadYourWritesTransaction* ryw) {
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2022-01-27 08:04:40 +08:00
|
|
|
|
2022-04-28 03:45:24 +08:00
|
|
|
Standalone<VectorRef<KeyValueRef>> insertions;
|
|
|
|
Standalone<VectorRef<KeyRangeRef>> clears;
|
|
|
|
|
2021-02-20 04:22:00 +08:00
|
|
|
// client_txn_sample_rate
|
|
|
|
Key sampleRateKey = LiteralStringRef("client_txn_sample_rate").withPrefix(getKeyRange().begin);
|
|
|
|
auto rateEntry = ryw->getSpecialKeySpaceWriteMap()[sampleRateKey];
|
|
|
|
|
|
|
|
if (rateEntry.first && rateEntry.second.present()) {
|
|
|
|
std::string sampleRateStr = rateEntry.second.get().toString();
|
2022-04-28 03:45:24 +08:00
|
|
|
if (sampleRateStr == "default") {
|
|
|
|
clears.push_back_deep(clears.arena(),
|
|
|
|
KeyRangeRef(fdbClientInfoTxnSampleRate, keyAfter(fdbClientInfoTxnSampleRate)));
|
|
|
|
} else {
|
2021-02-20 04:22:00 +08:00
|
|
|
try {
|
2022-04-28 03:45:24 +08:00
|
|
|
double sampleRate = boost::lexical_cast<double>(sampleRateStr);
|
2022-07-20 04:45:59 +08:00
|
|
|
Tuple rate = Tuple::makeTuple(sampleRate);
|
2022-04-28 03:45:24 +08:00
|
|
|
insertions.push_back_deep(insertions.arena(), KeyValueRef(fdbClientInfoTxnSampleRate, rate.pack()));
|
2021-02-20 04:22:00 +08:00
|
|
|
} catch (boost::bad_lexical_cast& e) {
|
|
|
|
return Optional<std::string>(ManagementAPIError::toJsonString(
|
|
|
|
false, "profile", "Invalid transaction sample rate(double): " + sampleRateStr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// client_txn_size_limit
|
|
|
|
Key txnSizeLimitKey = LiteralStringRef("client_txn_size_limit").withPrefix(getKeyRange().begin);
|
|
|
|
auto sizeLimitEntry = ryw->getSpecialKeySpaceWriteMap()[txnSizeLimitKey];
|
|
|
|
if (sizeLimitEntry.first && sizeLimitEntry.second.present()) {
|
|
|
|
std::string sizeLimitStr = sizeLimitEntry.second.get().toString();
|
2022-04-28 03:45:24 +08:00
|
|
|
if (sizeLimitStr == "default") {
|
|
|
|
clears.push_back_deep(clears.arena(),
|
|
|
|
KeyRangeRef(fdbClientInfoTxnSizeLimit, keyAfter(fdbClientInfoTxnSizeLimit)));
|
|
|
|
} else {
|
2021-02-20 04:22:00 +08:00
|
|
|
try {
|
2022-04-28 03:45:24 +08:00
|
|
|
int64_t sizeLimit = boost::lexical_cast<int64_t>(sizeLimitStr);
|
2022-07-20 04:45:59 +08:00
|
|
|
Tuple size = Tuple::makeTuple(sizeLimit);
|
2022-04-28 03:45:24 +08:00
|
|
|
insertions.push_back_deep(insertions.arena(), KeyValueRef(fdbClientInfoTxnSizeLimit, size.pack()));
|
2021-02-20 04:22:00 +08:00
|
|
|
} catch (boost::bad_lexical_cast& e) {
|
|
|
|
return Optional<std::string>(ManagementAPIError::toJsonString(
|
|
|
|
false, "profile", "Invalid transaction size limit(int64_t): " + sizeLimitStr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-04-28 03:45:24 +08:00
|
|
|
GlobalConfig::applyChanges(ryw->getTransaction(), insertions, clears);
|
2021-02-20 04:22:00 +08:00
|
|
|
return Optional<std::string>();
|
2021-02-20 06:29:08 +08:00
|
|
|
}
|
2021-02-26 05:02:56 +08:00
|
|
|
|
|
|
|
void ClientProfilingImpl::clear(ReadYourWritesTransaction* ryw, const KeyRangeRef& range) {
|
|
|
|
return throwSpecialKeyApiFailure(
|
|
|
|
ryw, "profile", "Clear range is forbidden for profile client. You can set it to default to disable profiling.");
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientProfilingImpl::clear(ReadYourWritesTransaction* ryw, const KeyRef& key) {
|
|
|
|
return throwSpecialKeyApiFailure(
|
2021-03-12 04:53:46 +08:00
|
|
|
ryw,
|
|
|
|
"profile",
|
2021-02-26 05:02:56 +08:00
|
|
|
"Clear operation is forbidden for profile client. You can set it to default to disable profiling.");
|
|
|
|
}
|
2021-04-16 04:50:50 +08:00
|
|
|
|
2021-06-04 06:10:04 +08:00
|
|
|
ActorLineageImpl::ActorLineageImpl(KeyRangeRef kr) : SpecialKeyRangeReadImpl(kr) {}
|
|
|
|
|
|
|
|
void parse(StringRef& val, int& i) {
|
|
|
|
i = std::stoi(val.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
void parse(StringRef& val, double& d) {
|
|
|
|
d = std::stod(val.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
void parse(StringRef& val, WaitState& w) {
|
2021-05-21 02:16:31 +08:00
|
|
|
if (val == LiteralStringRef("disk") || val == LiteralStringRef("Disk")) {
|
2021-06-04 06:10:04 +08:00
|
|
|
w = WaitState::Disk;
|
2021-05-21 02:16:31 +08:00
|
|
|
} else if (val == LiteralStringRef("network") || val == LiteralStringRef("Network")) {
|
2021-06-04 06:10:04 +08:00
|
|
|
w = WaitState::Network;
|
2021-05-21 02:16:31 +08:00
|
|
|
} else if (val == LiteralStringRef("running") || val == LiteralStringRef("Running")) {
|
2021-06-04 06:10:04 +08:00
|
|
|
w = WaitState::Running;
|
|
|
|
} else {
|
|
|
|
throw std::range_error("failed to parse run state");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void parse(StringRef& val, time_t& t) {
|
2021-10-29 04:52:52 +08:00
|
|
|
struct tm tm;
|
2021-08-18 21:46:20 +08:00
|
|
|
#ifdef _WIN32
|
|
|
|
std::istringstream s(val.toString());
|
|
|
|
s.imbue(std::locale(setlocale(LC_TIME, nullptr)));
|
|
|
|
s >> std::get_time(&tm, "%FT%T%z");
|
|
|
|
if (s.fail()) {
|
|
|
|
throw std::invalid_argument("failed to parse ISO 8601 datetime");
|
|
|
|
}
|
|
|
|
long timezone;
|
|
|
|
if (_get_timezone(&timezone) != 0) {
|
|
|
|
throw std::runtime_error("failed to convert ISO 8601 datetime");
|
|
|
|
}
|
|
|
|
timezone = -timezone;
|
|
|
|
#else
|
2021-06-04 06:10:04 +08:00
|
|
|
if (strptime(val.toString().c_str(), "%FT%T%z", &tm) == nullptr) {
|
|
|
|
throw std::invalid_argument("failed to parse ISO 8601 datetime");
|
|
|
|
}
|
|
|
|
long timezone = tm.tm_gmtoff;
|
|
|
|
t = timegm(&tm);
|
|
|
|
if (t == -1) {
|
|
|
|
throw std::runtime_error("failed to convert ISO 8601 datetime");
|
|
|
|
}
|
|
|
|
t -= timezone;
|
2021-08-18 21:46:20 +08:00
|
|
|
#endif
|
2021-06-04 06:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void parse(StringRef& val, NetworkAddress& a) {
|
|
|
|
auto address = NetworkAddress::parse(val.toString());
|
|
|
|
if (!address.isValid()) {
|
|
|
|
throw std::invalid_argument("invalid host");
|
|
|
|
}
|
|
|
|
a = address;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Base case function for parsing function below.
|
|
|
|
template <typename T>
|
|
|
|
void parse(std::vector<StringRef>::iterator it, std::vector<StringRef>::iterator end, T& t1) {
|
|
|
|
if (it == end) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
parse(*it, t1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Given an iterator into a vector of string tokens, an iterator to the end of
|
|
|
|
// the search space in the vector (exclusive), and a list of references to
|
|
|
|
// types, parses each token in the vector into the associated type according to
|
|
|
|
// the order of the arguments.
|
|
|
|
//
|
|
|
|
// For example, given the vector ["1", "1.5", "127.0.0.1:4000"] and the
|
|
|
|
// argument list int a, double b, NetworkAddress c, after this function returns
|
|
|
|
// each parameter passed in will hold the parsed value from the token list.
|
|
|
|
//
|
|
|
|
// The appropriate parsing function must be implemented for the type you wish
|
|
|
|
// to parse. See the existing parsing functions above, and add your own if
|
|
|
|
// necessary.
|
|
|
|
template <typename T, typename... Types>
|
|
|
|
void parse(std::vector<StringRef>::iterator it, std::vector<StringRef>::iterator end, T& t1, Types&... remaining) {
|
|
|
|
// Return as soon as all tokens have been parsed. This allows parameters
|
|
|
|
// passed at the end to act as optional parameters -- they will only be set
|
|
|
|
// if the value exists.
|
|
|
|
if (it == end) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
parse(*it, t1);
|
|
|
|
parse(++it, end, remaining...);
|
|
|
|
} catch (Error& e) {
|
|
|
|
throw e;
|
|
|
|
} catch (std::exception& e) {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR static Future<RangeResult> actorLineageGetRangeActor(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRef prefix,
|
|
|
|
KeyRangeRef kr) {
|
|
|
|
state RangeResult result;
|
|
|
|
|
|
|
|
// Set default values for all fields. The default will be used if the field
|
|
|
|
// is missing in the key.
|
|
|
|
state NetworkAddress host;
|
|
|
|
state WaitState waitStateStart = WaitState{ 0 };
|
|
|
|
state WaitState waitStateEnd = WaitState{ 2 };
|
|
|
|
state time_t timeStart = 0;
|
|
|
|
state time_t timeEnd = std::numeric_limits<time_t>::max();
|
|
|
|
state int seqStart = 0;
|
|
|
|
state int seqEnd = std::numeric_limits<int>::max();
|
|
|
|
|
|
|
|
state std::vector<StringRef> beginValues = kr.begin.removePrefix(prefix).splitAny("/"_sr);
|
|
|
|
state std::vector<StringRef> endValues = kr.end.removePrefix(prefix).splitAny("/"_sr);
|
|
|
|
// Require index (either "state" or "time") and address:port.
|
|
|
|
if (beginValues.size() < 2 || endValues.size() < 2) {
|
2022-08-04 00:22:49 +08:00
|
|
|
ryw->setSpecialKeySpaceErrorMsg(
|
|
|
|
ManagementAPIError::toJsonString(false, "read actor_lineage", "missing required parameters (index, host)"));
|
2021-06-04 06:10:04 +08:00
|
|
|
throw special_keys_api_failure();
|
|
|
|
}
|
|
|
|
|
|
|
|
state NetworkAddress endRangeHost;
|
|
|
|
try {
|
|
|
|
if (SpecialKeySpace::getActorLineageApiCommandRange("state").contains(kr)) {
|
|
|
|
// For the range \xff\xff/actor_lineage/state/ip:port/wait-state/time/seq
|
|
|
|
parse(beginValues.begin() + 1, beginValues.end(), host, waitStateStart, timeStart, seqStart);
|
|
|
|
if (kr.begin != kr.end) {
|
|
|
|
parse(endValues.begin() + 1, endValues.end(), endRangeHost, waitStateEnd, timeEnd, seqEnd);
|
|
|
|
}
|
|
|
|
} else if (SpecialKeySpace::getActorLineageApiCommandRange("time").contains(kr)) {
|
|
|
|
// For the range \xff\xff/actor_lineage/time/ip:port/time/wait-state/seq
|
|
|
|
parse(beginValues.begin() + 1, beginValues.end(), host, timeStart, waitStateStart, seqStart);
|
|
|
|
if (kr.begin != kr.end) {
|
|
|
|
parse(endValues.begin() + 1, endValues.end(), endRangeHost, timeEnd, waitStateEnd, seqEnd);
|
|
|
|
}
|
|
|
|
} else {
|
2022-08-04 00:22:49 +08:00
|
|
|
ryw->setSpecialKeySpaceErrorMsg(
|
|
|
|
ManagementAPIError::toJsonString(false, "read actor_lineage", "invalid index in actor_lineage"));
|
2021-06-04 06:10:04 +08:00
|
|
|
throw special_keys_api_failure();
|
|
|
|
}
|
|
|
|
} catch (Error& e) {
|
|
|
|
if (e.code() != special_keys_api_failure().code()) {
|
2022-08-04 00:22:49 +08:00
|
|
|
ryw->setSpecialKeySpaceErrorMsg(
|
|
|
|
ManagementAPIError::toJsonString(false, "read actor_lineage", "failed to parse key"));
|
2021-06-04 06:10:04 +08:00
|
|
|
throw special_keys_api_failure();
|
|
|
|
} else {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kr.begin != kr.end && host != endRangeHost) {
|
|
|
|
// The client doesn't know about all the hosts, so a get range covering
|
|
|
|
// multiple hosts has no way of knowing which IP:port combos to use.
|
2022-08-04 00:22:49 +08:00
|
|
|
ryw->setSpecialKeySpaceErrorMsg(ManagementAPIError::toJsonString(
|
|
|
|
false, "read actor_lineage", "the host must remain the same on both ends of the range"));
|
2021-06-04 06:10:04 +08:00
|
|
|
throw special_keys_api_failure();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open endpoint to target process on each call. This can be optimized at
|
|
|
|
// some point...
|
|
|
|
state ProcessInterface process;
|
2021-09-21 00:46:34 +08:00
|
|
|
process.getInterface = RequestStream<GetProcessInterfaceRequest>(Endpoint::wellKnown({ host }, WLTOKEN_PROCESS));
|
2021-06-04 06:10:04 +08:00
|
|
|
ProcessInterface p = wait(retryBrokenPromise(process.getInterface, GetProcessInterfaceRequest{}));
|
|
|
|
process = p;
|
|
|
|
|
|
|
|
ActorLineageRequest actorLineageRequest;
|
|
|
|
actorLineageRequest.waitStateStart = waitStateStart;
|
|
|
|
actorLineageRequest.waitStateEnd = waitStateEnd;
|
|
|
|
actorLineageRequest.timeStart = timeStart;
|
|
|
|
actorLineageRequest.timeEnd = timeEnd;
|
|
|
|
ActorLineageReply reply = wait(process.actorLineage.getReply(actorLineageRequest));
|
|
|
|
|
|
|
|
time_t dt = 0;
|
|
|
|
int seq = -1;
|
|
|
|
for (const auto& sample : reply.samples) {
|
2021-05-21 02:16:31 +08:00
|
|
|
time_t datetime = (time_t)sample.time;
|
|
|
|
char buf[50];
|
|
|
|
struct tm* tm;
|
|
|
|
tm = localtime(&datetime);
|
|
|
|
size_t size = strftime(buf, 50, "%FT%T%z", tm);
|
|
|
|
std::string date(buf, size);
|
|
|
|
|
|
|
|
seq = dt == datetime ? seq + 1 : 0;
|
|
|
|
dt = datetime;
|
2021-06-04 06:10:04 +08:00
|
|
|
|
2021-05-21 02:16:31 +08:00
|
|
|
for (const auto& [waitState, data] : sample.data) {
|
2021-06-04 06:10:04 +08:00
|
|
|
if (seq < seqStart) {
|
|
|
|
continue;
|
|
|
|
} else if (seq >= seqEnd) {
|
|
|
|
break;
|
2021-08-28 08:07:47 +08:00
|
|
|
}
|
2021-06-04 06:10:04 +08:00
|
|
|
|
|
|
|
std::ostringstream streamKey;
|
|
|
|
if (SpecialKeySpace::getActorLineageApiCommandRange("state").contains(kr)) {
|
|
|
|
streamKey << SpecialKeySpace::getActorLineageApiCommandPrefix("state").toString() << host.toString()
|
|
|
|
<< "/" << to_string(waitState) << "/" << date;
|
|
|
|
} else if (SpecialKeySpace::getActorLineageApiCommandRange("time").contains(kr)) {
|
|
|
|
streamKey << SpecialKeySpace::getActorLineageApiCommandPrefix("time").toString() << host.toString()
|
|
|
|
<< "/" << date << "/" << to_string(waitState);
|
|
|
|
} else {
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
streamKey << "/" << seq;
|
|
|
|
|
|
|
|
msgpack::object_handle oh = msgpack::unpack(data.data(), data.size());
|
|
|
|
msgpack::object deserialized = oh.get();
|
|
|
|
|
|
|
|
std::ostringstream stream;
|
|
|
|
stream << deserialized;
|
|
|
|
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(streamKey.str(), stream.str()));
|
|
|
|
}
|
2021-05-21 02:16:31 +08:00
|
|
|
|
|
|
|
if (sample.data.size() == 0) {
|
|
|
|
std::ostringstream streamKey;
|
|
|
|
if (SpecialKeySpace::getActorLineageApiCommandRange("state").contains(kr)) {
|
|
|
|
streamKey << SpecialKeySpace::getActorLineageApiCommandPrefix("state").toString() << host.toString()
|
|
|
|
<< "/Running/" << date;
|
|
|
|
} else if (SpecialKeySpace::getActorLineageApiCommandRange("time").contains(kr)) {
|
|
|
|
streamKey << SpecialKeySpace::getActorLineageApiCommandPrefix("time").toString() << host.toString()
|
|
|
|
<< "/" << date << "/Running";
|
|
|
|
} else {
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
streamKey << "/" << seq;
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(streamKey.str(), "{}"_sr));
|
|
|
|
}
|
2021-06-04 06:10:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ActorLineageImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-06-04 06:10:04 +08:00
|
|
|
return actorLineageGetRangeActor(ryw, getKeyRange().begin, kr);
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
std::string_view to_string_view(StringRef sr) {
|
|
|
|
return std::string_view(reinterpret_cast<const char*>(sr.begin()), sr.size());
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
ActorProfilerConf::ActorProfilerConf(KeyRangeRef kr)
|
|
|
|
: SpecialKeyRangeRWImpl(kr), config(ProfilerConfig::instance().getConfig()) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ActorProfilerConf::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-06-04 06:10:04 +08:00
|
|
|
RangeResult res;
|
|
|
|
std::string_view begin(to_string_view(kr.begin.removePrefix(range.begin))),
|
|
|
|
end(to_string_view(kr.end.removePrefix(range.begin)));
|
|
|
|
for (auto& p : config) {
|
|
|
|
if (p.first > end) {
|
|
|
|
break;
|
|
|
|
} else if (p.first > begin) {
|
|
|
|
KeyValueRef kv;
|
2021-05-21 02:16:31 +08:00
|
|
|
kv.key = StringRef(res.arena(), p.first).withPrefix(kr.begin, res.arena());
|
2021-06-04 06:10:04 +08:00
|
|
|
kv.value = StringRef(res.arena(), p.second);
|
|
|
|
res.push_back(res.arena(), kv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActorProfilerConf::set(ReadYourWritesTransaction* ryw, const KeyRef& key, const ValueRef& value) {
|
|
|
|
config[key.removePrefix(range.begin).toString()] = value.toString();
|
2021-05-21 02:16:31 +08:00
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(key, std::make_pair(true, Optional<Value>(value)));
|
2021-06-04 06:10:04 +08:00
|
|
|
didWrite = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActorProfilerConf::clear(ReadYourWritesTransaction* ryw, const KeyRangeRef& kr) {
|
|
|
|
std::string begin(kr.begin.removePrefix(range.begin).toString()), end(kr.end.removePrefix(range.begin).toString());
|
|
|
|
auto first = config.lower_bound(begin);
|
|
|
|
if (first == config.end()) {
|
|
|
|
// nothing to clear
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
didWrite = true;
|
|
|
|
auto last = config.upper_bound(end);
|
|
|
|
config.erase(first, last);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActorProfilerConf::clear(ReadYourWritesTransaction* ryw, const KeyRef& key) {
|
|
|
|
std::string k = key.removePrefix(range.begin).toString();
|
|
|
|
auto iter = config.find(k);
|
|
|
|
if (iter != config.end()) {
|
|
|
|
config.erase(iter);
|
|
|
|
}
|
|
|
|
didWrite = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> ActorProfilerConf::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
Optional<std::string> res{};
|
|
|
|
try {
|
|
|
|
if (didWrite) {
|
|
|
|
ProfilerConfig::instance().reset(config);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
} catch (ConfigError& err) {
|
|
|
|
return Optional<std::string>{ err.description };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 03:19:33 +08:00
|
|
|
MaintenanceImpl::MaintenanceImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2021-04-24 02:13:08 +08:00
|
|
|
// Used to read the healthZoneKey
|
|
|
|
// If the key is persisted and the delayed read version is still larger than current read version,
|
|
|
|
// we will calculate the remaining time(truncated to integer, the same as fdbcli) and return back as the value
|
|
|
|
// If the zoneId is the special one `ignoreSSFailuresZoneString`,
|
|
|
|
// value will be 0 (same as fdbcli)
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR static Future<RangeResult> MaintenanceGetRangeActor(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRef prefix,
|
|
|
|
KeyRangeRef kr) {
|
|
|
|
state RangeResult result;
|
2021-03-27 03:19:33 +08:00
|
|
|
// zoneId
|
2022-01-29 02:55:02 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-03-27 03:19:33 +08:00
|
|
|
Optional<Value> val = wait(ryw->getTransaction().get(healthyZoneKey));
|
|
|
|
if (val.present()) {
|
|
|
|
auto healthyZone = decodeHealthyZoneValue(val.get());
|
|
|
|
if ((healthyZone.first == ignoreSSFailuresZoneString) ||
|
|
|
|
(healthyZone.second > ryw->getTransaction().getReadVersion().get())) {
|
|
|
|
Key zone_key = healthyZone.first.withPrefix(prefix);
|
2021-04-21 05:04:00 +08:00
|
|
|
double seconds = healthyZone.first == ignoreSSFailuresZoneString
|
|
|
|
? 0
|
|
|
|
: (healthyZone.second - ryw->getTransaction().getReadVersion().get()) /
|
|
|
|
CLIENT_KNOBS->CORE_VERSIONSPERSECOND;
|
2021-03-27 03:19:33 +08:00
|
|
|
if (kr.contains(zone_key)) {
|
|
|
|
result.push_back_deep(result.arena(),
|
|
|
|
KeyValueRef(zone_key, Value(boost::lexical_cast<std::string>(seconds))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rywGetRange(ryw, kr, result);
|
|
|
|
}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> MaintenanceImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-03-27 03:19:33 +08:00
|
|
|
return MaintenanceGetRangeActor(ryw, getKeyRange().begin, kr);
|
|
|
|
}
|
|
|
|
|
2021-04-24 02:13:08 +08:00
|
|
|
// Commit the change to healthZoneKey
|
|
|
|
// We do not allow more than one zone to be set in maintenance in one transaction
|
|
|
|
// In addition, if the zoneId now is 'ignoreSSFailuresZoneString',
|
|
|
|
// which means the data distribution is disabled for storage failures.
|
|
|
|
// Only clear this specific key is allowed, any other operations will throw error
|
2021-03-27 03:19:33 +08:00
|
|
|
ACTOR static Future<Optional<std::string>> maintenanceCommitActor(ReadYourWritesTransaction* ryw, KeyRangeRef kr) {
|
|
|
|
// read
|
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-03-27 03:19:33 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
|
|
|
|
Optional<Value> val = wait(ryw->getTransaction().get(healthyZoneKey));
|
|
|
|
Optional<std::pair<Key, Version>> healthyZone =
|
|
|
|
val.present() ? decodeHealthyZoneValue(val.get()) : Optional<std::pair<Key, Version>>();
|
|
|
|
|
|
|
|
state RangeMap<Key, std::pair<bool, Optional<Value>>, KeyRangeRef>::Ranges ranges =
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().containedRanges(kr);
|
|
|
|
Key zoneId;
|
2021-04-21 05:04:00 +08:00
|
|
|
double seconds;
|
2021-03-27 03:19:33 +08:00
|
|
|
bool isSet = false;
|
|
|
|
// Since maintenance only allows one zone at the same time,
|
|
|
|
// if a transaction has more than one set operation on different zone keys,
|
|
|
|
// the commit will throw an error
|
|
|
|
for (auto iter = ranges.begin(); iter != ranges.end(); ++iter) {
|
2021-03-27 03:24:45 +08:00
|
|
|
if (!iter->value().first)
|
|
|
|
continue;
|
2021-03-27 03:19:33 +08:00
|
|
|
if (iter->value().second.present()) {
|
|
|
|
if (isSet)
|
|
|
|
return Optional<std::string>(ManagementAPIError::toJsonString(
|
|
|
|
false, "maintenance", "Multiple zones given for maintenance, only one allowed at the same time"));
|
|
|
|
isSet = true;
|
|
|
|
zoneId = iter->begin().removePrefix(kr.begin);
|
2021-04-21 05:04:00 +08:00
|
|
|
seconds = boost::lexical_cast<double>(iter->value().second.get().toString());
|
2021-03-27 03:19:33 +08:00
|
|
|
} else {
|
|
|
|
// if we already have set operation, then all clear operations will be meaningless, thus skip
|
|
|
|
if (!isSet && healthyZone.present() && iter.range().contains(healthyZone.get().first.withPrefix(kr.begin)))
|
|
|
|
ryw->getTransaction().clear(healthyZoneKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isSet) {
|
|
|
|
if (healthyZone.present() && healthyZone.get().first == ignoreSSFailuresZoneString) {
|
|
|
|
std::string msg = "Maintenance mode cannot be used while data distribution is disabled for storage "
|
|
|
|
"server failures.";
|
|
|
|
return Optional<std::string>(ManagementAPIError::toJsonString(false, "maintenance", msg));
|
2021-04-21 05:09:52 +08:00
|
|
|
} else if (seconds < 0) {
|
|
|
|
std::string msg =
|
|
|
|
"The specified maintenance time " + boost::lexical_cast<std::string>(seconds) + " is a negative value";
|
2021-04-21 05:04:00 +08:00
|
|
|
return Optional<std::string>(ManagementAPIError::toJsonString(false, "maintenance", msg));
|
2021-03-27 03:19:33 +08:00
|
|
|
} else {
|
|
|
|
TraceEvent(SevDebug, "SKSMaintenanceSet").detail("ZoneId", zoneId.toString());
|
|
|
|
ryw->getTransaction().set(healthyZoneKey,
|
2021-03-27 03:24:45 +08:00
|
|
|
healthyZoneValue(zoneId,
|
|
|
|
ryw->getTransaction().getReadVersion().get() +
|
|
|
|
(seconds * CLIENT_KNOBS->CORE_VERSIONSPERSECOND)));
|
2021-03-27 03:19:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return Optional<std::string>();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> MaintenanceImpl::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
return maintenanceCommitActor(ryw, getKeyRange());
|
|
|
|
}
|
|
|
|
|
|
|
|
DataDistributionImpl::DataDistributionImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2021-04-24 02:13:08 +08:00
|
|
|
// Read the system keys dataDistributionModeKey and rebalanceDDIgnoreKey
|
2021-05-04 04:14:16 +08:00
|
|
|
ACTOR static Future<RangeResult> DataDistributionGetRangeActor(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRef prefix,
|
|
|
|
KeyRangeRef kr) {
|
|
|
|
state RangeResult result;
|
2021-03-27 03:19:33 +08:00
|
|
|
// dataDistributionModeKey
|
|
|
|
state Key modeKey = LiteralStringRef("mode").withPrefix(prefix);
|
2022-01-27 08:04:40 +08:00
|
|
|
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2022-01-27 08:04:40 +08:00
|
|
|
|
2021-03-27 03:19:33 +08:00
|
|
|
if (kr.contains(modeKey)) {
|
|
|
|
auto entry = ryw->getSpecialKeySpaceWriteMap()[modeKey];
|
|
|
|
if (ryw->readYourWritesDisabled() || !entry.first) {
|
|
|
|
Optional<Value> f = wait(ryw->getTransaction().get(dataDistributionModeKey));
|
|
|
|
int mode = -1;
|
|
|
|
if (f.present()) {
|
|
|
|
mode = BinaryReader::fromStringRef<int>(f.get(), Unversioned());
|
|
|
|
}
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(modeKey, Value(boost::lexical_cast<std::string>(mode))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// rebalanceDDIgnoreKey
|
|
|
|
state Key rebalanceIgnoredKey = LiteralStringRef("rebalance_ignored").withPrefix(prefix);
|
|
|
|
if (kr.contains(rebalanceIgnoredKey)) {
|
|
|
|
auto entry = ryw->getSpecialKeySpaceWriteMap()[rebalanceIgnoredKey];
|
|
|
|
if (ryw->readYourWritesDisabled() || !entry.first) {
|
|
|
|
Optional<Value> f = wait(ryw->getTransaction().get(rebalanceDDIgnoreKey));
|
|
|
|
if (f.present()) {
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(rebalanceIgnoredKey, Value()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rywGetRange(ryw, kr, result);
|
|
|
|
}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> DataDistributionImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2021-03-27 03:19:33 +08:00
|
|
|
return DataDistributionGetRangeActor(ryw, getKeyRange().begin, kr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> DataDistributionImpl::commit(ReadYourWritesTransaction* ryw) {
|
|
|
|
// there are two valid keys in the range
|
|
|
|
// <prefix>/mode -> dataDistributionModeKey, the value is only allowed to be set as "0"(disable) or "1"(enable)
|
|
|
|
// <prefix>/rebalance_ignored -> rebalanceDDIgnoreKey, value is unused thus empty
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->getTransaction().setOption(FDBTransactionOptions::RAW_ACCESS);
|
2022-01-27 08:04:40 +08:00
|
|
|
|
2021-03-27 03:19:33 +08:00
|
|
|
Optional<std::string> msg;
|
|
|
|
KeyRangeRef kr = getKeyRange();
|
|
|
|
Key modeKey = LiteralStringRef("mode").withPrefix(kr.begin);
|
|
|
|
Key rebalanceIgnoredKey = LiteralStringRef("rebalance_ignored").withPrefix(kr.begin);
|
|
|
|
auto ranges = ryw->getSpecialKeySpaceWriteMap().containedRanges(kr);
|
|
|
|
for (auto iter = ranges.begin(); iter != ranges.end(); ++iter) {
|
2021-03-27 03:24:45 +08:00
|
|
|
if (!iter->value().first)
|
|
|
|
continue;
|
2021-03-27 03:19:33 +08:00
|
|
|
if (iter->value().second.present()) {
|
|
|
|
if (iter->range() == singleKeyRange(modeKey)) {
|
|
|
|
try {
|
|
|
|
int mode = boost::lexical_cast<int>(iter->value().second.get().toString());
|
|
|
|
Value modeVal = BinaryWriter::toValue(mode, Unversioned());
|
2021-05-28 01:25:08 +08:00
|
|
|
if (mode == 0 || mode == 1) {
|
|
|
|
// Whenever configuration changes or DD related system keyspace is changed,
|
|
|
|
// actor must grab the moveKeysLockOwnerKey and update moveKeysLockWriteKey.
|
|
|
|
// This prevents concurrent write to the same system keyspace.
|
|
|
|
// When the owner of the DD related system keyspace changes, DD will reboot
|
|
|
|
BinaryWriter wrMyOwner(Unversioned());
|
|
|
|
wrMyOwner << dataDistributionModeLock;
|
|
|
|
ryw->getTransaction().set(moveKeysLockOwnerKey, wrMyOwner.toValue());
|
|
|
|
BinaryWriter wrLastWrite(Unversioned());
|
|
|
|
wrLastWrite << deterministicRandom()->randomUniqueID();
|
|
|
|
ryw->getTransaction().set(moveKeysLockWriteKey, wrLastWrite.toValue());
|
|
|
|
// set mode
|
2021-03-27 03:19:33 +08:00
|
|
|
ryw->getTransaction().set(dataDistributionModeKey, modeVal);
|
2021-05-28 01:25:08 +08:00
|
|
|
} else
|
2021-03-27 03:24:45 +08:00
|
|
|
msg = ManagementAPIError::toJsonString(false,
|
|
|
|
"datadistribution",
|
2021-03-27 03:19:33 +08:00
|
|
|
"Please set the value of the data_distribution/mode to "
|
|
|
|
"0(disable) or 1(enable), other values are not allowed");
|
|
|
|
} catch (boost::bad_lexical_cast& e) {
|
2021-03-27 03:24:45 +08:00
|
|
|
msg = ManagementAPIError::toJsonString(false,
|
|
|
|
"datadistribution",
|
2021-03-27 03:19:33 +08:00
|
|
|
"Invalid datadistribution mode(int): " +
|
|
|
|
iter->value().second.get().toString());
|
|
|
|
}
|
|
|
|
} else if (iter->range() == singleKeyRange(rebalanceIgnoredKey)) {
|
2022-02-26 03:01:23 +08:00
|
|
|
ValueRef val = iter->value().second.get();
|
|
|
|
try {
|
|
|
|
boost::lexical_cast<int>(iter->value().second.get().toString());
|
|
|
|
} catch (boost::bad_lexical_cast& e) {
|
2022-03-01 02:39:29 +08:00
|
|
|
ManagementAPIError::toJsonString(
|
|
|
|
false,
|
|
|
|
"datadistribution",
|
|
|
|
"Invalid datadistribution rebalance ignore option (int or empty): " +
|
|
|
|
iter->value().second.get().toString());
|
2022-02-26 03:01:23 +08:00
|
|
|
val = ""_sr;
|
|
|
|
}
|
|
|
|
ryw->getTransaction().set(rebalanceDDIgnoreKey, iter->value().second.get());
|
2021-03-27 03:19:33 +08:00
|
|
|
} else {
|
|
|
|
msg = ManagementAPIError::toJsonString(
|
2021-03-27 03:24:45 +08:00
|
|
|
false,
|
|
|
|
"datadistribution",
|
2021-03-27 03:19:33 +08:00
|
|
|
"Changing invalid keys, please read the documentation to check valid keys in the range");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// clear
|
|
|
|
if (iter->range().contains(modeKey))
|
|
|
|
ryw->getTransaction().clear(dataDistributionModeKey);
|
|
|
|
else if (iter->range().contains(rebalanceIgnoredKey))
|
|
|
|
ryw->getTransaction().clear(rebalanceDDIgnoreKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
}
|
2021-05-19 14:48:04 +08:00
|
|
|
|
2021-06-10 08:04:05 +08:00
|
|
|
// Clears the special management api keys excludeLocality and failedLocality.
|
2021-05-19 14:48:04 +08:00
|
|
|
void includeLocalities(ReadYourWritesTransaction* ryw) {
|
|
|
|
ryw->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
|
|
|
|
ryw->setOption(FDBTransactionOptions::LOCK_AWARE);
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-05-19 14:48:04 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::USE_PROVISIONAL_PROXIES);
|
|
|
|
// includeLocalities might be used in an emergency transaction, so make sure it is retry-self-conflicting and
|
|
|
|
// CAUSAL_WRITE_RISKY
|
|
|
|
ryw->setOption(FDBTransactionOptions::CAUSAL_WRITE_RISKY);
|
|
|
|
std::string versionKey = deterministicRandom()->randomUniqueID().toString();
|
|
|
|
// for excluded localities
|
2021-06-05 06:23:04 +08:00
|
|
|
auto ranges = ryw->getSpecialKeySpaceWriteMap().containedRanges(
|
2022-03-07 13:52:39 +08:00
|
|
|
SpecialKeySpace::getManagementApiCommandRange("excludedlocality"));
|
2021-05-19 14:48:04 +08:00
|
|
|
Transaction& tr = ryw->getTransaction();
|
2021-06-05 06:23:04 +08:00
|
|
|
for (auto& iter : ranges) {
|
|
|
|
auto entry = iter.value();
|
2021-05-19 14:48:04 +08:00
|
|
|
if (entry.first && !entry.second.present()) {
|
|
|
|
tr.addReadConflictRange(singleKeyRange(excludedLocalityVersionKey));
|
|
|
|
tr.set(excludedLocalityVersionKey, versionKey);
|
2021-06-05 06:23:04 +08:00
|
|
|
tr.clear(ryw->getDatabase()->specialKeySpace->decode(iter.range()));
|
2021-05-19 14:48:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// for failed localities
|
2021-06-05 06:23:04 +08:00
|
|
|
ranges = ryw->getSpecialKeySpaceWriteMap().containedRanges(
|
2022-03-07 13:52:39 +08:00
|
|
|
SpecialKeySpace::getManagementApiCommandRange("failedlocality"));
|
2021-06-05 06:23:04 +08:00
|
|
|
for (auto& iter : ranges) {
|
|
|
|
auto entry = iter.value();
|
2021-05-19 14:48:04 +08:00
|
|
|
if (entry.first && !entry.second.present()) {
|
|
|
|
tr.addReadConflictRange(singleKeyRange(failedLocalityVersionKey));
|
|
|
|
tr.set(failedLocalityVersionKey, versionKey);
|
2021-06-05 06:23:04 +08:00
|
|
|
tr.clear(ryw->getDatabase()->specialKeySpace->decode(iter.range()));
|
2021-05-19 14:48:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-13 21:02:11 +08:00
|
|
|
// Reads the excludedlocality and failed locality keys using management api,
|
2021-06-10 08:04:05 +08:00
|
|
|
// parses them and returns the list.
|
2021-05-19 14:48:04 +08:00
|
|
|
bool parseLocalitiesFromKeys(ReadYourWritesTransaction* ryw,
|
|
|
|
bool failed,
|
|
|
|
std::unordered_set<std::string>& localities,
|
2021-06-05 06:23:04 +08:00
|
|
|
std::vector<AddressExclusion>& addresses,
|
2021-05-19 14:48:04 +08:00
|
|
|
std::set<AddressExclusion>& exclusions,
|
2021-06-05 06:23:04 +08:00
|
|
|
std::vector<ProcessData>& workers,
|
2021-05-19 14:48:04 +08:00
|
|
|
Optional<std::string>& msg) {
|
2022-03-07 13:52:39 +08:00
|
|
|
KeyRangeRef range = failed ? SpecialKeySpace::getManagementApiCommandRange("failedlocality")
|
|
|
|
: SpecialKeySpace::getManagementApiCommandRange("excludedlocality");
|
2021-05-19 14:48:04 +08:00
|
|
|
auto ranges = ryw->getSpecialKeySpaceWriteMap().containedRanges(range);
|
|
|
|
auto iter = ranges.begin();
|
|
|
|
while (iter != ranges.end()) {
|
|
|
|
auto entry = iter->value();
|
|
|
|
// only check for exclude(set) operation, include(clear) are not checked
|
|
|
|
TraceEvent(SevDebug, "ParseLocalities")
|
|
|
|
.detail("Valid", entry.first)
|
|
|
|
.detail("Set", entry.second.present())
|
|
|
|
.detail("Key", iter->begin().toString());
|
|
|
|
if (entry.first && entry.second.present()) {
|
|
|
|
Key locality = iter->begin().removePrefix(range.begin);
|
2021-06-26 04:05:32 +08:00
|
|
|
if (locality.startsWith(LocalityData::ExcludeLocalityPrefix) &&
|
|
|
|
locality.toString().find(':') != std::string::npos) {
|
2021-05-19 14:48:04 +08:00
|
|
|
std::set<AddressExclusion> localityAddresses = getAddressesByLocality(workers, locality.toString());
|
2021-06-05 06:23:04 +08:00
|
|
|
if (!localityAddresses.empty()) {
|
2021-05-19 14:48:04 +08:00
|
|
|
std::copy(localityAddresses.begin(), localityAddresses.end(), back_inserter(addresses));
|
|
|
|
exclusions.insert(localityAddresses.begin(), localityAddresses.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
localities.insert(locality.toString());
|
|
|
|
} else {
|
|
|
|
std::string error = "ERROR: \'" + locality.toString() + "\' is not a valid locality\n";
|
|
|
|
msg = ManagementAPIError::toJsonString(
|
|
|
|
false, entry.second.present() ? (failed ? "exclude failed" : "exclude") : "include", error);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-06-10 08:04:05 +08:00
|
|
|
// On commit, parses the special exclusion keys and get the localities to be excluded, check for exclusions
|
|
|
|
// and add them to the exclusion list. Also, clears the special management api keys with includeLocalities.
|
2021-05-19 14:48:04 +08:00
|
|
|
ACTOR Future<Optional<std::string>> excludeLocalityCommitActor(ReadYourWritesTransaction* ryw, bool failed) {
|
|
|
|
state Optional<std::string> result;
|
|
|
|
state std::unordered_set<std::string> localities;
|
|
|
|
state std::vector<AddressExclusion> addresses;
|
|
|
|
state std::set<AddressExclusion> exclusions;
|
2022-01-27 08:04:40 +08:00
|
|
|
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2022-01-27 08:04:40 +08:00
|
|
|
|
2021-05-19 14:48:04 +08:00
|
|
|
state std::vector<ProcessData> workers = wait(getWorkers(&ryw->getTransaction()));
|
|
|
|
if (!parseLocalitiesFromKeys(ryw, failed, localities, addresses, exclusions, workers, result))
|
|
|
|
return result;
|
|
|
|
// If force option is not set, we need to do safety check
|
|
|
|
auto force = ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandOptionSpecialKey(
|
2021-06-17 15:27:26 +08:00
|
|
|
failed ? "failed_locality" : "excluded_locality", "force")];
|
2021-05-19 14:48:04 +08:00
|
|
|
// only do safety check when we have localities to be excluded and the force option key is not set
|
|
|
|
if (localities.size() && !(force.first && force.second.present())) {
|
|
|
|
bool safe = wait(checkExclusion(ryw->getDatabase(), &addresses, &exclusions, failed, &result));
|
|
|
|
if (!safe)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-06-17 15:27:26 +08:00
|
|
|
excludeLocalities(ryw->getTransaction(), localities, failed);
|
2021-05-19 14:48:04 +08:00
|
|
|
includeLocalities(ryw);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ExcludedLocalitiesRangeImpl::ExcludedLocalitiesRangeImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> ExcludedLocalitiesRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-05-19 14:48:04 +08:00
|
|
|
return rwModuleWithMappingGetRangeActor(ryw, this, kr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExcludedLocalitiesRangeImpl::set(ReadYourWritesTransaction* ryw, const KeyRef& key, const ValueRef& value) {
|
|
|
|
// ignore value
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(key, std::make_pair(true, Optional<Value>(ValueRef())));
|
|
|
|
}
|
|
|
|
|
|
|
|
Key ExcludedLocalitiesRangeImpl::decode(const KeyRef& key) const {
|
|
|
|
return key.removePrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin)
|
|
|
|
.withPrefix(LiteralStringRef("\xff/conf/"));
|
|
|
|
}
|
|
|
|
|
|
|
|
Key ExcludedLocalitiesRangeImpl::encode(const KeyRef& key) const {
|
|
|
|
return key.removePrefix(LiteralStringRef("\xff/conf/"))
|
|
|
|
.withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> ExcludedLocalitiesRangeImpl::commit(ReadYourWritesTransaction* ryw) {
|
2021-06-10 08:04:05 +08:00
|
|
|
// exclude locality with failed option as false.
|
2021-05-19 14:48:04 +08:00
|
|
|
return excludeLocalityCommitActor(ryw, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
FailedLocalitiesRangeImpl::FailedLocalitiesRangeImpl(KeyRangeRef kr) : SpecialKeyRangeRWImpl(kr) {}
|
|
|
|
|
2022-03-07 13:52:39 +08:00
|
|
|
Future<RangeResult> FailedLocalitiesRangeImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
2022-03-17 00:32:31 +08:00
|
|
|
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
|
2021-05-19 14:48:04 +08:00
|
|
|
return rwModuleWithMappingGetRangeActor(ryw, this, kr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FailedLocalitiesRangeImpl::set(ReadYourWritesTransaction* ryw, const KeyRef& key, const ValueRef& value) {
|
|
|
|
// ignore value
|
|
|
|
ryw->getSpecialKeySpaceWriteMap().insert(key, std::make_pair(true, Optional<Value>(ValueRef())));
|
|
|
|
}
|
|
|
|
|
|
|
|
Key FailedLocalitiesRangeImpl::decode(const KeyRef& key) const {
|
|
|
|
return key.removePrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin)
|
|
|
|
.withPrefix(LiteralStringRef("\xff/conf/"));
|
|
|
|
}
|
|
|
|
|
|
|
|
Key FailedLocalitiesRangeImpl::encode(const KeyRef& key) const {
|
|
|
|
return key.removePrefix(LiteralStringRef("\xff/conf/"))
|
|
|
|
.withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Optional<std::string>> FailedLocalitiesRangeImpl::commit(ReadYourWritesTransaction* ryw) {
|
2021-06-10 08:04:05 +08:00
|
|
|
// exclude locality with failed option as true.
|
2021-05-19 14:48:04 +08:00
|
|
|
return excludeLocalityCommitActor(ryw, true);
|
|
|
|
}
|
2022-07-22 05:42:08 +08:00
|
|
|
|
2022-08-16 02:05:07 +08:00
|
|
|
// Defined in ReadYourWrites.actor.cpp
|
|
|
|
ACTOR Future<RangeResult> getWorkerInterfaces(Reference<IClusterConnectionRecord> clusterRecord);
|
|
|
|
// Defined in NativeAPI.actor.cpp
|
|
|
|
ACTOR Future<bool> verifyInterfaceActor(Reference<FlowLock> connectLock, ClientWorkerInterface workerInterf);
|
|
|
|
|
|
|
|
ACTOR static Future<RangeResult> workerInterfacesImplGetRangeActor(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRef prefix,
|
|
|
|
KeyRangeRef kr) {
|
|
|
|
if (!ryw->getDatabase().getPtr() || !ryw->getDatabase()->getConnectionRecord())
|
|
|
|
return RangeResult();
|
|
|
|
|
|
|
|
state RangeResult interfs = wait(getWorkerInterfaces(ryw->getDatabase()->getConnectionRecord()));
|
|
|
|
// for options' special keys, the boolean flag indicates if it's a SET operation
|
|
|
|
auto [verify, _] = ryw->getSpecialKeySpaceWriteMap()[SpecialKeySpace::getManagementApiCommandOptionSpecialKey(
|
|
|
|
"worker_interfaces", "verify")];
|
|
|
|
state RangeResult result;
|
|
|
|
if (verify) {
|
|
|
|
// if verify option is set, we try to talk to every worker and only returns those we can talk to
|
|
|
|
Reference<FlowLock> connectLock(new FlowLock(CLIENT_KNOBS->CLI_CONNECT_PARALLELISM));
|
|
|
|
state std::vector<Future<bool>> verifyInterfs;
|
|
|
|
for (const auto& [k_, value] : interfs) {
|
|
|
|
auto k = k_.withPrefix(prefix);
|
|
|
|
if (kr.contains(k)) {
|
|
|
|
ClientWorkerInterface workerInterf =
|
|
|
|
BinaryReader::fromStringRef<ClientWorkerInterface>(value, IncludeVersion());
|
|
|
|
verifyInterfs.push_back(verifyInterfaceActor(connectLock, workerInterf));
|
|
|
|
} else {
|
|
|
|
verifyInterfs.push_back(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wait(waitForAll(verifyInterfs));
|
|
|
|
// state int index;
|
|
|
|
for (int index = 0; index < interfs.size(); index++) {
|
|
|
|
if (verifyInterfs[index].get()) {
|
|
|
|
// if we can establish a connection, add the kv pair into the result
|
|
|
|
result.push_back_deep(result.arena(),
|
|
|
|
KeyValueRef(interfs[index].key.withPrefix(prefix), interfs[index].value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (const auto& [k_, v] : interfs) {
|
|
|
|
auto k = k_.withPrefix(prefix);
|
|
|
|
if (kr.contains(k))
|
|
|
|
result.push_back_deep(result.arena(), KeyValueRef(k, v));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::sort(result.begin(), result.end(), KeyValueRef::OrderByKey{});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
WorkerInterfacesSpecialKeyImpl::WorkerInterfacesSpecialKeyImpl(KeyRangeRef kr) : SpecialKeyRangeReadImpl(kr) {}
|
|
|
|
|
|
|
|
Future<RangeResult> WorkerInterfacesSpecialKeyImpl::getRange(ReadYourWritesTransaction* ryw,
|
|
|
|
KeyRangeRef kr,
|
|
|
|
GetRangeLimits limitsHint) const {
|
|
|
|
return workerInterfacesImplGetRangeActor(ryw, getKeyRange().begin, kr);
|
|
|
|
}
|
|
|
|
|
2022-07-22 05:42:08 +08:00
|
|
|
ACTOR Future<Void> validateSpecialSubrangeRead(ReadYourWritesTransaction* ryw,
|
|
|
|
KeySelector begin,
|
|
|
|
KeySelector end,
|
|
|
|
GetRangeLimits limits,
|
|
|
|
Reverse reverse,
|
|
|
|
RangeResult result) {
|
|
|
|
if (!result.size()) {
|
|
|
|
RangeResult testResult = wait(ryw->getRange(begin, end, limits, Snapshot::True, reverse));
|
|
|
|
ASSERT(testResult == result);
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reverse) {
|
|
|
|
ASSERT(std::is_sorted(result.begin(), result.end(), KeyValueRef::OrderByKeyBack{}));
|
|
|
|
} else {
|
|
|
|
ASSERT(std::is_sorted(result.begin(), result.end(), KeyValueRef::OrderByKey{}));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate a keyrange where we can determine the expected result based solely on the previous readrange, and whose
|
|
|
|
// boundaries may or may not be keys in result.
|
|
|
|
std::vector<Key> candidateKeys;
|
|
|
|
if (reverse) {
|
|
|
|
for (int i = result.size() - 1; i >= 0; --i) {
|
|
|
|
candidateKeys.emplace_back(result[i].key);
|
|
|
|
if (i - 1 >= 0) {
|
|
|
|
candidateKeys.emplace_back(keyBetween(KeyRangeRef(result[i].key, result[i - 1].key)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = 0; i < result.size(); ++i) {
|
|
|
|
candidateKeys.emplace_back(result[i].key);
|
|
|
|
if (i + 1 < result.size()) {
|
|
|
|
candidateKeys.emplace_back(keyBetween(KeyRangeRef(result[i].key, result[i + 1].key)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::sort(candidateKeys.begin(), candidateKeys.end());
|
|
|
|
int originalSize = candidateKeys.size();
|
|
|
|
// Add more candidate keys so that we might read a range between two adjacent result keys.
|
|
|
|
for (int i = 0; i < originalSize - 1; ++i) {
|
|
|
|
candidateKeys.emplace_back(keyBetween(KeyRangeRef(candidateKeys[i], candidateKeys[i + 1])));
|
|
|
|
}
|
|
|
|
std::vector<Key> keys;
|
|
|
|
keys = { deterministicRandom()->randomChoice(candidateKeys), deterministicRandom()->randomChoice(candidateKeys) };
|
|
|
|
std::sort(keys.begin(), keys.end());
|
|
|
|
state KeySelector testBegin = firstGreaterOrEqual(keys[0]);
|
|
|
|
state KeySelector testEnd = firstGreaterOrEqual(keys[1]);
|
|
|
|
|
|
|
|
// Generate expected result. Linear time is ok here since we're in simulation, and there's a benefit to keeping this
|
|
|
|
// simple (as we're using it as an test oracle)
|
|
|
|
state RangeResult expectedResult;
|
|
|
|
// The reverse parameter should be the same as for the original read, so if
|
|
|
|
// reverse is true then the results are _already_ in reverse order.
|
|
|
|
for (const auto& kr : result) {
|
|
|
|
if (kr.key >= keys[0] && kr.key < keys[1]) {
|
|
|
|
expectedResult.push_back(expectedResult.arena(), kr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test
|
|
|
|
RangeResult testResult = wait(ryw->getRange(testBegin, testEnd, limits, Snapshot::True, reverse));
|
|
|
|
if (testResult != expectedResult) {
|
|
|
|
fmt::print("Reverse: {}\n", reverse);
|
|
|
|
fmt::print("Original range: [{}, {})\n", begin.toString(), end.toString());
|
|
|
|
fmt::print("Original result:\n");
|
|
|
|
for (const auto& kr : result) {
|
|
|
|
fmt::print(" {} -> {}\n", kr.key.printable(), kr.value.printable());
|
|
|
|
}
|
|
|
|
fmt::print("Test range: [{}, {})\n", testBegin.getKey().printable(), testEnd.getKey().printable());
|
|
|
|
fmt::print("Expected:\n");
|
|
|
|
for (const auto& kr : expectedResult) {
|
|
|
|
fmt::print(" {} -> {}\n", kr.key.printable(), kr.value.printable());
|
|
|
|
}
|
|
|
|
fmt::print("Got:\n");
|
|
|
|
for (const auto& kr : testResult) {
|
|
|
|
fmt::print(" {} -> {}\n", kr.key.printable(), kr.value.printable());
|
|
|
|
}
|
|
|
|
ASSERT(testResult == expectedResult);
|
|
|
|
}
|
|
|
|
return Void();
|
|
|
|
}
|