Compare commits

...

63 Commits

Author SHA1 Message Date
MarkSh1 7b2ac5f9de
Merge ca121e5b57 into d96b3b2cd8 2026-08-04 09:52:19 +00:00
Trevor Clinkenbeard d96b3b2cd8
Merge pull request #13742 from tclinkenbeard-oai/dev/tclinkenbeard/swizzled-dd-balance-timeout-trace-overflow
Coordinate Attrition targets within a test phase
2026-08-04 02:20:40 -07:00
Trevor Clinkenbeard cad7d1bf49
Merge pull request #13809 from tclinkenbeard-oai/dev/tclinkenbeard/native-cdc-parallel-metadata-reads
Read Native CDC stream metadata concurrently
2026-08-04 02:13:17 -07:00
Trevor Clinkenbeard 27dd94f291
Merge pull request #13736 from tclinkenbeard-oai/dev/tclinkenbeard/protocol-version-quiet-database-recovery 2026-07-31 16:50:16 -07:00
Trevor Clinkenbeard 147da3d0e3 Merge latest origin/main into dev/tclinkenbeard/protocol-version-quiet-database-recovery 2026-07-31 14:35:55 -07:00
Trevor Clinkenbeard 9bde464a80
Merge pull request #13806 from tclinkenbeard-oai/dev/tclinkenbeard/mako-workload-parity
Align standalone and simulation Mako workloads
2026-07-31 14:28:32 -07:00
Trevor Clinkenbeard 874f0aab27 Merge origin/main into dev/tclinkenbeard/protocol-version-quiet-database-recovery 2026-07-31 14:26:29 -07:00
Trevor Clinkenbeard adfc37d7e9
Merge pull request #13739 from tclinkenbeard-oai/dev/tclinkenbeard/task-bucket-quiet-database-dd-active
Recheck DD team health after storage failure suppression
2026-07-31 14:22:42 -07:00
Trevor Clinkenbeard c1d388a06f
Merge pull request #13770 from tclinkenbeard-oai/dev/tclinkenbeard/foundationdb-complexity-simplifications-20260722
Reduce duplicated logic
2026-07-31 14:22:25 -07:00
Trevor Clinkenbeard dadc0fe643
Merge pull request #13772 from tclinkenbeard-oai/dev/tclinkenbeard/exclude-include-redwood-trace-overflow
Avoid colocating remote log routers with satellite TLogs
2026-07-31 14:22:02 -07:00
Trevor Clinkenbeard 54c167e9a9 Avoid reading past the CDC tag history range 2026-07-31 13:43:50 -07:00
Trevor Clinkenbeard 1a34aaff6a Read Native CDC stream metadata concurrently 2026-07-31 13:30:59 -07:00
Trevor Clinkenbeard 8185dca8b5
Merge pull request #13785 from tclinkenbeard-oai/dev/tclinkenbeard/client-metric-restart-failed-assertion
Fail ClientMetric workload when metric verification times out
2026-07-31 12:23:48 -07:00
Trevor Clinkenbeard 21ec1f4402
Merge pull request #13797 from tclinkenbeard-oai/dev/tclinkenbeard/client-transaction-profiling-self-sampling-recovery
Prevent client profiling maintenance from profiling itself
2026-07-31 11:12:40 -07:00
Trevor Clinkenbeard 74a648879f Merge origin/main into ClientMetric workload fix 2026-07-31 09:36:14 -07:00
Trevor Clinkenbeard d5f8647cfb Merge origin/main into dev/tclinkenbeard/protocol-version-quiet-database-recovery 2026-07-31 09:32:06 -07:00
Mark Shabanov ca121e5b57 fix preresolution latency clock source 2026-07-31 14:58:14 +03:00
Trevor Clinkenbeard 902276ca2d Remove unnecessary Mako parity simulation test 2026-07-30 21:40:18 -07:00
Trevor Clinkenbeard 5689b2542e Include Mako sources in clang-tidy compile database 2026-07-30 21:39:46 -07:00
Trevor Clinkenbeard 6537932e04 Align standalone and simulation Mako workloads 2026-07-30 20:22:12 -07:00
Trevor Clinkenbeard 20241eefbd Merge origin/main into complexity simplifications branch 2026-07-30 11:42:18 -07:00
Trevor Clinkenbeard 1238c02cec Merge main into dev/tclinkenbeard/protocol-version-quiet-database-recovery 2026-07-29 12:12:58 -07:00
Trevor Clinkenbeard 571ed1155d Merge remote-tracking branch 'origin/main' into dev/tclinkenbeard/client-metric-restart-failed-assertion 2026-07-28 21:48:36 -07:00
Trevor Clinkenbeard 1c81b53528 Merge remote-tracking branch 'origin/main' into dev/tclinkenbeard/client-metric-restart-failed-assertion 2026-07-28 14:42:19 -07:00
Trevor Clinkenbeard 1790dd35b7 Merge remote-tracking branch 'origin/main' into dev/tclinkenbeard/client-metric-restart-failed-assertion
# Conflicts:
#	fdbserver/workloads/ClientMetric.cpp
2026-07-28 09:07:38 -07:00
Trevor Clinkenbeard 95b6983aea Merge remote-tracking branch 'origin/main' into dev/tclinkenbeard/protocol-version-quiet-database-recovery 2026-07-27 17:59:39 -07:00
Trevor Clinkenbeard 971d883db7 Prevent client profiling maintenance from profiling itself 2026-07-27 13:03:21 -07:00
Trevor Clinkenbeard 469ccf9aea Register Attrition injectors only after selection 2026-07-24 17:45:30 -07:00
Trevor Clinkenbeard bd86555bfb Fail ClientMetric workload when metric verification times out 2026-07-23 23:38:38 -07:00
Trevor Clinkenbeard a4a4a6edfe Avoid colocating remote log routers with satellite TLogs 2026-07-22 11:53:50 -07:00
Trevor Clinkenbeard 484293f8d3 Fix backup key-file size limit clang-tidy warning 2026-07-22 10:56:29 -07:00
Trevor Clinkenbeard 6fc44a884e Use explicit non-coroutine management callbacks 2026-07-22 10:56:24 -07:00
Trevor Clinkenbeard 44ec67153b Fix management helper thread-future lifetime 2026-07-22 10:08:12 -07:00
Trevor Clinkenbeard 6b3d22e699 Share deferred backup task scheduling 2026-07-22 10:02:46 -07:00
Trevor Clinkenbeard a9ec6195d8 Reuse RYW transaction retries for exclusion management 2026-07-22 10:01:50 -07:00
Trevor Clinkenbeard 6d539589b6 Share bounded busy-tag collection across storage and ratekeeper 2026-07-22 10:01:49 -07:00
Trevor Clinkenbeard e3247b4815 Share TSS range-read mismatch trace extraction 2026-07-22 10:01:48 -07:00
Trevor Clinkenbeard cce59c835f Share guarded storage-server read request dispatch 2026-07-22 10:01:47 -07:00
Trevor Clinkenbeard 77d1d415fc Deduplicate common backup command-line option groups 2026-07-22 09:59:27 -07:00
Trevor Clinkenbeard 0aa71138a1 Share coroutine awaiter lifecycle and cancellation state 2026-07-22 09:59:26 -07:00
Trevor Clinkenbeard 02add7bdf0 Share queue metric smoothing between storage servers and TLogs 2026-07-22 09:59:24 -07:00
Trevor Clinkenbeard b7950d9e52 Share exclusion special-key ranges and range readers 2026-07-22 09:59:23 -07:00
Trevor Clinkenbeard 2596bdd809 Share conflict-range resolver selection in commit proxy 2026-07-22 09:58:58 -07:00
Trevor Clinkenbeard e08bf4bbd1 Unify TLS material loading and correct CA diagnostics 2026-07-22 09:57:02 -07:00
Trevor Clinkenbeard 8bf5aa60e4 Fix fdbctl exclusion polling retry handling 2026-07-22 09:56:17 -07:00
Trevor Clinkenbeard a14955a4d4 Remove unused ratekeeper tag-throttling calculation 2026-07-22 09:56:13 -07:00
Trevor Clinkenbeard c0fc45a38c Reuse existing team lookup when building emergency teams 2026-07-22 09:55:51 -07:00
Trevor Clinkenbeard 13b9a799cf Make unsupported C client configuration tests explicit 2026-07-22 09:55:24 -07:00
Trevor Clinkenbeard efe90ace9f Share outbound TLS connection setup and throttling 2026-07-22 09:55:24 -07:00
Trevor Clinkenbeard 8960c3051d Remove unreachable non-thread-safe fast allocator paths 2026-07-22 09:55:23 -07:00
Trevor Clinkenbeard 75ec77e8bb Fix duplicated local cluster convergence polling 2026-07-22 09:54:23 -07:00
Trevor Clinkenbeard 7874b1d143 Preserve bounded degraded-team retries and async state transitions 2026-07-22 07:38:02 -07:00
Trevor Clinkenbeard 648018dfcc Pass team check delay directly to trackers 2026-07-22 06:23:35 -07:00
Trevor Clinkenbeard 5553848256 Retry degraded shards after unhealthy relocations drain 2026-07-22 01:25:14 -07:00
Trevor Clinkenbeard d88216a779 Retry degraded shards when healthy teams return 2026-07-21 15:52:45 -07:00
Trevor Clinkenbeard b2b5353f39 Avoid collection scans on degraded team retries 2026-07-21 13:55:48 -07:00
Trevor Clinkenbeard 1c862cbd49 Deduplicate degraded team shard retries 2026-07-21 13:16:17 -07:00
Trevor Clinkenbeard 7ea79c300c Merge remote-tracking branch 'origin/main' into dev/tclinkenbeard/protocol-version-quiet-database-recovery
# Conflicts:
#	fdbserver/datadistributor/ShardsAffectedByTeamFailureTests.cpp
2026-07-21 12:47:51 -07:00
Trevor Clinkenbeard 348b22ef09 Coordinate Attrition targets within a test phase 2026-07-18 11:16:46 -07:00
Trevor Clinkenbeard 6691e6d952 Recheck DD team health after storage failure suppression 2026-07-18 04:15:53 -07:00
Trevor Clinkenbeard 1d18a70995 Retry degraded teams with stranded merged shards 2026-07-17 23:53:19 -07:00
Mark Shabanov 4b3af741ed added new metrics into mr-status-json-schemas.rst.inc 2026-07-01 17:25:08 +03:00
Mark Shabanov 5e1b2d3eea Added new commit statistics metrics 2026-07-01 16:34:40 +03:00
43 changed files with 1746 additions and 1452 deletions

View File

@ -39,7 +39,7 @@ jobs:
# CXXFLAGS to work-around "fdb_c_options.g.h not found" by clang-tidy
CC=clang CXX=clang++ USE_LD=LLD USE_LIBCXX=1 \
CXXFLAGS=-I${PWD}/fdbclient/include/fdbclient \
cmake -S .. -G Ninja -D BUILD_AWS_BACKUP=ON -D CMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake -S .. -G Ninja -D BUILD_AWS_BACKUP=ON -D BUILD_MAKO=ON -D CMAKE_EXPORT_COMPILE_COMMANDS=ON
ninja -v \
processed_compile_commands \

View File

@ -432,7 +432,8 @@ class ClientConfigTests(unittest.TestCase):
)
test.check_current_client(CURRENT_VERSION)
def test_no_external_client_support_api_version_ignore(self):
@unittest.skip("API functions are present in the two supported previous releases")
def test_no_external_client_support_api_version(self):
# Multiple external clients, API version supported by none of them
# Note: Ignored because API function won't be missing in last 2 releases.
test = ClientConfigTest(self)
@ -452,7 +453,8 @@ class ClientConfigTests(unittest.TestCase):
test.expected_error = 2124 # All external clients failed
test.exec()
def test_one_external_client_wrong_api_version_ignore(self):
@unittest.skip("API functions are present in the two supported previous releases")
def test_one_external_client_wrong_api_version(self):
# Multiple external clients, API version unsupported by one of them.
# Note: Ignored because API function won't be missing in last 2 releases.
test = ClientConfigTest(self)

View File

@ -179,6 +179,7 @@ Format
------
| One operation type is defined as ``<Type><Count>`` or ``<Type><Count>:<Range>``.
| When Count is omitted, it's equivalent to setting it to 1. (e.g. ``g`` is equivalent to ``g1``)
| For ``gr`` and ``sgr``, a negative Range scans the specified number of keys in reverse order.
| Multiple operation types within the same transaction can be concatenated. (e.g. ``g9u1`` = 9 GETs and 1 update)
Transaction Specification Examples
@ -189,6 +190,9 @@ Transaction Specification Examples
- | 10 GET RANGE with Range of 50 (Non-commited)
| ``gr10:50``
- | 10 snapshot GET RANGE operations with Range of 50 in reverse order (Non-commited)
| ``sgr10:-50``
- | 90 GETs and 10 Updates (Committed)
| ``g90u10``

View File

@ -101,7 +101,7 @@ const std::array<Operation, MAX_OP> opTable{ {
args.streaming_mode,
0 /*iteration*/,
true /*snapshot*/,
args.txnspec.ops[OP_GETRANGE][OP_REVERSE])
args.txnspec.ops[OP_SGETRANGE][OP_REVERSE])
.eraseType();
},
[](Future& f, Transaction&, Arguments const&, ByteString&, ByteString&, ByteString& val) {

View File

@ -304,6 +304,102 @@
"p99":0.0,
"p99.9":0.0
},
"commit_batch_transactions":{ // Number of transactions in the commit batch
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_batch_bytes":{ // Total size of transactions in the commit batch (bytes)
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_batching_waiting":{ // Time transactions spent waiting in queue before commit batch processing (seconds)
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_preresolution_latency":{ // Latency of the pre-resolution phase (seconds)
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_resolution_latency":{ // Latency of the resolution phase (seconds)
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_postresolution_latency":{ // Latency of the post-resolution phase (seconds)
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_tlog_logging_latency":{ // Latency of the TLog logging phase (seconds)
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_reply_latency":{ // Latency of the reply phase (seconds)
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"grv_latency_bands":{ // How many GRV requests belong to the latency (in seconds) band (e.g., How many requests belong to [0.01,0.1] latency band). The key is the upper bound of the band and the lower bound is the next smallest band (or 0, if none). Example: {0.01: 27, 0.1: 18, 1: 1, inf: 98,filtered: 10}, we have 18 requests in [0.01, 0.1) band.
"$map_key=upperBoundOfBand": 1
},

View File

@ -199,6 +199,37 @@ enum {
OPT_TRACE_FORMAT,
};
#define BACKUP_LOG_OPTIONS \
{ OPT_TRACE, "--log", SO_NONE }, { OPT_TRACE_DIR, "--logdir", SO_REQ_SEP }, \
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP }, { \
OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP \
}
#define BACKUP_QUIET_OPTIONS \
{ OPT_QUIET, "-q", SO_NONE }, { \
OPT_QUIET, "--quiet", SO_NONE \
}
#define BACKUP_MEMORY_OPTIONS \
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP }, { OPT_MEMLIMIT, "--memory", SO_REQ_SEP }, { \
OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP \
}
#define BACKUP_HELP_OPTIONS \
{ OPT_HELP, "-?", SO_NONE }, { OPT_HELP, "-h", SO_NONE }, { OPT_HELP, "--help", SO_NONE }, { \
OPT_DEVHELP, "--dev-help", SO_NONE \
}
#define BACKUP_CLUSTER_FILE_OPTIONS \
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP }, { \
OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP \
}
#define BACKUP_TAG_OPTIONS \
{ OPT_TAGNAME, "-t", SO_REQ_SEP }, { \
OPT_TAGNAME, "--tagname", SO_REQ_SEP \
}
// Top level binary commands.
CSimpleOpt::SOption g_rgOptions[] = { { OPT_VERSION, "-v", SO_NONE },
{ OPT_VERSION, "--version", SO_NONE },
@ -213,27 +244,17 @@ CSimpleOpt::SOption g_rgAgentOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
{ OPT_VERSION, "--version", SO_NONE },
{ OPT_VERSION, "-v", SO_NONE },
{ OPT_BUILD_FLAGS, "--build-flags", SO_NONE },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
BACKUP_QUIET_OPTIONS,
BACKUP_LOG_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_LOCALITY, "--locality-", SO_REQ_SEP },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_PROXY, "--proxy", SO_REQ_SEP },
TLS_OPTION_FLAGS,
@ -244,8 +265,7 @@ CSimpleOpt::SOption g_rgBackupStartOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
{ OPT_WAITFORDONE, "-w", SO_NONE },
{ OPT_WAITFORDONE, "--waitfordone", SO_NONE },
{ OPT_NOSTOPWHENDONE, "-z", SO_NONE },
@ -257,27 +277,17 @@ CSimpleOpt::SOption g_rgBackupStartOptions[] = {
{ OPT_SNAPSHOTINTERVAL, "-s", SO_REQ_SEP },
{ OPT_SNAPSHOTINTERVAL, "--snapshot-interval", SO_REQ_SEP },
{ OPT_INITIAL_SNAPSHOT_INTERVAL, "--initial-snapshot-interval", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
BACKUP_TAG_OPTIONS,
{ OPT_BACKUPKEYS, "-k", SO_REQ_SEP },
{ OPT_BACKUPKEYS_FILE, "--keys-file", SO_REQ_SEP },
{ OPT_BACKUPKEYS, "--keys", SO_REQ_SEP },
{ OPT_DRYRUN, "-n", SO_NONE },
{ OPT_DRYRUN, "--dryrun", SO_NONE },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_INCREMENTALONLY, "--incremental", SO_NONE },
@ -292,26 +302,15 @@ CSimpleOpt::SOption g_rgBackupModifyOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
BACKUP_TAG_OPTIONS,
{ OPT_MOD_VERIFY_UID, "--verify-uid", SO_REQ_SEP },
{ OPT_DESTCONTAINER, "-d", SO_REQ_SEP },
{ OPT_DESTCONTAINER, "--destcontainer", SO_REQ_SEP },
@ -328,26 +327,15 @@ CSimpleOpt::SOption g_rgBackupStatusOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
{ OPT_ERRORLIMIT, "-e", SO_REQ_SEP },
{ OPT_ERRORLIMIT, "--errorlimit", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_TAG_OPTIONS,
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_JSON, "--json", SO_NONE },
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
@ -358,24 +346,13 @@ CSimpleOpt::SOption g_rgBackupAbortOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_CLUSTER_FILE_OPTIONS,
BACKUP_TAG_OPTIONS,
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
@ -385,22 +362,12 @@ CSimpleOpt::SOption g_rgBackupCleanupOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_CLUSTER_FILE_OPTIONS,
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
{ OPT_DELETE_DATA, "--delete-data", SO_NONE },
{ OPT_MIN_CLEANUP_SECONDS, "--min-cleanup-seconds", SO_REQ_SEP },
@ -412,26 +379,15 @@ CSimpleOpt::SOption g_rgBackupDiscontinueOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
BACKUP_TAG_OPTIONS,
{ OPT_WAITFORDONE, "-w", SO_NONE },
{ OPT_WAITFORDONE, "--waitfordone", SO_NONE },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
@ -441,26 +397,15 @@ CSimpleOpt::SOption g_rgBackupWaitOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
BACKUP_TAG_OPTIONS,
{ OPT_NOSTOPWHENDONE, "-z", SO_NONE },
{ OPT_NOSTOPWHENDONE, "--no-stop-when-done", SO_NONE },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
@ -470,22 +415,12 @@ CSimpleOpt::SOption g_rgBackupPauseOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_CLUSTER_FILE_OPTIONS,
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
@ -495,25 +430,15 @@ CSimpleOpt::SOption g_rgBackupExpireOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
{ OPT_DESTCONTAINER, "-d", SO_REQ_SEP },
{ OPT_DESTCONTAINER, "--destcontainer", SO_REQ_SEP },
{ OPT_PROXY, "--proxy", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
{ OPT_FORCE, "-f", SO_NONE },
@ -535,20 +460,11 @@ CSimpleOpt::SOption g_rgBackupDeleteOptions[] = {
{ OPT_DESTCONTAINER, "-d", SO_REQ_SEP },
{ OPT_DESTCONTAINER, "--destcontainer", SO_REQ_SEP },
{ OPT_PROXY, "--proxy", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
@ -559,25 +475,15 @@ CSimpleOpt::SOption g_rgBackupDescribeOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
{ OPT_DESTCONTAINER, "-d", SO_REQ_SEP },
{ OPT_DESTCONTAINER, "--destcontainer", SO_REQ_SEP },
{ OPT_PROXY, "--proxy", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
{ OPT_DESCRIBE_DEEP, "--deep", SO_NONE },
@ -591,24 +497,17 @@ CSimpleOpt::SOption g_rgBackupDumpOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
{ OPT_DESTCONTAINER, "-d", SO_REQ_SEP },
{ OPT_DESTCONTAINER, "--destcontainer", SO_REQ_SEP },
{ OPT_PROXY, "--proxy", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
{ OPT_DUMP_BEGIN, "--begin", SO_REQ_SEP },
@ -621,14 +520,9 @@ CSimpleOpt::SOption g_rgBackupTagsOptions[] = {
#ifdef _WIN32
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_CLUSTER_FILE_OPTIONS,
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
};
@ -640,20 +534,11 @@ CSimpleOpt::SOption g_rgBackupListOptions[] = {
{ OPT_BASEURL, "-b", SO_REQ_SEP },
{ OPT_BASEURL, "--base-url", SO_REQ_SEP },
{ OPT_PROXY, "--proxy", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
@ -665,8 +550,7 @@ CSimpleOpt::SOption g_rgBackupQueryOptions[] = {
{ OPT_PARENTPID, "--parentpid", SO_REQ_SEP },
#endif
{ OPT_RESTORE_TIMESTAMP, "--query-restore-timestamp", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "-C", SO_REQ_SEP },
{ OPT_CLUSTERFILE, "--cluster-file", SO_REQ_SEP },
BACKUP_CLUSTER_FILE_OPTIONS,
{ OPT_DESTCONTAINER, "-d", SO_REQ_SEP },
{ OPT_DESTCONTAINER, "--destcontainer", SO_REQ_SEP },
{ OPT_PROXY, "--proxy", SO_REQ_SEP },
@ -675,22 +559,13 @@ CSimpleOpt::SOption g_rgBackupQueryOptions[] = {
{ OPT_RESTORE_SNAPSHOT_VERSION, "--query-restore-snapshot-version", SO_REQ_SEP },
{ OPT_BACKUPKEYS_FILTER, "-k", SO_REQ_SEP },
{ OPT_BACKUPKEYS_FILTER, "--keys", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_VERSION, "-v", SO_NONE },
{ OPT_VERSION, "--version", SO_NONE },
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
@ -712,8 +587,7 @@ CSimpleOpt::SOption g_rgRestoreOptions[] = {
{ OPT_PROXY, "--proxy", SO_REQ_SEP },
{ OPT_PREFIX_ADD, "--add-prefix", SO_REQ_SEP },
{ OPT_PREFIX_REMOVE, "--remove-prefix", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
BACKUP_TAG_OPTIONS,
{ OPT_BACKUPKEYS, "-k", SO_REQ_SEP },
{ OPT_BACKUPKEYS_FILE, "--keys-file", SO_REQ_SEP },
{ OPT_BACKUPKEYS, "--keys", SO_REQ_SEP },
@ -724,23 +598,14 @@ CSimpleOpt::SOption g_rgRestoreOptions[] = {
{ OPT_MODE, "--mode", SO_REQ_SEP },
{ OPT_RESTORE_VERSION, "--version", SO_REQ_SEP },
{ OPT_RESTORE_VERSION, "-v", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_DRYRUN, "-n", SO_NONE },
{ OPT_DRYRUN, "--dryrun", SO_NONE },
{ OPT_FORCE, "-f", SO_NONE },
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_BLOB_CREDENTIALS, "--blob-credentials", SO_REQ_SEP },
{ OPT_INCREMENTALONLY, "--incremental", SO_NONE },
{ OPT_RESTORE_BEGIN_VERSION, "--begin-version", SO_REQ_SEP },
@ -762,21 +627,12 @@ CSimpleOpt::SOption g_rgDBAgentOptions[] = {
{ OPT_VERSION, "--version", SO_NONE },
{ OPT_VERSION, "-v", SO_NONE },
{ OPT_BUILD_FLAGS, "--build-flags", SO_NONE },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
BACKUP_QUIET_OPTIONS,
BACKUP_LOG_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_LOCALITY, "--locality-", SO_REQ_SEP },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
};
@ -789,25 +645,15 @@ CSimpleOpt::SOption g_rgDBStartOptions[] = {
{ OPT_SOURCE_CLUSTER, "--source", SO_REQ_SEP },
{ OPT_DEST_CLUSTER, "-d", SO_REQ_SEP },
{ OPT_DEST_CLUSTER, "--destination", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
BACKUP_TAG_OPTIONS,
{ OPT_BACKUPKEYS, "-k", SO_REQ_SEP },
{ OPT_BACKUPKEYS_FILE, "--keys-file", SO_REQ_SEP },
{ OPT_BACKUPKEYS, "--keys", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
@ -823,22 +669,12 @@ CSimpleOpt::SOption g_rgDBStatusOptions[] = {
{ OPT_DEST_CLUSTER, "--destination", SO_REQ_SEP },
{ OPT_ERRORLIMIT, "-e", SO_REQ_SEP },
{ OPT_ERRORLIMIT, "--errorlimit", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_TAG_OPTIONS,
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
@ -852,23 +688,13 @@ CSimpleOpt::SOption g_rgDBSwitchOptions[] = {
{ OPT_SOURCE_CLUSTER, "--source", SO_REQ_SEP },
{ OPT_DEST_CLUSTER, "-d", SO_REQ_SEP },
{ OPT_DEST_CLUSTER, "--destination", SO_REQ_SEP },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_TAG_OPTIONS,
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_FORCE, "-f", SO_NONE },
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
@ -884,22 +710,12 @@ CSimpleOpt::SOption g_rgDBAbortOptions[] = {
{ OPT_DEST_CLUSTER, "--destination", SO_REQ_SEP },
{ OPT_CLEANUP, "--cleanup", SO_NONE },
{ OPT_DSTONLY, "--dstonly", SO_NONE },
{ OPT_TAGNAME, "-t", SO_REQ_SEP },
{ OPT_TAGNAME, "--tagname", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_TAG_OPTIONS,
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
@ -913,25 +729,23 @@ CSimpleOpt::SOption g_rgDBPauseOptions[] = {
{ OPT_SOURCE_CLUSTER, "--source", SO_REQ_SEP },
{ OPT_DEST_CLUSTER, "-d", SO_REQ_SEP },
{ OPT_DEST_CLUSTER, "--destination", SO_REQ_SEP },
{ OPT_TRACE, "--log", SO_NONE },
{ OPT_TRACE_DIR, "--logdir", SO_REQ_SEP },
{ OPT_TRACE_FORMAT, "--trace-format", SO_REQ_SEP },
{ OPT_TRACE_LOG_GROUP, "--loggroup", SO_REQ_SEP },
{ OPT_QUIET, "-q", SO_NONE },
{ OPT_QUIET, "--quiet", SO_NONE },
BACKUP_LOG_OPTIONS,
BACKUP_QUIET_OPTIONS,
{ OPT_CRASHONERROR, "--crash", SO_NONE },
{ OPT_MEMLIMIT, "-m", SO_REQ_SEP },
{ OPT_MEMLIMIT, "--memory", SO_REQ_SEP },
{ OPT_VMEMLIMIT, "--memory-vsize", SO_REQ_SEP },
{ OPT_HELP, "-?", SO_NONE },
{ OPT_HELP, "-h", SO_NONE },
{ OPT_HELP, "--help", SO_NONE },
{ OPT_DEVHELP, "--dev-help", SO_NONE },
BACKUP_MEMORY_OPTIONS,
BACKUP_HELP_OPTIONS,
{ OPT_KNOB, "--knob-", SO_REQ_SEP },
TLS_OPTION_FLAGS,
SO_END_OF_OPTIONS
};
#undef BACKUP_LOG_OPTIONS
#undef BACKUP_QUIET_OPTIONS
#undef BACKUP_MEMORY_OPTIONS
#undef BACKUP_HELP_OPTIONS
#undef BACKUP_CLUSTER_FILE_OPTIONS
#undef BACKUP_TAG_OPTIONS
const KeyRef exeAgent = "backup_agent"_sr;
const KeyRef exeBackup = "fdbbackup"_sr;
const KeyRef exeRestore = "fdbrestore"_sr;
@ -3839,7 +3653,7 @@ int main(int argc, char* argv[]) {
break;
case OPT_BACKUPKEYS_FILE:
try {
std::string line = readFileBytes(args->OptionArg(), 64 * 1024 * 1024);
std::string line = readFileBytes(args->OptionArg(), 64ULL * 1024 * 1024);
addKeyRange(line, backupKeys);
} catch (Error&) {
printHelpTeaser(newArgV[0]);

View File

@ -86,100 +86,22 @@ Future<bool> excludeServersAndLocalities(Reference<IDatabase> db,
}
}
AsyncResult<std::vector<std::string>> getExcludedServers(Reference<IDatabase> db) {
Reference<ITransaction> tr = db->createTransaction();
while (true) {
Error err;
try {
ThreadFuture<RangeResult> resultFuture =
tr->getRange(fdb_cli::excludedServersSpecialKeyRange, CLIENT_KNOBS->TOO_MANY);
RangeResult r = co_await safeThreadFutureToFuture(resultFuture);
ASSERT(!r.more && r.size() < CLIENT_KNOBS->TOO_MANY);
std::vector<std::string> exclusions;
for (const auto& i : r) {
auto addr = i.key.removePrefix(fdb_cli::excludedServersSpecialKeyRange.begin).toString();
exclusions.push_back(addr);
}
co_return exclusions;
} catch (Error& e) {
err = e;
}
TraceEvent(SevWarn, "GetExcludedServersError").error(err);
co_await safeThreadFutureToFuture(tr->onError(err));
}
Future<std::vector<std::string>> getExcludedServers(Reference<IDatabase> db) {
return getManagementApiSpecialKeyValues(db, fdb_cli::excludedServersSpecialKeyRange, "GetExcludedServersError");
}
// Get the list of excluded localities by reading the keys.
AsyncResult<std::vector<std::string>> getExcludedLocalities(Reference<IDatabase> db) {
Reference<ITransaction> tr = db->createTransaction();
while (true) {
Error err;
try {
ThreadFuture<RangeResult> resultFuture =
tr->getRange(fdb_cli::excludedLocalitySpecialKeyRange, CLIENT_KNOBS->TOO_MANY);
RangeResult r = co_await safeThreadFutureToFuture(resultFuture);
ASSERT(!r.more && r.size() < CLIENT_KNOBS->TOO_MANY);
std::vector<std::string> excludedLocalities;
for (const auto& i : r) {
auto locality = i.key.removePrefix(fdb_cli::excludedLocalitySpecialKeyRange.begin).toString();
excludedLocalities.push_back(locality);
}
co_return excludedLocalities;
} catch (Error& e) {
err = e;
}
co_await safeThreadFutureToFuture(tr->onError(err));
}
Future<std::vector<std::string>> getExcludedLocalities(Reference<IDatabase> db) {
return getManagementApiSpecialKeyValues(db, fdb_cli::excludedLocalitySpecialKeyRange);
}
AsyncResult<std::vector<std::string>> getFailedServers(Reference<IDatabase> db) {
Reference<ITransaction> tr = db->createTransaction();
while (true) {
Error err;
try {
ThreadFuture<RangeResult> resultFuture =
tr->getRange(fdb_cli::failedServersSpecialKeyRange, CLIENT_KNOBS->TOO_MANY);
RangeResult r = co_await safeThreadFutureToFuture(resultFuture);
ASSERT(!r.more && r.size() < CLIENT_KNOBS->TOO_MANY);
std::vector<std::string> exclusions;
for (const auto& i : r) {
auto addr = i.key.removePrefix(fdb_cli::failedServersSpecialKeyRange.begin).toString();
exclusions.push_back(addr);
}
co_return exclusions;
} catch (Error& e) {
err = e;
}
co_await safeThreadFutureToFuture(tr->onError(err));
}
Future<std::vector<std::string>> getFailedServers(Reference<IDatabase> db) {
return getManagementApiSpecialKeyValues(db, fdb_cli::failedServersSpecialKeyRange);
}
// Get the list of failed localities by reading the keys.
AsyncResult<std::vector<std::string>> getFailedLocalities(Reference<IDatabase> db) {
Reference<ITransaction> tr = db->createTransaction();
while (true) {
Error err;
try {
ThreadFuture<RangeResult> resultFuture =
tr->getRange(fdb_cli::failedLocalitySpecialKeyRange, CLIENT_KNOBS->TOO_MANY);
RangeResult r = co_await safeThreadFutureToFuture(resultFuture);
ASSERT(!r.more && r.size() < CLIENT_KNOBS->TOO_MANY);
std::vector<std::string> excludedLocalities;
for (const auto& i : r) {
auto locality = i.key.removePrefix(fdb_cli::failedLocalitySpecialKeyRange.begin).toString();
excludedLocalities.push_back(locality);
}
co_return excludedLocalities;
} catch (Error& e) {
err = e;
}
TraceEvent(SevWarn, "GetExcludedLocalitiesError").error(err);
co_await safeThreadFutureToFuture(tr->onError(err));
}
Future<std::vector<std::string>> getFailedLocalities(Reference<IDatabase> db) {
return getManagementApiSpecialKeyValues(db, fdb_cli::failedLocalitySpecialKeyRange, "GetExcludedLocalitiesError");
}
Future<std::set<NetworkAddress>> getInProgressExclusion(Reference<ITransaction> tr) {
@ -286,20 +208,6 @@ Future<Void> checkForCoordinators(Reference<IDatabase> db, std::set<AddressExclu
namespace fdb_cli {
const KeyRangeRef excludedServersSpecialKeyRange("\xff\xff/management/excluded/"_sr,
"\xff\xff/management/excluded0"_sr);
const KeyRangeRef failedServersSpecialKeyRange("\xff\xff/management/failed/"_sr, "\xff\xff/management/failed0"_sr);
const KeyRangeRef excludedLocalitySpecialKeyRange("\xff\xff/management/excluded_locality/"_sr,
"\xff\xff/management/excluded_locality0"_sr);
const KeyRangeRef failedLocalitySpecialKeyRange("\xff\xff/management/failed_locality/"_sr,
"\xff\xff/management/failed_locality0"_sr);
const KeyRef excludedForceOptionSpecialKey = "\xff\xff/management/options/excluded/force"_sr;
const KeyRef failedForceOptionSpecialKey = "\xff\xff/management/options/failed/force"_sr;
const KeyRef excludedLocalityForceOptionSpecialKey = "\xff\xff/management/options/excluded_locality/force"_sr;
const KeyRef failedLocalityForceOptionSpecialKey = "\xff\xff/management/options/failed_locality/force"_sr;
const KeyRangeRef exclusionInProgressSpecialKeyRange("\xff\xff/management/in_progress_exclusion/"_sr,
"\xff\xff/management/in_progress_exclusion0"_sr);
Future<bool> excludeCommandActor(Reference<IDatabase> db, std::vector<StringRef> tokens, Future<Void> _warn) {
auto warn = std::move(_warn);
if (tokens.size() <= 1) {

View File

@ -98,15 +98,15 @@ extern const KeyRef coordinatorsProcessSpecialKey;
extern const KeyRef ddModeSpecialKey;
extern const KeyRef ddIgnoreRebalanceSpecialKey;
extern const KeyRangeRef excludedServersSpecialKeyRange;
extern const KeyRangeRef failedServersSpecialKeyRange;
extern const KeyRangeRef excludedLocalitySpecialKeyRange;
extern const KeyRangeRef failedLocalitySpecialKeyRange;
extern const KeyRef excludedForceOptionSpecialKey;
extern const KeyRef failedForceOptionSpecialKey;
extern const KeyRef excludedLocalityForceOptionSpecialKey;
extern const KeyRef failedLocalityForceOptionSpecialKey;
extern const KeyRangeRef exclusionInProgressSpecialKeyRange;
using management_api::excludedForceOptionSpecialKey;
using management_api::excludedLocalityForceOptionSpecialKey;
using management_api::excludedLocalitySpecialKeyRange;
using management_api::excludedServersSpecialKeyRange;
using management_api::exclusionInProgressSpecialKeyRange;
using management_api::failedForceOptionSpecialKey;
using management_api::failedLocalityForceOptionSpecialKey;
using management_api::failedLocalitySpecialKeyRange;
using management_api::failedServersSpecialKeyRange;
extern const KeyRef lockSpecialKey;

View File

@ -108,6 +108,19 @@ bool copyDefaultParameters(Reference<Task> source, Reference<Task> dest) {
return false;
}
Future<Key> addTaskWithOptionalDependency(Reference<ReadYourWritesTransaction> tr,
Reference<TaskBucket> taskBucket,
Reference<Task> task,
Key validationKey,
Reference<TaskFuture> waitFor) {
if (!waitFor) {
co_return taskBucket->addTask(tr, task, validationKey, task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr, taskBucket, task, validationKey, task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
}
template <class Tr>
Future<Void> checkTaskVersion(Tr tr, Reference<Task> task, StringRef name, uint32_t version) {
uint32_t taskVersion = task->getVersion();
@ -188,19 +201,8 @@ struct BackupRangeTaskFunc : TaskFuncBase {
task->params[BackupAgentBase::keyBeginKey] = begin;
task->params[BackupAgentBase::keyEndKey] = end;
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
static Future<Void> _execute(Database cx,
@ -582,19 +584,8 @@ struct FinishFullBackupTaskFunc : TaskFuncBase {
copyDefaultParameters(parentTask, task);
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
StringRef getName() const override { return name; };
@ -683,19 +674,8 @@ struct EraseLogRangeTaskFunc : TaskFuncBase {
BinaryWriter::toValue(1, Unversioned()); // FIXME: remove in 6.X, only needed for 5.2 backward compatibility
task->params[DatabaseBackupAgent::keyEndVersion] = BinaryWriter::toValue(endVersion, Unversioned());
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr,
@ -965,19 +945,8 @@ struct CopyLogRangeTaskFunc : TaskFuncBase {
task->params[DatabaseBackupAgent::keyBeginVersion] = BinaryWriter::toValue(beginVersion, Unversioned());
task->params[DatabaseBackupAgent::keyEndVersion] = BinaryWriter::toValue(endVersion, Unversioned());
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr,
@ -1127,19 +1096,8 @@ struct CopyLogsTaskFunc : TaskFuncBase {
task->params[BackupAgentBase::keyBeginVersion] = BinaryWriter::toValue(beginVersion, Unversioned());
task->params[DatabaseBackupAgent::keyPrevBeginVersion] = BinaryWriter::toValue(prevBeginVersion, Unversioned());
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
StringRef getName() const override { return name; };
@ -1250,19 +1208,8 @@ struct FinishedFullBackupTaskFunc : TaskFuncBase {
copyDefaultParameters(parentTask, task);
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr,
@ -1409,19 +1356,8 @@ struct CopyDiffLogsTaskFunc : TaskFuncBase {
task->params[DatabaseBackupAgent::keyBeginVersion] = BinaryWriter::toValue(beginVersion, Unversioned());
task->params[DatabaseBackupAgent::keyPrevBeginVersion] = BinaryWriter::toValue(prevBeginVersion, Unversioned());
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
StringRef getName() const override { return name; };
@ -1670,19 +1606,8 @@ struct OldCopyLogRangeTaskFunc : TaskFuncBase {
task->params[DatabaseBackupAgent::keyBeginVersion] = BinaryWriter::toValue(beginVersion, Unversioned());
task->params[DatabaseBackupAgent::keyEndVersion] = BinaryWriter::toValue(endVersion, Unversioned());
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
static Future<Void> _finish(Reference<ReadYourWritesTransaction> tr,
@ -1772,19 +1697,8 @@ struct AbortOldBackupTaskFunc : TaskFuncBase {
copyDefaultParameters(parentTask, task);
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
StringRef getName() const override { return name; };
@ -2066,19 +1980,8 @@ struct BackupRestorableTaskFunc : TaskFuncBase {
copyDefaultParameters(parentTask, task);
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
parentTask->params[Task::reservedTaskParamValidKey],
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
co_return co_await addTaskWithOptionalDependency(
tr, taskBucket, task, parentTask->params[Task::reservedTaskParamValidKey], waitFor);
}
StringRef getName() const override { return name; };
@ -2351,25 +2254,11 @@ struct StartFullBackupTaskFunc : TaskFuncBase {
task->params[DatabaseBackupAgent::keyDatabasesInSync] =
backupAction == DatabaseBackupAgent::PreBackupAction::NONE ? "t"_sr : "f"_sr;
if (!waitFor) {
co_return taskBucket->addTask(tr,
task,
Subspace(databaseBackupPrefixRange.begin)
.get(BackupAgentBase::keyConfig)
.get(logUid)
.pack(BackupAgentBase::keyFolderId),
task->params[BackupAgentBase::keyFolderId]);
}
co_await waitFor->onSetAddTask(tr,
taskBucket,
task,
Subspace(databaseBackupPrefixRange.begin)
.get(BackupAgentBase::keyConfig)
.get(logUid)
.pack(BackupAgentBase::keyFolderId),
task->params[BackupAgentBase::keyFolderId]);
co_return "OnSetAddTask"_sr;
Key validationKey = Subspace(databaseBackupPrefixRange.begin)
.get(BackupAgentBase::keyConfig)
.get(logUid)
.pack(BackupAgentBase::keyFolderId);
co_return co_await addTaskWithOptionalDependency(tr, taskBucket, task, validationKey, waitFor);
}
StringRef getName() const override { return name; };

View File

@ -616,13 +616,19 @@ struct TrInfoChunk {
static const Key CLIENT_LATENCY_INFO_PREFIX = "client_latency/"_sr;
static const Key CLIENT_LATENCY_INFO_CTR_PREFIX = "client_latency_counter/"_sr;
static void resetClientStatusTransaction(Transaction* tr) {
tr->reset();
// Profiling maintenance must not generate more records for itself to persist.
tr->trState->trLogInfo.clear();
}
static Future<Void> transactionInfoCommitActor(Transaction* tr, std::vector<TrInfoChunk>* chunks) {
const Key clientLatencyAtomicCtr = CLIENT_LATENCY_INFO_CTR_PREFIX.withPrefix(fdbClientInfoPrefixRange.begin);
int retryCount = 0;
while (true) {
Error err;
try {
tr->reset();
resetClientStatusTransaction(tr);
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
[[maybe_unused]] Future<Standalone<StringRef>> vstamp = tr->getVersionstamp();
@ -656,7 +662,7 @@ static Future<Void> delExcessClntTxnEntriesActor(Transaction* tr, int64_t client
while (true) {
Error err;
try {
tr->reset();
resetClientStatusTransaction(tr);
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
Optional<Value> ctrValue = co_await tr->get(KeyRef(clientLatencyAtomicCtr), Snapshot::True);

View File

@ -27,6 +27,7 @@
#include "fdbclient/BulkLoading.h"
#include "fdbclient/GenericManagementAPI.h"
#include "fdbclient/KeyRangeMap.h"
#include "fdbclient/IClientApi.h"
#include "fdbclient/RangeLock.h"
#include "flow/Error.h"
#include "fmt/format.h"
@ -36,6 +37,7 @@
#include "fdbclient/FDBOptions.g.h"
#include "fdbclient/FDBTypes.h"
#include "fdbclient/ReadYourWrites.h"
#include "fdbclient/RunRYWTransaction.h"
#include "fdbclient/ManagementAPI.h"
#include "fdbclient/SystemData.h"
@ -1422,6 +1424,37 @@ Reference<IQuorumChange> autoQuorumChange(int desired) {
return makeReference<AutoQuorumChange>(desired);
}
Future<std::vector<std::string>> getManagementApiSpecialKeyValues(Reference<IDatabase> db,
KeyRange range,
const char* errorEvent) {
Reference<ITransaction> tr = db->createTransaction();
while (true) {
Error err;
try {
ThreadFuture<RangeResult> resultFuture = tr->getRange(range, CLIENT_KNOBS->TOO_MANY);
RangeResult result = co_await safeThreadFutureToFuture(resultFuture);
ASSERT(!result.more && result.size() < CLIENT_KNOBS->TOO_MANY);
std::vector<std::string> values;
values.reserve(result.size());
for (const auto& entry : result) {
values.push_back(entry.key.removePrefix(range.begin).toString());
}
co_return values;
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
throw;
}
err = e;
}
if (errorEvent != nullptr) {
TraceEvent(SevWarn, errorEvent).error(err);
}
co_await safeThreadFutureToFuture(tr->onError(err));
}
}
Future<Void> excludeServers(Transaction* tr, std::vector<AddressExclusion> servers, bool failed) {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
@ -1457,32 +1490,28 @@ Future<Void> excludeServers(Transaction* tr, std::vector<AddressExclusion> serve
Future<Void> excludeServers(Database cx, std::vector<AddressExclusion> servers, bool failed) {
if (cx->apiVersionAtLeast(700)) {
ReadYourWritesTransaction ryw(cx);
while (true) {
Error err;
try {
ryw.setOption(FDBTransactionOptions::RAW_ACCESS);
ryw.setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
ryw.set(
SpecialKeySpace::getManagementApiCommandOptionSpecialKey(failed ? "failed" : "excluded", "force"),
ValueRef());
for (auto& s : servers) {
Key addr = failed
? SpecialKeySpace::getManagementApiCommandPrefix("failed").withSuffix(s.toString())
: SpecialKeySpace::getManagementApiCommandPrefix("exclude").withSuffix(s.toString());
ryw.set(addr, ValueRef());
}
TraceEvent("ExcludeServersSpecialKeySpaceCommit")
.detail("Servers", describe(servers))
.detail("ExcludeFailed", failed);
co_await ryw.commit();
co_return;
} catch (Error& e) {
err = e;
}
TraceEvent("ExcludeServersError").errorUnsuppressed(err);
co_await ryw.onError(err);
}
co_await runRYWTransactionVoid(
cx,
[&servers, failed](Reference<ReadYourWritesTransaction> ryw) -> Future<Void> {
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
ryw->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
ryw->set(
SpecialKeySpace::getManagementApiCommandOptionSpecialKey(failed ? "failed" : "excluded", "force"),
ValueRef());
for (const auto& server : servers) {
Key address =
failed
? SpecialKeySpace::getManagementApiCommandPrefix("failed").withSuffix(server.toString())
: SpecialKeySpace::getManagementApiCommandPrefix("exclude").withSuffix(server.toString());
ryw->set(address, ValueRef());
}
TraceEvent("ExcludeServersSpecialKeySpaceCommit")
.detail("Servers", describe(servers))
.detail("ExcludeFailed", failed);
return Void();
},
"ExcludeServersError");
co_return;
} else {
Transaction tr(cx);
while (true) {
@ -1536,33 +1565,28 @@ Future<Void> excludeLocalities(Transaction* tr, std::unordered_set<std::string>
// excludes localities by setting the keys.
Future<Void> excludeLocalities(Database cx, std::unordered_set<std::string> localities, bool failed) {
if (cx->apiVersionAtLeast(700)) {
ReadYourWritesTransaction ryw(cx);
while (true) {
Error err;
try {
ryw.setOption(FDBTransactionOptions::RAW_ACCESS);
ryw.setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
ryw.set(SpecialKeySpace::getManagementApiCommandOptionSpecialKey(
failed ? "failed_locality" : "excluded_locality", "force"),
ValueRef());
for (const auto& l : localities) {
Key addr = failed
? SpecialKeySpace::getManagementApiCommandPrefix("failedlocality").withSuffix(l)
: SpecialKeySpace::getManagementApiCommandPrefix("excludedlocality").withSuffix(l);
ryw.set(addr, ValueRef());
}
TraceEvent("ExcludeLocalitiesSpecialKeySpaceCommit")
.detail("Localities", describe(localities))
.detail("ExcludeFailed", failed);
co_await ryw.commit();
co_return;
} catch (Error& e) {
err = e;
}
TraceEvent("ExcludeLocalitiesError").errorUnsuppressed(err);
co_await ryw.onError(err);
}
co_await runRYWTransactionVoid(
cx,
[&localities, failed](Reference<ReadYourWritesTransaction> ryw) -> Future<Void> {
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
ryw->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
ryw->set(SpecialKeySpace::getManagementApiCommandOptionSpecialKey(
failed ? "failed_locality" : "excluded_locality", "force"),
ValueRef());
for (const auto& locality : localities) {
Key address =
failed
? SpecialKeySpace::getManagementApiCommandPrefix("failedlocality").withSuffix(locality)
: SpecialKeySpace::getManagementApiCommandPrefix("excludedlocality").withSuffix(locality);
ryw->set(address, ValueRef());
}
TraceEvent("ExcludeLocalitiesSpecialKeySpaceCommit")
.detail("Localities", describe(localities))
.detail("ExcludeFailed", failed);
return Void();
},
"ExcludeLocalitiesError");
co_return;
} else {
Transaction tr(cx);
while (true) {
@ -1583,46 +1607,35 @@ Future<Void> excludeLocalities(Database cx, std::unordered_set<std::string> loca
Future<Void> includeServers(Database cx, std::vector<AddressExclusion> servers, bool failed) {
std::string versionKey = deterministicRandom()->randomUniqueID().toString();
if (cx->apiVersionAtLeast(700)) {
ReadYourWritesTransaction ryw(cx);
while (true) {
Error err;
try {
ryw.setOption(FDBTransactionOptions::RAW_ACCESS);
ryw.setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
for (auto& s : servers) {
if (!s.isValid()) {
if (failed) {
ryw.clear(SpecialKeySpace::getManagementApiCommandRange("failed"));
} else {
ryw.clear(SpecialKeySpace::getManagementApiCommandRange("exclude"));
}
} else {
Key addr =
failed ? SpecialKeySpace::getManagementApiCommandPrefix("failed").withSuffix(s.toString())
: SpecialKeySpace::getManagementApiCommandPrefix("exclude").withSuffix(s.toString());
ryw.clear(addr);
// Eliminate both any ip-level exclusion (1.2.3.4) and any
// port-level exclusions (1.2.3.4:5)
// The range ['IP', 'IP;'] was originally deleted. ';' is
// char(':' + 1). This does not work, as other for all
// x between 0 and 9, 'IPx' will also be in this range.
//
// This is why we now make two clears: first only of the ip
// address, the second will delete all ports.
if (s.isWholeMachine())
ryw.clear(KeyRangeRef(addr.withSuffix(":"_sr), addr.withSuffix(";"_sr)));
}
}
TraceEvent("IncludeServersCommit").detail("Servers", describe(servers)).detail("Failed", failed);
co_await ryw.commit();
co_return;
} catch (Error& e) {
err = e;
}
TraceEvent("IncludeServersError").errorUnsuppressed(err);
co_await ryw.onError(err);
}
co_await runRYWTransactionVoid(
cx,
[&servers, failed](Reference<ReadYourWritesTransaction> ryw) -> Future<Void> {
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
ryw->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
for (const auto& server : servers) {
if (!server.isValid()) {
ryw->clear(SpecialKeySpace::getManagementApiCommandRange(failed ? "failed" : "exclude"));
} else {
Key address =
failed
? SpecialKeySpace::getManagementApiCommandPrefix("failed").withSuffix(server.toString())
: SpecialKeySpace::getManagementApiCommandPrefix("exclude").withSuffix(
server.toString());
ryw->clear(address);
// Eliminate both any ip-level exclusion (1.2.3.4) and any
// port-level exclusions (1.2.3.4:5). Clearing ['IP', 'IP;']
// would also include 'IPx', so clear the IP and port range
// separately.
if (server.isWholeMachine()) {
ryw->clear(KeyRangeRef(address.withSuffix(":"_sr), address.withSuffix(";"_sr)));
}
}
}
TraceEvent("IncludeServersCommit").detail("Servers", describe(servers)).detail("Failed", failed);
return Void();
},
"IncludeServersError");
co_return;
} else {
Transaction tr(cx);
while (true) {
@ -1690,39 +1703,32 @@ Future<Void> includeServers(Database cx, std::vector<AddressExclusion> servers,
Future<Void> includeLocalities(Database cx, std::vector<std::string> localities, bool failed, bool includeAll) {
std::string versionKey = deterministicRandom()->randomUniqueID().toString();
if (cx->apiVersionAtLeast(700)) {
ReadYourWritesTransaction ryw(cx);
while (true) {
Error err;
try {
ryw.setOption(FDBTransactionOptions::RAW_ACCESS);
ryw.setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
if (includeAll) {
if (failed) {
ryw.clear(SpecialKeySpace::getManagementApiCommandRange("failedlocality"));
} else {
ryw.clear(SpecialKeySpace::getManagementApiCommandRange("excludedlocality"));
}
} else {
for (const auto& l : localities) {
Key locality =
failed ? SpecialKeySpace::getManagementApiCommandPrefix("failedlocality").withSuffix(l)
: SpecialKeySpace::getManagementApiCommandPrefix("excludedlocality").withSuffix(l);
ryw.clear(locality);
}
}
TraceEvent("IncludeLocalitiesCommit")
.detail("Localities", describe(localities))
.detail("Failed", failed)
.detail("IncludeAll", includeAll);
co_await ryw.commit();
co_return;
} catch (Error& e) {
err = e;
}
TraceEvent("IncludeLocalitiesError").errorUnsuppressed(err);
co_await ryw.onError(err);
}
co_await runRYWTransactionVoid(
cx,
[&localities, failed, includeAll](Reference<ReadYourWritesTransaction> ryw) -> Future<Void> {
ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
ryw->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
if (includeAll) {
ryw->clear(
SpecialKeySpace::getManagementApiCommandRange(failed ? "failedlocality" : "excludedlocality"));
} else {
for (const auto& locality : localities) {
Key key =
failed
? SpecialKeySpace::getManagementApiCommandPrefix("failedlocality").withSuffix(locality)
: SpecialKeySpace::getManagementApiCommandPrefix("excludedlocality")
.withSuffix(locality);
ryw->clear(key);
}
}
TraceEvent("IncludeLocalitiesCommit")
.detail("Localities", describe(localities))
.detail("Failed", failed)
.detail("IncludeAll", includeAll);
return Void();
},
"IncludeLocalitiesError");
co_return;
} else {
Transaction tr(cx);
while (true) {

View File

@ -289,6 +289,102 @@ const KeyRef JSONSchemas::statusSchema = R"statusSchema(
"p99":0.0,
"p99.9":0.0
},
"commit_batch_transactions":{
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_batch_bytes":{
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_batching_waiting":{
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_preresolution_latency":{
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_resolution_latency":{
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_postresolution_latency":{
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_tlog_logging_latency":{
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"commit_reply_latency":{
"count":0,
"min":0.0,
"max":0.0,
"median":0.0,
"mean":0.0,
"p25":0.0,
"p90":0.0,
"p95":0.0,
"p99":0.0,
"p99.9":0.0
},
"grv_latency_bands":{
"$map": 1
},

View File

@ -235,19 +235,25 @@ static std::string hexStringRef(const StringRef& s) {
return result;
}
template <class Request, class Reply>
static void traceKeyValuesDiff(TraceEvent& event,
const KeySelectorRef& begin,
const KeySelectorRef& end,
Version version,
int limit,
int limitBytes,
const VectorRef<KeyValueRef>& ssKV,
bool ssMore,
const VectorRef<KeyValueRef>& tssKV,
bool tssMore,
const Request& request,
const Reply& source,
const Reply& target,
const ComparisonType& type) {
traceKeyValuesSummary(
event, begin, end, version, limit, limitBytes, ssKV.size(), ssMore, tssKV.size(), tssMore, type);
const auto& ssKV = source.data;
const auto& tssKV = target.data;
traceKeyValuesSummary(event,
request.begin,
request.end,
request.version,
request.limit,
request.limitBytes,
ssKV.size(),
source.more,
tssKV.size(),
target.more,
type);
bool mismatchFound = false;
for (int i = 0; i < std::max(ssKV.size(), tssKV.size()); i++) {
if (i >= ssKV.size() || i >= tssKV.size() || ssKV[i] != tssKV[i]) {
@ -279,17 +285,7 @@ void TSS_traceMismatch(TraceEvent& event,
const GetKeyValuesReply& src,
const GetKeyValuesReply& tss,
const ComparisonType& type) {
traceKeyValuesDiff(event,
req.begin,
req.end,
req.version,
req.limit,
req.limitBytes,
src.data,
src.more,
tss.data,
tss.more,
type);
traceKeyValuesDiff(event, req, src, tss, type);
}
// range reads and flat map
@ -334,24 +330,13 @@ const char* LB_mismatchTraceName(const GetKeyValuesStreamRequest& req, const Com
return type == TSS_COMPARISON ? "TSSMismatchGetKeyValuesStream" : "ReplicaMismatchGetKeyValuesStream";
}
// TODO this is all duplicated from above, simplify?
template <>
void TSS_traceMismatch(TraceEvent& event,
const GetKeyValuesStreamRequest& req,
const GetKeyValuesStreamReply& src,
const GetKeyValuesStreamReply& tss,
const ComparisonType& type) {
traceKeyValuesDiff(event,
req.begin,
req.end,
req.version,
req.limit,
req.limitBytes,
src.data,
src.more,
tss.data,
tss.more,
type);
traceKeyValuesDiff(event, req, src, tss, type);
}
template <>

View File

@ -36,6 +36,25 @@ standard API and some knowledge of the contents of the system key space.
#include "fdbclient/DatabaseConfiguration.h"
#include "fdbclient/MonitorLeader.h"
class IDatabase;
namespace management_api {
inline const KeyRangeRef excludedServersSpecialKeyRange("\xff\xff/management/excluded/"_sr,
"\xff\xff/management/excluded0"_sr);
inline const KeyRangeRef failedServersSpecialKeyRange("\xff\xff/management/failed/"_sr,
"\xff\xff/management/failed0"_sr);
inline const KeyRangeRef excludedLocalitySpecialKeyRange("\xff\xff/management/excluded_locality/"_sr,
"\xff\xff/management/excluded_locality0"_sr);
inline const KeyRangeRef failedLocalitySpecialKeyRange("\xff\xff/management/failed_locality/"_sr,
"\xff\xff/management/failed_locality0"_sr);
inline const KeyRef excludedForceOptionSpecialKey = "\xff\xff/management/options/excluded/force"_sr;
inline const KeyRef failedForceOptionSpecialKey = "\xff\xff/management/options/failed/force"_sr;
inline const KeyRef excludedLocalityForceOptionSpecialKey = "\xff\xff/management/options/excluded_locality/force"_sr;
inline const KeyRef failedLocalityForceOptionSpecialKey = "\xff\xff/management/options/failed_locality/force"_sr;
inline const KeyRangeRef exclusionInProgressSpecialKeyRange("\xff\xff/management/in_progress_exclusion/"_sr,
"\xff\xff/management/in_progress_exclusion0"_sr);
} // namespace management_api
Future<DatabaseConfiguration> getDatabaseConfiguration(Transaction* tr, bool useSystemPriority = false);
Future<DatabaseConfiguration> getDatabaseConfiguration(Database cx, bool useSystemPriority = false);
Future<Void> waitForFullReplication(Database cx);
@ -57,6 +76,11 @@ Future<CoordinatorsResult> changeQuorum(Database cx, Reference<IQuorumChange> ch
Reference<IQuorumChange> autoQuorumChange(int desired = -1);
Reference<IQuorumChange> nameQuorumChange(std::string const& name, Reference<IQuorumChange> const& other);
// Return the suffixes of all keys in a management special-key range.
Future<std::vector<std::string>> getManagementApiSpecialKeyValues(Reference<IDatabase> db,
KeyRange range,
const char* errorEvent = nullptr);
// Exclude the given set of servers from use as state servers. Returns as soon as the change is durable, without
// necessarily waiting for the servers to be evacuated. A NetworkAddress with a port of 0 means all servers on the
// given IP.

View File

@ -91,7 +91,7 @@ Future<RunRYWTransactionResult<Function>> runRYWTransactionDebug(Database cx,
// The supplied function should be idempotent. Otherwise, outcome of this function will depend on how many times the
// transaction is retried.
template <class Function>
Future<Void> runRYWTransactionVoid(Database cx, Function func) {
Future<Void> runRYWTransactionVoid(Database cx, Function func, const char* errorEvent = nullptr) {
Reference<ReadYourWritesTransaction> tr(new ReadYourWritesTransaction(cx));
while (true) {
Error err;
@ -102,6 +102,9 @@ Future<Void> runRYWTransactionVoid(Database cx, Function func) {
} catch (Error& e) {
err = e;
}
if (errorEvent != nullptr) {
TraceEvent(errorEvent).errorUnsuppressed(err);
}
co_await tr->onError(err);
}
}

View File

@ -36,90 +36,17 @@ namespace fdbctl {
namespace utils {
Future<std::vector<std::string>> getExcludedServers(Reference<IDatabase> db) {
Reference<ITransaction> tr = db->createTransaction();
loop {
Error err;
try {
ThreadFuture<RangeResult> resultFuture =
tr->getRange(special_keys::excludedServersSpecialKeyRange, CLIENT_KNOBS->TOO_MANY);
RangeResult r = co_await safeThreadFutureToFuture(resultFuture);
ASSERT(!r.more && r.size() < CLIENT_KNOBS->TOO_MANY);
std::vector<std::string> exclusions;
for (const auto& i : r) {
auto addr = i.key.removePrefix(special_keys::excludedServersSpecialKeyRange.begin).toString();
exclusions.push_back(addr);
}
co_return exclusions;
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
throw e;
}
TraceEvent(SevWarn, "GetExcludedServersError").error(e);
err = e;
}
co_await safeThreadFutureToFuture(tr->onError(err));
}
return getManagementApiSpecialKeyValues(
db, special_keys::excludedServersSpecialKeyRange, "GetExcludedServersError");
}
Future<std::vector<std::string>> getFailedServers(Reference<IDatabase> db) {
Reference<ITransaction> tr = db->createTransaction();
loop {
Error err;
try {
ThreadFuture<RangeResult> resultFuture =
tr->getRange(special_keys::failedServersSpecialKeyRange, CLIENT_KNOBS->TOO_MANY);
RangeResult r = co_await safeThreadFutureToFuture(resultFuture);
ASSERT(!r.more && r.size() < CLIENT_KNOBS->TOO_MANY);
std::vector<std::string> exclusions;
for (const auto& i : r) {
auto addr = i.key.removePrefix(special_keys::failedServersSpecialKeyRange.begin).toString();
exclusions.push_back(addr);
}
co_return exclusions;
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
throw e;
}
TraceEvent(SevWarn, "GetExcludedServersError").error(e);
err = e;
}
co_await safeThreadFutureToFuture(tr->onError(err));
}
return getManagementApiSpecialKeyValues(db, special_keys::failedServersSpecialKeyRange, "GetExcludedServersError");
}
Future<std::vector<std::string>> getExcludedLocalities(Reference<IDatabase> db) {
Reference<ITransaction> tr = db->createTransaction();
loop {
Error err;
try {
ThreadFuture<RangeResult> resultFuture =
tr->getRange(special_keys::excludedLocalitySpecialKeyRange, CLIENT_KNOBS->TOO_MANY);
RangeResult r = co_await safeThreadFutureToFuture(resultFuture);
ASSERT(!r.more && r.size() < CLIENT_KNOBS->TOO_MANY);
std::vector<std::string> excludedLocalities;
for (const auto& i : r) {
auto locality = i.key.removePrefix(special_keys::excludedLocalitySpecialKeyRange.begin).toString();
excludedLocalities.push_back(locality);
}
co_return excludedLocalities;
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
throw e;
}
TraceEvent(SevWarn, "GetExcludedLocalitiesError").error(e);
err = e;
}
co_await safeThreadFutureToFuture(tr->onError(err));
}
return getManagementApiSpecialKeyValues(
db, special_keys::excludedLocalitySpecialKeyRange, "GetExcludedLocalitiesError");
}
Future<std::set<NetworkAddress>> getInProgressExclusion(Reference<ITransaction> tr) {
@ -136,32 +63,8 @@ Future<std::set<NetworkAddress>> getInProgressExclusion(Reference<ITransaction>
}
Future<std::vector<std::string>> getFailedLocalities(Reference<IDatabase> db) {
Reference<ITransaction> tr = db->createTransaction();
loop {
Error err;
try {
ThreadFuture<RangeResult> resultFuture =
tr->getRange(special_keys::failedLocalitySpecialKeyRange, CLIENT_KNOBS->TOO_MANY);
RangeResult r = co_await safeThreadFutureToFuture(resultFuture);
ASSERT(!r.more && r.size() < CLIENT_KNOBS->TOO_MANY);
std::vector<std::string> excludedLocalities;
for (const auto& i : r) {
auto locality = i.key.removePrefix(special_keys::failedLocalitySpecialKeyRange.begin).toString();
excludedLocalities.push_back(locality);
}
co_return excludedLocalities;
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
throw e;
}
TraceEvent(SevWarn, "GetFailedLocalitiesError").error(e);
err = e;
}
co_await safeThreadFutureToFuture(tr->onError(err));
}
return getManagementApiSpecialKeyValues(
db, special_keys::failedLocalitySpecialKeyRange, "GetFailedLocalitiesError");
}
} // namespace utils
@ -263,6 +166,7 @@ Future<std::set<NetworkAddress>> checkForExcludingServers(Reference<IDatabase> d
break;
co_await delayJittered(1.0); // SOMEDAY: watches!
continue;
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) {
throw;

View File

@ -23,6 +23,7 @@
#define FDB_CTL_LIB_FDB_CONTROL_COMMANDS_H
#include "fdbclient/IClientApi.h"
#include "fdbclient/ManagementAPI.h"
#include "fdbclient/StorageServerInterface.h"
#include "fdbrpc/FlowGrpc.h"
#include "fdbctl/control_service/control_service.pb.h"
@ -73,26 +74,19 @@ Future<bool> getWorkersProcessData(Reference<IDatabase> db, std::vector<ProcessD
namespace special_keys {
// TODO: Point fdbcli ones to this.
const KeyRef clusterDescriptionSpecialKey = "\xff\xff/configuration/coordinators/cluster_description"_sr;
const KeyRef coordinatorsAutoSpecialKey = "\xff\xff/management/auto_coordinators"_sr;
const KeyRef coordinatorsProcessSpecialKey = "\xff\xff/configuration/coordinators/processes"_sr;
// Special key ranges for include/exclude functionality
const KeyRange excludedServersSpecialKeyRange =
KeyRangeRef("\xff\xff/management/excluded/"_sr, "\xff\xff/management/excluded0"_sr);
const KeyRange failedServersSpecialKeyRange =
KeyRangeRef("\xff\xff/management/failed/"_sr, "\xff\xff/management/failed0"_sr);
const KeyRange excludedLocalitySpecialKeyRange =
KeyRangeRef("\xff\xff/management/excluded_locality/"_sr, "\xff\xff/management/excluded_locality0"_sr);
const KeyRange failedLocalitySpecialKeyRange =
KeyRangeRef("\xff\xff/management/failed_locality/"_sr, "\xff\xff/management/failed_locality0"_sr);
const KeyRef excludedForceOptionSpecialKey = "\xff\xff/management/options/excluded/force"_sr;
const KeyRef failedForceOptionSpecialKey = "\xff\xff/management/options/failed/force"_sr;
const KeyRef excludedLocalityForceOptionSpecialKey = "\xff\xff/management/options/excluded_locality/force"_sr;
const KeyRef failedLocalityForceOptionSpecialKey = "\xff\xff/management/options/failed_locality/force"_sr;
const KeyRangeRef exclusionInProgressSpecialKeyRange("\xff\xff/management/in_progress_exclusion/"_sr,
"\xff\xff/management/in_progress_exclusion0"_sr);
using management_api::excludedForceOptionSpecialKey;
using management_api::excludedLocalityForceOptionSpecialKey;
using management_api::excludedLocalitySpecialKeyRange;
using management_api::excludedServersSpecialKeyRange;
using management_api::exclusionInProgressSpecialKeyRange;
using management_api::failedForceOptionSpecialKey;
using management_api::failedLocalityForceOptionSpecialKey;
using management_api::failedLocalitySpecialKeyRange;
using management_api::failedServersSpecialKeyRange;
const KeyRef workerInterfacesVerifyOptionSpecialKey = "\xff\xff/management/options/worker_interfaces/verify"_sr;
} // namespace special_keys

View File

@ -436,32 +436,36 @@ Future<CDCStreamReadState> readCDCStreamState(Database cx,
tr.setOption(FDBTransactionOptions::READ_LOCK_AWARE);
tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
Future<Optional<Value>> keysFuture = tr.get(cdcStreamKeyFor(streamId));
Future<Optional<Value>> minVersionFuture = tr.get(cdcMinVersionKeyFor(streamId));
Future<RangeResult> assignedProxiesFuture = tr.getRange(cdcProxyRangeFor(streamId), 2);
KeyRange tagHistoryRange = cdcTagHistoryRangeFor(streamId);
Future<RangeResult> historyFuture = tr.getRange(tagHistoryRange, CLIENT_KNOBS->TOO_MANY);
CDCStreamReadState result;
Optional<Value> keysValue = co_await tr.get(cdcStreamKeyFor(streamId));
Optional<Value> keysValue = co_await keysFuture;
if (keysValue.present()) {
result.keys = decodeCDCStreamKeysValue(keysValue.get());
} else if (requireKeys) {
throw client_invalid_operation();
}
Optional<Value> minVersionValue = co_await tr.get(cdcMinVersionKeyFor(streamId));
Optional<Value> minVersionValue = co_await minVersionFuture;
if (!minVersionValue.present()) {
throw client_invalid_operation();
}
result.minVersion = decodeCDCMinVersionValue(minVersionValue.get());
RangeResult assignedProxies = co_await tr.getRange(cdcProxyRangeFor(streamId), 2);
RangeResult assignedProxies = co_await assignedProxiesFuture;
if (assignedProxies.size() != 1 || decodeCDCProxyKey(assignedProxies[0].key).second != expectedProxyId) {
CODE_PROBE(true, "CDC proxy rejects request for stream owned elsewhere", probe::decoration::rare);
throw wrong_shard_server();
}
std::vector<std::pair<Version, Tag>> tagAssignments;
KeyRange tagHistoryRange = cdcTagHistoryRangeFor(streamId);
Key begin = tagHistoryRange.begin;
while (begin < tagHistoryRange.end) {
RangeResult history =
co_await tr.getRange(KeyRangeRef(begin, tagHistoryRange.end), CLIENT_KNOBS->TOO_MANY);
RangeResult history = co_await historyFuture;
for (KeyValueRef const& kv : history) {
const CDCTagHistoryEntry historyEntry = decodeCDCTagHistoryKey(kv.key);
ASSERT_WE_THINK(historyEntry.streamId == streamId);
@ -472,6 +476,10 @@ Future<CDCStreamReadState> readCDCStreamState(Database cx,
break;
}
begin = keyAfter(history.back().key);
if (begin >= tagHistoryRange.end) {
break;
}
historyFuture = tr.getRange(KeyRangeRef(begin, tagHistoryRange.end), CLIENT_KNOBS->TOO_MANY);
}
if (tagAssignments.empty()) {
throw client_invalid_operation();

View File

@ -3566,6 +3566,56 @@ TEST_CASE("/fdbserver/clustercontroller/recoverForExcludedOldTLogLocality") {
return Void();
}
TEST_CASE("/fdbserver/clustercontroller/avoidSatelliteTLogRouterColocation") {
ClusterControllerData data(ClusterControllerFullInterface(),
LocalityData(),
ServerCoordinators(Reference<IClusterConnectionRecord>(
new ClusterConnectionMemoryRecord(ClusterConnectionString()))),
makeReference<AsyncVar<Optional<UID>>>());
data.goodRemoteRecruitmentTime = Void();
DatabaseConfiguration configuration;
configuration.tLogReplicationFactor = 1;
configuration.tLogPolicy = makeReference<PolicyAcross>(1, "zoneid", makeReference<PolicyOne>());
configuration.remoteTLogReplicationFactor = 1;
configuration.remoteDesiredTLogCount = 1;
configuration.remoteTLogPolicy = makeReference<PolicyAcross>(1, "zoneid", makeReference<PolicyOne>());
const Key remoteDcId = "remote"_sr;
auto addWorker = [&](std::string const& processId, ProcessClass::ClassType classType) {
LocalityData locality;
locality.set(LocalityData::keyProcessId, Standalone<StringRef>(processId));
locality.set(LocalityData::keyZoneId, Standalone<StringRef>(processId));
locality.set(LocalityData::keyDcId, remoteDcId);
WorkerInterface worker(locality);
worker.initEndpoints();
auto& workerInfo = data.id_worker[locality.processId()];
workerInfo.details.interf = worker;
workerInfo.details.processClass = ProcessClass(classType, ProcessClass::CommandLineSource);
workerInfo.details.recoveredDiskFiles = true;
return worker.id();
};
const UID satelliteA = addWorker("satellite-a", ProcessClass::UnsetClass);
const UID satelliteB = addWorker("satellite-b", ProcessClass::UnsetClass);
const UID remoteLog = addWorker("remote-log", ProcessClass::LogClass);
addWorker("router-a", ProcessClass::UnsetClass);
addWorker("router-b", ProcessClass::UnsetClass);
const std::vector<UID> satelliteWorkers{ satelliteA, satelliteB };
for (int attempt = 0; attempt < 16; ++attempt) {
RecruitRemoteFromConfigurationRequest req(configuration, remoteDcId, 2, satelliteWorkers);
auto result = data.findRemoteWorkersForConfiguration(req);
ASSERT_EQ(result.remoteTLogs.size(), 1);
ASSERT(result.remoteTLogs[0].id() == remoteLog);
ASSERT_EQ(result.logRouters.size(), 2);
for (const auto& router : result.logRouters) {
ASSERT(std::find(satelliteWorkers.begin(), satelliteWorkers.end(), router.id()) == satelliteWorkers.end());
}
}
return Void();
}
// Tests `ClusterControllerData::updateWorkerHealth()` can update `ClusterControllerData::workerHealth`
// based on `UpdateWorkerHealth` request correctly.
TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {

View File

@ -1690,6 +1690,14 @@ public:
std::map<Optional<Standalone<StringRef>>, int> id_used;
updateKnownIds(&id_used);
// Primary and satellite TLogs can share the remote DC. Account for their workers when placing log routers so
// recovery makes the same colocation choice that betterMasterExists() projects.
for (const auto& [processId, worker] : id_worker) {
if (std::find(req.exclusionWorkerIds.begin(), req.exclusionWorkerIds.end(), worker.details.interf.id()) !=
req.exclusionWorkerIds.end()) {
id_used[processId]++;
}
}
if (req.dbgId.present()) {
TraceEvent(SevDebug, "FindRemoteWorkersForConf", req.dbgId.get())

View File

@ -671,6 +671,46 @@ struct RolesInfo {
if (commitBatchingWindowSize.size()) {
obj["commit_batching_window_size"] = addLatencyStatistics(commitBatchingWindowSize);
}
TraceEventFields const& commitBatchTransactions = metrics.at("CommitBatchTransactions");
if (commitBatchTransactions.size()) {
obj["commit_batch_transactions"] = addLatencyStatistics(commitBatchTransactions);
}
TraceEventFields const& commitBatchBytes = metrics.at("CommitBatchBytes");
if (commitBatchBytes.size()) {
obj["commit_batch_bytes"] = addLatencyStatistics(commitBatchBytes);
}
TraceEventFields const& commitBatchingWaiting = metrics.at("CommitBatchingWaiting");
if (commitBatchingWaiting.size()) {
obj["commit_batching_waiting"] = addLatencyStatistics(commitBatchingWaiting);
}
TraceEventFields const& commitPreresolutionLatency = metrics.at("CommitPreresolutionLatency");
if (commitPreresolutionLatency.size()) {
obj["commit_preresolution_latency"] = addLatencyStatistics(commitPreresolutionLatency);
}
TraceEventFields const& commitResolutionLatency = metrics.at("CommitResolutionLatency");
if (commitResolutionLatency.size()) {
obj["commit_resolution_latency"] = addLatencyStatistics(commitResolutionLatency);
}
TraceEventFields const& commitPostresolutionLatency = metrics.at("CommitPostresolutionLatency");
if (commitPostresolutionLatency.size()) {
obj["commit_postresolution_latency"] = addLatencyStatistics(commitPostresolutionLatency);
}
TraceEventFields const& commitTLogLoggingLatency = metrics.at("CommitTLogLoggingLatency");
if (commitTLogLoggingLatency.size()) {
obj["commit_tlog_logging_latency"] = addLatencyStatistics(commitTLogLoggingLatency);
}
TraceEventFields const& commitReplyLatency = metrics.at("CommitReplyLatency");
if (commitReplyLatency.size()) {
obj["commit_reply_latency"] = addLatencyStatistics(commitReplyLatency);
}
} catch (Error& e) {
if (e.code() != error_code_attribute_not_found) {
throw e;
@ -1997,7 +2037,12 @@ static Future<std::vector<std::pair<TLogInterface, EventMap>>> getTLogsAndMetric
static Future<std::vector<std::pair<CommitProxyInterface, EventMap>>> getCommitProxiesAndMetrics(
Reference<AsyncVar<ServerDBInfo>> db,
std::unordered_map<NetworkAddress, WorkerInterface> address_workers) {
std::vector<std::string> eventNames{ "CommitLatencyMetrics", "CommitLatencyBands", "CommitBatchingWindowSize" };
std::vector<std::string> eventNames{
"CommitLatencyMetrics", "CommitLatencyBands", "CommitBatchingWindowSize",
"CommitBatchTransactions", "CommitBatchBytes", "CommitBatchingWaiting",
"CommitPreresolutionLatency", "CommitResolutionLatency", "CommitPostresolutionLatency",
"CommitTLogLoggingLatency", "CommitReplyLatency"
};
std::vector<std::pair<CommitProxyInterface, EventMap>> results =
co_await getServerMetrics(db->get().client.commitProxies, address_workers, std::move(eventNames));

View File

@ -112,38 +112,50 @@ struct ResolutionRequestBuilder {
return *out;
}
std::vector<int> getResolversForRange(const KeyRangeRef& range, Optional<Version> readSnapshot) const {
std::vector<int> resolvers;
resolvers.reserve(self->resolvers.size());
std::vector<unsigned char> seen(self->resolvers.size(), 0);
for (auto& intersectingRange : self->keyResolvers.intersectingRanges(range)) {
auto& versionResolvers = intersectingRange.value();
if (readSnapshot.present()) {
for (int i = versionResolvers.size() - 1; i >= 0; --i) {
const int resolver = versionResolvers[i].second;
if (!seen[resolver]) {
seen[resolver] = 1;
resolvers.push_back(resolver);
}
if (versionResolvers[i].first < readSnapshot.get()) {
break;
}
}
} else if (!versionResolvers.empty()) {
const int resolver = versionResolvers.back().second;
if (!seen[resolver]) {
seen[resolver] = 1;
resolvers.push_back(resolver);
}
}
}
if (SERVER_KNOBS->PROXY_USE_RESOLVER_PRIVATE_MUTATIONS && systemKeys.intersects(range)) {
resolvers.clear();
for (int resolver = 0; resolver < self->resolvers.size(); ++resolver) {
resolvers.push_back(resolver);
}
}
ASSERT(!resolvers.empty());
return resolvers;
}
// Returns a read conflict index map: [resolver_index][read_conflict_range_index_on_the_resolver]
// -> read_conflict_range's original index
std::vector<std::vector<int>> addReadConflictRanges(CommitTransactionRef& trIn) {
std::vector<std::vector<int>> rCRIndexMap(requests.size());
for (int idx = 0; idx < trIn.read_conflict_ranges.size(); ++idx) {
const auto& r = trIn.read_conflict_ranges[idx];
auto ranges = self->keyResolvers.intersectingRanges(r);
std::vector<int> resolvers;
resolvers.reserve(self->resolvers.size());
// O(1) de-dup keyed by resolver id (deterministic)
std::vector<unsigned char> seen(self->resolvers.size(), 0);
for (auto& ir : ranges) {
auto& version_resolver = ir.value();
for (int i = version_resolver.size() - 1; i >= 0; i--) {
const int resolver_id = version_resolver[i].second;
if (!seen[resolver_id]) {
seen[resolver_id] = 1;
resolvers.push_back(resolver_id);
}
if (version_resolver[i].first < trIn.read_snapshot)
break;
}
}
if (SERVER_KNOBS->PROXY_USE_RESOLVER_PRIVATE_MUTATIONS && systemKeys.intersects(r)) {
// All resolvers are eligible; skip per-id de-dup and just fill 0..N-1.
resolvers.clear();
for (int k = 0; k < self->resolvers.size(); ++k) {
resolvers.push_back(k);
}
}
ASSERT(!resolvers.empty());
for (int resolver : resolvers) {
for (int resolver : getResolversForRange(r, trIn.read_snapshot)) {
getOutTransaction(resolver, trIn.read_snapshot)
.read_conflict_ranges.push_back(requests[resolver].arena, r);
rCRIndexMap[resolver].push_back(idx);
@ -154,31 +166,10 @@ struct ResolutionRequestBuilder {
void addWriteConflictRanges(CommitTransactionRef& trIn) {
for (auto& r : trIn.write_conflict_ranges) {
auto ranges = self->keyResolvers.intersectingRanges(r);
std::vector<int> resolvers;
resolvers.reserve(self->resolvers.size());
std::vector<unsigned char> seen(self->resolvers.size(), 0);
for (auto& ir : ranges) {
auto& version_resolver = ir.value();
if (!version_resolver.empty()) {
const int resolver_id = version_resolver.back().second;
if (!seen[resolver_id]) {
seen[resolver_id] = 1;
resolvers.push_back(resolver_id);
}
}
}
if (SERVER_KNOBS->PROXY_USE_RESOLVER_PRIVATE_MUTATIONS && systemKeys.intersects(r)) {
// All resolvers are eligible.
resolvers.clear();
for (int k = 0; k < self->resolvers.size(); ++k) {
resolvers.push_back(k);
}
}
ASSERT(!resolvers.empty());
for (int resolver : resolvers)
for (int resolver : getResolversForRange(r, Optional<Version>())) {
getOutTransaction(resolver, trIn.read_snapshot)
.write_conflict_ranges.push_back(requests[resolver].arena, r);
}
}
}
@ -903,6 +894,7 @@ Future<Void> preresolutionProcessing(CommitBatchContext* self) {
r->value().emplace_back(versionReply.resolverChangesVersion, it.dest);
}
pProxyCommitData->stats.commitPreresolutionLatency.addMeasurement(g_network->timer_monotonic() - startTime);
//TraceEvent("ProxyGotVer", pProxyContext->dbgid).detail("Commit", commitVersion).detail("Prev", prevVersion);
if (debugID.present()) {
@ -992,7 +984,10 @@ Future<Void> getResolution(CommitBatchContext* self) {
self->resolution = std::move(resolutionResp);
}
self->pProxyCommitData->stats.resolutionDist->sampleSeconds(g_network->timer_monotonic() - resolutionStart);
double resolutionDuration = g_network->timer_monotonic() - resolutionStart;
self->pProxyCommitData->stats.commitResolutionLatency.addMeasurement(resolutionDuration);
self->pProxyCommitData->stats.resolutionDist->sampleSeconds(resolutionDuration);
if (self->debugID.present()) {
g_traceBatch.addEvent(
"CommitDebug", self->debugID.get().first(), "CommitProxyServer.commitBatch.AfterResolution");
@ -1829,7 +1824,10 @@ Future<Void> postResolution(CommitBatchContext* self) {
}
}
pProxyCommitData->stats.processingMutationDist->sampleSeconds(g_network->timer_monotonic() - postResolutionQueuing);
double postResolutionEnd = g_network->timer_monotonic();
pProxyCommitData->stats.commitPostresolutionLatency.addMeasurement(postResolutionEnd - postResolutionStart);
pProxyCommitData->stats.processingMutationDist->sampleSeconds(postResolutionEnd - postResolutionQueuing);
}
Future<Void> transactionLogging(CommitBatchContext* self) {
@ -1869,7 +1867,11 @@ Future<Void> transactionLogging(CommitBatchContext* self) {
pProxyCommitData->txsPopVersions.emplace_back(self->commitVersion, self->msg.popTo);
}
pProxyCommitData->logSystemConsumer->popTxs(self->msg.popTo);
pProxyCommitData->stats.tlogLoggingDist->sampleSeconds(g_network->timer_monotonic() - tLoggingStart);
double tLoggingDuration = g_network->timer_monotonic() - tLoggingStart;
pProxyCommitData->stats.commitTLogLoggingLatency.addMeasurement(tLoggingDuration);
pProxyCommitData->stats.tlogLoggingDist->sampleSeconds(tLoggingDuration);
}
Future<Void> reply(CommitBatchContext* self) {
@ -2011,6 +2013,7 @@ Future<Void> reply(CommitBatchContext* self) {
// TODO: filter if pipelined with large commit
const double duration = endTime - tr.requestTime();
pProxyCommitData->stats.commitLatencySample.addMeasurement(duration);
pProxyCommitData->stats.commitBatchingWaiting.addMeasurement(self->startTime - tr.requestTime());
if (pProxyCommitData->latencyBandConfig.present()) {
bool filter = self->maxTransactionBytes >
pProxyCommitData->latencyBandConfig.get().commitConfig.maxCommitBytes.orDefault(
@ -2071,7 +2074,10 @@ Future<Void> reply(CommitBatchContext* self) {
pProxyCommitData->commitBatchesMemBytesCount -= self->currentBatchMemBytesCount;
ASSERT_ABORT(pProxyCommitData->commitBatchesMemBytesCount >= 0);
co_await self->releaseFuture;
pProxyCommitData->stats.replyCommitDist->sampleSeconds(g_network->timer_monotonic() - replyStart);
double replyDuration = g_network->timer_monotonic() - replyStart;
pProxyCommitData->stats.commitReplyLatency.addMeasurement(replyDuration);
pProxyCommitData->stats.replyCommitDist->sampleSeconds(replyDuration);
}
// Commit one batch of transactions trs
@ -2089,6 +2095,8 @@ Future<Void> commitBatchImpl(CommitBatchContext* pContext) {
pContext->pProxyCommitData->lastVersionTime = pContext->startTime;
++pContext->pProxyCommitData->stats.commitBatchIn;
pContext->setupTraceBatch();
pContext->pProxyCommitData->stats.commitBatchBytes.addMeasurement(pContext->currentBatchMemBytesCount);
pContext->pProxyCommitData->stats.commitBatchTransactions.addMeasurement(pContext->trs.size());
/////// Phase 1: Pre-resolution processing (CPU bound except waiting for a version # which is separately pipelined
/// and *should* be available by now (unless empty commit); ordered; currently atomic but could yield)

View File

@ -89,6 +89,19 @@ struct ProxyStats {
LatencySample commitBatchingWindowSize;
// Number of transactions in the batch
LatencySample commitBatchTransactions;
// Summary length of transactions in the batch
LatencySample commitBatchBytes;
// what time transactions were waiting in the batch before they
// started processing with commitBatch()
LatencySample commitBatchingWaiting;
LatencySample commitPreresolutionLatency;
LatencySample commitResolutionLatency;
LatencySample commitPostresolutionLatency;
LatencySample commitTLogLoggingLatency;
LatencySample commitReplyLatency;
LatencySample computeLatency;
Future<Void> logger;
@ -183,6 +196,38 @@ struct ProxyStats {
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY),
commitBatchTransactions("CommitBatchTransactions",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY),
commitBatchBytes("CommitBatchBytes",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY),
commitBatchingWaiting("CommitBatchingWaiting",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY),
commitPreresolutionLatency("CommitPreresolutionLatency",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY),
commitResolutionLatency("CommitResolutionLatency",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY),
commitPostresolutionLatency("CommitPostresolutionLatency",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY),
commitTLogLoggingLatency("CommitTLogLoggingLatency",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY),
commitReplyLatency("CommitReplyLatency",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY),
computeLatency("ComputeLatency",
id,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,

View File

@ -0,0 +1,57 @@
/*
* BusyTagCollector.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2026 Apple Inc. and the FoundationDB project authors
*
* 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.
*/
#pragma once
#include <functional>
#include <queue>
#include <vector>
#include "fdbclient/StorageServerInterface.h"
class BusyTagCollector {
std::priority_queue<BusyTagInfo, std::vector<BusyTagInfo>, std::greater<BusyTagInfo>> busiestTags;
size_t maxTagsTracked;
double minRateTracked;
public:
BusyTagCollector(int maxTagsTracked, double minRateTracked)
: maxTagsTracked(maxTagsTracked), minRateTracked(minRateTracked) {}
bool add(const TransactionTag& tag, double rate, double fractionalBusyness) {
if (rate < minRateTracked) {
return false;
}
if (busiestTags.size() < maxTagsTracked) {
busiestTags.emplace(tag, rate, fractionalBusyness);
} else if (busiestTags.top().rate < rate) {
busiestTags.pop();
busiestTags.emplace(tag, rate, fractionalBusyness);
}
return true;
}
void drainInto(std::vector<BusyTagInfo>& result) {
while (!busiestTags.empty()) {
result.push_back(busiestTags.top());
busiestTags.pop();
}
}
};

View File

@ -19,6 +19,7 @@
*/
#include <climits>
#include <unordered_set>
#include "fdbclient/SystemData.h"
#include "fdbrpc/simulator.h"
@ -759,43 +760,19 @@ public:
for (; idx < largeOrBadTeams.size(); idx++) {
servers.clear();
serverIds.clear();
for (const auto& server : largeOrBadTeams[idx]->getServers()) {
if (server->isInDesiredDC() && !self->server_status.get(server->getId()).isUnhealthy()) {
servers.push_back(server);
serverIds.push_back(server->getId());
}
}
// For the bad team that is too big (too many servers), we will try to find a subset of servers in the
// team to construct a new healthy team, so that moving data to the new healthy team will not cause too
// much data movement overhead
// FIXME: This code logic can be simplified.
if (servers.size() >= self->configuration.storageTeamSize) {
bool foundTeam = false;
for (int j = 0; j < servers.size() - self->configuration.storageTeamSize + 1 && !foundTeam; j++) {
auto const& serverTeams = servers[j]->getTeams();
for (int k = 0; k < serverTeams.size(); k++) {
auto& testTeam = serverTeams[k]->getServerIDs();
bool allInTeam = true; // All servers in testTeam belong to the healthy servers
for (int l = 0; l < testTeam.size(); l++) {
bool foundServer = false;
for (auto it : servers) {
if (it->getId() == testTeam[l]) {
foundServer = true;
break;
}
}
if (!foundServer) {
allInTeam = false;
break;
}
}
if (allInTeam) {
foundTeam = true;
break;
}
}
}
if (!foundTeam) {
if (!self->findTeamFromServers(serverIds, /*wantHealthy=*/false).present()) {
if (self->satisfiesPolicy(servers)) {
if (servers.size() == self->configuration.storageTeamSize ||
self->satisfiesPolicy(servers, self->configuration.storageTeamSize)) {
@ -1025,7 +1002,8 @@ public:
static Future<Void> teamTracker(DDTeamCollection* self,
Reference<TCTeamInfo> team,
IsBadTeam badTeam,
IsRedundantTeam redundantTeam) {
IsRedundantTeam redundantTeam,
double checkTeamDelay) {
int lastServersLeft = team->size();
bool lastAnyUndesired = false;
bool lastAnyWigglingServer = false;
@ -1035,9 +1013,12 @@ public:
bool lastHealthy{ false };
bool lastOptimal{ false };
bool lastWrongConfiguration = team->isWrongConfiguration();
bool lastContainsFailed = false;
bool trackHealthyTeam = team->size() == self->configuration.storageTeamSize;
bool lastZeroHealthy = self->zeroHealthyTeams->get();
bool firstCheck = true;
bool firstHealthChangeTrace = true;
std::unordered_set<KeyRange> submittedShards;
Future<Void> zeroServerLeftLogger;
@ -1050,14 +1031,6 @@ public:
try {
while (true) {
if (logTeamEvents) {
TraceEvent("ServerTeamHealthChangeDetected", self->distributorId)
.detail("ServerTeam", team->getDesc())
.detail("Primary", self->primary)
.detail("IsReady", self->initialFailureReactionDelay.isReady());
self->traceTeamCollectionInfo();
}
// Check if the number of degraded machines has changed
std::vector<Future<Void>> change;
bool anyUndesired = false;
@ -1099,14 +1072,44 @@ public:
if (!self->initialFailureReactionDelay.isReady()) {
change.push_back(self->initialFailureReactionDelay);
}
if (!badTeam) {
// SS failure suppression changes effective team health without changing server status.
change.push_back(self->healthyZone.onChange());
}
change.push_back(self->zeroHealthyTeams->onChange());
bool healthy = !badTeam && !anyUndesired && serversLeft == team->size();
team->setHealthy(healthy); // Unhealthy teams won't be chosen by bestTeam
bool optimal = team->isOptimal() && healthy;
bool containsFailed = self->teamContainsFailedServer(team);
bool retryUnhealthyShards =
!healthy && self->shardsAffectedByTeamFailure->hasShards(
ShardsAffectedByTeamFailure::Team(team->getServerIDs(), self->primary));
if (retryUnhealthyShards) {
change.push_back(self->processingUnhealthy->onChange());
change.push_back(self->pipelineFull->onChange());
// Partial moves can leave a merged shard associated with this team without another health change.
change.push_back(delay(checkTeamDelay, TaskPriority::DataDistributionLow));
}
const bool healthyTeamBecameAvailable = lastZeroHealthy && !self->zeroHealthyTeams->get();
const bool processingUnhealthy = self->processingUnhealthy->get();
const bool pipelineFull = self->pipelineFull->get();
bool recheck = !healthy && (lastReady != self->initialFailureReactionDelay.isReady() ||
(lastZeroHealthy && !self->zeroHealthyTeams->get()) || containsFailed);
healthyTeamBecameAvailable || containsFailed || retryUnhealthyShards);
bool teamStateChanged = serversLeft != lastServersLeft || anyUndesired != lastAnyUndesired ||
anyWrongConfiguration != lastWrongConfiguration ||
anyWigglingServer != lastAnyWigglingServer ||
containsFailed != lastContainsFailed;
// A retry timer can wake many trackers at once; avoid collection-wide scans when team state is
// unchanged.
if (logTeamEvents && (firstHealthChangeTrace || teamStateChanged)) {
TraceEvent("ServerTeamHealthChangeDetected", self->distributorId)
.detail("ServerTeam", team->getDesc())
.detail("Primary", self->primary)
.detail("IsReady", self->initialFailureReactionDelay.isReady());
self->traceTeamCollectionInfo();
}
firstHealthChangeTrace = false;
TraceEvent(SevVerbose, "TeamHealthChangeDetected", self->distributorId)
.detail("Team", team->getDesc())
@ -1140,9 +1143,7 @@ public:
lastOptimal = optimal;
}
if (serversLeft != lastServersLeft || anyUndesired != lastAnyUndesired ||
anyWrongConfiguration != lastWrongConfiguration || anyWigglingServer != lastAnyWigglingServer ||
recheck) { // NOTE: do not check wrongSize
if (teamStateChanged || recheck) { // NOTE: do not check wrongSize
if (logTeamEvents) {
TraceEvent("ServerTeamHealthChanged", self->distributorId)
.suppressFor(1.0)
@ -1196,6 +1197,7 @@ public:
lastAnyUndesired = anyUndesired;
lastWrongConfiguration = anyWrongConfiguration;
lastAnyWigglingServer = anyWigglingServer;
lastContainsFailed = containsFailed;
int lastPriority = team->getPriority();
if (team->size() == 0) {
@ -1257,6 +1259,24 @@ public:
std::vector<KeyRange> shards = self->shardsAffectedByTeamFailure->getShardsFor(
ShardsAffectedByTeamFailure::Team(team->getServerIDs(), self->primary));
if (teamStateChanged || !retryUnhealthyShards || healthyTeamBecameAvailable ||
(!processingUnhealthy && !pipelineFull)) {
// Undesired and explicitly failed relocations do not set processingUnhealthy. Keep
// retrying stranded ranges when the queue is idle and its pipeline can accept them.
submittedShards.clear();
} else {
// An unchanged range may still be waiting behind the relocation pipeline gate. Only retry
// newly mapped ranges while unhealthy relocations remain in the queue, and forget ranges
// that disappeared.
std::unordered_set<KeyRange> mappedShards(shards.begin(), shards.end());
for (auto it = submittedShards.begin(); it != submittedShards.end();) {
if (!mappedShards.contains(*it)) {
it = submittedShards.erase(it);
} else {
++it;
}
}
}
TraceEvent(SevVerbose, "ServerTeamRelocatingShards", self->distributorId)
.detail("Info", team->getDesc())
@ -1264,6 +1284,9 @@ public:
.detail("Shards", shards.size());
for (int i = 0; i < shards.size(); i++) {
if (retryUnhealthyShards && !submittedShards.insert(shards[i]).second) {
continue;
}
// Make it high priority to move keys off failed server or else RelocateShards may never be
// addressed
int maxPriority = containsFailed ? SERVER_KNOBS->PRIORITY_TEAM_FAILED : team->getPriority();
@ -4215,8 +4238,9 @@ Future<Void> DDTeamCollection::buildTeams() {
Future<Void> DDTeamCollection::teamTracker(Reference<TCTeamInfo> team,
IsBadTeam isBadTeam,
IsRedundantTeam isRedundantTeam) {
return DDTeamCollectionImpl::teamTracker(this, team, isBadTeam, isRedundantTeam);
IsRedundantTeam isRedundantTeam,
double checkTeamDelay) {
return DDTeamCollectionImpl::teamTracker(this, team, isBadTeam, isRedundantTeam, checkTeamDelay);
}
Future<Void> DDTeamCollection::storageServerTracker(TCServerInfo* server,
@ -4537,7 +4561,8 @@ DDTeamCollection::DDTeamCollection(DDTeamCollectionInitParams const& params)
restartRecruiting(SERVER_KNOBS->DEBOUNCE_RECRUITING_DELAY), healthyTeamCount(0),
zeroHealthyTeams(params.zeroHealthyTeams), optimalTeamCount(0), zeroOptimalTeams(true), isTssRecruiting(false),
includedDCs(params.includedDCs), otherTrackedDCs(params.otherTrackedDCs),
processingUnhealthy(params.processingUnhealthy), getAverageShardBytes(params.getAverageShardBytes),
processingUnhealthy(params.processingUnhealthy), pipelineFull(params.pipelineFull),
getAverageShardBytes(params.getAverageShardBytes),
triggerStorageQueueRebalance(params.triggerStorageQueueRebalance), readyToStart(params.readyToStart),
checkTeamDelay(delay(SERVER_KNOBS->CHECK_TEAM_DELAY, TaskPriority::DataDistribution)), badTeamRemover(Void()),
checkInvalidLocalities(Void()), wrongStoreTypeRemover(Void()), clearHealthyZoneFuture(true),
@ -5001,14 +5026,15 @@ void DDTeamCollection::addTeam(std::set<UID> const& team, IsInitialTeam isInitia
void DDTeamCollection::addTeam(const std::vector<Reference<TCServerInfo>>& newTeamServers,
IsInitialTeam isInitialTeam,
IsRedundantTeam redundantTeam) {
IsRedundantTeam redundantTeam,
double checkTeamDelay) {
auto teamInfo = makeReference<TCTeamInfo>(newTeamServers);
// Move satisfiesPolicy to the end for performance benefit
auto badTeam = IsBadTeam{ redundantTeam || !satisfiesPolicy(teamInfo->getServers()) ||
(!ddLargeTeamEnabled() && teamInfo->size() != configuration.storageTeamSize) };
teamInfo->tracker = teamTracker(teamInfo, badTeam, redundantTeam);
teamInfo->tracker = teamTracker(teamInfo, badTeam, redundantTeam, checkTeamDelay);
// ASSERT( teamInfo->serverIDs.size() > 0 ); //team can be empty at DB initialization
if (badTeam) {
badTeams.push_back(teamInfo);
@ -7177,6 +7203,205 @@ public:
recruitment.cancel();
co_await delay(0);
}
static Future<Void> TeamTracker_RetriesMergedShardForUndesiredServer() {
constexpr double checkTeamDelay = 0.05;
auto shards = makeReference<ShardsAffectedByTeamFailure>();
shards->setCheckMode(ShardsAffectedByTeamFailure::CheckMode::ForceCheck);
const UID undesired(1, 0), leftServer(2, 0), rightServer(3, 0), healthy1(4, 0), healthy2(5, 0);
const ShardsAffectedByTeamFailure::Team left({ undesired, leftServer }, true);
const ShardsAffectedByTeamFailure::Team right({ undesired, rightServer }, true);
const ShardsAffectedByTeamFailure::Team healthy({ healthy1, healthy2 }, true);
const KeyRange mergedRange = KeyRangeRef("a"_sr, "c"_sr);
const KeyRange leftRange = KeyRangeRef("a"_sr, "b"_sr);
const KeyRange rightRange = KeyRangeRef("b"_sr, "c"_sr);
shards->assignRangeToTeams(leftRange, { left });
shards->assignRangeToTeams(rightRange, { right });
Reference<IReplicationPolicy> policy = makeReference<PolicyAcross>(2, "zoneid", makeReference<PolicyOne>());
auto collection = testTeamCollection(2, policy, 5, shards);
collection->teamCollections = { collection.get() };
collection->initialFailureReactionDelay = Future<Void>(Void());
ASSERT(!collection->processingUnhealthy->get());
collection->teamCollectionInfoEventHolder =
makeReference<EventCacheHolder>("TeamTrackerRetriesMergedShardForUndesiredServer");
collection->server_status.set(
undesired,
ServerStatus(IsFailed::False,
IsUndesired::True,
IsWiggling::False,
collection->server_info[undesired]->getLastKnownInterface().locality));
FutureStream<RelocateShard> relocations = collection->output.getFuture();
const int processingUnhealthyListenersWithoutTrackers =
collection->processingUnhealthy->onChange().getFutureReferenceCount();
const int pipelineFullListenersWithoutTrackers = collection->pipelineFull->onChange().getFutureReferenceCount();
collection->addTeam({ collection->server_info[healthy1], collection->server_info[healthy2] },
IsInitialTeam::True,
IsRedundantTeam::False,
checkTeamDelay);
co_await delay(0.01);
ASSERT_EQ(collection->processingUnhealthy->onChange().getFutureReferenceCount(),
processingUnhealthyListenersWithoutTrackers);
ASSERT_EQ(collection->pipelineFull->onChange().getFutureReferenceCount(), pipelineFullListenersWithoutTrackers);
collection->addTeam({ collection->server_info[undesired], collection->server_info[leftServer] },
IsInitialTeam::True,
IsRedundantTeam::False,
checkTeamDelay);
collection->addTeam({ collection->server_info[undesired], collection->server_info[rightServer] },
IsInitialTeam::True,
IsRedundantTeam::False,
checkTeamDelay);
co_await delay(0.01);
ASSERT(relocations.isReady());
RelocateShard initialLeft = relocations.pop();
ASSERT(relocations.isReady());
RelocateShard initialRight = relocations.pop();
ASSERT(!relocations.isReady());
ASSERT((initialLeft.keys == leftRange && initialRight.keys == rightRange) ||
(initialLeft.keys == rightRange && initialRight.keys == leftRange));
TraceEventFields initialTeamCollectionInfo =
latestEventCache.get(collection->teamCollectionInfoEventHolder->trackingKey);
ASSERT_GT(initialTeamCollectionInfo.size(), 0);
const std::string initialTeamCollectionInfoTime = initialTeamCollectionInfo.getValue("Time");
shards->defineShard(mergedRange);
shards->moveShard(leftRange, { healthy });
shards->finishMove(leftRange);
shards->moveShard(rightRange, { healthy });
shards->finishMove(rightRange);
ASSERT_EQ(shards->getNumberOfShards(undesired), 2);
co_await delay(checkTeamDelay + 0.01);
ASSERT(relocations.isReady());
RelocateShard retryLeft = relocations.pop();
ASSERT(relocations.isReady());
RelocateShard retryRight = relocations.pop();
ASSERT(!relocations.isReady());
ASSERT(retryLeft.keys == mergedRange);
ASSERT(retryRight.keys == mergedRange);
ASSERT_EQ(retryLeft.priority, SERVER_KNOBS->PRIORITY_TEAM_CONTAINS_UNDESIRED_SERVER);
ASSERT_EQ(retryRight.priority, SERVER_KNOBS->PRIORITY_TEAM_CONTAINS_UNDESIRED_SERVER);
// Undesired relocations never make processingUnhealthy true, so unchanged stranded ranges
// must still be retried at the next polling interval.
co_await delay(checkTeamDelay + 0.01);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().keys, mergedRange);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().keys, mergedRange);
ASSERT(!relocations.isReady());
ASSERT(!collection->processingUnhealthy->get());
// A full pipeline must retain submitted ranges even when undesired relocations are not
// reflected in processingUnhealthy; otherwise every retry interval grows its input backlog.
collection->pipelineFull->set(true);
co_await delay(2 * checkTeamDelay + 0.01);
ASSERT(!relocations.isReady());
collection->pipelineFull->set(false);
co_await delay(0.01);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().keys, mergedRange);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().keys, mergedRange);
ASSERT(!relocations.isReady());
collection->processingUnhealthy->set(true);
co_await delay(checkTeamDelay + 0.01);
ASSERT(!relocations.isReady());
ASSERT_EQ(latestEventCache.get(collection->teamCollectionInfoEventHolder->trackingKey).getValue("Time"),
initialTeamCollectionInfoTime);
collection->processingUnhealthy->set(false);
co_await delay(0.01);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().keys, mergedRange);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().keys, mergedRange);
ASSERT(!relocations.isReady());
collection->processingUnhealthy->set(true);
co_await delay(checkTeamDelay + 0.01);
ASSERT(!relocations.isReady());
collection->zeroHealthyTeams->set(true);
co_await delay(0.01);
ASSERT(!relocations.isReady());
collection->zeroHealthyTeams->set(false);
co_await delay(0.01);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().keys, mergedRange);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().keys, mergedRange);
ASSERT(!relocations.isReady());
co_await delay(checkTeamDelay + 0.01);
ASSERT(!relocations.isReady());
NetworkAddress failedAddress = collection->server_info[undesired]->getLastKnownInterface().address();
collection->excludedServers.set(AddressExclusion(failedAddress.ip, failedAddress.port),
DDTeamCollection::Status::FAILED);
co_await delay(checkTeamDelay + 0.01);
ASSERT(relocations.isReady());
RelocateShard failedLeft = relocations.pop();
ASSERT(relocations.isReady());
RelocateShard failedRight = relocations.pop();
ASSERT(!relocations.isReady());
ASSERT(failedLeft.keys == mergedRange);
ASSERT(failedRight.keys == mergedRange);
ASSERT_EQ(failedLeft.priority, SERVER_KNOBS->PRIORITY_TEAM_FAILED);
ASSERT_EQ(failedRight.priority, SERVER_KNOBS->PRIORITY_TEAM_FAILED);
// Explicitly failed-team relocations are also absent from processingUnhealthy and must remain
// retryable after the tracked unhealthy queue is idle.
collection->processingUnhealthy->set(false);
co_await delay(0.01);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().priority, SERVER_KNOBS->PRIORITY_TEAM_FAILED);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().priority, SERVER_KNOBS->PRIORITY_TEAM_FAILED);
ASSERT(!relocations.isReady());
co_await delay(checkTeamDelay + 0.01);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().priority, SERVER_KNOBS->PRIORITY_TEAM_FAILED);
ASSERT(relocations.isReady());
ASSERT_EQ(relocations.pop().priority, SERVER_KNOBS->PRIORITY_TEAM_FAILED);
ASSERT(!relocations.isReady());
ASSERT_NE(latestEventCache.get(collection->teamCollectionInfoEventHolder->trackingKey).getValue("Time"),
initialTeamCollectionInfoTime);
}
static Future<Void> TeamTracker_RechecksHealthyZone() {
Reference<IReplicationPolicy> policy = makeReference<PolicyAcross>(3, "zoneid", makeReference<PolicyOne>());
auto collection = testTeamCollection(3, policy, 3);
const UID failedServer(1, 0);
collection->healthyZone.set(ignoreSSFailuresZoneString);
collection->server_status.set(
failedServer,
ServerStatus(IsFailed::True,
IsUndesired::False,
IsWiggling::False,
collection->server_info[failedServer]->getLastKnownInterface().locality));
collection->addTeam(std::set<UID>({ failedServer, UID(2, 0), UID(3, 0) }), IsInitialTeam::True);
co_await delay(0.1);
ASSERT_EQ(collection->teams.size(), 1);
ASSERT(collection->teams.front()->isHealthy());
ASSERT_EQ(collection->teams.front()->getPriority(), SERVER_KNOBS->PRIORITY_TEAM_HEALTHY);
collection->healthyZone.set(Optional<Key>());
co_await delay(0.1);
ASSERT(!collection->teams.front()->isHealthy());
ASSERT_EQ(collection->teams.front()->getPriority(), SERVER_KNOBS->PRIORITY_TEAM_2_LEFT);
}
};
TEST_CASE("DataDistribution/AddTeamsBestOf/UseMachineID") {
@ -7340,3 +7565,13 @@ TEST_CASE("/DataDistribution/Recruitment/RecruitmentFailedCooldownReleasesId") {
wait(DDTeamCollectionUnitTest::InitializeStorage_RecruitmentFailedCooldownReleasesId());
return Void();
}
TEST_CASE("/DataDistribution/TeamTracker/RetriesMergedShardForUndesiredServer") {
wait(DDTeamCollectionUnitTest::TeamTracker_RetriesMergedShardForUndesiredServer());
return Void();
}
TEST_CASE("/DataDistribution/TeamTracker/RechecksHealthyZone") {
wait(DDTeamCollectionUnitTest::TeamTracker_RechecksHealthyZone());
return Void();
}

View File

@ -205,6 +205,7 @@ struct DDTeamCollectionInitParams {
PromiseStream<Promise<int64_t>> getAverageShardBytes;
PromiseStream<RebalanceStorageQueueRequest> triggerStorageQueueRebalance;
Reference<BulkLoadTaskCollection> bulkLoadTaskCollection;
Reference<AsyncVar<bool>> pipelineFull = makeReference<AsyncVar<bool>>(false);
};
class DDTeamCollection : public ReferenceCounted<DDTeamCollection> {
@ -275,6 +276,7 @@ protected:
std::vector<Optional<Key>> includedDCs;
Optional<std::vector<Optional<Key>>> otherTrackedDCs;
Reference<AsyncVar<bool>> processingUnhealthy;
Reference<AsyncVar<bool>> pipelineFull;
Future<Void> readyToStart;
Future<Void> checkTeamDelay;
// A map of teamSize to first failure time
@ -469,7 +471,10 @@ protected:
// Track a team and issue RelocateShards when the level of degradation changes
// A bad team can be unhealthy or just a redundant team removed by machineTeamRemover() or serverTeamRemover()
Future<Void> teamTracker(Reference<TCTeamInfo> team, IsBadTeam, IsRedundantTeam);
Future<Void> teamTracker(Reference<TCTeamInfo> team,
IsBadTeam,
IsRedundantTeam,
double checkTeamDelay = SERVER_KNOBS->CHECK_TEAM_DELAY);
// Check the status of a storage server.
// Apply all requirements to the server and mark it as excluded if it fails to satisfies these requirements
@ -636,7 +641,8 @@ protected:
void addTeam(const std::vector<Reference<TCServerInfo>>& newTeamServers,
IsInitialTeam,
IsRedundantTeam = IsRedundantTeam::False);
IsRedundantTeam = IsRedundantTeam::False,
double checkTeamDelay = SERVER_KNOBS->CHECK_TEAM_DELAY);
void addTeam(std::set<UID> const& team, IsInitialTeam isInitialTeam);

View File

@ -3046,7 +3046,8 @@ Future<Void> dataDistribution(Reference<DataDistributor> self,
getUnhealthyRelocationCount,
getAverageShardBytes,
triggerStorageQueueRebalance,
self->bulkLoadTaskCollection });
self->bulkLoadTaskCollection,
self->context->ddQueue->pipelineFull });
teamCollectionsPtrs.push_back(self->context->primaryTeamCollection.getPtr());
Reference<IAsyncListener<RequestStream<RecruitStorageRequest>>> recruitStorage =
IAsyncListener<RequestStream<RecruitStorageRequest>>::create(
@ -3071,7 +3072,8 @@ Future<Void> dataDistribution(Reference<DataDistributor> self,
getUnhealthyRelocationCount,
getAverageShardBytes,
triggerStorageQueueRebalance,
self->bulkLoadTaskCollection });
self->bulkLoadTaskCollection,
self->context->ddQueue->pipelineFull });
teamCollectionsPtrs.push_back(self->context->remoteTeamCollection.getPtr());
self->context->remoteTeamCollection->teamCollections = teamCollectionsPtrs;
actors.push_back(reportErrorsExcept(DDTeamCollection::run(self->context->remoteTeamCollection,

View File

@ -129,6 +129,42 @@ TEST_CASE("/DataDistributor/ShardsAffectedByTeamFailure/DestinationSourceTransit
return Void();
}
TEST_CASE("/DataDistributor/ShardsAffectedByTeamFailure/RetryMergedShardAfterPartialMoves") {
ShardsAffectedByTeamFailure shards;
shards.setCheckMode(ShardsAffectedByTeamFailure::CheckMode::ForceCheck);
const UID failed(1, 0), leftServer(2, 0), rightServer(3, 0), healthy1(4, 0), healthy2(5, 0);
const ShardsAffectedByTeamFailure::Team left({ failed, leftServer }, true);
const ShardsAffectedByTeamFailure::Team right({ failed, rightServer }, true);
const ShardsAffectedByTeamFailure::Team healthy({ healthy1, healthy2 }, true);
const KeyRange mergedRange = KeyRangeRef("a"_sr, "c"_sr);
const KeyRange leftRange = KeyRangeRef("a"_sr, "b"_sr);
const KeyRange rightRange = KeyRangeRef("b"_sr, "c"_sr);
shards.assignRangeToTeams(leftRange, { left });
shards.assignRangeToTeams(rightRange, { right });
shards.defineShard(mergedRange);
shards.moveShard(leftRange, { healthy });
shards.finishMove(leftRange);
shards.moveShard(rightRange, { healthy });
shards.finishMove(rightRange);
ASSERT_EQ(shards.getNumberOfShards(failed), 2);
ASSERT(shards.getShardsFor(left) == std::vector<KeyRange>{ mergedRange });
ASSERT(shards.getShardsFor(right) == std::vector<KeyRange>{ mergedRange });
shards.moveShard(mergedRange, { healthy });
shards.finishMove(mergedRange);
ASSERT_EQ(shards.getNumberOfShards(failed), 0);
ASSERT_EQ(shards.getNumberOfShards(left), 0);
ASSERT_EQ(shards.getNumberOfShards(right), 0);
ASSERT(shards.getShardsFor(healthy) == std::vector<KeyRange>{ mergedRange });
return Void();
}
TEST_CASE("/DataDistributor/ShardsAffectedByTeamFailure/CancelMove") {
ShardsAffectedByTeamFailure shards;
shards.setCheckMode(ShardsAffectedByTeamFailure::CheckMode::ForceCheck);

View File

@ -21,6 +21,7 @@
#include <algorithm>
#include "fdbclient/Knobs.h"
#include "fdbserver/core/BusyTagCollector.h"
#include "fdbserver/core/Knobs.h"
#include "fdbserver/core/ServerDBInfo.h"
#include "fdbserver/core/WaitFailure.h"
@ -1163,16 +1164,41 @@ Future<Void> ratekeeper(RatekeeperInterface rkInterf, Reference<AsyncVar<ServerD
co_await Ratekeeper::run(rkInterf, dbInfo);
}
StorageQueueInfo::StorageQueueInfo(const UID& ratekeeperID_, const UID& id_, const LocalityData& locality_)
: valid(false), ratekeeperID(ratekeeperID_), id(id_), locality(locality_), acceptingRequests(false),
smoothDurableBytes(SERVER_KNOBS->SMOOTHING_AMOUNT), smoothInputBytes(SERVER_KNOBS->SMOOTHING_AMOUNT),
verySmoothDurableBytes(SERVER_KNOBS->SLOW_SMOOTHING_AMOUNT), smoothDurableVersion(SERVER_KNOBS->SMOOTHING_AMOUNT),
smoothLatestVersion(SERVER_KNOBS->SMOOTHING_AMOUNT), smoothFreeSpace(SERVER_KNOBS->SMOOTHING_AMOUNT),
smoothTotalSpace(SERVER_KNOBS->SMOOTHING_AMOUNT), limitReason(limitReason_t::unlimited) {
// FIXME: this is a tacky workaround for a potential uninitialized use in trackStorageServerQueueInfo
lastReply.instanceID = -1;
QueueMetricsSmoother::QueueMetricsSmoother()
: smoothDurableBytes(SERVER_KNOBS->SMOOTHING_AMOUNT), smoothInputBytes(SERVER_KNOBS->SMOOTHING_AMOUNT),
verySmoothDurableBytes(SERVER_KNOBS->SLOW_SMOOTHING_AMOUNT), smoothFreeSpace(SERVER_KNOBS->SMOOTHING_AMOUNT),
smoothTotalSpace(SERVER_KNOBS->SMOOTHING_AMOUNT) {}
bool QueueMetricsSmoother::update(int64_t newInstanceID,
int64_t newDurableBytes,
int64_t inputBytes,
const StorageBytes& storageBytes,
Smoother& smoothTotalDurableBytes) {
const bool reset = !instanceID.present() || instanceID.get() != newInstanceID;
if (reset) {
smoothDurableBytes.reset(newDurableBytes);
verySmoothDurableBytes.reset(newDurableBytes);
smoothInputBytes.reset(inputBytes);
smoothFreeSpace.reset(storageBytes.available);
smoothTotalSpace.reset(storageBytes.total);
} else {
smoothTotalDurableBytes.addDelta(newDurableBytes - durableBytes);
smoothDurableBytes.setTotal(newDurableBytes);
verySmoothDurableBytes.setTotal(newDurableBytes);
smoothInputBytes.setTotal(inputBytes);
smoothFreeSpace.setTotal(storageBytes.available);
smoothTotalSpace.setTotal(storageBytes.total);
}
instanceID = newInstanceID;
durableBytes = newDurableBytes;
return reset;
}
StorageQueueInfo::StorageQueueInfo(const UID& ratekeeperID_, const UID& id_, const LocalityData& locality_)
: ratekeeperID(ratekeeperID_), smoothDurableVersion(SERVER_KNOBS->SMOOTHING_AMOUNT),
smoothLatestVersion(SERVER_KNOBS->SMOOTHING_AMOUNT), valid(false), id(id_), locality(locality_),
acceptingRequests(false), limitReason(limitReason_t::unlimited) {}
StorageQueueInfo::StorageQueueInfo(const UID& id_, const LocalityData& locality_)
: StorageQueueInfo(UID(), id_, locality_) {}
@ -1184,23 +1210,12 @@ void StorageQueueInfo::addCommitCost(TransactionTagRef tagName, TransactionCommi
void StorageQueueInfo::update(StorageQueuingMetricsReply const& reply, Smoother& smoothTotalDurableBytes) {
valid = true;
auto prevReply = std::move(lastReply);
lastReply = reply;
if (prevReply.instanceID != reply.instanceID) {
smoothDurableBytes.reset(reply.bytesDurable);
verySmoothDurableBytes.reset(reply.bytesDurable);
smoothInputBytes.reset(reply.bytesInput);
smoothFreeSpace.reset(reply.storageBytes.available);
smoothTotalSpace.reset(reply.storageBytes.total);
if (queueMetrics.update(
reply.instanceID, reply.bytesDurable, reply.bytesInput, reply.storageBytes, smoothTotalDurableBytes)) {
smoothDurableVersion.reset(reply.durableVersion);
smoothLatestVersion.reset(reply.version);
} else {
smoothTotalDurableBytes.addDelta(reply.bytesDurable - prevReply.bytesDurable);
smoothDurableBytes.setTotal(reply.bytesDurable);
verySmoothDurableBytes.setTotal(reply.bytesDurable);
smoothInputBytes.setTotal(reply.bytesInput);
smoothFreeSpace.setTotal(reply.storageBytes.available);
smoothTotalSpace.setTotal(reply.storageBytes.total);
smoothDurableVersion.setTotal(reply.durableVersion);
smoothLatestVersion.setTotal(reply.version);
}
@ -1213,19 +1228,14 @@ UpdateCommitCostRequest StorageQueueInfo::refreshCommitCost(double elapsed) {
TransactionTag busiestTag;
TransactionCommitCostEstimation maxCost;
double maxRate = 0;
std::priority_queue<BusyTagInfo, std::vector<BusyTagInfo>, std::greater<BusyTagInfo>> topKWriters;
BusyTagCollector busiestWriters(SERVER_KNOBS->SS_THROTTLE_TAGS_TRACKED,
SERVER_KNOBS->MIN_TAG_WRITE_PAGES_RATE * CLIENT_KNOBS->TAG_THROTTLING_PAGE_SIZE);
for (const auto& [tag, cost] : tagCostEst) {
double rate = cost.getCostSum() / elapsed;
double busyness = static_cast<double>(maxCost.getCostSum()) / totalWriteCosts;
if (rate < SERVER_KNOBS->MIN_TAG_WRITE_PAGES_RATE * CLIENT_KNOBS->TAG_THROTTLING_PAGE_SIZE) {
if (!busiestWriters.add(tag, rate, busyness)) {
continue;
}
if (topKWriters.size() < SERVER_KNOBS->SS_THROTTLE_TAGS_TRACKED) {
topKWriters.emplace(tag, rate, busyness);
} else if (topKWriters.top().rate < rate) {
topKWriters.pop();
topKWriters.emplace(tag, rate, busyness);
}
if (rate > maxRate) {
busiestTag = tag;
@ -1234,10 +1244,7 @@ UpdateCommitCostRequest StorageQueueInfo::refreshCommitCost(double elapsed) {
}
}
while (!topKWriters.empty()) {
busiestWriteTags.push_back(topKWriters.top());
topKWriters.pop();
}
busiestWriters.drainInto(busiestWriteTags);
UpdateCommitCostRequest updateCommitCostRequest{ ratekeeperID,
now(),
@ -1257,43 +1264,11 @@ UpdateCommitCostRequest StorageQueueInfo::refreshCommitCost(double elapsed) {
return updateCommitCostRequest;
}
Optional<double> StorageQueueInfo::getTagThrottlingRatio(int64_t storageTargetBytes, int64_t storageSpringBytes) const {
auto const storageQueue = getStorageQueueBytes();
// TODO: Remove duplicate calculation from Ratekeeper::updateRate
double inverseResult = std::min(
2.0, (storageQueue - storageTargetBytes + storageSpringBytes) / static_cast<double>(storageSpringBytes));
if (inverseResult > 0) {
return 1.0 / inverseResult;
} else {
return {};
}
}
TLogQueueInfo::TLogQueueInfo(UID id)
: valid(false), id(id), smoothDurableBytes(SERVER_KNOBS->SMOOTHING_AMOUNT),
smoothInputBytes(SERVER_KNOBS->SMOOTHING_AMOUNT), verySmoothDurableBytes(SERVER_KNOBS->SLOW_SMOOTHING_AMOUNT),
smoothFreeSpace(SERVER_KNOBS->SMOOTHING_AMOUNT), smoothTotalSpace(SERVER_KNOBS->SMOOTHING_AMOUNT) {
// FIXME: this is a tacky workaround for a potential uninitialized use in trackTLogQueueInfo (copied
// from storageQueueInfO)
lastReply.instanceID = -1;
}
TLogQueueInfo::TLogQueueInfo(UID id) : valid(false), id(id) {}
void TLogQueueInfo::update(TLogQueuingMetricsReply const& reply, Smoother& smoothTotalDurableBytes) {
valid = true;
auto prevReply = lastReply;
lastReply = reply;
if (prevReply.instanceID != reply.instanceID) {
smoothDurableBytes.reset(reply.bytesDurable);
verySmoothDurableBytes.reset(reply.bytesDurable);
smoothInputBytes.reset(reply.bytesInput);
smoothFreeSpace.reset(reply.storageBytes.available);
smoothTotalSpace.reset(reply.storageBytes.total);
} else {
smoothTotalDurableBytes.addDelta(reply.bytesDurable - prevReply.bytesDurable);
smoothDurableBytes.setTotal(reply.bytesDurable);
verySmoothDurableBytes.setTotal(reply.bytesDurable);
smoothInputBytes.setTotal(reply.bytesInput);
smoothFreeSpace.setTotal(reply.storageBytes.available);
smoothTotalSpace.setTotal(reply.storageBytes.total);
}
queueMetrics.update(
reply.instanceID, reply.bytesDurable, reply.bytesInput, reply.storageBytes, smoothTotalDurableBytes);
}

View File

@ -34,6 +34,30 @@
struct ServerDBInfo;
class QueueMetricsSmoother {
Optional<int64_t> instanceID;
int64_t durableBytes{ 0 };
Smoother smoothDurableBytes;
Smoother smoothInputBytes;
Smoother verySmoothDurableBytes;
Smoother smoothFreeSpace;
Smoother smoothTotalSpace;
public:
QueueMetricsSmoother();
bool update(int64_t newInstanceID,
int64_t newDurableBytes,
int64_t inputBytes,
const StorageBytes& storageBytes,
Smoother& smoothTotalDurableBytes);
double getSmoothFreeSpace() const { return smoothFreeSpace.smoothTotal(); }
double getSmoothTotalSpace() const { return smoothTotalSpace.smoothTotal(); }
double getSmoothDurableBytes() const { return smoothDurableBytes.smoothTotal(); }
double getSmoothInputBytesRate() const { return smoothInputBytes.smoothRate(); }
double getVerySmoothDurableBytesRate() const { return verySmoothDurableBytes.smoothRate(); }
};
class StorageQueueInfo {
uint64_t totalWriteCosts{ 0 };
int totalWriteOps{ 0 };
@ -41,8 +65,7 @@ class StorageQueueInfo {
TransactionTagMap<TransactionCommitCostEstimation> tagCostEst;
UID ratekeeperID;
Smoother smoothFreeSpace, smoothTotalSpace;
Smoother smoothDurableBytes, smoothInputBytes, verySmoothDurableBytes;
QueueMetricsSmoother queueMetrics;
Smoother smoothDurableVersion, smoothLatestVersion;
public:
@ -57,36 +80,33 @@ public:
StorageQueueInfo(const UID& id, const LocalityData& locality);
StorageQueueInfo(const UID& rateKeeperID, const UID& id, const LocalityData& locality);
UpdateCommitCostRequest refreshCommitCost(double elapsed);
int64_t getStorageQueueBytes() const { return lastReply.bytesInput - smoothDurableBytes.smoothTotal(); }
int64_t getStorageQueueBytes() const { return lastReply.bytesInput - queueMetrics.getSmoothDurableBytes(); }
int64_t getDurabilityLag() const { return smoothLatestVersion.smoothTotal() - smoothDurableVersion.smoothTotal(); }
void update(StorageQueuingMetricsReply const&, Smoother& smoothTotalDurableBytes);
void addCommitCost(TransactionTagRef tagName, TransactionCommitCostEstimation const& cost);
double getSmoothFreeSpace() const { return smoothFreeSpace.smoothTotal(); }
double getSmoothTotalSpace() const { return smoothTotalSpace.smoothTotal(); }
double getSmoothDurableBytes() const { return smoothDurableBytes.smoothTotal(); }
double getSmoothInputBytesRate() const { return smoothInputBytes.smoothRate(); }
double getVerySmoothDurableBytesRate() const { return verySmoothDurableBytes.smoothRate(); }
double getSmoothFreeSpace() const { return queueMetrics.getSmoothFreeSpace(); }
double getSmoothTotalSpace() const { return queueMetrics.getSmoothTotalSpace(); }
double getSmoothDurableBytes() const { return queueMetrics.getSmoothDurableBytes(); }
double getSmoothInputBytesRate() const { return queueMetrics.getSmoothInputBytesRate(); }
double getVerySmoothDurableBytesRate() const { return queueMetrics.getVerySmoothDurableBytesRate(); }
Version getLatestVersion() const { return lastReply.version; }
Optional<double> getTagThrottlingRatio(int64_t storageTargetBytes, int64_t storageSpringBytes) const;
};
class TLogQueueInfo {
Smoother smoothDurableBytes, smoothInputBytes, verySmoothDurableBytes;
Smoother smoothFreeSpace;
Smoother smoothTotalSpace;
QueueMetricsSmoother queueMetrics;
public:
TLogQueuingMetricsReply lastReply;
bool valid;
UID id;
double getSmoothFreeSpace() const { return smoothFreeSpace.smoothTotal(); }
double getSmoothTotalSpace() const { return smoothTotalSpace.smoothTotal(); }
double getSmoothDurableBytes() const { return smoothDurableBytes.smoothTotal(); }
double getSmoothInputBytesRate() const { return smoothInputBytes.smoothRate(); }
double getVerySmoothDurableBytesRate() const { return verySmoothDurableBytes.smoothRate(); }
double getSmoothFreeSpace() const { return queueMetrics.getSmoothFreeSpace(); }
double getSmoothTotalSpace() const { return queueMetrics.getSmoothTotalSpace(); }
double getSmoothDurableBytes() const { return queueMetrics.getSmoothDurableBytes(); }
double getSmoothInputBytesRate() const { return queueMetrics.getSmoothInputBytesRate(); }
double getVerySmoothDurableBytesRate() const { return queueMetrics.getVerySmoothDurableBytesRate(); }
explicit TLogQueueInfo(UID id);
Version getLastCommittedVersion() const { return lastReply.v; }

View File

@ -19,6 +19,7 @@
*/
#include "fdbclient/NativeAPI.actor.h"
#include "fdbserver/core/BusyTagCollector.h"
#include "fdbserver/core/Knobs.h"
#include "TransactionTagCounter.h"
#include "flow/Coroutines.h"
@ -36,24 +37,14 @@ class TransactionTagCounterImpl {
Reference<EventCacheHolder> busiestReadTagEventHolder;
std::vector<BusyTagInfo> getBusiestTagsFromLastInterval(double elapsed) const {
std::priority_queue<BusyTagInfo, std::vector<BusyTagInfo>, std::greater<BusyTagInfo>> topKTags;
BusyTagCollector busiestTags(maxTagsTracked, minRateTracked);
for (auto const& [tag, cost] : intervalCosts) {
auto const rate = cost / elapsed;
auto const fractionalBusyness = std::min(1.0, cost / intervalTotalCost);
if (rate < minRateTracked) {
continue;
} else if (topKTags.size() < maxTagsTracked) {
topKTags.emplace(tag, rate, fractionalBusyness);
} else if (topKTags.top().rate < rate) {
topKTags.pop();
topKTags.emplace(tag, rate, fractionalBusyness);
}
busiestTags.add(tag, rate, fractionalBusyness);
}
std::vector<BusyTagInfo> result;
while (!topKTags.empty()) {
result.push_back(topKTags.top());
topKTags.pop();
}
busiestTags.drainInto(result);
return result;
}

View File

@ -11589,28 +11589,30 @@ Future<Void> serveGetValueRequests(StorageServer* self, FutureStream<GetValueReq
}
}
Future<Void> serveGetKeyValuesRequests(StorageServer* self, FutureStream<GetKeyValuesRequest> getKeyValues) {
getCurrentLineage()->modify(&TransactionLineage::operation) = TransactionLineage::Operation::GetKeyValues;
template <class Request, class Handler>
Future<Void> serveGuardedReadRequests(StorageServer* self,
FutureStream<Request> requests,
TransactionLineage::Operation operation,
Handler handler) {
getCurrentLineage()->modify(&TransactionLineage::operation) = operation;
while (true) {
GetKeyValuesRequest req = co_await getKeyValues;
Request req = co_await requests;
// Warning: This code is executed at extremely high priority (TaskPriority::LoadBalancedEndpoint), so
// downgrade before doing real work
self->actors.add(self->readGuard(req, getKeyValuesQ));
self->actors.add(self->readGuard(req, handler));
}
}
Future<Void> serveGetKeyValuesRequests(StorageServer* self, FutureStream<GetKeyValuesRequest> getKeyValues) {
return serveGuardedReadRequests(
self, std::move(getKeyValues), TransactionLineage::Operation::GetKeyValues, getKeyValuesQ);
}
Future<Void> serveGetMappedKeyValuesRequests(StorageServer* self,
FutureStream<GetMappedKeyValuesRequest> getMappedKeyValues) {
// TODO: Is it fine to keep TransactionLineage::Operation::GetKeyValues here?
getCurrentLineage()->modify(&TransactionLineage::operation) = TransactionLineage::Operation::GetKeyValues;
while (true) {
GetMappedKeyValuesRequest req = co_await getMappedKeyValues;
// Warning: This code is executed at extremely high priority (TaskPriority::LoadBalancedEndpoint), so
// downgrade before doing real work
self->actors.add(self->readGuard(req, getMappedKeyValuesQ));
}
return serveGuardedReadRequests(
self, std::move(getMappedKeyValues), TransactionLineage::Operation::GetKeyValues, getMappedKeyValuesQ);
}
Future<Void> serveGetKeyValuesStreamRequests(StorageServer* self,
@ -11625,13 +11627,7 @@ Future<Void> serveGetKeyValuesStreamRequests(StorageServer* self,
}
Future<Void> serveGetKeyRequests(StorageServer* self, FutureStream<GetKeyRequest> getKey) {
getCurrentLineage()->modify(&TransactionLineage::operation) = TransactionLineage::Operation::GetKey;
while (true) {
GetKeyRequest req = co_await getKey;
// Warning: This code is executed at extremely high priority (TaskPriority::LoadBalancedEndpoint), so
// downgrade before doing real work
self->actors.add(self->readGuard(req, getKeyQ));
}
return serveGuardedReadRequests(self, std::move(getKey), TransactionLineage::Operation::GetKey, getKeyQ);
}
Future<Void> watchValueWaitForVersion(StorageServer* self,

View File

@ -234,7 +234,13 @@ struct ClientMetricWorkload : TestWorkload {
}
}
Future<bool> check(Database const& cx) override { return clientId != 0 || observedAdvancingMetrics; }
Future<bool> check(Database const& cx) override {
if (clientId != 0 || observedAdvancingMetrics) {
return true;
}
TraceEvent(SevError, "ClientMetricCheckFailed").detail("Reason", "WorkloadDidNotComplete");
return false;
}
void getMetrics(std::vector<PerfMetric>& m) override {}
};

View File

@ -32,6 +32,9 @@
#include "flow/DeterministicRandom.h"
#include "fdbrpc/SimulatorProcessInfo.h"
#include <algorithm>
#include <memory>
static std::set<int> const& normalAttritionErrors() {
static std::set<int> s;
if (s.empty()) {
@ -65,6 +68,73 @@ Future<bool> ignoreSSFailuresForDuration(Database cx, double duration) {
}
}
class MachineAttritionPhase {
public:
using Owner = uint64_t;
static std::shared_ptr<MachineAttritionPhase> get(int64_t phaseId) {
static std::map<int64_t, std::weak_ptr<MachineAttritionPhase>> phases;
for (auto it = phases.begin(); it != phases.end();) {
if (it->second.expired()) {
it = phases.erase(it);
} else {
++it;
}
}
auto& phase = phases[phaseId];
if (auto existing = phase.lock()) {
return existing;
}
auto created = std::shared_ptr<MachineAttritionPhase>(new MachineAttritionPhase());
phase = created;
return created;
}
Owner registerWorkload(int machinesToLeave) {
this->machinesToLeave = std::max(this->machinesToLeave, machinesToLeave);
return nextOwner++;
}
void observe(std::vector<LocalityData> const& machines) {
for (auto const& machine : machines) {
knownZones.insert(machine.zoneId());
}
}
bool prepareTargets(std::vector<LocalityData>& machines, Owner owner) const {
bool canClaimNew =
static_cast<int64_t>(knownZones.size()) - static_cast<int64_t>(targetOwners.size()) > machinesToLeave;
machines.erase(std::remove_if(machines.begin(),
machines.end(),
[this, owner, canClaimNew](LocalityData const& machine) {
auto target = targetOwners.find(machine.zoneId());
return (target != targetOwners.end() && target->second != owner) ||
(target == targetOwners.end() && !canClaimNew);
}),
machines.end());
return !machines.empty();
}
void claim(LocalityData const& machine, Owner owner) {
auto target = targetOwners.find(machine.zoneId());
if (target != targetOwners.end()) {
ASSERT(target->second == owner);
return;
}
ASSERT(static_cast<int64_t>(knownZones.size()) - static_cast<int64_t>(targetOwners.size()) > machinesToLeave);
targetOwners.emplace(machine.zoneId(), owner);
}
private:
MachineAttritionPhase() = default;
Owner nextOwner = 1;
int machinesToLeave = 0;
std::set<Optional<Standalone<StringRef>>> knownZones;
std::map<Optional<Standalone<StringRef>>, Owner> targetOwners;
};
struct MachineAttritionWorkload : FailureInjectionWorkload {
static constexpr auto NAME = "Attrition";
bool enabled;
@ -88,6 +158,8 @@ struct MachineAttritionWorkload : FailureInjectionWorkload {
// This is set in setup from the list of workers when the cluster is started
std::vector<LocalityData> machines;
std::shared_ptr<MachineAttritionPhase> phase;
MachineAttritionPhase::Owner phaseOwner = 0;
MachineAttritionWorkload(WorkloadContext const& wcx, NoOptions) : FailureInjectionWorkload(wcx) {
enabled = !clientId && g_network->isSimulated() && faultInjectionActivated;
@ -120,6 +192,17 @@ struct MachineAttritionWorkload : FailureInjectionWorkload {
replacement = getOption(options, "replacement"_sr, reboot && deterministicRandom()->random01() < 0.5);
waitForVersion = getOption(options, "waitForVersion"_sr, waitForVersion);
allowFaultInjection = getOption(options, "allowFaultInjection"_sr, allowFaultInjection);
if (enabled) {
phase = MachineAttritionPhase::get(wcx.sharedRandomNumber);
phaseOwner = phase->registerWorkload(machinesToLeave);
}
}
void initFailureInjectionMode(DeterministicRandom&) override {
if (enabled) {
phase = MachineAttritionPhase::get(sharedRandomNumber);
phaseOwner = phase->registerWorkload(machinesToLeave);
}
}
bool shouldInject(DeterministicRandom& random,
@ -182,6 +265,7 @@ struct MachineAttritionWorkload : FailureInjectionWorkload {
for (auto it = machineIDMap.begin(); it != machineIDMap.end(); ++it) {
machines.push_back(it->second);
}
phase->observe(machines);
deterministicRandom()->randomShuffle(machines);
double meanDelay = testDuration / machinesToKill;
TraceEvent("AttritionStarting")
@ -382,7 +466,7 @@ struct MachineAttritionWorkload : FailureInjectionWorkload {
g_simulator->toggleGlobalSwitchCluster();
} else {
int killedMachines = 0;
while (killedMachines < machinesToKill && machines.size() > machinesToLeave) {
while (killedMachines < machinesToKill && phase->prepareTargets(machines, phaseOwner)) {
TraceEvent("WorkerKillBegin")
.detail("KilledMachines", killedMachines)
.detail("MachinesToKill", machinesToKill)
@ -411,7 +495,11 @@ struct MachineAttritionWorkload : FailureInjectionWorkload {
}
// decide on a machine to kill
if (!phase->prepareTargets(machines, phaseOwner)) {
break;
}
LocalityData targetMachine = machines.back();
phase->claim(targetMachine, phaseOwner);
if (buggify(0.01)) {
CODE_PROBE(true, "Marked a zone for maintenance before killing it");
co_await setHealthyZone(
@ -494,5 +582,87 @@ struct MachineAttritionWorkload : FailureInjectionWorkload {
}
};
TEST_CASE("/fdbserver/workloads/MachineAttrition/phaseCoordination") {
auto locality = [](StringRef zone) {
return LocalityData(Optional<Standalone<StringRef>>(),
Standalone<StringRef>(zone),
Standalone<StringRef>(zone),
Optional<Standalone<StringRef>>());
};
std::vector<LocalityData> allMachines = {
locality("zone0"_sr), locality("zone1"_sr), locality("zone2"_sr), locality("zone3"_sr), locality("zone4"_sr)
};
auto phase = MachineAttritionPhase::get(-1);
auto firstOwner = phase->registerWorkload(1);
auto secondOwner = phase->registerWorkload(3);
auto thirdOwner = phase->registerWorkload(0);
phase->observe(allMachines);
auto firstTargets = allMachines;
ASSERT(phase->prepareTargets(firstTargets, firstOwner));
LocalityData firstTarget = firstTargets.back();
phase->claim(firstTarget, firstOwner);
firstTargets.pop_back();
auto secondTargets = allMachines;
ASSERT(phase->prepareTargets(secondTargets, secondOwner));
LocalityData secondTarget = secondTargets.back();
ASSERT(secondTarget.zoneId() != firstTarget.zoneId());
phase->claim(secondTarget, secondOwner);
ASSERT(phase->prepareTargets(secondTargets, secondOwner));
ASSERT(secondTargets.size() == 1);
ASSERT(secondTargets.back().zoneId() == secondTarget.zoneId());
phase->claim(secondTargets.back(), secondOwner);
ASSERT(!phase->prepareTargets(firstTargets, firstOwner));
auto thirdTargets = allMachines;
ASSERT(!phase->prepareTargets(thirdTargets, thirdOwner));
auto nextPhase = MachineAttritionPhase::get(-2);
auto nextOwner = nextPhase->registerWorkload(0);
auto nextTargets = allMachines;
nextPhase->observe(nextTargets);
ASSERT(phase.get() != nextPhase.get());
ASSERT(nextPhase->prepareTargets(nextTargets, nextOwner));
ASSERT(nextTargets.size() == allMachines.size());
return Void();
}
TEST_CASE("/fdbserver/workloads/MachineAttrition/failureInjectorSelection") {
WorkloadContext wcx;
wcx.clientId = 0;
wcx.clientCount = 1;
wcx.sharedRandomNumber = -3;
auto phase = MachineAttritionPhase::get(wcx.sharedRandomNumber);
auto explicitOwner = phase->registerWorkload(0);
std::vector<LocalityData> machines = { LocalityData(Optional<Standalone<StringRef>>(),
Standalone<StringRef>("zone0"_sr),
Standalone<StringRef>("zone0"_sr),
Optional<Standalone<StringRef>>()) };
phase->observe(machines);
TestWorkloadImpl<MachineAttritionWorkload, true> unselected(wcx, NoOptions{});
ASSERT(!unselected.phase);
ASSERT(unselected.phaseOwner == 0);
auto unselectedTargets = machines;
ASSERT(phase->prepareTargets(unselectedTargets, explicitOwner));
ASSERT(unselectedTargets.size() == 1);
TestWorkloadImpl<MachineAttritionWorkload, true> selected(wcx, NoOptions{});
ASSERT(!selected.phase);
selected.enabled = true;
DeterministicRandom random(wcx.sharedRandomNumber);
selected.initFailureInjectionMode(random);
ASSERT(selected.phase == phase);
ASSERT(selected.phaseOwner != 0);
auto selectedTargets = machines;
ASSERT(!phase->prepareTargets(selectedTargets, explicitOwner));
return Void();
}
WorkloadFactory<MachineAttritionWorkload> MachineAttritionWorkloadFactory;
FailureInjectorFactory<MachineAttritionWorkload> MachineAttritionFailureWorkloadFactory;

View File

@ -32,9 +32,11 @@ enum {
OP_GETRANGE,
OP_SGET,
OP_SGETRANGE,
OP_STATUSJSON,
OP_UPDATE,
OP_INSERT,
OP_INSERTRANGE,
OP_OVERWRITE,
OP_CLEAR,
OP_SETCLEAR,
OP_CLEARRANGE,
@ -42,7 +44,7 @@ enum {
OP_COMMIT,
MAX_OP
};
enum { OP_COUNT, OP_RANGE };
enum { OP_COUNT, OP_RANGE, OP_REVERSE };
struct MakoWorkload : TestWorkload {
static constexpr auto NAME = "Mako";
@ -58,7 +60,7 @@ struct MakoWorkload : TestWorkload {
std::vector<std::pair<uint64_t, double>> ratesAtKeyCounts;
std::string operationsSpec;
// store operations to execute
int operations[MAX_OP][2];
int operations[MAX_OP][3];
// used for periodically tracing
std::vector<PerfMetric> periodicMetrics;
// store latency of each operation with sampling
@ -68,10 +70,10 @@ struct MakoWorkload : TestWorkload {
// key prefix of for all generated keys
std::string keyPrefix;
int KEYPREFIXLEN;
const std::array<std::string, MAX_OP> opNames = { "GRV", "GET", "GETRANGE", "SGET",
"SGETRANGE", "UPDATE", "INSERT", "INSERTRANGE",
"CLEAR", "SETCLEAR", "CLEARRANGE", "SETCLEARRANGE",
"COMMIT" };
const std::array<std::string, MAX_OP> opNames = { "GRV", "GET", "GETRANGE", "SGET",
"SGETRANGE", "STATUSJSON", "UPDATE", "INSERT",
"INSERTRANGE", "OVERWRITE", "CLEAR", "SETCLEAR",
"CLEARRANGE", "SETCLEARRANGE", "COMMIT" };
explicit MakoWorkload(WorkloadContext const& wcx)
: TestWorkload(wcx), loadTime(0.0), xacts("Transactions"), retries("Retries"), conflicts("Conflicts"),
commits("Commits"), totalOps("Operations") {
@ -127,14 +129,17 @@ struct MakoWorkload : TestWorkload {
// gr GET RANGE
// sg Snapshot GET
// sgr Snapshot GET RANGE
// sj GET special-key-space status JSON
// u Update (= GET followed by SET)
// i Insert (= SET with a new key)
// ir Insert Range (Sequential)
// o Overwrite (= blind SET on an existing key)
// c CLEAR
// sc SET & CLEAR
// cr CLEAR RANGE
// scr SET & CLEAR RANGE
// grv GetReadVersion()
// A negative range on gr or sgr reads the selected keys in reverse order.
// Every transaction is committed unless it contains only GET / GET RANGE operations.
operationsSpec = getOption(options, "operations"_sr, "g100"_sr).contents().toString();
// parse the sequence and extract operations to be executed
@ -216,7 +221,8 @@ struct MakoWorkload : TestWorkload {
}
// Meaningful Latency metrics
const int opExecutedAtOnce[] = { OP_GETREADVERSION, OP_GET, OP_GETRANGE, OP_SGET, OP_SGETRANGE, OP_COMMIT };
const int opExecutedAtOnce[] = { OP_GETREADVERSION, OP_GET, OP_GETRANGE, OP_SGET,
OP_SGETRANGE, OP_STATUSJSON, OP_COMMIT };
for (const int& op : opExecutedAtOnce) {
m.emplace_back("Mean " + opNames[op] + " Latency (us)", 1e6 * opLatencies[op].mean(), Averaged::True);
m.emplace_back(
@ -226,7 +232,7 @@ struct MakoWorkload : TestWorkload {
}
// Latency for local operations if needed
if (latencyForLocalOperation) {
const int localOp[] = { OP_INSERT, OP_CLEAR, OP_CLEARRANGE };
const int localOp[] = { OP_INSERT, OP_OVERWRITE, OP_CLEAR, OP_CLEARRANGE };
for (const int& op : localOp) {
TraceEvent(SevDebug, "LocalLatency")
.detail("Name", opNames[op])
@ -465,7 +471,7 @@ struct MakoWorkload : TestWorkload {
// used for mako-level consistency check
if (checksumVerification) {
if (i == OP_INSERT | i == OP_UPDATE | i == OP_CLEAR) {
if (i == OP_INSERT || i == OP_UPDATE || i == OP_OVERWRITE || i == OP_CLEAR) {
updateCSFlags(csChangedFlags, indBegin, indBegin + 1);
} else if (i == OP_CLEARRANGE) {
updateCSFlags(csChangedFlags, indBegin, indEnd);
@ -477,14 +483,22 @@ struct MakoWorkload : TestWorkload {
} else if (i == OP_GET) {
co_await logLatency(tr.get(rkey, Snapshot::False), &opLatencies[i]);
} else if (i == OP_GETRANGE) {
co_await logLatency(tr.getRange(rkeyRangeRef, CLIENT_KNOBS->TOO_MANY, Snapshot::False),
co_await logLatency(tr.getRange(rkeyRangeRef,
CLIENT_KNOBS->TOO_MANY,
Snapshot::False,
operations[i][OP_REVERSE] ? Reverse::True : Reverse::False),
&opLatencies[i]);
} else if (i == OP_SGET) {
co_await logLatency(tr.get(rkey, Snapshot::True), &opLatencies[i]);
} else if (i == OP_SGETRANGE) {
// do snapshot get range here
co_await logLatency(tr.getRange(rkeyRangeRef, CLIENT_KNOBS->TOO_MANY, Snapshot::True),
co_await logLatency(tr.getRange(rkeyRangeRef,
CLIENT_KNOBS->TOO_MANY,
Snapshot::True,
operations[i][OP_REVERSE] ? Reverse::True : Reverse::False),
&opLatencies[i]);
} else if (i == OP_STATUSJSON) {
co_await logLatency(tr.get("\xff\xff/status/json"_sr, Snapshot::False), &opLatencies[i]);
} else if (i == OP_UPDATE) {
co_await logLatency(tr.get(rkey, Snapshot::False), &opLatencies[OP_GET]);
if (latencyForLocalOperation) {
@ -522,6 +536,15 @@ struct MakoWorkload : TestWorkload {
}
}
doCommit = true;
} else if (i == OP_OVERWRITE) {
if (latencyForLocalOperation) {
double opBegin = timer();
tr.set(rkey, rval);
opLatencies[OP_OVERWRITE].addSample(timer() - opBegin);
} else {
tr.set(rkey, rval);
}
doCommit = true;
} else if (i == OP_CLEAR) {
if (latencyForLocalOperation) {
double opBegin = timer();
@ -620,15 +643,18 @@ struct MakoWorkload : TestWorkload {
} catch (Error& e) {
err = e;
}
TraceEvent("FailedToExecOperations").error(err);
if (err.code() == error_code_operation_cancelled)
throw err;
else if (err.code() == error_code_not_committed)
++conflicts;
co_await tr.onError(err);
++retries;
if (err.isValid()) {
TraceEvent("FailedToExecOperations").error(err);
if (err.code() == error_code_operation_cancelled)
throw err;
else if (err.code() == error_code_not_committed)
++conflicts;
co_await tr.onError(err);
++retries;
}
// reset all the operations' counters to 0
std::fill(perOpCount.begin(), perOpCount.end(), 0);
std::fill(csChangedFlags.begin(), csChangedFlags.end(), false);
tr.reset();
}
}
@ -678,6 +704,7 @@ struct MakoWorkload : TestWorkload {
for (op = 0; op < MAX_OP; op++) {
operations[op][OP_COUNT] = 0;
operations[op][OP_RANGE] = 0;
operations[op][OP_REVERSE] = 0;
}
op = 0;
@ -699,6 +726,9 @@ struct MakoWorkload : TestWorkload {
} else if (strncmp(ptr, "sg", 2) == 0) {
op = OP_SGET;
ptr += 2;
} else if (strncmp(ptr, "sj", 2) == 0) {
op = OP_STATUSJSON;
ptr += 2;
} else if (strncmp(ptr, "u", 1) == 0) {
op = OP_UPDATE;
ptr++;
@ -709,6 +739,9 @@ struct MakoWorkload : TestWorkload {
} else if (strncmp(ptr, "i", 1) == 0) {
op = OP_INSERT;
ptr++;
} else if (strncmp(ptr, "o", 1) == 0) {
op = OP_OVERWRITE;
ptr++;
} else if (strncmp(ptr, "cr", 2) == 0) {
op = OP_CLEARRANGE;
rangeop = 1;
@ -747,6 +780,12 @@ struct MakoWorkload : TestWorkload {
break;
} else {
ptr++; /* skip ':' */
if (*ptr == '-') {
operations[op][OP_REVERSE] = 1;
ptr++;
} else {
operations[op][OP_REVERSE] = 0;
}
num = 0;
if ((*ptr < '0') || (*ptr > '9')) {
error = 1;

View File

@ -110,9 +110,6 @@ bool valgrindPrecise() {
}
#endif
template <int Size>
void* FastAllocator<Size>::freelist = nullptr;
std::atomic<int64_t> g_hugeArenaMemory(0);
double hugeArenaLastLogged = 0;
@ -398,7 +395,6 @@ void* FastAllocator<Size>::allocate() {
}
#endif
#if FASTALLOC_THREAD_SAFE
ThreadData& thr = threadData();
if (!thr.freelist) {
ASSERT(thr.count == 0);
@ -418,15 +414,6 @@ void* FastAllocator<Size>::allocate() {
thr.freelist = *(void**)p;
ASSERT(!thr.freelist == (thr.count == 0)); // freelist is empty if and only if count is 0
// check( p, true );
#else
void* p = freelist;
if (!p)
getMagazine();
#if VALGRIND
VALGRIND_MAKE_MEM_DEFINED(p, sizeof(void*));
#endif
freelist = *(void**)p;
#endif
#if VALGRIND
VALGRIND_MALLOCLIKE_BLOCK(p, Size, 0, 0);
#endif
@ -481,7 +468,6 @@ void FastAllocator<Size>::release(void* ptr) {
}
#endif
#if FASTALLOC_THREAD_SAFE
ThreadData& thr = threadData();
if (thr.count == magazine_size) {
if (thr.alternate) // Two full magazines, return one
@ -500,10 +486,6 @@ void FastAllocator<Size>::release(void* ptr) {
*(void**)ptr = thr.freelist;
// check(ptr, false);
thr.freelist = ptr;
#else
*(void**)ptr = freelist;
freelist = ptr;
#endif
#if VALGRIND
VALGRIND_FREELIKE_BLOCK(ptr, 0);

View File

@ -922,7 +922,8 @@ public:
static Future<Reference<IConnection>> connect(boost::asio::io_service* ios,
Reference<ReferencedObject<boost::asio::ssl::context>> context,
NetworkAddress addr,
tcp::socket* existingSocket = nullptr) {
tcp::socket* existingSocket = nullptr,
std::string hostname = {}) {
std::pair<IPAddress, uint16_t> peerIP = std::make_pair(addr.ip, addr.port);
auto iter(g_network->networkInfo.serverTLSConnectionThrottler.find(peerIP));
if (iter != g_network->networkInfo.serverTLSConnectionThrottler.end()) {
@ -949,6 +950,7 @@ public:
Reference<SSLConnection> self(new SSLConnection(*ios, context));
self->peer_address = addr;
self->sni_hostname = std::move(hostname);
try {
auto to = tcpEndpoint(self->peer_address);
BindPromise p("N2_ConnectError", self->id, self->peer_address);
@ -965,51 +967,6 @@ public:
}
}
// Connect with hostname for SNI (Server Name Indication) support
static Future<Reference<IConnection>> connectWithHostname(
boost::asio::io_service* ios,
Reference<ReferencedObject<boost::asio::ssl::context>> context,
NetworkAddress addr,
std::string hostname) {
std::pair<IPAddress, uint16_t> peerIP = std::make_pair(addr.ip, addr.port);
auto iter(g_network->networkInfo.serverTLSConnectionThrottler.find(peerIP));
if (iter != g_network->networkInfo.serverTLSConnectionThrottler.end()) {
if (now() < iter->second.second) {
if (iter->second.first >= FLOW_KNOBS->TLS_CLIENT_CONNECTION_THROTTLE_ATTEMPTS) {
TraceEvent("TLSOutgoingConnectionThrottlingWarning").suppressFor(1.0).detail("PeerIP", addr);
co_await delay(FLOW_KNOBS->CONNECTION_MONITOR_TIMEOUT);
throw connection_failed();
}
} else {
g_network->networkInfo.serverTLSConnectionThrottler.erase(peerIP);
}
}
Reference<SSLConnection> self(new SSLConnection(*ios, context));
self->peer_address = addr;
self->sni_hostname = hostname; // Store hostname for SNI during handshake
// Store hostname for SNI use during handshake
try {
auto to = tcpEndpoint(self->peer_address);
BindPromise p("N2_ConnectError", self->id, self->peer_address);
Future<Void> onConnected = p.getFuture();
self->socket.async_connect(to, std::move(p));
co_await onConnected;
// SNI will be set later in doConnectHandshake before SSL handshake
self->init();
co_return self;
} catch (Error&) {
// Either the connection failed, or was cancelled by the caller
self->closeSocket();
throw;
}
}
// This is not part of the IConnection interface, because it is wrapped by IListener::accept()
void accept(NetworkAddress peerAddr) {
this->peer_address = peerAddr;
@ -2020,7 +1977,7 @@ Future<Reference<IConnection>> Net2::connectExternal(NetworkAddress toAddr) {
Future<Reference<IConnection>> Net2::connectExternalWithHostname(NetworkAddress toAddr, const std::string& hostname) {
if (toAddr.isTLS()) {
initTLS(ETLSInitState::CONNECT);
return SSLConnection::connectWithHostname(&this->reactor.ios, this->sslContextVar.get(), toAddr, hostname);
return SSLConnection::connect(&this->reactor.ios, this->sslContextVar.get(), toAddr, nullptr, hostname);
}
return connect(toAddr);
}

View File

@ -26,6 +26,7 @@
TLSPolicy::~TLSPolicy() {}
#include <algorithm>
#include <array>
#include <cstring>
#include <exception>
#include <map>
@ -241,60 +242,6 @@ bool TLSConfig::getDisablePlainTextConnection() const {
return tlsDisablePlainTextConnection;
}
LoadedTLSConfig TLSConfig::loadSync() const {
LoadedTLSConfig loaded;
const std::string certPath = getCertificatePathSync();
if (!certPath.empty()) {
try {
loaded.tlsCertBytes = readFileBytes(certPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE);
} catch (Error& e) {
fprintf(stderr, "Warning: Error reading TLS Certificate [%s]: %s\n", certPath.c_str(), e.what());
throw;
}
} else {
loaded.tlsCertBytes = tlsCertBytes;
}
const std::string keyPath = getKeyPathSync();
if (!keyPath.empty()) {
try {
loaded.tlsKeyBytes = readFileBytes(keyPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE);
} catch (Error& e) {
fprintf(stderr, "Warning: Error reading TLS Key [%s]: %s\n", keyPath.c_str(), e.what());
throw;
}
} else {
loaded.tlsKeyBytes = tlsKeyBytes;
}
const std::string CAPath = getCAPathSync();
if (!CAPath.empty()) {
try {
loaded.tlsCABytes = readFileBytes(CAPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE);
} catch (Error& e) {
fprintf(stderr, "Warning: Error reading TLS CA [%s]: %s\n", CAPath.c_str(), e.what());
throw;
}
} else {
loaded.tlsCABytes = tlsCABytes;
}
loaded.tlsPassword = tlsPassword;
loaded.tlsVerifyPeers = tlsVerifyPeers;
loaded.endpointType = endpointType;
loaded.tlsDisablePlainTextConnection = tlsDisablePlainTextConnection;
return loaded;
}
TLSPolicy::TLSPolicy(const LoadedTLSConfig& loaded, std::function<void()> on_failure)
: rules(), on_failure(std::move(on_failure)), is_client(loaded.getEndpointType() == TLSEndpointType::CLIENT) {
set_verify_peers(loaded.getVerifyPeers());
}
// And now do the same thing, but async...
static Future<Void> readEntireFile(std::string filename, std::string* destination) {
Reference<IAsyncFile> file = co_await IAsyncFileSystem::filesystem()->open(
filename, IAsyncFile::OPEN_READONLY | IAsyncFile::OPEN_UNCACHED, 0);
@ -306,59 +253,102 @@ static Future<Void> readEntireFile(std::string filename, std::string* destinatio
co_await file->read(&((*destination)[0]), filesize, 0);
}
class TLSMaterialSource {
public:
TLSMaterialSource(const char* name, std::string path, const std::string& configuredBytes, std::string& loadedBytes)
: name(name), path(std::move(path)), configuredBytes(configuredBytes), loadedBytes(loadedBytes) {}
bool hasFile() const { return !path.empty(); }
void copyConfiguredBytes() const { loadedBytes = configuredBytes; }
void loadSync() const {
if (!hasFile()) {
copyConfiguredBytes();
return;
}
try {
loadedBytes = readFileBytes(path, FLOW_KNOBS->CERT_FILE_MAX_SIZE);
} catch (Error& error) {
reportError(error);
throw;
}
}
Future<Void> loadAsync() const { return readEntireFile(path, &loadedBytes); }
void reportError(const Error& error) const {
fprintf(stderr, "Warning: Error reading TLS %s [%s]: %s\n", name, path.c_str(), error.what());
}
private:
const char* name;
std::string path;
const std::string& configuredBytes;
std::string& loadedBytes;
};
static std::array<TLSMaterialSource, 3> getTLSMaterialSources(const TLSConfig& config, LoadedTLSConfig& loaded) {
return { TLSMaterialSource(
"Certificate", config.getCertificatePathSync(), config.tlsCertBytes, loaded.tlsCertBytes),
TLSMaterialSource("Key", config.getKeyPathSync(), config.tlsKeyBytes, loaded.tlsKeyBytes),
TLSMaterialSource("CA", config.getCAPathSync(), config.tlsCABytes, loaded.tlsCABytes) };
}
static void copyTLSSettings(const TLSConfig& config, LoadedTLSConfig& loaded) {
loaded.tlsPassword = config.tlsPassword;
loaded.tlsVerifyPeers = config.tlsVerifyPeers;
loaded.endpointType = config.endpointType;
loaded.tlsDisablePlainTextConnection = config.tlsDisablePlainTextConnection;
}
LoadedTLSConfig TLSConfig::loadSync() const {
LoadedTLSConfig loaded;
for (const auto& material : getTLSMaterialSources(*this, loaded)) {
material.loadSync();
}
copyTLSSettings(*this, loaded);
return loaded;
}
TLSPolicy::TLSPolicy(const LoadedTLSConfig& loaded, std::function<void()> on_failure)
: rules(), on_failure(std::move(on_failure)), is_client(loaded.getEndpointType() == TLSEndpointType::CLIENT) {
set_verify_peers(loaded.getVerifyPeers());
}
Future<LoadedTLSConfig> TLSConfig::loadAsync(const TLSConfig* self) {
LoadedTLSConfig loaded;
auto materials = getTLSMaterialSources(*self, loaded);
std::vector<Future<Void>> reads;
int32_t certIdx = -1;
int32_t keyIdx = -1;
int32_t caIdx = -1;
std::string certPath = self->getCertificatePathSync();
if (!certPath.empty()) {
reads.push_back(readEntireFile(certPath, &loaded.tlsCertBytes));
certIdx = reads.size() - 1;
} else {
loaded.tlsCertBytes = self->tlsCertBytes;
}
std::string keyPath = self->getKeyPathSync();
if (!keyPath.empty()) {
reads.push_back(readEntireFile(keyPath, &loaded.tlsKeyBytes));
keyIdx = reads.size() - 1;
} else {
loaded.tlsKeyBytes = self->tlsKeyBytes;
}
std::string CAPath = self->getCAPathSync();
if (!CAPath.empty()) {
reads.push_back(readEntireFile(CAPath, &loaded.tlsCABytes));
caIdx = reads.size() - 1;
} else {
loaded.tlsCABytes = self->tlsCABytes;
for (const auto& material : materials) {
if (material.hasFile()) {
reads.push_back(material.loadAsync());
} else {
material.copyConfiguredBytes();
}
}
try {
co_await waitForAll(reads);
} catch (Error& e) {
if (certIdx != -1 && reads[certIdx].isError()) {
fprintf(stderr, "Warning: Error reading TLS Certificate [%s]: %s\n", certPath.c_str(), e.what());
} else if (keyIdx != -1 && reads[keyIdx].isError()) {
fprintf(stderr, "Warning: Error reading TLS Key [%s]: %s\n", keyPath.c_str(), e.what());
} else if (caIdx != -1 && reads[caIdx].isError()) {
fprintf(stderr, "Warning: Error reading TLS Key [%s]: %s\n", CAPath.c_str(), e.what());
} else {
fprintf(stderr, "Warning: Error reading TLS needed file: %s\n", e.what());
} catch (Error& error) {
bool reported = false;
size_t readIndex = 0;
for (const auto& material : materials) {
if (material.hasFile() && reads[readIndex++].isError()) {
material.reportError(error);
reported = true;
break;
}
}
if (!reported) {
fprintf(stderr, "Warning: Error reading TLS needed file: %s\n", error.what());
}
throw;
}
loaded.tlsPassword = self->tlsPassword;
loaded.tlsVerifyPeers = self->tlsVerifyPeers;
loaded.endpointType = self->endpointType;
loaded.tlsDisablePlainTextConnection = self->tlsDisablePlainTextConnection;
copyTLSSettings(*self, loaded);
co_return loaded;
}

View File

@ -267,6 +267,23 @@ TEST_CASE("/flow/genericactors/AsyncListener") {
return Void();
}
TEST_CASE("/flow/genericactors/DelayedAsyncVarPreservesReentrantInputChange") {
state Reference<AsyncVar<bool>> input = makeReference<AsyncVar<bool>>(true);
state Reference<AsyncVar<bool>> output = makeReference<AsyncVar<bool>>(true);
state Future<Void> feedback = generic_coro::trigger(SetAsyncVarTrue{ input }, output->onChange());
state Future<Void> publisher = delayedAsyncVar(input, output, 0);
wait(delay(0));
input->set(false);
wait(feedback);
wait(delay(0.01));
ASSERT(input->get());
ASSERT(output->get());
publisher.cancel();
return Void();
}
TEST_CASE("/flow/genericactors/WaitForMost") {
state std::vector<Future<ErrorOr<Void>>> futures;
{

View File

@ -606,12 +606,58 @@ template <class Awaiter, class ValueType>
struct AwaitableResume<Awaiter, ValueType, /* IsStream = */ true, /* ReturnsExplicitVoid = */ true>
: AwaitableResume<Awaiter, ValueType, /* IsStream = */ true, /* ReturnsExplicitVoid = */ false> {};
template <class Awaiter, class PromiseType>
class AwaitableFutureState : public AwaitCancelHandler {
public:
[[maybe_unused]] [[nodiscard]] bool await_ready() const {
auto* self = static_cast<const Awaiter*>(this);
if (actorWaitStateIsCancelled(self->pt->waitState())) {
self->pt->waitState() = ACTOR_WAIT_STATE_CANCELLED_DURING_READY_CHECK;
return true;
}
return self->future.isReady();
}
[[maybe_unused]] void await_suspend(n_coroutine::coroutine_handle<> handle) {
auto* self = static_cast<Awaiter*>(this);
self->pt->setHandle(handle);
self->pt->waitState() = ACTOR_WAIT_STATE_WAITING;
auto callbackFuture = self->getCallbackFuture();
callbackFuture.addCallbackAndClear(self);
self->pt->setCancelHandler(this);
}
// NoThrowOnCancel destroys the coroutine frame instead of resuming this
// awaiter, so detach its callback before destroying the frame.
void cancelWait() override { static_cast<Awaiter*>(this)->remove(); }
bool resumeImpl() {
auto* self = static_cast<Awaiter*>(this);
self->pt->clearCancelHandler(this);
switch (self->pt->waitState()) {
case ACTOR_WAIT_STATE_CANCELLED:
self->remove();
case ACTOR_WAIT_STATE_CANCELLED_DURING_READY_CHECK:
// await_ready() observed cancellation before callback registration.
throw actor_cancelled();
}
bool wasReady = self->pt->waitState() == ACTOR_WAIT_STATE_NOT_WAITING;
if (actorWaitStateIsWaiting(self->pt->waitState())) {
self->remove();
self->pt->waitState() = ACTOR_WAIT_STATE_NOT_WAITING;
}
return wasReady;
}
};
// Awaiter for `Future<T>` and `FutureStream<T>` values transformed through a
// coroutine promise.
template <class PromiseType, class ValueType, bool IsStream, bool ReturnsExplicitVoid = false>
struct AwaitableFuture
: std::conditional_t<IsStream, SingleCallback<ToFutureVal<ValueType>>, Callback<ToFutureVal<ValueType>>>,
AwaitCancelHandler,
AwaitableFutureState<AwaitableFuture<PromiseType, ValueType, IsStream, ReturnsExplicitVoid>, PromiseType>,
AwaitableResume<AwaitableFuture<PromiseType, ValueType, IsStream, ReturnsExplicitVoid>,
ValueType,
IsStream,
@ -647,60 +693,18 @@ struct AwaitableFuture
pt->resume();
}
[[maybe_unused]] [[nodiscard]] bool await_ready() const {
if (actorWaitStateIsCancelled(pt->waitState())) {
pt->waitState() = ACTOR_WAIT_STATE_CANCELLED_DURING_READY_CHECK;
return true;
}
return future.isReady();
}
[[maybe_unused]] void await_suspend(n_coroutine::coroutine_handle<> h) {
// Create a coroutine callback if it's the first time being suspended
pt->setHandle(h);
// Set wait_state and add callback
pt->waitState() = ACTOR_WAIT_STATE_WAITING;
auto getCallbackFuture() const {
if constexpr (IsStream) {
auto sf = future;
sf.addCallbackAndClear(this);
return future;
} else {
StrictFuture<FutureValue> sf = future;
sf.addCallbackAndClear(this);
return StrictFuture<FutureValue>(future);
}
pt->setCancelHandler(this);
}
// NoThrowOnCancel destroys the coroutine frame instead of resuming this
// awaiter, so detach the callback from the Future before that happens.
void cancelWait() override { this->remove(); }
bool resumeImpl() {
pt->clearCancelHandler(this);
// If actor is cancelled, then throw actor_cancelled()
switch (pt->waitState()) {
case ACTOR_WAIT_STATE_CANCELLED:
this->remove();
case ACTOR_WAIT_STATE_CANCELLED_DURING_READY_CHECK:
// await_ready() observed cancellation before await_suspend() registered a callback, so there is nothing to
// remove here.
throw actor_cancelled();
}
bool wasReady = pt->waitState() == ACTOR_WAIT_STATE_NOT_WAITING;
// Actor return from waiting, remove callback and reset wait_state.
if (actorWaitStateIsWaiting(pt->waitState())) {
this->remove();
pt->waitState() = ACTOR_WAIT_STATE_NOT_WAITING;
}
return wasReady;
}
};
template <class PromiseType, class ValueType>
struct AwaitableFutureOwning : Callback<ToFutureVal<ValueType>>, AwaitCancelHandler {
struct AwaitableFutureOwning : Callback<ToFutureVal<ValueType>>,
AwaitableFutureState<AwaitableFutureOwning<PromiseType, ValueType>, PromiseType> {
using FutureValue = ToFutureVal<ValueType>;
Future<FutureValue> future;
PromiseType* pt = nullptr;
@ -711,43 +715,7 @@ struct AwaitableFutureOwning : Callback<ToFutureVal<ValueType>>, AwaitCancelHand
void fire(FutureValue&&) override { pt->resume(); }
void error(Error) override { pt->resume(); }
[[maybe_unused]] [[nodiscard]] bool await_ready() const {
if (actorWaitStateIsCancelled(pt->waitState())) {
pt->waitState() = ACTOR_WAIT_STATE_CANCELLED_DURING_READY_CHECK;
return true;
}
return future.isReady();
}
[[maybe_unused]] void await_suspend(n_coroutine::coroutine_handle<> h) {
pt->setHandle(h);
pt->waitState() = ACTOR_WAIT_STATE_WAITING;
StrictFuture<FutureValue> sf = future;
sf.addCallbackAndClear(this);
pt->setCancelHandler(this);
}
// NoThrowOnCancel destroys the coroutine frame instead of resuming this
// awaiter, so detach the callback from the Future before that happens.
void cancelWait() override { this->remove(); }
bool resumeImpl() {
pt->clearCancelHandler(this);
switch (pt->waitState()) {
case ACTOR_WAIT_STATE_CANCELLED:
this->remove();
case ACTOR_WAIT_STATE_CANCELLED_DURING_READY_CHECK:
throw actor_cancelled();
}
bool wasReady = pt->waitState() == ACTOR_WAIT_STATE_NOT_WAITING;
if (actorWaitStateIsWaiting(pt->waitState())) {
this->remove();
pt->waitState() = ACTOR_WAIT_STATE_NOT_WAITING;
}
return wasReady;
}
StrictFuture<FutureValue> getCallbackFuture() const { return future; }
};
template <class PromiseType, class ValueType>
@ -799,7 +767,7 @@ struct AwaitableFutureErrorOr : AwaitableFutureOwning<PromiseType, SourceValue>
template <class PromiseType, class ValueType, bool ReturnsExplicitVoid = false>
struct ThreadAwaitableFutureStream
: SingleCallback<ToFutureVal<ValueType>>,
AwaitCancelHandler,
AwaitableFutureState<ThreadAwaitableFutureStream<PromiseType, ValueType, ReturnsExplicitVoid>, PromiseType>,
AwaitableResume<ThreadAwaitableFutureStream<PromiseType, ValueType, ReturnsExplicitVoid>,
ValueType,
true,
@ -826,53 +794,7 @@ struct ThreadAwaitableFutureStream
pt->resume();
}
[[maybe_unused]] [[nodiscard]] bool await_ready() const {
if (actorWaitStateIsCancelled(pt->waitState())) {
pt->waitState() = ACTOR_WAIT_STATE_CANCELLED_DURING_READY_CHECK;
// actor was cancelled
return true;
}
return future.isReady();
}
[[maybe_unused]] void await_suspend(n_coroutine::coroutine_handle<> h) {
// Create a coroutine callback if it's the first time being suspended
pt->setHandle(h);
// Set wait_state and add callback
pt->waitState() = ACTOR_WAIT_STATE_WAITING;
auto sf = future;
sf.addCallbackAndClear(this);
pt->setCancelHandler(this);
}
// NoThrowOnCancel destroys the coroutine frame instead of resuming this
// awaiter, so detach the callback from the FutureStream before that happens.
void cancelWait() override { this->remove(); }
bool resumeImpl() {
pt->clearCancelHandler(this);
// If actor is cancelled, then throw actor_cancelled()
switch (pt->waitState()) {
case ACTOR_WAIT_STATE_CANCELLED:
this->remove();
case ACTOR_WAIT_STATE_CANCELLED_DURING_READY_CHECK:
// await_ready() observed cancellation before await_suspend() registered a callback, so there is nothing to
// remove here.
// if the wait_state is -1 we still have to throw, so we fall through to the -2 case
throw actor_cancelled();
}
bool wasReady = pt->waitState() == ACTOR_WAIT_STATE_NOT_WAITING;
// Actor return from waiting, remove callback and reset wait_state.
if (actorWaitStateIsWaiting(pt->waitState())) {
this->remove();
pt->waitState() = ACTOR_WAIT_STATE_NOT_WAITING;
}
return wasReady;
}
FutureType getCallbackFuture() const { return future; }
};
// Promise for fire-and-forget coroutines. It deliberately has no result SAV:

View File

@ -32,13 +32,6 @@
// #define ALLOC_INSTRUMENTATION_STDOUT ENABLED(NOT_IN_CLEAN)
// #define ALLOC_INSTRUMENTATION ENABLED(NOT_IN_CLEAN)
// The form "(1==1)" in this context is used to satisfy both clang and vc++ with a single syntax. Clang rejects "1"
// and vc++ rejects "true".
// FIXME: this has been set to true for 4+ years. We probably do not need the "not thread safe"
// version of the code. Consider removing this and just making it thread safe.
// Also, explain why thread safety is required here and not elsewhere (e.g. Arena and ArenaBlock).
#define FASTALLOC_THREAD_SAFE (FLOW_THREAD_SAFE || (1 == 1))
#if VALGRIND
#include <drd.h>
#include <memcheck.h>
@ -174,8 +167,6 @@ private:
return data;
}
static void* freelist;
static void getMagazine();
static void releaseMagazine(void*);
};

View File

@ -790,8 +790,9 @@ Future<Void> delayedAsyncVar(Reference<AsyncVar<T>> in, Reference<AsyncVar<T>> o
try {
while (true) {
co_await delay(time);
Future<Void> nextChange = in->onChange();
out->set(in->get());
co_await in->onChange();
co_await nextChange;
}
} catch (Error& e) {
out->set(in->get());

View File

@ -531,17 +531,27 @@ knob_min_trace_severity=5
)
self.active_servers.remove(server_id)
def _wait_for_status_update(self, expected, read_status, description, timeout):
deadline = time.monotonic() + timeout
while True:
actual = read_status()
if actual == expected:
return
if time.monotonic() >= deadline:
raise AssertionError(
"Failed to apply {} changes after {}sec. Expected: {}, Actual: {}".format(
description, timeout, expected, actual
)
)
time.sleep(RETRY_INTERVAL_SEC)
# Wait until changes to the set of servers (additions & removals) are applied
def wait_for_server_update(self, timeout=CLUSTER_UPDATE_TIMEOUT_SEC):
time_limit = time.time() + timeout
servers_found = set()
while time.time() <= time_limit:
servers_found = self.get_all_servers_from_status()
if servers_found != self.active_servers:
break
time.sleep(RETRY_INTERVAL_SEC)
assert "Failed to apply server changes after {}sec. Expected: {}, Actual: {}".format(
timeout, self.active_servers, servers_found
self._wait_for_status_update(
self.active_servers,
self.get_all_servers_from_status,
"server",
timeout,
)
# Apply changes to the set of the coordinators, based on the current value of self.coordinators
@ -554,15 +564,11 @@ knob_min_trace_severity=5
# Wait until the changes to the set of the coordinators are applied
def wait_for_coordinator_update(self, timeout=CLUSTER_UPDATE_TIMEOUT_SEC):
time_limit = time.time() + timeout
coord_found = set()
while time.time() <= time_limit:
coord_found = self.get_coordinators_from_status()
if coord_found != self.coordinators:
break
time.sleep(RETRY_INTERVAL_SEC)
assert "Failed to apply coordinator changes after {}sec. Expected: {}, Actual: {}".format(
timeout, self.coordinators, coord_found
self._wait_for_status_update(
self.coordinators,
self.get_coordinators_from_status,
"coordinator",
timeout,
)
# Check if the cluster file was successfully updated too
connection_string = open(self.cluster_file, "r").read()