Merge origin/main into native CDC C bindings

This commit is contained in:
Trevor Clinkenbeard 2026-07-20 06:47:16 -07:00
commit 200931f7e3
139 changed files with 2911 additions and 2295 deletions

View File

@ -2,6 +2,7 @@
Checks: >
-*,
bugprone-dangling-handle,
bugprone-implicit-widening-of-multiplication-result,
bugprone-redundant-branch-condition,
bugprone-shared-ptr-array-mismatch,
bugprone-sizeof-container,
@ -14,9 +15,12 @@ Checks: >
bugprone-suspicious-semicolon,
bugprone-suspicious-string-compare,
bugprone-suspicious-stringview-data-usage,
bugprone-too-small-loop-variable,
bugprone-unique-ptr-array-mismatch,
bugprone-use-after-move,
cppcoreguidelines-avoid-capturing-lambda-coroutines,
misc-coroutine-hostile-raii,
misc-redundant-expression,
modernize-use-auto,
modernize-use-equals-default,
modernize-use-override,
@ -31,6 +35,8 @@ Checks: >
readability-duplicate-include,
readability-inconsistent-ifelse-braces
CheckOptions:
- key: bugprone-dangling-handle.HandleClasses
value: 'std::basic_string_view;std::experimental::basic_string_view;std::span;StringRef'
- key: modernize-use-auto.MinTypeNameLength
value: '11'
- key: readability-braces-around-statements.ShortStatementLines

125
.github/workflows/codebuild-cleanup.yml vendored Normal file
View File

@ -0,0 +1,125 @@
name: CodeBuild Comment Cleanup
on:
issue_comment:
types: [created]
permissions:
issues: write
pull-requests: write
jobs:
cleanup:
if: >
github.event.issue.pull_request &&
github.event.comment.user.login == 'foundationdb-ci'
runs-on: ubuntu-latest
steps:
- name: Minimize Outdated Comments
uses: actions/github-script@v7
with:
script: |
// Example header: ### Result of foundationdb-pr-clang on Linux RHEL 9
// Check if header is at beginning of trimmed body
const commentBody = context.payload.comment.body.trimStart();
const match = commentBody.match(/^### Result of (.*) on (.*)$/m);
if (!match || commentBody.indexOf(match[0]) !== 0) {
console.log("Comment does not start with the expected header format. Skipping.");
return;
}
const header = match[0].trim();
// Get PR info to find the head SHA
const { data: pullRequest } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
const headSha = pullRequest.head.sha;
console.log(`Current PR head SHA: ${headSha}`);
const allOldComments = [];
const commentsToMinimize = [];
let oldCommentAtHead = null;
console.log(`Searching for previous comments with header: "${header}"`);
for await (const { data: comments } of github.paginate.iterator(
github.rest.issues.listComments,
{
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100,
}
)) {
for (const comment of comments) {
if (
comment.user.login === 'foundationdb-ci' &&
comment.id !== context.payload.comment.id &&
comment.body.trimStart().startsWith(header)
) {
allOldComments.push(comment);
// Check for older comment matching PR head commit, in case reports came out-of-order.
// Default sort by created ascending, newest matching head commit wins.
const m = comment.body.match(/^\* Commit ID: ([a-f0-9]+)/m);
if (m && m[1] === headSha) {
oldCommentAtHead = comment;
}
}
}
}
const newCommentM = commentBody.match(/^\* Commit ID: ([a-f0-9]+)/m);
const newCommentSha = newCommentM ? newCommentM[1] : null;
if (newCommentSha && newCommentSha !== headSha && oldCommentAtHead) {
console.log(`New comment is for old commit (${newCommentSha}), but an older comment exists for the head commit (${headSha})`);
commentsToMinimize.push(context.payload.comment);
for (const old of allOldComments) {
if (old.id !== oldCommentAtHead.id) {
commentsToMinimize.push(old);
}
}
} else {
commentsToMinimize.push(...allOldComments);
}
console.log(`Found ${commentsToMinimize.length} comments to minimize.`);
for (const comment of commentsToMinimize) {
const checkQuery = `
query($id: ID!) {
node(id: $id) {
... on Minimizable {
isMinimized
}
}
}
`;
try {
const checkResult = await github.graphql(checkQuery, { id: comment.node_id });
if (checkResult.node.isMinimized) {
console.log(`Comment ${comment.id} is already minimized.`);
continue;
}
} catch (error) {
console.error(`Failed to check minimization status for comment ${comment.id}:`, error);
continue;
}
console.log(`Minimizing comment ${comment.id}`);
const minimizeMutation = `
mutation($id: ID!, $classifier: ReportedContentClassifiers!) {
minimizeComment(input: { subjectId: $id, classifier: $classifier }) {
minimizedComment {
isMinimized
}
}
}
`;
try {
await github.graphql(minimizeMutation, {
id: comment.node_id,
classifier: 'OUTDATED'
});
} catch (error) {
console.error(`Failed to minimize comment ${comment.id}:`, error);
}
}

View File

@ -43,9 +43,10 @@ jobs:
ninja -v \
processed_compile_commands \
fdb_c_generated \
fdboptions \
ProtocolVersion
ProtocolVersion \
fdb_c_generated \
fdb-java
# all flow actors, for generated headers
ACTORS=$(

View File

@ -1022,6 +1022,19 @@ extern "C" DLLEXPORT FDBFuture* fdb_transaction_get_range_split_points(FDBTransa
return (FDBFuture*)(TXN(tr)->getRangeSplitPoints(range, chunk_size).extractPtr()););
}
extern "C" DLLEXPORT FDBFuture* fdb_transaction_get_range_split_points_with_limit(FDBTransaction* tr,
uint8_t const* begin_key_name,
int begin_key_name_length,
uint8_t const* end_key_name,
int end_key_name_length,
int64_t chunk_size,
int limit) {
RETURN_FUTURE_ON_ERROR(
Standalone<VectorRef<KeyRef>>,
KeyRangeRef range(KeyRef(begin_key_name, begin_key_name_length), KeyRef(end_key_name, end_key_name_length));
return (FDBFuture*)(TXN(tr)->getRangeSplitPoints(range, chunk_size, limit).extractPtr()););
}
#include "fdb_c_function_pointers.g.h"
#define FDB_API_CHANGED(func, ver) \

View File

@ -647,6 +647,14 @@ DLLEXPORT WARN_UNUSED_RESULT FDBFuture* fdb_transaction_get_range_split_points(F
int end_key_name_length,
int64_t chunk_size);
DLLEXPORT WARN_UNUSED_RESULT FDBFuture* fdb_transaction_get_range_split_points_with_limit(FDBTransaction* tr,
uint8_t const* begin_key_name,
int begin_key_name_length,
uint8_t const* end_key_name,
int end_key_name_length,
int64_t chunk_size,
int limit);
#define FDB_KEYSEL_LAST_LESS_THAN(k, l) k, l, 0, 0
#define FDB_KEYSEL_LAST_LESS_OR_EQUAL(k, l) k, l, 1, 0
#define FDB_KEYSEL_FIRST_GREATER_THAN(k, l) k, l, 1, 1

View File

@ -115,6 +115,18 @@ func (s Snapshot) GetRangeSplitPoints(r ExactRange, chunkSize int64) FutureKeyAr
)
}
// GetRangeSplitPointsWithLimit returns at most limit interior split points, including shard boundaries.
// The start and end keys of the given range are always included.
func (s Snapshot) GetRangeSplitPointsWithLimit(r ExactRange, chunkSize int64, limit int) FutureKeyArray {
beginKey, endKey := r.FDBRangeKeys()
return s.getRangeSplitPointsWithLimit(
beginKey.FDBKey(),
endKey.FDBKey(),
chunkSize,
limit,
)
}
// Snapshot returns the receiver and allows Snapshot to satisfy the
// ReadTransaction interface.
func (s Snapshot) Options() TransactionOptions {

View File

@ -41,6 +41,7 @@ type ReadTransaction interface {
Snapshot() Snapshot
GetEstimatedRangeSizeBytes(r ExactRange) FutureInt64
GetRangeSplitPoints(r ExactRange, chunkSize int64) FutureKeyArray
GetRangeSplitPointsWithLimit(r ExactRange, chunkSize int64, limit int) FutureKeyArray
Options() TransactionOptions
Cancel()
@ -354,6 +355,20 @@ func (t *transaction) getRangeSplitPoints(beginKey Key, endKey Key, chunkSize in
}
}
func (t *transaction) getRangeSplitPointsWithLimit(beginKey Key, endKey Key, chunkSize int64, limit int) FutureKeyArray {
return &futureKeyArray{
future: newFuture(t, C.fdb_transaction_get_range_split_points_with_limit(
t.ptr,
byteSliceToPtr(beginKey),
C.int(len(beginKey)),
byteSliceToPtr(endKey),
C.int(len(endKey)),
C.int64_t(chunkSize),
C.int(limit),
)),
}
}
// GetRangeSplitPoints returns a list of keys that can split the given range
// into (roughly) equally sized chunks based on chunkSize.
// Note: the returned split points contain the start key and end key of the given range.
@ -366,6 +381,18 @@ func (t Transaction) GetRangeSplitPoints(r ExactRange, chunkSize int64) FutureKe
)
}
// GetRangeSplitPointsWithLimit returns at most limit interior split points, including shard boundaries.
// The start and end keys of the given range are always included.
func (t Transaction) GetRangeSplitPointsWithLimit(r ExactRange, chunkSize int64, limit int) FutureKeyArray {
beginKey, endKey := r.FDBRangeKeys()
return t.getRangeSplitPointsWithLimit(
beginKey.FDBKey(),
endKey.FDBKey(),
chunkSize,
limit,
)
}
func (t *transaction) getReadVersion() FutureInt64 {
return &futureInt64{
future: newFuture(t, C.fdb_transaction_get_read_version(t.ptr)),

View File

@ -1278,6 +1278,42 @@ Java_com_apple_foundationdb_FDBTransaction_Transaction_1getRangeSplitPoints(JNIE
return (jlong)f;
}
JNIEXPORT jlong JNICALL
Java_com_apple_foundationdb_FDBTransaction_Transaction_1getRangeSplitPointsWithLimit(JNIEnv* jenv,
jobject,
jlong tPtr,
jbyteArray beginKeyBytes,
jbyteArray endKeyBytes,
jlong chunkSize,
jint limit) {
if (!tPtr || !beginKeyBytes || !endKeyBytes) {
throwParamNotNull(jenv);
return 0;
}
auto* tr = (FDBTransaction*)tPtr;
auto* startKey = (uint8_t*)jenv->GetByteArrayElements(beginKeyBytes, JNI_NULL);
if (!startKey) {
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
return 0;
}
auto* endKey = (uint8_t*)jenv->GetByteArrayElements(endKeyBytes, JNI_NULL);
if (!endKey) {
jenv->ReleaseByteArrayElements(beginKeyBytes, (jbyte*)startKey, JNI_ABORT);
if (!jenv->ExceptionOccurred())
throwRuntimeEx(jenv, "Error getting handle to native resources");
return 0;
}
FDBFuture* f = fdb_transaction_get_range_split_points_with_limit(
tr, startKey, jenv->GetArrayLength(beginKeyBytes), endKey, jenv->GetArrayLength(endKeyBytes), chunkSize, limit);
jenv->ReleaseByteArrayElements(beginKeyBytes, (jbyte*)startKey, JNI_ABORT);
jenv->ReleaseByteArrayElements(endKeyBytes, (jbyte*)endKey, JNI_ABORT);
return (jlong)f;
}
JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDBTransaction_Transaction_1set(JNIEnv* jenv,
jobject,
jlong tPtr,

View File

@ -87,11 +87,21 @@ class FDBTransaction extends NativeObjectWrapper implements Transaction, OptionC
return FDBTransaction.this.getRangeSplitPoints(begin, end, chunkSize);
}
@Override
public CompletableFuture<KeyArrayResult> getRangeSplitPoints(byte[] begin, byte[] end, long chunkSize, int limit) {
return FDBTransaction.this.getRangeSplitPoints(begin, end, chunkSize, limit);
}
@Override
public CompletableFuture<KeyArrayResult> getRangeSplitPoints(Range range, long chunkSize) {
return FDBTransaction.this.getRangeSplitPoints(range, chunkSize);
}
@Override
public CompletableFuture<KeyArrayResult> getRangeSplitPoints(Range range, long chunkSize, int limit) {
return FDBTransaction.this.getRangeSplitPoints(range, chunkSize, limit);
}
@Override
public AsyncIterable<MappedKeyValue> getMappedRange(KeySelector begin, KeySelector end, byte[] mapper,
int limit, boolean reverse, StreamingMode mode) {
@ -341,11 +351,26 @@ class FDBTransaction extends NativeObjectWrapper implements Transaction, OptionC
}
}
@Override
public CompletableFuture<KeyArrayResult> getRangeSplitPoints(byte[] begin, byte[] end, long chunkSize, int limit) {
pointerReadLock.lock();
try {
return new FutureKeyArray(Transaction_getRangeSplitPointsWithLimit(getPtr(), begin, end, chunkSize, limit), executor);
} finally {
pointerReadLock.unlock();
}
}
@Override
public CompletableFuture<KeyArrayResult> getRangeSplitPoints(Range range, long chunkSize) {
return this.getRangeSplitPoints(range.begin, range.end, chunkSize);
}
@Override
public CompletableFuture<KeyArrayResult> getRangeSplitPoints(Range range, long chunkSize, int limit) {
return this.getRangeSplitPoints(range.begin, range.end, chunkSize, limit);
}
@Override
public AsyncIterable<MappedKeyValue> getMappedRange(KeySelector begin, KeySelector end, byte[] mapper, int limit,
boolean reverse, StreamingMode mode) {
@ -842,4 +867,5 @@ class FDBTransaction extends NativeObjectWrapper implements Transaction, OptionC
private native long Transaction_getKeyLocations(long cPtr, byte[] key);
private native long Transaction_getEstimatedRangeSizeBytes(long cPtr, byte[] keyBegin, byte[] keyEnd);
private native long Transaction_getRangeSplitPoints(long cPtr, byte[] keyBegin, byte[] keyEnd, long chunkSize);
private native long Transaction_getRangeSplitPointsWithLimit(long cPtr, byte[] keyBegin, byte[] keyEnd, long chunkSize, int limit);
}

View File

@ -504,6 +504,18 @@ public interface ReadTransaction extends ReadTransactionContext {
*/
CompletableFuture<KeyArrayResult> getRangeSplitPoints(byte[] begin, byte[] end, long chunkSize);
/**
* Gets at most <code>limit</code> interior split points, including shard boundaries.
* The start and end keys of the given range are always included.
*
* @param begin the beginning of the range (inclusive)
* @param end the end of the range (exclusive)
* @param chunkSize the target estimated byte size of each chunk
* @param limit the maximum number of interior split points, or a negative value for no limit
* @return a handle to access the results of the asynchronous call
*/
CompletableFuture<KeyArrayResult> getRangeSplitPoints(byte[] begin, byte[] end, long chunkSize, int limit);
/**
* Gets a list of keys that can split the given range into (roughly) equally sized chunks based on <code>chunkSize</code>
* Note: the returned split points contain the start key and end key of the given range.
@ -515,6 +527,17 @@ public interface ReadTransaction extends ReadTransactionContext {
*/
CompletableFuture<KeyArrayResult> getRangeSplitPoints(Range range, long chunkSize);
/**
* Gets at most <code>limit</code> interior split points, including shard boundaries.
* The start and end keys of the given range are always included.
*
* @param range the range of the keys
* @param chunkSize the target estimated byte size of each chunk
* @param limit the maximum number of interior split points, or a negative value for no limit
* @return a handle to access the results of the asynchronous call
*/
CompletableFuture<KeyArrayResult> getRangeSplitPoints(Range range, long chunkSize, int limit);
/**
* Returns a set of options that can be set on a {@code Transaction}
*

View File

@ -790,4 +790,3 @@ public class StackTester {
private StackTester() {}
}

View File

@ -541,17 +541,29 @@ class TransactionRead(_FDBBase):
)
)
def get_range_split_points(self, begin_key, end_key, chunk_size):
def get_range_split_points(self, begin_key, end_key, chunk_size, limit=-1):
if begin_key is None or end_key is None or chunk_size <= 0:
raise Exception("Invalid begin key, end key or chunk size")
if limit < 0:
return FutureKeyArray(
self.capi.fdb_transaction_get_range_split_points(
self.tpointer,
begin_key,
len(begin_key),
end_key,
len(end_key),
chunk_size,
)
)
return FutureKeyArray(
self.capi.fdb_transaction_get_range_split_points(
self.capi.fdb_transaction_get_range_split_points_with_limit(
self.tpointer,
begin_key,
len(begin_key),
end_key,
len(end_key),
chunk_size,
limit,
)
)
@ -1793,6 +1805,17 @@ def init_c_api():
]
_capi.fdb_transaction_get_range_split_points.restype = ctypes.c_void_p
_capi.fdb_transaction_get_range_split_points_with_limit.argtypes = [
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_int,
ctypes.c_void_p,
ctypes.c_int,
ctypes.c_int64,
ctypes.c_int,
]
_capi.fdb_transaction_get_range_split_points_with_limit.restype = ctypes.c_void_p
_capi.fdb_transaction_add_conflict_range.argtypes = [
ctypes.c_void_p,
ctypes.c_void_p,

View File

@ -225,6 +225,19 @@ def test_get_client_status(db):
assert status["Healthy"]
def test_range_split_points(db):
begin = b"\x02range-split-points-a"
end = b"\x02range-split-points-z"
tr = db.create_transaction()
for limit in (-1, 0, 1, 2):
split_points = tr.get_range_split_points(begin, end, 1000000, limit).wait()
assert split_points[0] == begin
assert split_points[-1] == end
if limit >= 0:
assert len(split_points) <= limit + 2
def run_unit_tests(db):
try:
log("test_db_options")
@ -255,6 +268,8 @@ def run_unit_tests(db):
test_get_approximate_size(db)
log("test_get_client_status")
test_get_client_status(db)
log("test_range_split_points")
test_range_split_points(db)
except fdb.FDBError as e:
print("Unit tests failed: %s" % e.description)

View File

@ -89,6 +89,7 @@ module FDB
attach_function :fdb_future_get_key, [ :pointer, :pointer, :pointer ], :fdb_error
attach_function :fdb_future_get_value, [ :pointer, :pointer, :pointer, :pointer ], :fdb_error
attach_function :fdb_future_get_keyvalue_array, [ :pointer, :pointer, :pointer, :pointer ], :fdb_error
attach_function :fdb_future_get_key_array, [ :pointer, :pointer, :pointer ], :fdb_error
attach_function :fdb_future_get_string_array, [ :pointer, :pointer, :pointer ], :fdb_error
attach_function :fdb_create_database, [ :string, :pointer ], :fdb_error
@ -485,7 +486,7 @@ module FDB
ks = FFI::MemoryPointer.new :pointer
count = FFI::MemoryPointer.new :int
FDBC.check_error FDBC.fdb_future_get_key_array(@fpointer, kvs, count)
FDBC.check_error FDBC.fdb_future_get_key_array(@fpointer, ks, count)
ks = ks.read_pointer
(0..count.read_int-1).map{|i|

View File

@ -321,7 +321,7 @@ class Tester
inst.tr.get_estimated_range_size_bytes(inst.wait_and_pop, inst.wait_and_pop).to_i
inst.push("GOT_ESTIMATED_RANGE_SIZE")
when "GET_RANGE_SPLIT_POINTS"
inst.tr.get_range_split_points(inst.wait_and_pop, inst.wait_and_pop, inst.wait_and_pop).length()
inst.tr.get_range_split_points(inst.wait_and_pop, inst.wait_and_pop, inst.wait_and_pop).wait()
inst.push("GOT_RANGE_SPLIT_POINTS")
when "GET_KEY"
selector = FDB::KeySelector.new(inst.wait_and_pop, inst.wait_and_pop, inst.wait_and_pop)

View File

@ -1457,7 +1457,7 @@ Serializes all endpoints directly: `waitFailure`, `getRateInfo`, `haltRatekeeper
## 13. Worker Protocol
**Source:** `fdbserver/core/include/fdbserver/core/WorkerInterface.actor.h`
**Source:** `fdbserver/core/include/fdbserver/core/WorkerInterface.h`
Workers host server roles. The cluster controller sends initialization requests.
@ -1543,9 +1543,6 @@ All follow the pattern: fields describing the role configuration + `ReplyPromise
### NetworkTestRequest
`Key key`, `uint32_t replySize`, `reply`**NetworkTestReply** {`Value value`}.
### NetworkTestStreamingRequest
`reply` (stream) → **NetworkTestStreamingReply** {`Optional<UID> acknowledgeToken`, `uint16_t sequence`, `int index`}.
---
## 15. Client Worker / Debug / Process Protocols

View File

@ -97,7 +97,7 @@ Plus supporting code: [`fdbserver/worker/`](https://github.com/apple/foundationd
- Coordinator state is persisted via `KeyValueStoreMemory` backed by a `DiskQueue` (`.fdq` files) -- the same engine used by TLogs.
**Role recruitment:**
- Worker processes register with CC via `registrationClient()` ([`worker.actor.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/worker/worker.actor.cpp)).
- Worker processes register with CC via `registrationClient()` ([`worker.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/worker/worker.cpp)).
- CC maintains a pool of available workers with their `ProcessClass` and `LocalityData`.
- When the transaction system needs to be (re)constituted, CC recruits: Master/Sequencer, CommitProxies, GrvProxies, Resolvers, TLogs.
- Recruitment considers fitness (class match), locality (datacenter placement), and excludes failed/excluded processes.
@ -107,7 +107,7 @@ Plus supporting code: [`fdbserver/worker/`](https://github.com/apple/foundationd
- `ServerDBInfo` is the cluster-wide configuration broadcast. Contains: master interface, proxy lists, log system config, recovery state, latency band config.
- Updated by CC and distributed to all workers. Workers react to changes (e.g., new proxy set).
**Principal files:** [`ClusterController.actor.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/clustercontroller/ClusterController.actor.cpp), `ClusterController.h`, [`Coordination.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/coordinator/Coordination.cpp), [`LeaderElection.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/LeaderElection.cpp), [`CoordinatedState.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/CoordinatedState.cpp), [`WorkerInterface.actor.h`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/include/fdbserver/core/WorkerInterface.actor.h)
**Principal files:** [`ClusterController.actor.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/clustercontroller/ClusterController.actor.cpp), `ClusterController.h`, [`Coordination.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/coordinator/Coordination.cpp), [`LeaderElection.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/LeaderElection.cpp), [`CoordinatedState.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/CoordinatedState.cpp), [`WorkerInterface.h`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/include/fdbserver/core/WorkerInterface.h)
---

View File

@ -294,7 +294,7 @@ struct ServerDBInfo {
---
## WorkerInterface -- [`WorkerInterface.actor.h`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/include/fdbserver/core/WorkerInterface.actor.h)`:45-126`
## WorkerInterface -- [`WorkerInterface.h`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/include/fdbserver/core/WorkerInterface.h)`:45-126`
RPCs exposed by every worker process:
@ -315,5 +315,5 @@ RPCs exposed by every worker process:
| [`fdbserver/coordinator/Coordination.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/coordinator/Coordination.cpp) | leaderRegister, generation register, coordination |
| [`fdbserver/core/LeaderElection.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/LeaderElection.cpp) | tryBecomeLeaderInternal, candidacy, heartbeat |
| [`fdbserver/core/CoordinatedState.cpp`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/CoordinatedState.cpp) | Replicated read/write over generation registers |
| [`fdbserver/core/include/fdbserver/core/WorkerInterface.actor.h`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/include/fdbserver/core/WorkerInterface.actor.h) | WorkerInterface, ClusterControllerFullInterface |
| [`fdbserver/core/include/fdbserver/core/WorkerInterface.h`](https://github.com/apple/foundationdb/blob/main/fdbserver/core/include/fdbserver/core/WorkerInterface.h) | WorkerInterface, ClusterControllerFullInterface |
| `fdbserver/core/include/fdbserver/core/ServerDBInfo.h` | ServerDBInfo structure and broadcasting |

View File

@ -760,6 +760,12 @@ Applications must provide error handling and an appropriate retry loop around th
|future-return0| the list of split points. |future-return1| call :func:`fdb_future_get_key_array()` to extract the array, |future-return2|
.. function:: FDBFuture* fdb_transaction_get_range_split_points_with_limit( FDBTransaction* tr, uint8_t const* begin_key_name, int begin_key_name_length, uint8_t const* end_key_name, int end_key_name_length, int64_t chunk_size, int limit)
Returns at most ``limit`` interior split points, including shard boundaries. The start and end keys of the given range are always included. A negative ``limit`` preserves the unlimited behavior of :func:`fdb_transaction_get_range_split_points`.
|future-return0| the list of split points. |future-return1| call :func:`fdb_future_get_key_array()` to extract the array, |future-return2|
.. function:: FDBFuture* fdb_transaction_get_key(FDBTransaction* transaction, uint8_t const* key_name, int key_name_length, fdb_bool_t or_equal, int offset, fdb_bool_t snapshot)
Resolves a :ref:`key selector <key-selectors>` against the keys in the database snapshot represented by ``transaction``.

View File

@ -839,9 +839,9 @@ Transaction misc functions
.. note:: The estimated size is calculated based on the sampling done by FDB server. The sampling algorithm works roughly in this way: the larger the key-value pair is, the more likely it would be sampled and the more accurate its sampled size would be. And due to that reason it is recommended to use this API to query against large ranges for accuracy considerations. For a rough reference, if the returned size is larger than 3MB, one can consider the size to be accurate.
.. method:: Transaction.get_range_split_points(self, begin_key, end_key, chunk_size)
.. method:: Transaction.get_range_split_points(self, begin_key, end_key, chunk_size, limit=-1)
Gets a list of keys that can split the given range into (roughly) equally sized chunks based on ``chunk_size``. Returns a :class:`FutureKeyArray`.
Gets a list of keys that can split the given range into (roughly) equally sized chunks based on ``chunk_size``. A non-negative ``limit`` caps the number of interior split points, including shard boundaries. Returns a :class:`FutureKeyArray`.
.. note:: The returned split points contain the start key and end key of the given range
.. method:: Transaction.get_approximate_size()
@ -1547,4 +1547,3 @@ Locality information
.. method:: fdb.locality.get_addresses_for_key(tr, key)
Returns a :class:`fdb.FutureStringArray`. You must call the :meth:`fdb.Future.wait()` method on this object to retrieve a list of public network addresses as strings, one for each of the storage servers responsible for storing ``key`` and its associated value.

View File

@ -10,11 +10,12 @@ This guide explains how to run ``clang-tidy`` locally so you can fix issues befo
What clang-tidy checks
======================
FoundationDB enables 29 checks configured in the ``.clang-tidy`` file at the repository root. The
FoundationDB enables 33 checks configured in the ``.clang-tidy`` file at the repository root. The
intent is to enable more as we go forward. Here are some example rules:
* **15 Bugprone rules** -- catch potential runtime errors (e.g., ``bugprone-use-after-move``, ``bugprone-suspicious-memory-comparison``)
* **1 Misc rule** -- catch RAII objects held across coroutine suspension points (``misc-coroutine-hostile-raii``)
* **17 Bugprone rules** -- catch potential runtime errors (e.g., ``bugprone-too-small-loop-variable``, ``bugprone-implicit-widening-of-multiplication-result``)
* **1 C++ Core Guidelines rule** -- catch unsafe captures in coroutine lambdas (``cppcoreguidelines-avoid-capturing-lambda-coroutines``)
* **2 Misc rules** -- catch redundant expressions and RAII objects held across coroutine suspension points
* **4 Modernize rules** -- encourage modern C++ practices (e.g., ``modernize-use-auto``, ``modernize-use-override``)
* **2 Performance rules** -- avoid unnecessary copies and pointless moves (e.g., ``performance-for-range-copy``, ``performance-move-const-arg``)
* **7 Readability rules** -- improve code clarity (e.g., ``readability-container-contains``, ``readability-container-size-empty``)

View File

@ -1619,7 +1619,8 @@ Future<std::vector<LogFile>> BackupContainerFileSystem::listLogFiles(Version beg
std::string firstPath =
BackupContainerFileSystemImpl::cleanFolderString(BackupContainerFileSystemImpl::logVersionFolderString(
std::max<Version>(0,
beginVersion - CLIENT_KNOBS->BACKUP_MAX_LOG_RANGES * CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE),
beginVersion - static_cast<Version>(CLIENT_KNOBS->BACKUP_MAX_LOG_RANGES) *
CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE),
mutationLogType));
std::string lastPath = BackupContainerFileSystemImpl::cleanFolderString(
BackupContainerFileSystemImpl::logVersionFolderString(targetVersion, mutationLogType));

View File

@ -41,7 +41,6 @@
#include "fdbclient/ManagementAPI.h"
#include "fdbclient/RangeLock.h"
#include "PartitionedLogIterator.h"
#include "RestoreInterface.h"
#include "fdbclient/Status.h"
#include "fdbclient/SystemData.h"
#include "fdbclient/TaskBucket.h"
@ -171,13 +170,6 @@ Future<bool> verifyBulkDumpDatasetCompleteness(Reference<IBackupContainer> bc, s
Optional<std::string> fileBackupAgentProxy = Optional<std::string>();
#define SevFRTestInfo SevVerbose
// #define SevFRTestInfo SevInfo
static std::string boolToYesOrNo(bool val) {
return val ? std::string("Yes") : std::string("No");
}
static std::string versionToString(Optional<Version> version) {
if (version.present())
return std::to_string(version.get());
@ -1087,7 +1079,7 @@ PartitionedLogIteratorSimple::PartitionedLogIteratorSimple(Reference<IBackupCont
std::vector<RestoreConfig::RestoreFile> _files,
std::vector<Version> _endVersions)
: bc(_bc), tag(_tag), endVersions(_endVersions), files(std::move(_files)), bufferOffset(0) {
bufferCapacity = BATCH_READ_BLOCK_COUNT * BLOCK_SIZE;
bufferCapacity = static_cast<size_t>(BATCH_READ_BLOCK_COUNT) * BLOCK_SIZE;
buffer = std::shared_ptr<char[]>(new char[bufferCapacity]());
fileOffset = 0;
fileIndex = 0;
@ -3174,9 +3166,10 @@ struct BackupLogsDispatchTask : BackupTaskFuncBase {
co_return;
}
Version endVersion = std::max<Version>(tr->getReadVersion().get() + 1,
beginVersion + (CLIENT_KNOBS->BACKUP_MAX_LOG_RANGES - 1) *
CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE);
Version endVersion =
std::max<Version>(tr->getReadVersion().get() + 1,
beginVersion + static_cast<Version>(CLIENT_KNOBS->BACKUP_MAX_LOG_RANGES - 1) *
CLIENT_KNOBS->LOG_RANGE_BLOCK_SIZE);
TraceEvent("FileBackupLogDispatch")
.suppressFor(60)
@ -8429,76 +8422,3 @@ Future<EBackupState> FileBackupAgent::waitBackup(Database cx,
Future<Void> FileBackupAgent::changePause(Database db, bool pause) {
return FileBackupAgentImpl::changePause(this, db, pause);
}
// Fast Restore addPrefix test helper functions
static std::pair<bool, bool> insideValidRange(KeyValueRef kv,
Standalone<VectorRef<KeyRangeRef>> restoreRanges,
Standalone<VectorRef<KeyRangeRef>> backupRanges) {
bool insideRestoreRange = false;
bool insideBackupRange = false;
for (auto& range : restoreRanges) {
TraceEvent(SevFRTestInfo, "InsideValidRestoreRange")
.detail("Key", kv.key)
.detail("Range", range)
.detail("Inside", (kv.key >= range.begin && kv.key < range.end));
if (kv.key >= range.begin && kv.key < range.end) {
insideRestoreRange = true;
break;
}
}
for (auto& range : backupRanges) {
TraceEvent(SevFRTestInfo, "InsideValidBackupRange")
.detail("Key", kv.key)
.detail("Range", range)
.detail("Inside", (kv.key >= range.begin && kv.key < range.end));
if (kv.key >= range.begin && kv.key < range.end) {
insideBackupRange = true;
break;
}
}
return std::make_pair(insideBackupRange, insideRestoreRange);
}
// Write [begin, end) in kvs to DB
static Future<Void> writeKVs(Database cx, Standalone<VectorRef<KeyValueRef>> kvs, int begin, int end) {
co_await runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) -> Future<Void> {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
int index = begin;
while (index < end) {
TraceEvent(SevFRTestInfo, "TransformDatabaseContentsWriteKV")
.detail("Index", index)
.detail("KVs", kvs.size())
.detail("Key", kvs[index].key)
.detail("Value", kvs[index].value);
tr->set(kvs[index].key, kvs[index].value);
++index;
}
return Void();
});
// Sanity check data has been written to DB
ReadYourWritesTransaction tr(cx);
while (true) {
Error err;
try {
tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr.setOption(FDBTransactionOptions::READ_LOCK_AWARE);
KeyRef k1 = kvs[begin].key;
KeyRef k2 = end < kvs.size() ? kvs[end].key : allKeys.end;
TraceEvent(SevFRTestInfo, "TransformDatabaseContentsWriteKVReadBack")
.detail("Range", KeyRangeRef(k1, k2))
.detail("Begin", begin)
.detail("End", end);
RangeResult readKVs = co_await tr.getRange(KeyRangeRef(k1, k2), CLIENT_KNOBS->TOO_MANY);
ASSERT(!readKVs.empty() || begin == end);
break;
} catch (Error& e) {
err = e;
}
TraceEvent("TransformDatabaseContentsWriteKVReadBackError").error(err);
co_await tr.onError(err);
}
TraceEvent(SevFRTestInfo, "TransformDatabaseContentsWriteKVDone").detail("Begin", begin).detail("End", end);
}

View File

@ -246,12 +246,21 @@ ThreadFuture<int64_t> DLTransaction::getEstimatedRangeSizeBytes(const KeyRangeRe
}
ThreadFuture<Standalone<VectorRef<KeyRef>>> DLTransaction::getRangeSplitPoints(const KeyRangeRef& range,
int64_t chunkSize) {
int64_t chunkSize,
int limit) {
if (!api->transactionGetRangeSplitPoints) {
return unsupported_operation();
}
FdbCApi::FDBFuture* f = api->transactionGetRangeSplitPoints(
tr, range.begin.begin(), range.begin.size(), range.end.begin(), range.end.size(), chunkSize);
FdbCApi::FDBFuture* f;
if (limit < 0) {
f = api->transactionGetRangeSplitPoints(
tr, range.begin.begin(), range.begin.size(), range.end.begin(), range.end.size(), chunkSize);
} else if (api->transactionGetRangeSplitPointsWithLimit) {
f = api->transactionGetRangeSplitPointsWithLimit(
tr, range.begin.begin(), range.begin.size(), range.end.begin(), range.end.size(), chunkSize, limit);
} else {
return unsupported_operation();
}
return toThreadFuture<Standalone<VectorRef<KeyRef>>>(api, f, [](FdbCApi::FDBFuture* f, FdbCApi* api) {
const FdbCApi::FDBKey* splitKeys;
@ -872,6 +881,11 @@ void DLApi::init() {
fdbCPath,
"fdb_transaction_get_range_split_points",
headerVersion >= 700);
loadClientFunction(&api->transactionGetRangeSplitPointsWithLimit,
lib,
fdbCPath,
"fdb_transaction_get_range_split_points_with_limit",
false);
loadClientFunction(&api->futureGetDouble,
lib,
@ -1246,8 +1260,10 @@ ThreadFuture<int64_t> MultiVersionTransaction::getEstimatedRangeSizeBytes(const
}
ThreadFuture<Standalone<VectorRef<KeyRef>>> MultiVersionTransaction::getRangeSplitPoints(const KeyRangeRef& range,
int64_t chunkSize) {
return executeOperation(&ITransaction::getRangeSplitPoints, range, std::forward<int64_t>(chunkSize));
int64_t chunkSize,
int limit) {
return executeOperation(
&ITransaction::getRangeSplitPoints, range, std::forward<int64_t>(chunkSize), std::forward<int>(limit));
}
void MultiVersionTransaction::atomicOp(const KeyRef& key, const ValueRef& value, uint32_t operationType) {

View File

@ -300,14 +300,6 @@ int64_t extractIntOption(Optional<StringRef> value, int64_t minValue, int64_t ma
return passed;
}
uint64_t extractHexOption(StringRef value) {
char* end;
uint64_t id = strtoull(value.toString().c_str(), &end, 16);
if (*end)
throw invalid_option_value();
return id;
}
void DatabaseContext::setOption(FDBDatabaseOptions::Option option, Optional<StringRef> value) {
int defaultFor = FDBDatabaseOptions::optionInfo.getMustExist(option).defaultFor;
if (defaultFor >= 0) {
@ -3246,7 +3238,8 @@ ACTOR Future<Void> getRangeStreamImpl(Reference<TransactionState> trState,
ACTOR Future<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(Reference<TransactionState> trState,
KeyRange keys,
int64_t chunkSize);
int64_t chunkSize,
int limit);
// Streams the requested key range directly from storage servers without fragment-level parallelism.
ACTOR Future<Void> getRangeStream(Reference<TransactionState> trState,
PromiseStream<RangeResult> _results,
@ -5970,51 +5963,126 @@ Future<Standalone<VectorRef<ReadHotRangeWithMetrics>>> DatabaseContext::getReadH
return ::getReadHotRanges(Database(Reference<DatabaseContext>::addRef(this)), keys);
}
static int getRangeSplitPointsLocationLimit(int splitPointLimit, int maxLocations, int avoidLocationLimit) {
int locationLimit = splitPointLimit >= 0 && splitPointLimit < maxLocations ? splitPointLimit + 1 : maxLocations;
if (locationLimit == avoidLocationLimit) {
ASSERT(maxLocations > 1);
locationLimit += locationLimit < maxLocations ? 1 : -1;
}
return locationLimit;
}
class RangeSplitPointsBuilder {
Standalone<VectorRef<KeyRef>> results;
int remaining;
public:
RangeSplitPointsBuilder(KeyRef begin, int limit) : remaining(limit) {
results.push_back_deep(results.arena(), begin);
}
int getRemaining() const { return remaining; }
void appendShardBoundary(KeyRef boundary) {
if (results.back() == boundary || remaining == 0) {
return;
}
results.push_back_deep(results.arena(), boundary);
if (remaining > 0) {
--remaining;
}
}
void appendSplitPoints(Standalone<VectorRef<KeyRef>> const& splitPoints) {
int splitPointCount = splitPoints.size();
if (remaining >= 0) {
splitPointCount = std::min(splitPointCount, remaining);
}
if (splitPointCount == 0) {
return;
}
results.append(results.arena(), splitPoints.begin(), splitPointCount);
results.arena().dependsOn(splitPoints.arena());
if (remaining > 0) {
remaining -= splitPointCount;
}
}
Standalone<VectorRef<KeyRef>> finish(KeyRef end) {
if (results.back() != end) {
results.push_back_deep(results.arena(), end);
}
return results;
}
};
ACTOR Future<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(Reference<TransactionState> trState,
KeyRange keys,
int64_t chunkSize) {
int64_t chunkSize,
int limit) {
state Span span("NAPI:GetRangeSplitPoints"_loc, trState->spanContext);
state Key beginKey = keys.begin;
state RangeSplitPointsBuilder results(keys.begin, limit);
if (limit == 0) {
return results.finish(keys.end);
}
loop {
state std::vector<KeyRangeLocationInfo> locations = wait(getKeyRangeLocations(
trState, keys, CLIENT_KNOBS->TOO_MANY, Reverse::False, &StorageServerInterface::getRangeSplitPoints));
trState,
KeyRangeRef(beginKey, keys.end),
getRangeSplitPointsLocationLimit(
results.getRemaining(), CLIENT_KNOBS->TOO_MANY, CLIENT_KNOBS->STORAGE_METRICS_SHARD_LIMIT),
Reverse::False,
&StorageServerInterface::getRangeSplitPoints));
try {
state int nLocs = locations.size();
state std::vector<Future<SplitRangeReply>> fReplies(nLocs);
KeyRef partBegin, partEnd;
for (int i = 0; i < nLocs; i++) {
partBegin = (i == 0) ? keys.begin : locations[i].range.begin;
partEnd = (i == nLocs - 1) ? keys.end : locations[i].range.end;
SplitRangeRequest req(KeyRangeRef(partBegin, partEnd), chunkSize);
fReplies[i] = loadBalance(locations[i].locations->locations(),
&StorageServerInterface::getRangeSplitPoints,
req,
TaskPriority::DataDistribution);
}
wait(waitForAll(fReplies));
Standalone<VectorRef<KeyRef>> results;
results.push_back_deep(results.arena(), keys.begin);
for (int i = 0; i < nLocs; i++) {
if (i > 0) {
results.push_back_deep(results.arena(),
locations[i].range.begin); // Need this shard boundary
if (limit >= 0) {
state int i = 0;
for (; i < nLocs; i++) {
if (i > 0 || beginKey != keys.begin) {
results.appendShardBoundary(locations[i].range.begin);
}
if (results.getRemaining() == 0) {
break;
}
KeyRef partBegin = (i == 0) ? beginKey : locations[i].range.begin;
KeyRef partEnd = std::min(keys.end, locations[i].range.end);
SplitRangeRequest req(KeyRangeRef(partBegin, partEnd), chunkSize, results.getRemaining());
SplitRangeReply reply = wait(loadBalance(locations[i].locations->locations(),
&StorageServerInterface::getRangeSplitPoints,
req,
TaskPriority::DataDistribution));
results.appendSplitPoints(reply.splitPoints);
}
if (fReplies[i].get().splitPoints.size() > 0) {
results.append(
results.arena(), fReplies[i].get().splitPoints.begin(), fReplies[i].get().splitPoints.size());
results.arena().dependsOn(fReplies[i].get().splitPoints.arena());
} else {
state std::vector<Future<SplitRangeReply>> fReplies(nLocs);
for (int i = 0; i < nLocs; i++) {
KeyRef partBegin = (i == 0) ? beginKey : locations[i].range.begin;
KeyRef partEnd = std::min(keys.end, locations[i].range.end);
SplitRangeRequest req(KeyRangeRef(partBegin, partEnd), chunkSize, limit);
fReplies[i] = loadBalance(locations[i].locations->locations(),
&StorageServerInterface::getRangeSplitPoints,
req,
TaskPriority::DataDistribution);
}
wait(waitForAll(fReplies));
for (int i = 0; i < nLocs; i++) {
if (i > 0 || beginKey != keys.begin) {
results.appendShardBoundary(locations[i].range.begin);
}
results.appendSplitPoints(fReplies[i].get().splitPoints);
}
}
if (results.back() != keys.end) {
results.push_back_deep(results.arena(), keys.end);
if (results.getRemaining() == 0 || keys.end <= locations.back().range.end) {
return results.finish(keys.end);
}
return results;
beginKey = locations.back().range.end;
} catch (Error& e) {
if (e.code() == error_code_wrong_shard_server || e.code() == error_code_all_alternatives_failed) {
trState->cx->invalidateCache(keys);
beginKey = keys.begin;
results = RangeSplitPointsBuilder(keys.begin, limit);
wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, TaskPriority::DataDistribution));
} else {
TraceEvent(SevError, "GetRangeSplitPoints").error(e);
@ -6024,8 +6092,95 @@ ACTOR Future<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(Reference<Transa
}
}
Future<Standalone<VectorRef<KeyRef>>> Transaction::getRangeSplitPoints(KeyRange const& keys, int64_t chunkSize) {
return ::getRangeSplitPoints(trState, keys, chunkSize);
Future<Standalone<VectorRef<KeyRef>>> Transaction::getRangeSplitPoints(KeyRange const& keys,
int64_t chunkSize,
int limit) {
return ::getRangeSplitPoints(trState, keys, chunkSize, limit);
}
TEST_CASE("/fdbclient/NativeAPI/rangeSplitPoints/locationLimit") {
constexpr int maxLocations = 1000;
constexpr int dataDistributionLocationLimit = 100;
ASSERT(getRangeSplitPointsLocationLimit(-1, maxLocations, dataDistributionLocationLimit) == maxLocations);
ASSERT(getRangeSplitPointsLocationLimit(0, maxLocations, dataDistributionLocationLimit) == 1);
ASSERT(getRangeSplitPointsLocationLimit(16, maxLocations, dataDistributionLocationLimit) == 17);
ASSERT(getRangeSplitPointsLocationLimit(99, maxLocations, dataDistributionLocationLimit) == 101);
ASSERT(getRangeSplitPointsLocationLimit(9, maxLocations, 10) == 11);
ASSERT(getRangeSplitPointsLocationLimit(maxLocations - 1, maxLocations, dataDistributionLocationLimit) ==
maxLocations);
ASSERT(getRangeSplitPointsLocationLimit(maxLocations, maxLocations, dataDistributionLocationLimit) == maxLocations);
ASSERT(getRangeSplitPointsLocationLimit(
std::numeric_limits<int>::max(), maxLocations, dataDistributionLocationLimit) == maxLocations);
ASSERT(getRangeSplitPointsLocationLimit(maxLocations, maxLocations, maxLocations) == maxLocations - 1);
return Void();
}
TEST_CASE("/fdbclient/NativeAPI/rangeSplitPoints/multipleShards") {
Standalone<VectorRef<KeyRef>> firstShard;
firstShard.push_back_deep(firstShard.arena(), "A1"_sr);
firstShard.push_back_deep(firstShard.arena(), "A2"_sr);
Standalone<VectorRef<KeyRef>> secondShard;
secondShard.push_back_deep(secondShard.arena(), "B1"_sr);
secondShard.push_back_deep(secondShard.arena(), "B2"_sr);
Standalone<VectorRef<KeyRef>> firstShardEndingAtBoundary;
firstShardEndingAtBoundary.push_back_deep(firstShardEndingAtBoundary.arena(), "B"_sr);
RangeSplitPointsBuilder zero("A"_sr, 0);
zero.appendSplitPoints(firstShard);
zero.appendShardBoundary("B"_sr);
Standalone<VectorRef<KeyRef>> zeroResults = zero.finish("Z"_sr);
ASSERT(zeroResults.size() == 2 && zeroResults[0] == "A"_sr && zeroResults[1] == "Z"_sr);
RangeSplitPointsBuilder one("A"_sr, 1);
one.appendSplitPoints(firstShard);
ASSERT(one.getRemaining() == 0);
one.appendShardBoundary("B"_sr);
Standalone<VectorRef<KeyRef>> oneResults = one.finish("Z"_sr);
ASSERT(oneResults.size() == 3 && oneResults[1] == "A1"_sr && oneResults[2] == "Z"_sr);
RangeSplitPointsBuilder two("A"_sr, 2);
two.appendShardBoundary("B"_sr);
ASSERT(two.getRemaining() == 1);
two.appendSplitPoints(secondShard);
ASSERT(two.getRemaining() == 0);
two.appendShardBoundary("C"_sr);
Standalone<VectorRef<KeyRef>> twoResults = two.finish("Z"_sr);
ASSERT(twoResults.size() == 4 && twoResults[1] == "B"_sr && twoResults[2] == "B1"_sr && twoResults[3] == "Z"_sr);
RangeSplitPointsBuilder four("A"_sr, 4);
four.appendSplitPoints(firstShard);
ASSERT(four.getRemaining() == 2);
four.appendShardBoundary("B"_sr);
ASSERT(four.getRemaining() == 1);
four.appendSplitPoints(secondShard);
ASSERT(four.getRemaining() == 0);
Standalone<VectorRef<KeyRef>> fourResults = four.finish("Z"_sr);
ASSERT(fourResults.size() == 6 && fourResults[1] == "A1"_sr && fourResults[2] == "A2"_sr &&
fourResults[3] == "B"_sr && fourResults[4] == "B1"_sr && fourResults[5] == "Z"_sr);
RangeSplitPointsBuilder duplicateBoundary("A"_sr, 2);
duplicateBoundary.appendSplitPoints(firstShardEndingAtBoundary);
ASSERT(duplicateBoundary.getRemaining() == 1);
duplicateBoundary.appendShardBoundary("B"_sr);
ASSERT(duplicateBoundary.getRemaining() == 1);
duplicateBoundary.appendSplitPoints(secondShard);
Standalone<VectorRef<KeyRef>> duplicateBoundaryResults = duplicateBoundary.finish("Z"_sr);
ASSERT(duplicateBoundaryResults.size() == 4 && duplicateBoundaryResults[1] == "B"_sr &&
duplicateBoundaryResults[2] == "B1"_sr && duplicateBoundaryResults[3] == "Z"_sr);
RangeSplitPointsBuilder unlimited("A"_sr, -1);
unlimited.appendSplitPoints(firstShard);
unlimited.appendShardBoundary("B"_sr);
unlimited.appendSplitPoints(secondShard);
unlimited.appendShardBoundary("C"_sr);
Standalone<VectorRef<KeyRef>> unlimitedResults = unlimited.finish("Z"_sr);
ASSERT(unlimitedResults.size() == 8 && unlimitedResults[1] == "A1"_sr && unlimitedResults[2] == "A2"_sr &&
unlimitedResults[3] == "B"_sr && unlimitedResults[4] == "B1"_sr && unlimitedResults[5] == "B2"_sr &&
unlimitedResults[6] == "C"_sr && unlimitedResults[7] == "Z"_sr);
return Void();
}
Future<Version> setPerpetualStorageWiggle(Database cx, bool enable, LockAware lockAware) {

View File

@ -394,10 +394,12 @@ Future<CDCStreamId> registerNativeCdcStream(Database cx, Key name, KeyRange keys
// Disabling CDC stops new admission, but existing registrations and
// owner repair must remain available so durable streams can drain.
validateNativeCdcEnabled(cx->clientInfo->get().nativeCdcEnabled);
const bool nativeCdcEnabled = cx->clientInfo->get().nativeCdcEnabled;
const int nativeCdcTagCount = cx->clientInfo->get().nativeCdcTagCount;
validateNativeCdcEnabled(nativeCdcEnabled);
NativeCdcIdentifierAllocator allocator;
co_await observeNativeCdcMetadata(&tr, &allocator);
const auto [streamId, tag] = allocator.allocate(cx->clientInfo->get().nativeCdcTagCount);
const auto [streamId, tag] = allocator.allocate(nativeCdcTagCount);
// The read version is a conservative lower bound for tag routing.
// The versionstamped minimum below is the commit version, and stream
// initialization takes their maximum before exposing mutations.

View File

@ -144,7 +144,7 @@ public:
co_await getRangeValue(ryw, read.key, firstGreaterOrEqual(ryw->getMaxReadKey()), GetRangeLimits(1), it);
if (result.readToBegin)
co_return allKeys.begin;
if (result.readThroughEnd || !result.size())
if (result.readThroughEnd || result.empty())
co_return ryw->getMaxReadKey();
co_return result[0].key;
} else {
@ -153,7 +153,7 @@ public:
co_await getRangeValueBack(ryw, firstGreaterOrEqual(allKeys.begin), read.key, GetRangeLimits(1), it);
if (result.readThroughEnd)
co_return ryw->getMaxReadKey();
if (result.readToBegin || !result.size())
if (result.readToBegin || result.empty())
co_return allKeys.begin;
co_return result[0].key;
}
@ -201,7 +201,7 @@ public:
RangeResult v = co_await ryw->tr.getRange(
read.begin, read.end, read.limits, snapshot, backwards ? Reverse::True : Reverse::False);
KeyRef maxKey = ryw->getMaxReadKey();
if (v.size() > 0) {
if (!v.empty()) {
if (!backwards && v[v.size() - 1].key >= maxKey) {
RangeResult _v = v;
int i = _v.size() - 2;
@ -229,14 +229,15 @@ public:
static void addConflictRange(ReadYourWritesTransaction* ryw, GetKeyReq read, WriteMap::iterator& it, Key result) {
KeyRangeRef readRange;
if (read.key.offset <= 0)
if (read.key.offset <= 0) {
readRange = KeyRangeRef(KeyRef(ryw->arena, result),
read.key.orEqual ? keyAfter(read.key.getKey(), ryw->arena)
: KeyRef(ryw->arena, read.key.getKey()));
else
} else {
readRange = KeyRangeRef(read.key.orEqual ? keyAfter(read.key.getKey(), ryw->arena)
: KeyRef(ryw->arena, read.key.getKey()),
keyAfter(result, ryw->arena));
}
it.skip(readRange.begin);
ryw->updateConflictMap(readRange, it);
@ -476,7 +477,7 @@ public:
if (data.readThroughEnd)
endKey = allKeys.end;
if (data.size()) {
if (!data.empty()) {
beginKey = std::min(beginKey, data[0].key);
if (data.readThrough.present()) {
endKey = std::max<ExtStringRef>(endKey, data.readThrough.get());
@ -511,8 +512,9 @@ public:
return singleEmpty;
}
singleEmpty++;
} else
} else {
b = e;
}
++it;
e = it.endKey();
}
@ -541,8 +543,9 @@ public:
singleEmpty++;
if (singleEmpty >= maxClears)
return maxClears;
} else
} else {
b = e;
}
++it;
e = it.endKey();
}
@ -633,7 +636,7 @@ public:
.detail("Unknown", it.is_unknown_range())
.detail("Requests", requestCount);*/
if (!result.size() && actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
if (result.empty() && actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
co_return RangeResultRef(false, false);
}
@ -645,7 +648,7 @@ public:
(begin.offset >= 1 && begin.getKey() >= ryw->getMaxReadKey())) {
if (end.isFirstGreaterOrEqual())
break;
if (!result.size())
if (result.empty())
break;
Key resolvedEnd = co_await read(
ryw,
@ -673,7 +676,7 @@ public:
break;
if (it.is_unknown_range()) {
if (limits.hasByteLimit() && limits.hasSatisfiedMinRows() && result.size() &&
if (limits.hasByteLimit() && limits.hasSatisfiedMinRows() && !result.empty() &&
itemsPastEnd >= 1 - end.offset) {
result.more = true;
break;
@ -775,8 +778,9 @@ public:
if (count)
result.append(result.arena(), start, count);
++it;
} else
} else {
++it;
}
}
result.more = result.more || limits.isReached();
@ -805,7 +809,7 @@ public:
if (data.readThroughEnd)
endKey = allKeys.end;
if (data.size()) {
if (!data.empty()) {
if (data.readThrough.present()) {
beginKey = std::min(data.readThrough.get(), beginKey);
} else {
@ -840,8 +844,9 @@ public:
return singleEmpty;
}
singleEmpty++;
} else
} else {
e = b;
}
--it;
b = it.beginKey();
}
@ -868,8 +873,9 @@ public:
singleEmpty++;
if (singleEmpty >= maxClears)
return maxClears;
} else
} else {
e = b;
}
--it;
b = it.beginKey();
}
@ -937,7 +943,7 @@ public:
.detail("Kv", it.is_kv())
.detail("Requests", requestCount);*/
if (!result.size() && actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
if (result.empty() && actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
co_return RangeResultRef(false, false);
}
@ -949,7 +955,7 @@ public:
(end.offset <= 1 && end.getKey() == allKeys.begin)) {
if (begin.isFirstGreaterOrEqual())
break;
if (!result.size())
if (result.empty())
break;
Key resolvedBegin = co_await read(
ryw,
@ -980,7 +986,7 @@ public:
}
if (it.is_unknown_range()) {
if (limits.hasByteLimit() && result.size() && itemsPastBegin >= begin.offset - 1) {
if (limits.hasByteLimit() && !result.empty() && itemsPastBegin >= begin.offset - 1) {
result.more = true;
break;
}
@ -1227,7 +1233,7 @@ public:
auto itCopy = it;
++it;
ASSERT(itCopy->value.size());
ASSERT(!itCopy->value.empty());
CODE_PROBE(itCopy->value.size() > 1, "Multiple watches on the same key triggered by RYOW");
for (int i = 0; i < itCopy->value.size(); i++) {
@ -1248,7 +1254,7 @@ public:
}
}
if (itCopy->value.size() == 0)
if (itCopy->value.empty())
ryw->watchMap.erase(itCopy);
}
}
@ -1342,11 +1348,12 @@ public:
ryw->nativeReadRanges = ryw->tr.readConflictRanges();
ryw->nativeWriteRanges = ryw->tr.writeConflictRanges();
for (const auto& f : ryw->tr.getExtraReadConflictRanges()) {
if (f.isReady() && f.get().first < f.get().second)
if (f.isReady() && f.get().first < f.get().second) {
ryw->nativeReadRanges.push_back(
ryw->nativeReadRanges.arena(),
KeyRangeRef(f.get().first, f.get().second)
.withPrefix(readConflictRangeKeysRange.begin, ryw->nativeReadRanges.arena()));
}
}
if (ryw->resetPromise.isSet())
@ -1481,7 +1488,7 @@ public:
}
static Future<Void> onError(ReadYourWritesTransaction* ryw, Error e) {
if (ryw->debugTraces.size() > 0 || ryw->debugMessages.size() > 0) {
if (!ryw->debugTraces.empty() || !ryw->debugMessages.empty()) {
// printDebugMessages returns a future but will not block if called with an empty second argument
ASSERT(printDebugMessages(ryw, {}, e).isReady());
}
@ -1829,7 +1836,8 @@ Future<int64_t> ReadYourWritesTransaction::getEstimatedRangeSizeBytes(const KeyR
}
Future<Standalone<VectorRef<KeyRef>>> ReadYourWritesTransaction::getRangeSplitPoints(const KeyRange& range,
int64_t chunkSize) {
int64_t chunkSize,
int limit) {
if (checkUsedDuringCommit()) {
return used_during_commit();
}
@ -1840,7 +1848,7 @@ Future<Standalone<VectorRef<KeyRef>>> ReadYourWritesTransaction::getRangeSplitPo
if (range.begin > maxKey || range.end > maxKey)
return key_outside_legal_range();
return waitOrError(tr.getRangeSplitPoints(range, chunkSize), resetPromise.getFuture());
return waitOrError(tr.getRangeSplitPoints(range, chunkSize, limit), resetPromise.getFuture());
}
void ReadYourWritesTransaction::addReadConflictRange(KeyRangeRef const& keys) {
@ -2044,10 +2052,11 @@ RangeResult ReadYourWritesTransaction::getReadConflictRangeIntersecting(KeyRange
for (const auto& range : nativeReadRanges)
readConflicts.insert(range.withPrefix(readConflictRangeKeysRange.begin, result.arena()), "1"_sr);
for (const auto& f : tr.getExtraReadConflictRanges()) {
if (f.isReady() && f.get().first < f.get().second)
if (f.isReady() && f.get().first < f.get().second) {
readConflicts.insert(KeyRangeRef(f.get().first, f.get().second)
.withPrefix(readConflictRangeKeysRange.begin, result.arena()),
"1"_sr);
}
}
auto beginIter = readConflicts.rangeContaining(kr.begin);
if (beginIter->begin() != kr.begin)
@ -2074,11 +2083,12 @@ RangeResult ReadYourWritesTransaction::getWriteConflictRangeIntersecting(KeyRang
if (it.beginKey() > allKeys.begin)
--it;
for (; it.beginKey() < strippedWriteRangePrefix.end; ++it) {
if (it.is_conflict_range())
if (it.is_conflict_range()) {
writeConflicts.insert(
KeyRangeRef(it.beginKey().toArena(result.arena()), it.endKey().toArena(result.arena()))
.withPrefix(writeConflictRangeKeysRange.begin, result.arena()),
"1"_sr);
}
}
} else {
for (const auto& range : tr.writeConflictRanges())
@ -2188,7 +2198,7 @@ void ReadYourWritesTransaction::atomicOp(const KeyRef& key, const ValueRef& oper
}
approximateSize += k.expectedSize() + v.expectedSize() + sizeof(MutationRef) +
(addWriteConflict ? sizeof(KeyRangeRef) + 2 * key.expectedSize() + 1 : 0);
(addWriteConflict ? sizeof(KeyRangeRef) + 2ULL * key.expectedSize() + 1 : 0);
if (options.readYourWritesDisabled) {
return tr.atomicOp(k, v, (MutationRef::Type)operationType, addWriteConflict);
}
@ -2236,7 +2246,7 @@ void ReadYourWritesTransaction::set(const KeyRef& key, const ValueRef& value) {
throw key_outside_legal_range();
approximateSize += key.expectedSize() + value.expectedSize() + sizeof(MutationRef) +
(addWriteConflict ? sizeof(KeyRangeRef) + 2 * key.expectedSize() + 1 : 0);
(addWriteConflict ? sizeof(KeyRangeRef) + 2ULL * key.expectedSize() + 1 : 0);
if (options.readYourWritesDisabled) {
return tr.set(key, value, addWriteConflict);
}
@ -2410,7 +2420,7 @@ Future<Void> ReadYourWritesTransaction::commit() {
result = RYWImpl::commit(this);
}
return debugMessages.size() > 0 || debugTraces.size() > 0 ? RYWImpl::printDebugMessages(this, result) : result;
return !debugMessages.empty() || !debugTraces.empty() ? RYWImpl::printDebugMessages(this, result) : result;
}
Future<Standalone<StringRef>> ReadYourWritesTransaction::getVersionstamp() {
@ -2533,7 +2543,7 @@ void ReadYourWritesTransaction::operator=(ReadYourWritesTransaction&& r) noexcep
reading = std::move(r.reading);
resetPromise = std::move(r.resetPromise);
r.resetPromise = Promise<Void>();
deferredError = std::move(r.deferredError);
deferredError = r.deferredError;
retries = r.retries;
approximateSize = r.approximateSize;
timeoutActor = r.timeoutActor;
@ -2554,11 +2564,10 @@ void ReadYourWritesTransaction::operator=(ReadYourWritesTransaction&& r) noexcep
}
ReadYourWritesTransaction::ReadYourWritesTransaction(ReadYourWritesTransaction&& r) noexcept
: deferredError(std::move(r.deferredError)), arena(std::move(r.arena)), cache(std::move(r.cache)),
writes(std::move(r.writes)), resetPromise(std::move(r.resetPromise)), reading(std::move(r.reading)),
retries(r.retries), approximateSize(r.approximateSize), timeoutActor(std::move(r.timeoutActor)),
creationTime(r.creationTime), commitStarted(r.commitStarted), transactionDebugInfo(r.transactionDebugInfo),
options(r.options) {
: deferredError(r.deferredError), arena(std::move(r.arena)), cache(std::move(r.cache)), writes(std::move(r.writes)),
resetPromise(std::move(r.resetPromise)), reading(std::move(r.reading)), retries(r.retries),
approximateSize(r.approximateSize), timeoutActor(std::move(r.timeoutActor)), creationTime(r.creationTime),
commitStarted(r.commitStarted), transactionDebugInfo(r.transactionDebugInfo), options(r.options) {
cache.arena = &arena;
writes.arena = &arena;
tr = std::move(r.tr);
@ -2637,7 +2646,7 @@ void ReadYourWritesTransaction::cancel() {
}
void ReadYourWritesTransaction::reset() {
if (debugTraces.size() > 0 || debugMessages.size() > 0) {
if (!debugTraces.empty() || !debugMessages.empty()) {
// printDebugMessages returns a future but will not block if called with an empty second argument
ASSERT(RYWImpl::printDebugMessages(this, {}).isReady());
}
@ -2676,7 +2685,7 @@ ReadYourWritesTransaction::~ReadYourWritesTransaction() {
if (!resetPromise.isSet())
resetPromise.sendError(transaction_cancelled());
if (debugTraces.size() || debugMessages.size()) {
if (!debugTraces.empty() || !debugMessages.empty()) {
// printDebugMessages returns a future but will not block if called with an empty second argument
[[maybe_unused]] Future<Void> f = RYWImpl::printDebugMessages(this, {});
}
@ -2700,14 +2709,15 @@ void ReadYourWritesTransaction::debugLogRetries(Optional<Error> error) {
if (!transactionDebugInfo->transactionName.empty())
transactionNameStr =
format(" in transaction '%s'", printable(StringRef(transactionDebugInfo->transactionName)).c_str());
if (!g_network->isSimulated()) // Fuzz workload turns this on, but we do not want stderr output in
// simulation
// Fuzz workload turns this on, but we do not want stderr output in simulation.
if (!g_network->isSimulated()) {
fprintf(stderr,
"fdb WARNING: long transaction (%.2fs elapsed%s, %d retries, %s)\n",
elapsed,
transactionNameStr.c_str(),
retries,
committed ? "committed" : error.get().what());
}
{
TraceEvent trace = TraceEvent("LongTransaction");
if (error.present())

View File

@ -1,56 +0,0 @@
/*
* RestoreInterface.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.
*/
#include "RestoreInterface.h"
#include "flow/serialize.h"
const KeyRef restoreRequestDoneKey = "\xff\x02/restoreRequestDone"_sr;
const KeyRef restoreRequestTriggerKey = "\xff\x02/restoreRequestTrigger"_sr;
const KeyRangeRef restoreRequestKeys("\xff\x02/restoreRequests/"_sr, "\xff\x02/restoreRequests0"_sr);
// Encode and decode restore request value
Value restoreRequestTriggerValue(UID randomID, int numRequests) {
BinaryWriter wr(IncludeVersion(ProtocolVersion::withRestoreRequestTriggerValue()));
wr << numRequests;
wr << randomID;
return wr.toValue();
}
int decodeRestoreRequestTriggerValue(ValueRef const& value) {
int s;
UID randomID;
BinaryReader reader(value, IncludeVersion());
reader >> s;
reader >> randomID;
return s;
}
Key restoreRequestKeyFor(int index) {
BinaryWriter wr(Unversioned());
wr.serializeBytes(restoreRequestKeys.begin);
wr << index;
return wr.toValue();
}
Value restoreRequestValue(RestoreRequest const& request) {
BinaryWriter wr(IncludeVersion(ProtocolVersion::withRestoreRequestValue()));
wr << request;
return wr.toValue();
}

View File

@ -1,102 +0,0 @@
/*
* RestoreInterface.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 "fdbclient/FDBTypes.h"
#include "fdbrpc/fdbrpc.h"
struct RestoreCommonReply {
constexpr static FileIdentifier file_identifier = 5808787;
UID id; // unique ID of the server who sends the reply
bool isDuplicated;
RestoreCommonReply() = default;
explicit RestoreCommonReply(UID id, bool isDuplicated = false) : id(id), isDuplicated(isDuplicated) {}
std::string toString() const {
std::stringstream ss;
ss << "ServerNodeID:" << id.toString() << " isDuplicated:" << isDuplicated;
return ss.str();
}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, id, isDuplicated);
}
};
struct RestoreRequest {
constexpr static FileIdentifier file_identifier = 16035338;
int index;
Key tagName;
Key url;
Optional<std::string> proxy;
Version targetVersion;
KeyRange range;
UID randomUid;
// Every key in backup will first removePrefix and then addPrefix;
// Simulation testing does not cover when both addPrefix and removePrefix exist yet.
Key addPrefix;
Key removePrefix;
ReplyPromise<struct RestoreCommonReply> reply;
RestoreRequest() = default;
explicit RestoreRequest(const int index,
const Key& tagName,
const Key& url,
const Optional<std::string>& proxy,
Version targetVersion,
const KeyRange& range,
const UID& randomUid,
Key& addPrefix,
Key removePrefix)
: index(index), tagName(tagName), url(url), proxy(proxy), targetVersion(targetVersion), range(range),
randomUid(randomUid), addPrefix(addPrefix), removePrefix(removePrefix) {}
// To change this serialization, ProtocolVersion::RestoreRequestValue must be updated, and downgrades need to be
// considered
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, index, tagName, url, proxy, targetVersion, range, randomUid, addPrefix, removePrefix, reply);
}
std::string toString() const {
std::stringstream ss;
ss << "index:" << std::to_string(index) << " tagName:" << tagName.contents().toString()
<< " url:" << url.contents().toString() << " proxy:" << (proxy.present() ? proxy.get() : "")
<< " targetVersion:" << std::to_string(targetVersion) << " range:" << range.toString()
<< " randomUid:" << randomUid.toString() << " addPrefix:" << addPrefix.toString()
<< " removePrefix:" << removePrefix.toString();
return ss.str();
}
};
extern const KeyRef restoreRequestDoneKey;
extern const KeyRef restoreRequestTriggerKey;
extern const KeyRangeRef restoreRequestKeys;
Value restoreRequestTriggerValue(UID randomID, int numRequests);
int decodeRequestRequestTriggerValue(ValueRef const&);
Key restoreRequestKeyFor(int index);
Value restoreRequestValue(RestoreRequest const&);

View File

@ -228,7 +228,7 @@ static void traceKeyValuesSummary(TraceEvent& event,
// convert a StringRef to Hex string
static std::string hexStringRef(const StringRef& s) {
std::string result;
result.reserve(s.size() * 2);
result.reserve(static_cast<size_t>(s.size()) * 2);
for (int i = 0; i < s.size(); i++) {
result.append(format("%02x", s[i]));
}

View File

@ -21,7 +21,7 @@
#include "fdbclient/Subspace.h"
Subspace::Subspace(Tuple const& tuple, StringRef const& rawPrefix) {
StringRef packed = tuple.pack();
Standalone<StringRef> packed = tuple.pack();
this->rawPrefix.reserve(this->rawPrefix.arena(), rawPrefix.size() + packed.size());
this->rawPrefix.append(this->rawPrefix.arena(), rawPrefix.begin(), rawPrefix.size());

View File

@ -412,13 +412,14 @@ ThreadFuture<int64_t> ThreadSafeTransaction::getEstimatedRangeSizeBytes(const Ke
}
ThreadFuture<Standalone<VectorRef<KeyRef>>> ThreadSafeTransaction::getRangeSplitPoints(const KeyRangeRef& range,
int64_t chunkSize) {
int64_t chunkSize,
int limit) {
KeyRange r = range;
ReadYourWritesTransaction* tr = this->tr;
return onMainThread([tr, r, chunkSize]() -> Future<Standalone<VectorRef<KeyRef>>> {
return onMainThread([tr, r, chunkSize, limit]() -> Future<Standalone<VectorRef<KeyRef>>> {
tr->checkDeferredError();
return tr->getRangeSplitPoints(r, chunkSize);
return tr->getRangeSplitPoints(r, chunkSize, limit);
});
}

View File

@ -75,7 +75,8 @@ public:
virtual void addReadConflictRange(const KeyRangeRef& keys) = 0;
virtual ThreadFuture<int64_t> getEstimatedRangeSizeBytes(const KeyRangeRef& keys) = 0;
virtual ThreadFuture<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(const KeyRangeRef& range,
int64_t chunkSize) = 0;
int64_t chunkSize,
int limit = -1) = 0;
virtual void atomicOp(const KeyRef& key, const ValueRef& value, uint32_t operationType) = 0;
virtual void set(const KeyRef& key, const ValueRef& value) = 0;

View File

@ -257,6 +257,13 @@ struct FdbCApi : public ThreadSafeReferenceCounted<FdbCApi> {
uint8_t const* end_key_name,
int end_key_name_length,
int64_t chunkSize);
FDBFuture* (*transactionGetRangeSplitPointsWithLimit)(FDBTransaction* tr,
uint8_t const* begin_key_name,
int begin_key_name_length,
uint8_t const* end_key_name,
int end_key_name_length,
int64_t chunkSize,
int limit);
FDBFuture* (*transactionCommit)(FDBTransaction* tr);
fdb_error_t (*transactionGetCommittedVersion)(FDBTransaction* tr, int64_t* outVersion);
@ -357,7 +364,8 @@ public:
ThreadFuture<Standalone<StringRef>> getVersionstamp() override;
ThreadFuture<int64_t> getEstimatedRangeSizeBytes(const KeyRangeRef& keys) override;
ThreadFuture<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(const KeyRangeRef& range,
int64_t chunkSize) override;
int64_t chunkSize,
int limit = -1) override;
void addReadConflictRange(const KeyRangeRef& keys) override;
@ -528,7 +536,8 @@ public:
ThreadFuture<int64_t> getEstimatedRangeSizeBytes(const KeyRangeRef& keys) override;
ThreadFuture<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(const KeyRangeRef& range,
int64_t chunkSize) override;
int64_t chunkSize,
int limit = -1) override;
void atomicOp(const KeyRef& key, const ValueRef& value, uint32_t operationType) override;
void set(const KeyRef& key, const ValueRef& value) override;

View File

@ -425,7 +425,8 @@ public:
// Try to split the given range into equally sized chunks based on estimated size.
// The returned list would still be in form of [keys.begin, splitPoint1, splitPoint2, ... , keys.end]
Future<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(KeyRange const& keys, int64_t chunkSize);
// A non-negative limit caps the number of interior split points, including shard boundaries.
Future<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(KeyRange const& keys, int64_t chunkSize, int limit = -1);
// If checkWriteConflictRanges is true, existing write conflict ranges will be searched for this key
void set(const KeyRef& key, const ValueRef& value, AddConflictRange = AddConflictRange::True);

View File

@ -115,7 +115,7 @@ public:
Reverse = Reverse::False);
[[nodiscard]] Future<Standalone<VectorRef<const char*>>> getAddressesForKey(const Key& key);
Future<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(const KeyRange& range, int64_t chunkSize);
Future<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(const KeyRange& range, int64_t chunkSize, int limit = -1);
Future<int64_t> getEstimatedRangeSizeBytes(const KeyRange& keys);
void addReadConflictRange(KeyRangeRef const& keys);

View File

@ -779,14 +779,16 @@ struct SplitRangeRequest {
Arena arena;
KeyRangeRef keys;
int64_t chunkSize;
int limit = -1;
ReplyPromise<SplitRangeReply> reply;
SplitRangeRequest() = default;
SplitRangeRequest(KeyRangeRef const& keys, int64_t chunkSize) : keys(arena, keys), chunkSize(chunkSize) {}
SplitRangeRequest(KeyRangeRef const& keys, int64_t chunkSize, int limit = -1)
: keys(arena, keys), chunkSize(chunkSize), limit(limit) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, keys, chunkSize, reply, arena);
serializer(ar, keys, chunkSize, reply, limit, arena);
}
};

View File

@ -129,7 +129,8 @@ public:
ThreadFuture<Standalone<StringRef>> getVersionstamp() override;
ThreadFuture<int64_t> getEstimatedRangeSizeBytes(const KeyRangeRef& keys) override;
ThreadFuture<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(const KeyRangeRef& range,
int64_t chunkSize) override;
int64_t chunkSize,
int limit = -1) override;
void addReadConflictRange(const KeyRangeRef& keys) override;
void makeSelfConflicting();

View File

@ -1,134 +0,0 @@
/**
* FlowGrpcTests.actor.cpp
*
* 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.
*/
#ifdef FLOW_GRPC_ENABLED
#include <cstdio>
#include "fdbrpc/FlowGrpc.h"
#include "FlowGrpcTests.h"
#include "flow/UnitTest.h"
#include "flow/actorcompiler.h" // This must be the last #include.
// So that tests are not optimized out. :/
void forceLinkGrpcTests() {}
namespace fdbrpc_test {
TEST_CASE("/fdbrpc/grpc/basic_sync_client") {
state NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50001"));
state GrpcServer server(addr);
server.registerService(make_shared<TestEchoServiceImpl>());
state Future<Void> server_actor = server.run();
wait(server.onRunning());
EchoClient client(grpc::CreateChannel(addr.toString(), grpc::InsecureChannelCredentials()));
std::string reply = client.Echo("Ping!");
std::cout << "Echo received: " << reply << std::endl;
ASSERT_EQ(reply, "Echo: Ping!");
wait(server.shutdown());
wait(server_actor);
return Void();
}
TEST_CASE("/fdbrpc/grpc/basic_async_client") {
state NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50003"));
state GrpcServer server(addr);
server.registerService(make_shared<TestEchoServiceImpl>());
state Future<Void> _ = server.run();
wait(server.onRunning());
state shared_ptr<AsyncTaskExecutor> pool = make_shared<AsyncTaskExecutor>(4);
state AsyncGrpcClient<TestEchoService> client(addr.toString(), pool);
try {
state EchoRequest request;
request.set_message("Ping!");
EchoResponse response = wait(client.call(&TestEchoService::Stub::Echo, request));
std::cout << "Echo received: " << response.message() << std::endl;
ASSERT_EQ(response.message(), "Echo: Ping!");
} catch (Error& e) {
ASSERT_EQ(e.code(), error_code_grpc_error);
ASSERT(false);
}
return Void();
}
TEST_CASE("/fdbrpc/grpc/actor_basic_stream_server") {
state NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50002"));
state GrpcServer server(addr);
server.registerService(make_shared<TestEchoServiceImpl>());
state Future<Void> _ = server.run();
wait(server.onRunning());
state shared_ptr<AsyncTaskExecutor> pool = make_shared<AsyncTaskExecutor>(4);
state AsyncGrpcClient<TestEchoService> client(addr.toString(), pool);
state int count = 0;
try {
EchoRequest request;
request.set_message("Ping!");
state ThreadFutureStream<EchoResponse> stream = client.call(&TestEchoService::Stub::EchoRecvStream10, request);
while (true) {
EchoResponse response = waitNext(stream);
ASSERT_EQ(response.message(), "Echo: Ping!");
count += 1;
}
} catch (Error& e) {
std::cout << "Error: " << e.name() << std::endl;
if (e.code() == error_code_end_of_stream) {
ASSERT_EQ(count, 10); // Should send 10 reponses.
return Void();
}
ASSERT(false);
}
return Void();
}
TEST_CASE("/fdbrpc/grpc/no_server_running") {
state NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50004"));
state shared_ptr<AsyncTaskExecutor> pool = make_shared<AsyncTaskExecutor>(4);
state AsyncGrpcClient<TestEchoService> client(addr.toString(), pool);
try {
state EchoRequest request;
request.set_message("Ping!");
EchoResponse response = wait(client.call(&TestEchoService::Stub::Echo, request));
ASSERT(false); // RPC should fail as there is no server running.;
} catch (Error& e) {
ASSERT_EQ(e.code(), error_code_grpc_error);
}
return Void();
}
TEST_CASE("/fdbrpc/grpc/destroy_server_without_shutdown") {
state NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50005"));
state GrpcServer server(addr);
server.registerService(make_shared<TestEchoServiceImpl>());
state Future<Void> _ = server.run();
wait(server.onRunning());
return Void();
}
} // namespace fdbrpc_test
#endif

View File

@ -30,10 +30,102 @@
#include "flow/flow.h"
// So that tests are not optimized out. :/
void forceLinkGrpcTests() {}
void forceLinkGrpcTests2() {}
namespace fdbrpc_test {
TEST_CASE("/fdbrpc/grpc/basic_sync_client") {
NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50001"));
GrpcServer server(addr);
server.registerService(make_shared<TestEchoServiceImpl>());
Future<Void> server_actor = server.run();
co_await server.onRunning();
EchoClient client(grpc::CreateChannel(addr.toString(), grpc::InsecureChannelCredentials()));
std::string reply = client.Echo("Ping!");
std::cout << "Echo received: " << reply << std::endl;
ASSERT_EQ(reply, "Echo: Ping!");
co_await server.shutdown();
co_await server_actor;
}
TEST_CASE("/fdbrpc/grpc/basic_async_client") {
NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50003"));
GrpcServer server(addr);
server.registerService(make_shared<TestEchoServiceImpl>());
Future<Void> _ = server.run();
co_await server.onRunning();
auto pool = make_shared<AsyncTaskExecutor>(4);
AsyncGrpcClient<TestEchoService> client(addr.toString(), pool);
try {
EchoRequest request;
request.set_message("Ping!");
EchoResponse response = co_await client.call(&TestEchoService::Stub::Echo, request);
std::cout << "Echo received: " << response.message() << std::endl;
ASSERT_EQ(response.message(), "Echo: Ping!");
} catch (Error& e) {
ASSERT_EQ(e.code(), error_code_grpc_error);
ASSERT(false);
}
}
TEST_CASE("/fdbrpc/grpc/actor_basic_stream_server") {
NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50002"));
GrpcServer server(addr);
server.registerService(make_shared<TestEchoServiceImpl>());
Future<Void> _ = server.run();
co_await server.onRunning();
auto pool = make_shared<AsyncTaskExecutor>(4);
AsyncGrpcClient<TestEchoService> client(addr.toString(), pool);
int count = 0;
try {
EchoRequest request;
request.set_message("Ping!");
auto stream = client.call(&TestEchoService::Stub::EchoRecvStream10, request);
while (true) {
auto response = co_await stream;
ASSERT_EQ(response.message(), "Echo: Ping!");
count += 1;
}
} catch (Error& e) {
std::cout << "Error: " << e.name() << std::endl;
if (e.code() == error_code_end_of_stream) {
ASSERT_EQ(count, 10); // Should send 10 reponses.
co_return;
}
ASSERT(false);
}
}
TEST_CASE("/fdbrpc/grpc/no_server_running") {
NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50004"));
auto pool = make_shared<AsyncTaskExecutor>(4);
AsyncGrpcClient<TestEchoService> client(addr.toString(), pool);
try {
EchoRequest request;
request.set_message("Ping!");
EchoResponse response = co_await client.call(&TestEchoService::Stub::Echo, request);
ASSERT(false); // RPC should fail as there is no server running.;
} catch (Error& e) {
ASSERT_EQ(e.code(), error_code_grpc_error);
}
}
TEST_CASE("/fdbrpc/grpc/destroy_server_without_shutdown") {
NetworkAddress addr(NetworkAddress::parse("127.0.0.1:50005"));
GrpcServer server(addr);
server.registerService(make_shared<TestEchoServiceImpl>());
Future<Void> _ = server.run();
co_await server.onRunning();
}
void generate_random_string(std::string* buffer, int size) {
buffer->clear();
const std::string characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

View File

@ -30,6 +30,7 @@
#include "flow/IThreadPool.h"
#include "flow/WriteOnlySet.h"
#include "fdbrpc/fdbrpc.h"
#include "fdbrpc/AsyncFileNonDurable.h"
#include "flow/IAsyncFile.h"
#include "flow/TLSConfig.h"
#include "fdbrpc/grpc/AsyncTaskExecutor.h"
@ -340,6 +341,17 @@ TEST_CASE("/flow/flow/cancel1") {
return Void();
}
TEST_CASE("/fdbrpc/asyncFileNonDurable/sendErrorOnShutdownCancellation") {
Promise<Void> input;
Future<Void> wrapped = sendErrorOnShutdown(input.getFuture());
ASSERT(input.getFutureReferenceCount() > 0);
wrapped.cancel();
ASSERT(wrapped.isReady() && wrapped.isError() && wrapped.getError().code() == error_code_actor_cancelled);
ASSERT_EQ(input.getFutureReferenceCount(), 0);
input.send(Void());
return Void();
}
ACTOR static Future<Void> noteCancel(int* cancelled) {
*cancelled = 0;
try {

View File

@ -417,7 +417,7 @@ struct ConnectionLogWriter : IThreadPoolReceiver {
throw io_error();
}
if (file.tellg() > 100 * 1024 * 1024 /* 100 MB */) {
if (file.tellg() > 100LL * 1024 * 1024 /* 100 MB */) {
file.close();
fileName = newFileName();
TraceEvent("RollConnectionLog").detail("FileName", fileName);

View File

@ -57,10 +57,11 @@ Future<Void> runAsyncFileKAIOTestOps(Reference<IAsyncFile> f, int numIterations,
std::vector<Future<Void>> futures;
for (int numOps = deterministicRandom()->randomInt(1, 20); numOps > 0; --numOps) {
if (deterministicRandom()->coinflip()) {
futures.push_back(
success(f->read(buf, 4096, deterministicRandom()->randomInt(0, fileSize) / 4096 * 4096)));
futures.push_back(success(f->read(
buf, 4096, static_cast<int64_t>(deterministicRandom()->randomInt(0, fileSize)) / 4096 * 4096)));
} else {
futures.push_back(f->write(buf, 4096, deterministicRandom()->randomInt(0, fileSize) / 4096 * 4096));
futures.push_back(f->write(
buf, 4096, static_cast<int64_t>(deterministicRandom()->randomInt(0, fileSize)) / 4096 * 4096));
}
}
for (int fIndex = 0; fIndex < futures.size(); ++fIndex) {

View File

@ -1103,25 +1103,6 @@ ACTOR [[flow_allow_discard]] Future<Void> cycleTime(int nodes, int times) {
return Void();
}
void sleeptest() {
#ifdef __linux__
int times[] = { 0, 100, 500, 1000, 5000, 100000, 500000, 1000000 };
for (int j = 0; j < 8; j++) {
double b = timer();
int n = std::min(100, 4000000 / (1 + times[j]));
for (int i = 0; i < n; i++) {
timespec ts;
ts.tv_sec = times[j] / 1000000;
ts.tv_nsec = (times[j] % 1000000) * 1000;
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, nullptr);
// nanosleep(&ts, nullptr);
}
double t = timer() - b;
printf("Sleep test (%dus x %d): %0.1f\n", times[j], n, double(t) / n * 1e6);
}
#endif
}
void asyncMapTest() {
Future<Void> c;

View File

@ -39,7 +39,7 @@ extern Future<Void> waitShutdownSignal();
template <class T>
Future<T> sendErrorOnShutdown(Future<T> in, bool assertOnCancel = false) {
try {
auto res = co_await race(waitShutdownSignal(), in);
auto res = co_await race(waitShutdownSignal(), std::move(in));
if (res.index() == 0) {
throw io_error().asInjectedFault();
} else {

View File

@ -352,7 +352,7 @@ int runHost(TLSCreds creds, int addrPipe, int completionPipe, Result expect) {
Result getExpectedResult(ChainLength serverChainLen, ChainLength clientChainLen) {
auto expect = Result::ERROR;
if (serverChainLen > 0) {
if (clientChainLen == NO_TLS || clientChainLen < 0) {
if (clientChainLen < 0) {
expect = Result::TIMEOUT;
} else if (clientChainLen > 0) {
expect = Result::TRUSTED;

View File

@ -39,7 +39,7 @@
#include "fdbclient/DatabaseContext.h"
#include "fdbserver/tester/tester.h"
#include "fdbserver/core/FDBSimulatorProcessInfo.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbserver/worker/Worker.h"
#include "fdbclient/ClusterInterface.h"
#include "fdbserver/core/Knobs.h"

View File

@ -34,7 +34,7 @@
#include "fdbserver/core/ServerDBInfo.h"
#include "fdbserver/core/WaitFailure.h"
#include "fdbserver/backupworker/BackupWorker.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/Error.h"
#include "flow/IRandom.h"

View File

@ -37,7 +37,7 @@
#include "fdbserver/core/ServerDBInfo.h"
#include "fdbserver/core/SpanContextMessage.h"
#include "fdbserver/core/WaitFailure.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbserver/logsystem/LogSystemConsumer.h"
#include "fdbserver/logsystem/LogSystemFactory.h"
#include "flow/ActorCollection.h"

View File

@ -36,7 +36,7 @@
#include "fdbrpc/Locality.h"
#include "fdbserver/core/Knobs.h"
#include "fdbserver/core/ProcessClassRecruitment.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/ActorCollection.h"
#include "fdbclient/ClusterConnectionMemoryRecord.h"
#include "fdbclient/NativeAPI.actor.h"
@ -124,6 +124,8 @@ ClusterControllerData::ClusterControllerData(ClusterControllerFullInterface cons
serverInfo.masterLifetime.ccID = id;
serverInfo.clusterInterface = ccInterface;
serverInfo.myLocality = locality;
serverInfo.client.nativeCdcEnabled = CLIENT_KNOBS->ENABLE_NATIVE_CDC;
serverInfo.client.nativeCdcTagCount = CLIENT_KNOBS->NATIVE_CDC_TAG_COUNT;
db.serverInfo->set(serverInfo);
cx = openDBOnServer(db.serverInfo, TaskPriority::DefaultEndpoint, LockAware::True);
@ -1378,7 +1380,6 @@ void clusterRegisterMaster(ClusterControllerData* self, RegisterMasterRequest co
if (req.recoveryState == RecoveryState::FULLY_RECOVERED) {
self->db.unfinishedRecoveries = 0;
ASSERT(!req.logSystemConfig.oldTLogs.size());
}
db->masterRegistrationCount = req.registrationCount;
@ -2384,60 +2385,56 @@ Future<Void> updatedChangingDatacenters(ClusterControllerData* self) {
}
}
ACTOR Future<Void> updatedChangedDatacenters(ClusterControllerData* self) {
state Future<Void> changeDelay = delay(SERVER_KNOBS->CC_CHANGE_DELAY);
state Future<Void> onChange = self->changingDcIds.onChange();
loop {
choose {
when(wait(onChange)) {
changeDelay = delay(SERVER_KNOBS->CC_CHANGE_DELAY);
onChange = self->changingDcIds.onChange();
}
when(wait(changeDelay)) {
changeDelay = Never();
onChange = self->changingDcIds.onChange();
Future<Void> updatedChangedDatacenters(ClusterControllerData* self) {
Future<Void> changeDelay = delay(SERVER_KNOBS->CC_CHANGE_DELAY);
Future<Void> onChange = self->changingDcIds.onChange();
while (true) {
auto res = co_await race(onChange, changeDelay);
if (res.index() == 0) {
changeDelay = delay(SERVER_KNOBS->CC_CHANGE_DELAY);
onChange = self->changingDcIds.onChange();
} else {
changeDelay = Never();
onChange = self->changingDcIds.onChange();
self->changedDcIds.set(self->changingDcIds.get());
if (self->changedDcIds.get().second.present()) {
TraceEvent("UpdateChangedDatacenter", self->id).detail("CCFirst", self->changedDcIds.get().first);
if (!self->changedDcIds.get().first) {
auto& worker = self->id_worker[self->clusterControllerProcessId];
uint8_t newFitness = ClusterControllerPriorityInfo::calculateDCFitness(
worker.details.interf.locality.dcId(), self->changedDcIds.get().second.get());
if (worker.priorityInfo.dcFitness != newFitness) {
worker.priorityInfo.dcFitness = newFitness;
if (!worker.reply.isSet()) {
worker.reply.send(
RegisterWorkerReply(worker.details.processClass, worker.priorityInfo));
}
self->changedDcIds.set(self->changingDcIds.get());
if (self->changedDcIds.get().second.present()) {
TraceEvent("UpdateChangedDatacenter", self->id).detail("CCFirst", self->changedDcIds.get().first);
if (!self->changedDcIds.get().first) {
auto& worker = self->id_worker[self->clusterControllerProcessId];
uint8_t newFitness = ClusterControllerPriorityInfo::calculateDCFitness(
worker.details.interf.locality.dcId(), self->changedDcIds.get().second.get());
if (worker.priorityInfo.dcFitness != newFitness) {
worker.priorityInfo.dcFitness = newFitness;
if (!worker.reply.isSet()) {
worker.reply.send(RegisterWorkerReply(worker.details.processClass, worker.priorityInfo));
}
} else {
state int currentFit = recruitment::BestFit;
while (currentFit <= recruitment::NeverAssign) {
bool updated = false;
for (auto& it : self->id_worker) {
if ((!it.second.priorityInfo.isExcluded &&
it.second.priorityInfo.processClassFitness == currentFit) ||
currentFit == recruitment::NeverAssign) {
uint8_t fitness = ClusterControllerPriorityInfo::calculateDCFitness(
it.second.details.interf.locality.dcId(),
self->changedDcIds.get().second.get());
if (it.first != self->clusterControllerProcessId &&
it.second.priorityInfo.dcFitness != fitness) {
updated = true;
it.second.priorityInfo.dcFitness = fitness;
if (!it.second.reply.isSet()) {
it.second.reply.send(RegisterWorkerReply(it.second.details.processClass,
it.second.priorityInfo));
}
}
} else {
int currentFit = recruitment::BestFit;
while (currentFit <= recruitment::NeverAssign) {
bool updated = false;
for (auto& it : self->id_worker) {
if ((!it.second.priorityInfo.isExcluded &&
it.second.priorityInfo.processClassFitness == currentFit) ||
currentFit == recruitment::NeverAssign) {
uint8_t fitness = ClusterControllerPriorityInfo::calculateDCFitness(
it.second.details.interf.locality.dcId(), self->changedDcIds.get().second.get());
if (it.first != self->clusterControllerProcessId &&
it.second.priorityInfo.dcFitness != fitness) {
updated = true;
it.second.priorityInfo.dcFitness = fitness;
if (!it.second.reply.isSet()) {
it.second.reply.send(RegisterWorkerReply(it.second.details.processClass,
it.second.priorityInfo));
}
}
}
if (updated && currentFit < recruitment::NeverAssign) {
wait(delay(SERVER_KNOBS->CC_CLASS_DELAY));
}
currentFit++;
}
if (updated && currentFit < recruitment::NeverAssign) {
co_await delay(SERVER_KNOBS->CC_CLASS_DELAY);
}
currentFit++;
}
}
}
@ -2807,13 +2804,13 @@ Future<Void> startDataDistributor(ClusterControllerData* self, double waitTime)
}
}
ACTOR Future<Void> monitorDataDistributor(ClusterControllerData* self) {
state SingletonRecruitThrottler recruitThrottler;
Future<Void> monitorDataDistributor(ClusterControllerData* self) {
SingletonRecruitThrottler recruitThrottler;
while (self->db.serverInfo->get().recoveryState < RecoveryState::ACCEPTING_COMMITS) {
wait(self->db.serverInfo->onChange());
co_await self->db.serverInfo->onChange();
}
loop {
while (true) {
bool ddExist = self->db.serverInfo->get().distributor.present();
TraceEvent(SevInfo, "CCMonitorDataDistributor", self->id)
.detail("Recruiting", self->recruitDistributor.get())
@ -2821,18 +2818,17 @@ ACTOR Future<Void> monitorDataDistributor(ClusterControllerData* self) {
.detail("ExistingDD", ddExist ? self->db.serverInfo->get().distributor.get().id().toString() : "");
if (self->db.serverInfo->get().distributor.present() && !self->recruitDistributor.get()) {
choose {
when(wait(waitFailureClient(self->db.serverInfo->get().distributor.get().waitFailure,
SERVER_KNOBS->DD_FAILURE_TIME))) {
const auto& distributor = self->db.serverInfo->get().distributor;
TraceEvent("CCDataDistributorDied", self->id).detail("DDID", distributor.get().id());
DataDistributorSingleton(distributor).halt(*self, distributor.get().locality.processId());
self->db.clearInterf(ProcessClass::DataDistributorClass);
}
when(wait(self->recruitDistributor.onChange())) {}
auto res = co_await race(waitFailureClient(self->db.serverInfo->get().distributor.get().waitFailure,
SERVER_KNOBS->DD_FAILURE_TIME),
self->recruitDistributor.onChange());
if (res.index() == 0) {
const auto& distributor = self->db.serverInfo->get().distributor;
TraceEvent("CCDataDistributorDied", self->id).detail("DDID", distributor.get().id());
DataDistributorSingleton(distributor).halt(*self, distributor.get().locality.processId());
self->db.clearInterf(ProcessClass::DataDistributorClass);
}
} else {
wait(startDataDistributor(self, recruitThrottler.newRecruitment()));
co_await startDataDistributor(self, recruitThrottler.newRecruitment());
}
}
}
@ -2905,26 +2901,25 @@ Future<Void> startRatekeeper(ClusterControllerData* self, double waitTime) {
}
}
ACTOR Future<Void> monitorRatekeeper(ClusterControllerData* self) {
state SingletonRecruitThrottler recruitThrottler;
Future<Void> monitorRatekeeper(ClusterControllerData* self) {
SingletonRecruitThrottler recruitThrottler;
while (self->db.serverInfo->get().recoveryState < RecoveryState::ACCEPTING_COMMITS) {
wait(self->db.serverInfo->onChange());
co_await self->db.serverInfo->onChange();
}
loop {
while (true) {
if (self->db.serverInfo->get().ratekeeper.present() && !self->recruitRatekeeper.get()) {
choose {
when(wait(waitFailureClient(self->db.serverInfo->get().ratekeeper.get().waitFailure,
SERVER_KNOBS->RATEKEEPER_FAILURE_TIME))) {
const auto& ratekeeper = self->db.serverInfo->get().ratekeeper;
TraceEvent("CCRatekeeperDied", self->id).detail("RKID", ratekeeper.get().id());
RatekeeperSingleton(ratekeeper).halt(*self, ratekeeper.get().locality.processId());
self->db.clearInterf(ProcessClass::RatekeeperClass);
}
when(wait(self->recruitRatekeeper.onChange())) {}
auto res = co_await race(waitFailureClient(self->db.serverInfo->get().ratekeeper.get().waitFailure,
SERVER_KNOBS->RATEKEEPER_FAILURE_TIME),
self->recruitRatekeeper.onChange());
if (res.index() == 0) {
const auto& ratekeeper = self->db.serverInfo->get().ratekeeper;
TraceEvent("CCRatekeeperDied", self->id).detail("RKID", ratekeeper.get().id());
RatekeeperSingleton(ratekeeper).halt(*self, ratekeeper.get().locality.processId());
self->db.clearInterf(ProcessClass::RatekeeperClass);
}
} else {
wait(startRatekeeper(self, recruitThrottler.newRecruitment()));
co_await startRatekeeper(self, recruitThrottler.newRecruitment());
}
}
}
@ -2997,28 +2992,25 @@ Future<Void> startConsistencyScan(ClusterControllerData* self) {
}
}
ACTOR Future<Void> monitorConsistencyScan(ClusterControllerData* self) {
Future<Void> monitorConsistencyScan(ClusterControllerData* self) {
while (self->db.serverInfo->get().recoveryState < RecoveryState::ACCEPTING_COMMITS) {
TraceEvent("CCMonitorConsistencyScanWaitingForRecovery", self->id).log();
wait(self->db.serverInfo->onChange());
co_await self->db.serverInfo->onChange();
}
TraceEvent("CCMonitorConsistencyScan", self->id).log();
loop {
while (true) {
if (self->db.serverInfo->get().consistencyScan.present() && !self->recruitConsistencyScan.get()) {
state Future<Void> wfClient =
waitFailureClient(self->db.serverInfo->get().consistencyScan.get().waitFailure,
SERVER_KNOBS->CONSISTENCYSCAN_FAILURE_TIME);
choose {
when(wait(wfClient)) {
TraceEvent("CCMonitorConsistencyScanDied", self->id)
.detail("CKID", self->db.serverInfo->get().consistencyScan.get().id());
self->db.clearInterf(ProcessClass::ConsistencyScanClass);
}
when(wait(self->recruitConsistencyScan.onChange())) {}
Future<Void> wfClient = waitFailureClient(self->db.serverInfo->get().consistencyScan.get().waitFailure,
SERVER_KNOBS->CONSISTENCYSCAN_FAILURE_TIME);
auto res = co_await race(wfClient, self->recruitConsistencyScan.onChange());
if (res.index() == 0) {
TraceEvent("CCMonitorConsistencyScanDied", self->id)
.detail("CKID", self->db.serverInfo->get().consistencyScan.get().id());
self->db.clearInterf(ProcessClass::ConsistencyScanClass);
}
} else {
TraceEvent("CCMonitorConsistencyScanStarting", self->id).log();
wait(startConsistencyScan(self));
co_await startConsistencyScan(self);
}
}
}
@ -3532,6 +3524,48 @@ TEST_CASE("/fdbserver/clustercontroller/ignoreStaleWorkerRegistration") {
return Void();
}
TEST_CASE("/fdbserver/clustercontroller/recoverForExcludedOldTLogLocality") {
ClusterControllerData data(ClusterControllerFullInterface(),
LocalityData(),
ServerCoordinators(Reference<IClusterConnectionRecord>(
new ClusterConnectionMemoryRecord(ClusterConnectionString()))),
makeReference<AsyncVar<Optional<UID>>>());
LocalityData masterLocality;
masterLocality.set(LocalityData::keyProcessId, Standalone<StringRef>(std::string{ "master" }));
data.id_worker[masterLocality.processId()];
const NetworkAddress oldTLogAddress(IPAddress(0x02020202), 1);
LocalityData workerLocality;
workerLocality.set(LocalityData::keyProcessId, Standalone<StringRef>(std::string{ "old-tlog" }));
workerLocality.set("instance_id"_sr, Standalone<StringRef>(std::string{ "log-4296" }));
WorkerInterface worker(workerLocality);
worker.tLog = RequestStream<InitializeTLogRequest>(Endpoint({ oldTLogAddress }, UID(1, 2)));
data.id_worker[workerLocality.processId()].details.interf = worker;
LocalityData filteredLocality;
filteredLocality.set(LocalityData::keyZoneId, Standalone<StringRef>(std::string{ "zone" }));
TLogInterface oldTLog(filteredLocality);
oldTLog.peekMessages = RequestStream<TLogPeekRequest>(Endpoint({ oldTLogAddress }, UID(3, 4)));
data.db.config.set(StringRef(encodeExcludedLocalityKey("locality_instance_id:log-4296")), StringRef());
ASSERT(data.db.config.isExcludedServer(worker.addresses(), worker.locality));
ASSERT(!data.db.config.isExcludedServer(oldTLog.addresses(), oldTLog.filteredLocality));
TLogSet oldTLogSet;
oldTLogSet.tLogs.push_back(OptionalInterface(oldTLog));
OldTLogConf oldTLogConf;
oldTLogConf.tLogs.push_back(oldTLogSet);
ServerDBInfo dbInfo;
dbInfo.master.locality = masterLocality;
dbInfo.logSystemConfig.oldTLogs.push_back(oldTLogConf);
dbInfo.recoveryState = RecoveryState::FULLY_RECOVERED;
data.db.serverInfo->set(dbInfo);
ASSERT(data.betterMasterExists());
return Void();
}
// Tests `ClusterControllerData::updateWorkerHealth()` can update `ClusterControllerData::workerHealth`
// based on `UpdateWorkerHealth` request correctly.
TEST_CASE("/fdbserver/clustercontroller/updateWorkerHealth") {

View File

@ -20,6 +20,7 @@
#pragma once
#include <algorithm>
#include <utility>
#include "fdbclient/DatabaseContext.h"
@ -31,7 +32,7 @@
#include "RatekeeperMonitor.h"
#include "fdbserver/core/Knobs.h"
#include "fdbserver/core/ProcessClassRecruitment.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbrpc/Locality.h"
#include "flow/CoroUtils.h"
#include "flow/NetworkAddress.h"
@ -2418,6 +2419,31 @@ public:
std::vector<WorkerDetails> backup_workers;
std::set<NetworkAddress> backup_addresses;
if (dbi.recoveryState == RecoveryState::FULLY_RECOVERED) {
for (const auto& oldLog : dbi.logSystemConfig.oldTLogs) {
for (const auto& logSet : oldLog.tLogs) {
for (const auto& tlog : logSet.tLogs) {
if (!tlog.present()) {
continue;
}
auto tlogWorker = std::find_if(id_worker.begin(), id_worker.end(), [&tlog](const auto& worker) {
return worker.second.details.interf.address() == tlog.interf().address();
});
const auto& locality = tlogWorker == id_worker.end()
? tlog.interf().filteredLocality
: tlogWorker->second.details.interf.locality;
if (db.config.isExcludedServer(tlog.interf().addresses(), locality)) {
TraceEvent("BetterMasterExists", id)
.detail("Reason", "OldTLogExcluded")
.detail("ProcessID", locality.processId());
return true;
}
}
}
}
}
for (auto& logSet : dbi.logSystemConfig.tLogs) {
for (auto& it : logSet.tLogs) {
auto tlogWorker = id_worker.find(it.interf().filteredLocality.processId());

View File

@ -540,8 +540,8 @@ Future<Void> trackTlogRecovery(Reference<ClusterRecoveryData> self,
configuration.expectedLogSets(!self->primaryDcId.empty() ? self->primaryDcId[0] : Optional<Key>()))
.detail("RecoveryCount", newState.recoveryCount);
co_await self->cstate.write(newState, finalUpdate);
// Purge in memory state after durability to avoid race conditions.
self->logSystem->purgeOldRecoveredGenerationsInMemory(newState);
// Keep oldLogData in memory even after the coordinated state drops old generations. ServerDBInfo uses
// it to keep old-generation TLogs serving in case this master has to run recovery again.
if (self->cstateUpdated.canBeSet()) {
self->cstateUpdated.send(Void());
}

View File

@ -35,7 +35,7 @@
#include "fdbserver/logsystem/LogSystem.h"
#include "fdbserver/core/LogSystemConfig.h"
#include "fdbserver/logsystem/LogSystemDiskQueueAdapter.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/CoroUtils.h"
#include "flow/Error.h"
#include "flow/SystemMonitor.h"

View File

@ -32,7 +32,7 @@
#include "fdbclient/SystemData.h"
#include "fdbclient/ReadYourWrites.h"
#include "fdbserver/core/WorkerEvents.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include <time.h>
#include "ClusterRecovery.h"
#include "fdbclient/ClusterConnectionMemoryRecord.h"

View File

@ -22,7 +22,7 @@
#include "fdbrpc/fdbrpc.h"
#include "fdbserver/core/CoordinationInterface.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbserver/core/MasterInterface.h"
#include "fdbclient/ClusterInterface.h"

View File

@ -56,7 +56,7 @@
#include "fdbserver/core/ServerDBInfo.h"
#include "fdbserver/core/WaitFailure.h"
#include "fdbserver/commitproxy/CommitProxyServer.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/ActorCollection.h"
#include "flow/CodeProbe.h"
#include "flow/CoroUtils.h"

View File

@ -25,7 +25,7 @@
#include "fdbclient/json_spirit/json_spirit_writer_template.h"
#include "fdbserver/consistencyscan/ConsistencyScan.h"
#include "fdbserver/core/FDBSimulationPolicy.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/IRandom.h"
#include "flow/IndexedSet.h"
#include "fdbrpc/FailureMonitor.h"
@ -1687,8 +1687,9 @@ Future<Void> checkDataConsistency(Database cx,
if (firstClient && performQuiescentChecks &&
((configuration.usableRegions == 1 && (sourceStorageServers.size() > expectedReplicas ||
sourceStorageServers.size() < configuration.storageTeamSize)) ||
sourceStorageServers.size() < configuration.usableRegions * configuration.storageTeamSize ||
sourceStorageServers.size() > configuration.usableRegions * expectedReplicas)) {
sourceStorageServers.size() <
static_cast<size_t>(configuration.usableRegions) * configuration.storageTeamSize ||
sourceStorageServers.size() > static_cast<size_t>(configuration.usableRegions) * expectedReplicas)) {
TraceEvent("ConsistencyCheck_InvalidTeamSize")
.detail("ShardBegin", printable(range.begin))
.detail("ShardEnd", printable(range.end))

View File

@ -23,7 +23,7 @@
#include "fdbserver/coordinator/CoordinationServer.h"
#include "fdbserver/core/Knobs.h"
#include "OnDemandStore.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/ActorCollection.h"
#include "flow/ProtocolVersion.h"
#include "flow/UnitTest.h"
@ -713,7 +713,7 @@ class LeaderServer {
info.forward = forward.get().serializedInfo;
req.reply.send(CachedSerialization<ClientDBInfo>(info));
} else {
StringRef clusterName = ccr->getConnectionString().clusterKeyName();
Key clusterName = ccr->getConnectionString().clusterKeyName();
if (!SERVER_KNOBS->ENABLE_CROSS_CLUSTER_SUPPORT &&
getClusterDescriptor(req.clusterKey).compare(clusterName)) {
TraceEvent(SevWarn, "CCRMismatch")
@ -736,7 +736,7 @@ class LeaderServer {
if (forward.present()) {
req.reply.send(forward.get());
} else {
StringRef clusterName = ccr->getConnectionString().clusterKeyName();
Key clusterName = ccr->getConnectionString().clusterKeyName();
if (!SERVER_KNOBS->ENABLE_CROSS_CLUSTER_SUPPORT && getClusterDescriptor(req.key).compare(clusterName)) {
TraceEvent(SevWarn, "CCRMismatch")
.detail("RequestType", "ElectionResultRequest")
@ -759,7 +759,7 @@ class LeaderServer {
if (forward.present())
req.reply.send(forward.get());
else {
StringRef clusterName = ccr->getConnectionString().clusterKeyName();
Key clusterName = ccr->getConnectionString().clusterKeyName();
if (!SERVER_KNOBS->ENABLE_CROSS_CLUSTER_SUPPORT && getClusterDescriptor(req.key).compare(clusterName)) {
TraceEvent(SevWarn, "CCRMismatch")
.detail("RequestType", "GetLeaderRequest")
@ -781,7 +781,7 @@ class LeaderServer {
if (forward.present())
req.reply.send(forward.get());
else {
StringRef clusterName = ccr->getConnectionString().clusterKeyName();
Key clusterName = ccr->getConnectionString().clusterKeyName();
if (!SERVER_KNOBS->ENABLE_CROSS_CLUSTER_SUPPORT && getClusterDescriptor(req.key).compare(clusterName)) {
TraceEvent(SevWarn, "CCRMismatch")
.detail("RequestType", "CandidacyRequest")
@ -802,7 +802,7 @@ class LeaderServer {
if (forward.present())
req.reply.send(LeaderHeartbeatReply{ false });
else {
StringRef clusterName = ccr->getConnectionString().clusterKeyName();
Key clusterName = ccr->getConnectionString().clusterKeyName();
if (!SERVER_KNOBS->ENABLE_CROSS_CLUSTER_SUPPORT && getClusterDescriptor(req.key).compare(clusterName)) {
TraceEvent(SevWarn, "CCRMismatch")
.detail("RequestType", "LeaderHeartbeatRequest")
@ -823,7 +823,7 @@ class LeaderServer {
if (forward.present()) {
req.reply.send(Void());
} else {
StringRef clusterName = ccr->getConnectionString().clusterKeyName();
Key clusterName = ccr->getConnectionString().clusterKeyName();
if (!SERVER_KNOBS->ENABLE_CROSS_CLUSTER_SUPPORT && getClusterDescriptor(req.key).compare(clusterName)) {
TraceEvent(SevWarn, "CCRMismatch")
.detail("RequestType", "ForwardRequest")

View File

@ -575,7 +575,7 @@ Future<KeyRange> getBulkLoadJobFileManifestEntryFromJobManifestFile(
throw file_too_large();
}
int64_t chunkSize = 64 * 1024; // 64KB chunks
int64_t chunkSize = 64LL * 1024; // 64KB chunks
std::string buffer;
int64_t offset = 0;
std::string leftover;

View File

@ -22,7 +22,7 @@
#include "fdbclient/DatabaseContext.h"
#include "fdbclient/GlobalConfig.h"
#include "fdbclient/MonitorLeader.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
static Future<Void> extractClientInfo(Reference<AsyncVar<ServerDBInfo> const> db,
Reference<AsyncVar<ClientDBInfo>> info) {

View File

@ -37,7 +37,7 @@
#include "fdbclient/RunRYWTransaction.h"
#include "fdbserver/core/Knobs.h"
#include "fdbserver/core/QuietDatabase.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbserver/core/FDBSimulationPolicy.h"
#include "fdbclient/ManagementAPI.h"
#include "flow/CoroUtils.h"

View File

@ -358,7 +358,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( MAX_SHARD_BYTES, 500000000 );
init( KEY_SERVER_SHARD_BYTES, 500000000 );
init( SHARD_MAX_READ_OPS_PER_KSEC, 45000 * 1000 );
init( SHARD_MAX_READ_OPS_PER_KSEC, 45000LL * 1000 );
init( SHARD_READ_OPS_CHANGE_THRESHOLD, SHARD_MAX_READ_OPS_PER_KSEC / 4); if(randomize && buggify()) SHARD_READ_OPS_CHANGE_THRESHOLD = 2000;
/*
* The assumption is when the read ops reach to 45k/s the Storage Server instance will be CPU-saturated.
@ -367,7 +367,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
/*
The bytesRead/byteSize radio. Will be declared as read hot when larger than this. 8.0 was chosen to avoid reporting table scan as read hot.
*/
init ( SHARD_READ_HOT_BANDWIDTH_MIN_PER_KSECONDS, 1666667 * 1000);
init ( SHARD_READ_HOT_BANDWIDTH_MIN_PER_KSECONDS, 1666667LL * 1000);
/*
The read bandwidth of a given shard needs to be larger than this value in order to be evaluated if it's read hot. The roughly 1.67MB per second is calculated as following:
- Heuristic data suggests that each storage process can do max 500K read operations per second
@ -389,7 +389,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
team indefinitely, limiting performance.
*/
init( SHARD_MIN_BYTES_PER_KSEC, 100 * 1000 * 1000 ); if( buggifySmallBandwidthSplit ) SHARD_MIN_BYTES_PER_KSEC = 20*1000*1000;
init( SHARD_MIN_BYTES_PER_KSEC, 100LL * 1000 * 1000 ); if( buggifySmallBandwidthSplit ) SHARD_MIN_BYTES_PER_KSEC = 20LL*1000*1000;
/* 100*1KB/sec * 1000sec/ksec
Shards with more than this bandwidth will not be merged.
Obviously this needs to be significantly less than SHARD_MAX_BYTES_PER_KSEC, else we will repeatedly merge and split.
@ -403,7 +403,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
BYTES_WRITTEN_UNITS_PER_SAMPLE. If this number is too low, the storage server needs to spend more memory and time on sampling.
*/
init( SHARD_SPLIT_BYTES_PER_KSEC, 250 * 1000 * 1000 ); if( buggifySmallBandwidthSplit ) SHARD_SPLIT_BYTES_PER_KSEC = 50 * 1000 * 1000;
init( SHARD_SPLIT_BYTES_PER_KSEC, 250LL * 1000 * 1000 ); if( buggifySmallBandwidthSplit ) SHARD_SPLIT_BYTES_PER_KSEC = 50LL * 1000 * 1000;
/* 250*1KB/sec * 1000sec/ksec
When splitting a shard, it is split into pieces with less than this bandwidth.
Obviously this should be less than half of SHARD_MAX_BYTES_PER_KSEC.
@ -529,7 +529,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( DD_BULKDUMP_PARALLELISM, 50 ); if( randomize && buggify() ) DD_BULKDUMP_PARALLELISM = deterministicRandom()->randomInt(1, 5);
init( DD_BULKDUMP_BUILD_JOB_MANIFEST_BATCH_SIZE, 10000 ); if( isSimulated ) DD_BULKDUMP_BUILD_JOB_MANIFEST_BATCH_SIZE = deterministicRandom()->randomInt(1, 100);
init( SS_SERVE_BULKDUMP_PARALLELISM, 1 ); // TODO(BulkDump): Do not set to 1 after SS can resolve the file folder conflict
init( SS_BULKDUMP_BATCH_BYTES, 100*1024*1024 ); if( isSimulated ) SS_BULKDUMP_BATCH_BYTES = deterministicRandom()->randomInt(1000, 10000);
init( SS_BULKDUMP_BATCH_BYTES, 100LL*1024*1024 ); if( isSimulated ) SS_BULKDUMP_BATCH_BYTES = deterministicRandom()->randomInt(1000, 10000);
init( SS_BULKDUMP_BATCH_COUNT_MAX_PER_REQUEST, 10 ); if( isSimulated ) SS_BULKDUMP_BATCH_COUNT_MAX_PER_REQUEST = deterministicRandom()->randomInt(1, 10);
// TeamRemover
@ -629,7 +629,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( ROCKSDB_PERIODIC_COMPACTION_SECONDS, 0 ); if( isSimulated ) ROCKSDB_PERIODIC_COMPACTION_SECONDS = deterministicRandom()->randomInt(5*60, 24*60*60);
init( ROCKSDB_TTL_COMPACTION_SECONDS, 2160000 ); if( isSimulated ) ROCKSDB_TTL_COMPACTION_SECONDS = deterministicRandom()->randomInt(5*60, 24*60*60);
int64_t maxCompactionBytes = 160LL * 64 * 1024 * 1024;
init( ROCKSDB_MAX_COMPACTION_BYTES, 0 ); /* default = 25*64MB */ if( randomize && buggify() ) ROCKSDB_MAX_COMPACTION_BYTES = deterministicRandom()->randomInt64(5*64*1024*1024, maxCompactionBytes);
init( ROCKSDB_MAX_COMPACTION_BYTES, 0 ); /* default = 25*64MB */ if( randomize && buggify() ) ROCKSDB_MAX_COMPACTION_BYTES = deterministicRandom()->randomInt64(5LL*64*1024*1024, maxCompactionBytes);
init( ROCKSDB_PREFIX_LEN, 11 ); if( randomize && buggify() ) ROCKSDB_PREFIX_LEN = deterministicRandom()->randomInt(1, 20);
init( ROCKSDB_MEMTABLE_PREFIX_BLOOM_SIZE_RATIO, 0.1 );
init( ROCKSDB_BLOOM_BITS_PER_KEY, 10 );
@ -920,7 +920,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
// Backup Worker
init( BACKUP_TIMEOUT, 0.4 );
init( BACKUP_FILE_BLOCK_BYTES, 1024 * 1024 );
init( BACKUP_WORKER_LOCK_BYTES, 3e9 ); if(randomize && buggify()) BACKUP_WORKER_LOCK_BYTES = deterministicRandom()->randomInt(2048, 4096) * 4096;
init( BACKUP_WORKER_LOCK_BYTES, 3e9 ); if(randomize && buggify()) BACKUP_WORKER_LOCK_BYTES = deterministicRandom()->randomInt(2048, 4096) * 4096LL;
init( BACKUP_UPLOAD_DELAY, 10.0 ); if(randomize && buggify()) BACKUP_UPLOAD_DELAY = deterministicRandom()->random01() * 60;
//Cluster Controller
@ -1068,7 +1068,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( CONSISTENCY_CHECK_BACKWARD_READ, false ); if (isSimulated) CONSISTENCY_CHECK_BACKWARD_READ = deterministicRandom()->coinflip();
init (STORAGE_FETCH_KEYS_DELAY, 0.0 ); if ( randomize && buggify() ) { STORAGE_FETCH_KEYS_DELAY = deterministicRandom()->random01() * 5.0; }
init (STORAGE_FETCH_KEYS_USE_COMMIT_BUDGET, false ); if (isSimulated) STORAGE_FETCH_KEYS_USE_COMMIT_BUDGET = deterministicRandom()->coinflip();
init (STORAGE_FETCH_KEYS_RATE_LIMIT, 0 ); if (isSimulated && buggify()) STORAGE_FETCH_KEYS_RATE_LIMIT = 100 * 1024 * deterministicRandom()->randomInt(1, 10); // In MB/s
init (STORAGE_FETCH_KEYS_RATE_LIMIT, 0 ); if (isSimulated && buggify()) STORAGE_FETCH_KEYS_RATE_LIMIT = 100LL * 1024 * deterministicRandom()->randomInt(1, 10); // In MB/s
init (STORAGE_ROCKSDB_LOG_CLEAN_UP_DELAY, 3600 * 2 ); if (isSimulated) STORAGE_ROCKSDB_LOG_CLEAN_UP_DELAY = 20.0;
init (STORAGE_ROCKSDB_LOG_TTL, 3600 * 24 * 15 ); if (isSimulated) STORAGE_ROCKSDB_LOG_TTL = 3600.0;
@ -1321,7 +1321,7 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
// Timekeeper
init( TIME_KEEPER_DELAY, 10 );
init( TIME_KEEPER_MAX_ENTRIES, 3600 * 24 * 30 * 6 ); if( randomize && buggify() ) { TIME_KEEPER_MAX_ENTRIES = 2; }
init( TIME_KEEPER_MAX_ENTRIES, 3600LL * 24 * 30 * 6 ); if( randomize && buggify() ) { TIME_KEEPER_MAX_ENTRIES = 2; }
init( REDWOOD_DEFAULT_PAGE_SIZE, 8192 );
init( REDWOOD_DEFAULT_EXTENT_SIZE, 32 * 1024 * 1024 );

View File

@ -357,8 +357,8 @@ void StorageServerMetrics::splitMetrics(SplitMetricsRequest req) const {
//TraceEvent("SplitMetrics").detail("Begin", req.keys.begin).detail("End", req.keys.end).detail("Remaining", remaining.bytes).detail("Used", used.bytes).detail("MinSplitBytes", minSplitBytes);
while (true) {
if (remaining.bytes < 2 * minSplitBytes && (!SERVER_KNOBS->ENABLE_WRITE_BASED_SHARD_SPLIT ||
remaining.bytesWrittenPerKSecond < minSplitWriteTraffic))
if (remaining.bytes < 2LL * minSplitBytes && (!SERVER_KNOBS->ENABLE_WRITE_BASED_SHARD_SPLIT ||
remaining.bytesWrittenPerKSecond < minSplitWriteTraffic))
break;
KeyRef key = req.keys.end;
bool hasUsed = used.bytes != 0 || used.bytesWrittenPerKSecond != 0 || used.iosPerKSecond != 0;
@ -610,7 +610,7 @@ void StorageServerMetrics::getSplitPoints(SplitRangeRequest req, Optional<KeyRef
range = range.withPrefix(prefix.get(), req.arena);
}
std::vector<KeyRef> points = getSplitPoints(range, req.chunkSize, prefix);
std::vector<KeyRef> points = getSplitPoints(range, req.chunkSize, prefix, req.limit);
reply.splitPoints.append_deep(reply.splitPoints.arena(), points.data(), points.size());
req.reply.send(reply);
@ -618,13 +618,14 @@ void StorageServerMetrics::getSplitPoints(SplitRangeRequest req, Optional<KeyRef
std::vector<KeyRef> StorageServerMetrics::getSplitPoints(KeyRangeRef range,
int64_t chunkSize,
Optional<KeyRef> prefixToRemove) const {
Optional<KeyRef> prefixToRemove,
int limit) const {
std::vector<KeyRef> toReturn;
KeyRef beginKey = range.begin;
IndexedSet<Key, int64_t>::const_iterator endKey =
byteSample.sample.index(byteSample.sample.sumTo(byteSample.sample.lower_bound(beginKey)) + chunkSize);
while (endKey != byteSample.sample.end()) {
if (*endKey > range.end) {
while (endKey != byteSample.sample.end() && (limit < 0 || toReturn.size() < static_cast<size_t>(limit))) {
if (*endKey >= range.end) {
break;
}
if (*endKey == beginKey) {
@ -924,6 +925,54 @@ TEST_CASE("/fdbserver/StorageMetricSample/rangeSplitPoints/multipleReturnedPoint
return Void();
}
TEST_CASE("/fdbserver/StorageMetricSample/rangeSplitPoints/limit") {
int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE;
StorageServerMetrics ssm;
ssm.byteSample.sample.insert("A"_sr, 200 * sampleUnit);
ssm.byteSample.sample.insert("Absolute"_sr, 800 * sampleUnit);
ssm.byteSample.sample.insert("Apple"_sr, 1000 * sampleUnit);
ssm.byteSample.sample.insert("Bah"_sr, 20 * sampleUnit);
ssm.byteSample.sample.insert("Banana"_sr, 80 * sampleUnit);
ssm.byteSample.sample.insert("Bob"_sr, 200 * sampleUnit);
ssm.byteSample.sample.insert("But"_sr, 100 * sampleUnit);
ssm.byteSample.sample.insert("Cat"_sr, 300 * sampleUnit);
std::vector<KeyRef> limited = ssm.getSplitPoints(KeyRangeRef("A"_sr, "C"_sr), 600 * sampleUnit, {}, 2);
ASSERT(limited.size() == 2 && limited[0] == "Absolute"_sr && limited[1] == "Apple"_sr);
std::vector<KeyRef> none = ssm.getSplitPoints(KeyRangeRef("A"_sr, "C"_sr), 600 * sampleUnit, {}, 0);
ASSERT(none.empty());
SplitRangeRequest req(KeyRangeRef("A"_sr, "C"_sr), 600 * sampleUnit, 1);
Future<SplitRangeReply> reply = req.reply.getFuture();
ssm.getSplitPoints(req, {});
ASSERT(reply.isReady());
ASSERT(reply.get().splitPoints.size() == 1 && reply.get().splitPoints[0] == "Absolute"_sr);
return Void();
}
TEST_CASE("/fdbserver/StorageMetricSample/rangeSplitPoints/exclusiveEnd") {
int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE;
StorageServerMetrics ssm;
ssm.byteSample.sample.insert("A"_sr, 200 * sampleUnit);
ssm.byteSample.sample.insert("B"_sr, 800 * sampleUnit);
std::vector<KeyRef> direct = ssm.getSplitPoints(KeyRangeRef("A"_sr, "B"_sr), 600 * sampleUnit, {}, 1);
ASSERT(direct.empty());
SplitRangeRequest req(KeyRangeRef("A"_sr, "B"_sr), 600 * sampleUnit, 1);
Future<SplitRangeReply> reply = req.reply.getFuture();
ssm.getSplitPoints(req, {});
ASSERT(reply.isReady());
ASSERT(reply.get().splitPoints.empty());
return Void();
}
TEST_CASE("/fdbserver/StorageMetricSample/rangeSplitPoints/noneSplitable") {
int64_t sampleUnit = SERVER_KNOBS->BYTES_READ_UNITS_PER_SAMPLE;

View File

@ -18,7 +18,7 @@
* limitations under the License.
*/
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
Future<Void> extractClusterInterface(Reference<AsyncVar<Optional<ClusterControllerFullInterface>> const> in,
Reference<AsyncVar<Optional<ClusterInterface>>> out) {

View File

@ -18,7 +18,7 @@
* limitations under the License.
*/
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/ObjectSerializer.h"
#include "flow/UnitTest.h"

View File

@ -24,7 +24,7 @@
#include "fdbclient/FDBTypes.h"
#include "fdbclient/NativeAPI.actor.h"
#include "fdbserver/core/TesterInterface.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
Future<int64_t> getDataInFlight(Database cx, Reference<AsyncVar<struct ServerDBInfo> const> dbInfo);
Future<std::pair<int64_t, int64_t>> getTLogQueueInfo(Database cx,

View File

@ -29,7 +29,7 @@
#include "fdbserver/core/MasterInterface.h"
#include "fdbserver/core/RatekeeperInterface.h"
#include "fdbserver/core/RecoveryState.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
struct ServerDBInfo {
constexpr static FileIdentifier file_identifier = 13838807;
@ -111,7 +111,7 @@ struct GetServerDBInfoRequest {
}
};
// Instantiated in worker.actor.cpp
// Instantiated in worker.cpp
extern template class RequestStream<GetServerDBInfoRequest, false>;
extern template struct NetNotifiedQueue<GetServerDBInfoRequest, false>;

View File

@ -148,7 +148,10 @@ struct StorageServerMetrics {
int64_t getHotShards(const KeyRange& range) const;
std::vector<KeyRef> getSplitPoints(KeyRangeRef range, int64_t chunkSize, Optional<KeyRef> prefixToRemove) const;
std::vector<KeyRef> getSplitPoints(KeyRangeRef range,
int64_t chunkSize,
Optional<KeyRef> prefixToRemove,
int limit = -1) const;
void getSplitPoints(SplitRangeRequest req, Optional<KeyRef> prefix) const;

View File

@ -25,7 +25,7 @@
#include <string>
#include "flow/ITrace.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
struct WorkerEvents : std::map<NetworkAddress, TraceEventFields> {};

View File

@ -1,5 +1,5 @@
/*
* WorkerInterface.actor.h
* WorkerInterface.h
*
* This source file is part of the FoundationDB open source project
*
@ -19,11 +19,6 @@
*/
#pragma once
#if defined(NO_INTELLISENSE) && !defined(FDBSERVER_WORKERINTERFACE_ACTOR_G_H)
#define FDBSERVER_WORKERINTERFACE_ACTOR_G_H
#include "fdbserver/core/WorkerInterface.actor.g.h"
#elif !defined(FDBSERVER_WORKERINTERFACE_ACTOR_H)
#define FDBSERVER_WORKERINTERFACE_ACTOR_H
#include "fdbserver/core/BackupInterface.h"
#include "fdbserver/core/DataDistributorInterface.h"
@ -43,7 +38,7 @@
#include "fdbrpc/MultiInterface.h"
#include "fdbclient/ClientWorkerInterface.h"
#include "fdbserver/core/RecoveryState.h"
#include "flow/actorcompiler.h"
#include "flow/CoroUtils.h"
struct WorkerInterface {
constexpr static FileIdentifier file_identifier = 14712718;
@ -83,7 +78,7 @@ struct WorkerInterface {
NetworkAddressList addresses() const { return tLog.getEndpoint().addresses; }
Optional<NetworkAddress> grpcAddress() const { return clientInterface.grpcAddress; }
WorkerInterface() {}
WorkerInterface() = default;
explicit(false) WorkerInterface(const LocalityData& locality) : locality(locality) {}
void initEndpoints() {
@ -371,7 +366,7 @@ struct RecruitFromConfigurationRequest {
int maxOldLogRouters;
ReplyPromise<RecruitFromConfigurationReply> reply;
RecruitFromConfigurationRequest() {}
RecruitFromConfigurationRequest() = default;
explicit RecruitFromConfigurationRequest(DatabaseConfiguration const& configuration,
bool recruitSeedServers,
int maxOldLogRouters)
@ -404,7 +399,7 @@ struct RecruitRemoteFromConfigurationRequest {
Optional<UID> dbgId;
ReplyPromise<RecruitRemoteFromConfigurationReply> reply;
RecruitRemoteFromConfigurationRequest() {}
RecruitRemoteFromConfigurationRequest() = default;
RecruitRemoteFromConfigurationRequest(DatabaseConfiguration const& configuration,
Optional<Key> const& dcId,
int logRouterCount,
@ -550,7 +545,7 @@ struct TLogRejoinRequest {
TLogInterface myInterface;
ReplyPromise<TLogRejoinReply> reply;
TLogRejoinRequest() {}
TLogRejoinRequest() = default;
explicit TLogRejoinRequest(const TLogInterface& interf) : myInterface(interf) {}
template <class Ar>
void serialize(Ar& ar) {
@ -591,7 +586,7 @@ struct GetEncryptionAtRestModeRequest {
UID tlogId;
ReplyPromise<GetEncryptionAtRestModeResponse> reply;
GetEncryptionAtRestModeRequest() {}
GetEncryptionAtRestModeRequest() = default;
explicit(false) GetEncryptionAtRestModeRequest(UID tId) : tlogId(tId) {}
template <class Ar>
@ -776,7 +771,7 @@ struct RecruitMasterRequest {
}
};
// Instantiated in worker.actor.cpp
// Instantiated in worker.cpp
extern template class RequestStream<RecruitMasterRequest, false>;
extern template struct NetNotifiedQueue<RecruitMasterRequest, false>;
@ -805,7 +800,7 @@ struct InitializeCommitProxyRequest {
}
};
// Instantiated in worker.actor.cpp
// Instantiated in worker.cpp
extern template class RequestStream<InitializeCommitProxyRequest, false>;
extern template struct NetNotifiedQueue<InitializeCommitProxyRequest, false>;
@ -822,7 +817,7 @@ struct InitializeGrvProxyRequest {
}
};
// Instantiated in worker.actor.cpp
// Instantiated in worker.cpp
extern template class RequestStream<InitializeGrvProxyRequest, false>;
extern template struct NetNotifiedQueue<InitializeGrvProxyRequest, false>;
@ -846,7 +841,7 @@ struct InitializeDataDistributorRequest {
UID reqId;
ReplyPromise<DataDistributorInterface> reply;
InitializeDataDistributorRequest() {}
InitializeDataDistributorRequest() = default;
explicit InitializeDataDistributorRequest(UID uid) : reqId(uid) {}
template <class Ar>
void serialize(Ar& ar) {
@ -859,7 +854,7 @@ struct InitializeRatekeeperRequest {
UID reqId;
ReplyPromise<RatekeeperInterface> reply;
InitializeRatekeeperRequest() {}
InitializeRatekeeperRequest() = default;
explicit InitializeRatekeeperRequest(UID uid) : reqId(uid) {}
template <class Ar>
void serialize(Ar& ar) {
@ -872,7 +867,7 @@ struct InitializeConsistencyScanRequest {
UID reqId;
ReplyPromise<ConsistencyScanInterface> reply;
InitializeConsistencyScanRequest() {}
InitializeConsistencyScanRequest() = default;
explicit InitializeConsistencyScanRequest(UID uid) : reqId(uid) {}
template <class Ar>
void serialize(Ar& ar) {
@ -1044,7 +1039,7 @@ struct DebugEntryRef {
StringRef context;
Version version;
MutationRef mutation;
DebugEntryRef() {}
DebugEntryRef() = default;
DebugEntryRef(const char* c, Version v, MutationRef const& m)
: time(now()), address(g_network->getLocalAddress()), context((const uint8_t*)c, strlen(c)), version(v),
mutation(m) {}
@ -1174,42 +1169,44 @@ bool addressInDbAndRemoteDc(
extern bool isSimulatorProcessUnreliable();
ACTOR template <class T>
Future<T> ioTimeoutError(Future<T> what, double time, const char* context = nullptr) {
template <class T>
Future<T> ioTimeoutError(Future<T> what, double time, const char* context = nullptr, ExplicitVoid = {}) {
// Before simulation is sped up, IO operations can take a very long time so limit timeouts
// to not end until at least time after simulation is sped up.
state double orig = now();
state std::string trace = platform::get_backtrace();
double orig = now();
std::string trace = platform::get_backtrace();
if (g_network->isSimulated() && !g_simulator->speedUpSimulation) {
time += std::max(0.0, FLOW_KNOBS->SIM_SPEEDUP_AFTER_SECONDS - now());
}
Future<Void> end = lowPriorityDelay(time);
choose {
when(T t = wait(what)) {
return t;
auto res = co_await race(what, end);
if (res.index() == 0) {
T t = std::get<0>(std::move(res));
co_return t;
} else if (res.index() == 1) {
Error err = io_timeout();
if (isSimulatorProcessUnreliable()) {
err = err.asInjectedFault();
}
when(wait(end)) {
Error err = io_timeout();
if (isSimulatorProcessUnreliable()) {
err = err.asInjectedFault();
}
TraceEvent e(SevError, "IoTimeoutError");
e.error(err);
if (context != nullptr) {
e.detail("Context", context);
}
e.detail("OrigTime", orig).detail("OrigTrace", trace).log();
throw err;
TraceEvent e(SevError, "IoTimeoutError");
e.error(err);
if (context != nullptr) {
e.detail("Context", context);
}
e.detail("OrigTime", orig).detail("OrigTrace", trace).log();
throw err;
} else {
UNREACHABLE();
}
}
ACTOR template <class T>
template <class T>
Future<T> ioDegradedOrTimeoutError(Future<T> what,
double errTime,
Reference<AsyncVar<bool>> degraded,
double degradedTime,
const char* context = nullptr) {
const char* context = nullptr,
ExplicitVoid = {}) {
// Before simulation is sped up, IO operations can take a very long time so limit timeouts
// to not end until at least time after simulation is sped up.
if (g_network->isSimulated() && !g_simulator->speedUpSimulation) {
@ -1220,39 +1217,39 @@ Future<T> ioDegradedOrTimeoutError(Future<T> what,
if (degradedTime < errTime) {
Future<Void> degradedEnd = lowPriorityDelay(degradedTime);
choose {
when(T t = wait(what)) {
return t;
}
when(wait(degradedEnd)) {
CODE_PROBE(true, "TLog degraded", probe::func::deduplicate);
TraceEvent(SevWarnAlways, "IoDegraded").log();
degraded->set(true);
}
auto res = co_await race(what, degradedEnd);
if (res.index() == 0) {
T t = std::get<0>(std::move(res));
co_return t;
} else if (res.index() == 1) {
CODE_PROBE(true, "TLog degraded", probe::func::deduplicate);
TraceEvent(SevWarnAlways, "IoDegraded").log();
degraded->set(true);
} else {
UNREACHABLE();
}
}
Future<Void> end = lowPriorityDelay(errTime - degradedTime);
choose {
when(T t = wait(what)) {
return t;
auto res = co_await race(what, end);
if (res.index() == 0) {
T t = std::get<0>(std::move(res));
co_return t;
} else if (res.index() == 1) {
Error err = io_timeout();
if (isSimulatorProcessUnreliable()) {
err = err.asInjectedFault();
}
when(wait(end)) {
Error err = io_timeout();
if (isSimulatorProcessUnreliable()) {
err = err.asInjectedFault();
}
TraceEvent e(SevError, "IoTimeoutError");
e.error(err);
if (context != nullptr) {
e.detail("Context", context);
}
e.log();
throw err;
TraceEvent e(SevError, "IoTimeoutError");
e.error(err);
if (context != nullptr) {
e.detail("Context", context);
}
e.log();
throw err;
} else {
UNREACHABLE();
}
}
#include "flow/unactorcompiler.h"
#include "fdbserver/core/ServerDBInfo.h"
#endif

View File

@ -49,11 +49,6 @@
using ITeamRef = Reference<IDataDistributionTeam>;
using SrcDestTeamPair = std::pair<ITeamRef, ITeamRef>;
inline bool isDataMovementForDiskBalancing(DataMovementReason reason) {
return reason == DataMovementReason::REBALANCE_UNDERUTILIZED_TEAM ||
reason == DataMovementReason::REBALANCE_OVERUTILIZED_TEAM;
}
inline bool isDataMovementForReadBalancing(DataMovementReason reason) {
return reason == DataMovementReason::REBALANCE_READ_OVERUTIL_TEAM ||
reason == DataMovementReason::REBALANCE_READ_UNDERUTIL_TEAM;
@ -3069,9 +3064,23 @@ struct DDQueueImpl {
}
static Future<Void> waitAndValidate(RunState* state, Future<Void> future) {
co_await future;
Error error;
try {
co_await future;
} catch (Error& e) {
error = e;
}
// A relocator can signal an error inline while launchQueuedWork() is repairing its maps. Keep DD alive
// until that mutation finishes before propagating the error and tearing the queue down. Preserve the
// immediate error path when no mutation is active, since taking an available FlowLock still yields.
if (error.isValid() && state->queueMutationLock.available() > 0) {
throw error;
}
co_await state->queueMutationLock.take();
FlowLock::Releaser lockGuard(state->queueMutationLock);
if (error.isValid()) {
throw error;
}
validate(state);
}
@ -3218,3 +3227,34 @@ TEST_CASE("/DataDistribution/DDQueue/BatchDrainRelocationComplete") {
std::cout << "BatchDrainRelocationComplete: drained " << drained << " of " << N << " completions\n";
}
TEST_CASE("/DataDistribution/DDQueue/SerializeRelocatorError") {
Reference<DDQueue> self = makeReference<DDQueue>();
DDQueueImpl::RunState state(self);
Promise<Void> error;
Future<Void> propagated;
{
co_await state.queueMutationLock.take();
FlowLock::Releaser lockGuard(state.queueMutationLock);
propagated = DDQueueImpl::waitAndValidate(&state, error.getFuture());
error.sendError(movekeys_conflict());
ASSERT(!propagated.isReady());
}
Error observed;
try {
co_await propagated;
} catch (Error& e) {
observed = e;
}
ASSERT(observed.code() == error_code_movekeys_conflict);
Promise<Void> immediateError;
Future<Void> immediate = DDQueueImpl::waitAndValidate(&state, immediateError.getFuture());
immediateError.sendError(movekeys_conflict());
ASSERT(immediate.isReady());
ASSERT(immediate.isError());
ASSERT(immediate.getError().code() == error_code_movekeys_conflict);
co_return;
}

View File

@ -415,11 +415,6 @@ std::string describeSplit(KeyRange keys, Standalone<VectorRef<KeyRef>>& splitKey
return s;
}
void traceSplit(KeyRange keys, Standalone<VectorRef<KeyRef>>& splitKeys) {
auto s = describeSplit(keys, splitKeys);
TraceEvent(SevInfo, "ExecutingShardSplit").detail("AtKeys", s);
}
void executeShardSplit(DataDistributionTracker* self,
KeyRange keys,
Standalone<VectorRef<KeyRef>> splitKeys,
@ -465,39 +460,6 @@ void executeShardSplit(DataDistributionTracker* self,
self->actors.add(changeSizes(self, keys, shardSize->get().get().metrics.bytes, "ShardSplit"));
}
struct RangeToSplit {
RangeMap<Standalone<StringRef>, ShardTrackedData, KeyRangeRef>::iterator shard;
Standalone<VectorRef<KeyRef>> faultLines;
RangeToSplit(RangeMap<Standalone<StringRef>, ShardTrackedData, KeyRangeRef>::iterator shard,
Standalone<VectorRef<KeyRef>> faultLines)
: shard(shard), faultLines(faultLines) {}
};
bool faultLinesMatch(std::vector<RangeToSplit>& ranges, std::vector<std::vector<KeyRef>>& expectedFaultLines) {
if (ranges.size() != expectedFaultLines.size()) {
return false;
}
for (auto& range : ranges) {
KeyRangeRef keys = KeyRangeRef(range.shard->begin(), range.shard->end());
traceSplit(keys, range.faultLines);
}
for (int r = 0; r < ranges.size(); r++) {
if (ranges[r].faultLines.size() != expectedFaultLines[r].size()) {
return false;
}
for (int fl = 0; fl < ranges[r].faultLines.size(); fl++) {
if (ranges[r].faultLines[fl] != expectedFaultLines[r][fl]) {
return false;
}
}
}
return true;
}
Future<Void> shardSplitter(DataDistributionTracker* self,
KeyRange keys,
Reference<AsyncVar<Optional<ShardMetrics>>> shardSize,

View File

@ -420,41 +420,6 @@ Future<Void> monitorBackupPartitionRequired(Database cx, KeyRangeMap<ShardTracke
}
}
// Ensures that the serverKeys key space is properly coalesced
// This method is only used for testing and is not implemented in a manner that is safe for large databases
Future<Void> debugCheckCoalescing(Database cx) {
Transaction tr(cx);
while (true) {
Error err;
try {
RangeResult serverList = co_await tr.getRange(serverListKeys, CLIENT_KNOBS->TOO_MANY);
ASSERT(!serverList.more && serverList.size() < CLIENT_KNOBS->TOO_MANY);
int i{ 0 };
for (i = 0; i < serverList.size(); i++) {
UID id = decodeServerListValue(serverList[i].value).id();
RangeResult ranges = co_await krmGetRanges(&tr, serverKeysPrefixFor(id), allKeys);
ASSERT(ranges.end()[-1].key == allKeys.end);
for (int j = 0; j < ranges.size() - 2; j++) {
if (ranges[j].value == ranges[j + 1].value) {
TraceEvent(SevError, "UncoalescedValues", id)
.detail("Key1", ranges[j].key)
.detail("Key2", ranges[j + 1].key)
.detail("Value", ranges[j].value);
}
}
}
TraceEvent("DoneCheckingCoalescing").log();
co_return;
} catch (Error& e) {
err = e;
}
co_await tr.onError(err);
}
}
struct DataDistributor;
void runAuditStorage(
Reference<DataDistributor> self,

View File

@ -65,7 +65,7 @@
#include "fdbserver/core/FDBSimulationPolicy.h"
#include "fdbserver/tester/TestEncryptionUtils.h"
#include "fdbserver/tester/tester.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbserver/worker/Worker.h"
#include "fdbserver/mocks3/MockS3Server.h"
#ifdef WITH_ROCKSDB
@ -410,68 +410,6 @@ Future<Void> metricsReport() {
}
}
void testSerializationSpeed() {
double tstart;
double build = 0, serialize = 0, deserialize = 0, copy = 0, deallocate = 0;
double bytes = 0;
double testBegin = timer();
for (int a = 0; a < 10000; a++) {
{
tstart = timer();
Arena batchArena;
VectorRef<CommitTransactionRef> batch;
batch.resize(batchArena, 1000);
for (int t = 0; t < batch.size(); t++) {
CommitTransactionRef& tr = batch[t];
tr.read_snapshot = 0;
for (int i = 0; i < 2; i++)
tr.mutations.push_back_deep(batchArena,
MutationRef(MutationRef::SetValue, "KeyABCDE"_sr, "SomeValu"_sr));
tr.mutations.push_back_deep(batchArena,
MutationRef(MutationRef::ClearRange, "BeginKey"_sr, "EndKeyAB"_sr));
}
build += timer() - tstart;
tstart = timer();
BinaryWriter wr(IncludeVersion());
wr << batch;
bytes += wr.getLength();
serialize += timer() - tstart;
for (int i = 0; i < 1; i++) {
tstart = timer();
Arena arena;
StringRef data(arena, StringRef((const uint8_t*)wr.getData(), wr.getLength()));
copy += timer() - tstart;
tstart = timer();
ArenaReader rd(arena, data, IncludeVersion());
VectorRef<CommitTransactionRef> batch2;
rd >> arena >> batch2;
deserialize += timer() - tstart;
}
tstart = timer();
}
deallocate += timer() - tstart;
}
double elapsed = (timer() - testBegin);
printf("Test speed: %0.1f MB/sec (%0.0f/sec)\n", bytes / 1e6 / elapsed, 1000000 / elapsed);
printf(" Build: %0.1f MB/sec\n", bytes / 1e6 / build);
printf(" Serialize: %0.1f MB/sec\n", bytes / 1e6 / serialize);
printf(" Copy: %0.1f MB/sec\n", bytes / 1e6 / copy);
printf(" Deserialize: %0.1f MB/sec\n", bytes / 1e6 / deserialize);
printf(" Deallocate: %0.1f MB/sec\n", bytes / 1e6 / deallocate);
printf(" Bytes: %0.1f MB\n", bytes / 1e6);
printf("\n");
}
void memoryTest();
void skipListTest();

View File

@ -35,7 +35,7 @@
#include "fdbserver/logsystem/LogSystemFactory.h"
#include "fdbserver/logsystem/LogSystemDiskQueueAdapter.h"
#include "fdbserver/core/WaitFailure.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbrpc/sim_validation.h"
#include "flow/Buggify.h"
#include "flow/IRandom.h"

View File

@ -28,7 +28,6 @@
struct NetworkTestInterface {
RequestStream<struct NetworkTestRequest> test;
RequestStream<struct NetworkTestStreamingRequest> testStream;
NetworkTestInterface() = default;
explicit NetworkTestInterface(NetworkAddress remote);
explicit NetworkTestInterface(INetwork* local);
@ -58,29 +57,6 @@ struct NetworkTestRequest {
}
};
struct NetworkTestStreamingReply : ReplyPromiseStreamReply {
constexpr static FileIdentifier file_identifier = 3726830;
int index = 0;
NetworkTestStreamingReply() = default;
explicit NetworkTestStreamingReply(int index) : index(index) {}
size_t expectedSize() const { return 4e6; /*sizeof(*this);*/ }
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, ReplyPromiseStreamReply::acknowledgeToken, ReplyPromiseStreamReply::sequence, index);
}
};
struct NetworkTestStreamingRequest {
constexpr static FileIdentifier file_identifier = 2794452;
ReplyPromiseStream<struct NetworkTestStreamingReply> reply;
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, reply);
}
};
Future<Void> networkTestServer();
Future<Void> networkTestClient(std::string const& testServers);

View File

@ -172,9 +172,10 @@ public:
readingPage(-1), writingPos(-1), fileExtensionBytes(SERVER_KNOBS->DISK_QUEUE_FILE_EXTENSION_BYTES),
fileShrinkBytes(SERVER_KNOBS->DISK_QUEUE_FILE_SHRINK_BYTES) {
if (buggify())
fileExtensionBytes = _PAGE_SIZE * deterministicRandom()->randomSkewedUInt32(1, 10 << 10);
fileExtensionBytes =
static_cast<int64_t>(_PAGE_SIZE) * deterministicRandom()->randomSkewedUInt32(1, 10 << 10);
if (buggify())
fileShrinkBytes = _PAGE_SIZE * deterministicRandom()->randomSkewedUInt32(1, 10 << 10);
fileShrinkBytes = static_cast<int64_t>(_PAGE_SIZE) * deterministicRandom()->randomSkewedUInt32(1, 10 << 10);
files[0].dbgFilename = filename(0);
files[1].dbgFilename = filename(1);
// We issue reads into firstPages, so it needs to be 4k aligned.

View File

@ -38,8 +38,6 @@
#include "flow/flow.h"
#include "flow/genericactors.actor.h"
#include "flow/network.h"
#include "fdbrpc/simulator.h"
#include "fdbrpc/SimulatorProcessInfo.h"
#include "fdbclient/IClosable.h"
#include "fdbclient/versions.h"
#include "fdbserver/CoroFlow.h"
@ -63,7 +61,7 @@ void ExecCmdValueString::setCmdValueString(StringRef pCmdValueString) {
}
StringRef ExecCmdValueString::getCmdValueString() const {
return cmdValueString.toString();
return cmdValueString;
}
StringRef ExecCmdValueString::getBinaryPath() const {
@ -105,19 +103,6 @@ void ExecCmdValueString::dbgPrint() const {
return;
}
Future<Void> destroyChildProcess(Uncancellable,
Future<Void> parentSSClosed,
ISimulator::ProcessInfo* childInfo,
std::string message) {
// This code path should be bug free
co_await parentSSClosed;
TraceEvent(SevDebug, message.c_str()).log();
// This one is root cause for most failures, make sure it's okay to destroy
g_simulator->destroyProcess(childInfo);
// Explicitly reset the connection with the child process in case re-spawn very quickly
FlowTransport::transport().resetConnection(childInfo->address);
}
#if defined(_WIN32) || defined(__APPLE__) || defined(__INTEL_COMPILER)
Future<int> spawnProcess(std::string binPath,
std::vector<std::string> paramList,

View File

@ -1307,7 +1307,7 @@ struct RocksDBKeyValueStore : IKeyValueStore {
rateLimiter(SERVER_KNOBS->ROCKSDB_WRITE_RATE_LIMITER_BYTES_PER_SEC > 0
? rocksdb::NewGenericRateLimiter(
SERVER_KNOBS->ROCKSDB_WRITE_RATE_LIMITER_BYTES_PER_SEC, // rate_bytes_per_sec
100 * 1000, // refill_period_us
100LL * 1000, // refill_period_us
SERVER_KNOBS->ROCKSDB_WRITE_RATE_LIMITER_FAIRNESS, // fairness
rocksdb::RateLimiter::Mode::kAllIo,
SERVER_KNOBS->ROCKSDB_WRITE_RATE_LIMITER_AUTO_TUNE)

View File

@ -159,7 +159,7 @@ struct PageChecksumCodec {
if (g_network->isSimulated()) {
// Calculate file offsets for the read/write operation space
// Operation starts at a 1-based pageNumber and is of size pageLen
int64_t fileOffsetStart = (pageNumber - 1) * pageLen;
int64_t fileOffsetStart = static_cast<int64_t>(pageNumber - 1) * pageLen;
// End refers to the offset after the operation, not the last byte.
int64_t fileOffsetEnd = fileOffsetStart + pageLen;

View File

@ -1244,7 +1244,7 @@ public:
auto rateLimiter =
rocksdb::NewGenericRateLimiter(SERVER_KNOBS->SHARDED_ROCKSDB_WRITE_RATE_LIMITER_BYTES_PER_SEC,
100 * 1000, // refill_period_us
100LL * 1000, // refill_period_us
10, // fairness
mode,
SERVER_KNOBS->ROCKSDB_WRITE_RATE_LIMITER_AUTO_TUNE);

View File

@ -30,7 +30,7 @@
#include "fdbserver/core/Knobs.h"
#include "fdbserver/core/FDBSimulationPolicy.h"
#include "VersionedBTreeDebug.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/ActorCollection.h"
#include "flow/CoroUtils.h"
#include "flow/Error.h"
@ -1365,10 +1365,6 @@ public:
}
};
int nextPowerOf2(uint32_t x) {
return 1 << (32 - clz(x - 1));
}
struct RedwoodMetrics {
constexpr static unsigned int btreeLevels = 5;
static int maxRecordCount;

View File

@ -24,7 +24,7 @@
#include "fdbserver/logsystem/LogSystemConsumer.h"
#include "fdbserver/logrouter/LogRouter.h"
#include "fdbserver/logsystem/LogSystemFactory.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbserver/core/RecoveryState.h"
#include "fdbserver/core/TLogInterface.h"
#include "flow/ActorCollection.h"

View File

@ -363,7 +363,7 @@ UID LogSystem::getDebugID() const {
void LogSystem::addPseudoLocality(int8_t locality) {
ASSERT(locality < 0);
pseudoLocalities.insert(locality);
for (uint16_t i = 0; i < logRouterTags; i++) {
for (int i = 0; i < logRouterTags; i++) {
pseudoLocalityPopVersion[Tag(locality, i)] = 0;
}
}
@ -530,16 +530,6 @@ void LogSystem::purgeOldRecoveredGenerationsCoreState(DBCoreState& newState) {
}
}
void LogSystem::purgeOldRecoveredGenerationsInMemory(const DBCoreState& newState) {
auto generations = newState.oldTLogData.size();
if (generations < oldLogData.size()) {
TraceEvent("PurgeOldTLogGenerationsInMemory", dbgid)
.detail("OldGenerations", oldLogData.size())
.detail("NewGenerations", generations);
oldLogData.resize(generations);
}
}
void LogSystem::toCoreState(DBCoreState& newState) const {
if (recoveryComplete.isValid() && recoveryComplete.isError())
throw recoveryComplete.getError();
@ -1137,10 +1127,12 @@ LogSystemConfig LogSystem::getLogSystemConfig() const {
}
}
if (!recoveryCompleteWrittenToCoreState.get()) {
for (const auto& oldData : oldLogData) {
logSystemConfig.oldTLogs.push_back(toOldTLogConf(oldData));
}
// ServerDBInfo uses oldTLogs to keep old-generation TLog roles from displacing
// themselves while this cluster controller is alive. Durable state/logsKey can
// drop recovered old generations earlier, but these roles may still be needed
// if this recovery has to run again.
for (const auto& oldData : oldLogData) {
logSystemConfig.oldTLogs.push_back(toOldTLogConf(oldData));
}
return logSystemConfig;
}
@ -1597,7 +1589,7 @@ void getTLogLocIds(const std::vector<Reference<LogSet>>& tLogs,
if (!it->isLocal) {
continue;
}
for (uint16_t i = 0; i < it->logServers.size(); i++) {
for (size_t i = 0; i < it->logServers.size(); i++) {
if (it->logServers[i]->get().present()) {
interfLocMap[it->logServers[i]->get().interf().id()] = location;
}

View File

@ -41,7 +41,7 @@
#include "fdbserver/core/OTELSpanContextMessage.h"
#include "fdbserver/core/SpanContextMessage.h"
#include "fdbserver/core/TLogInterface.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/Arena.h"
#include "flow/Error.h"
#include "flow/ActorCollection.h"
@ -338,9 +338,8 @@ struct LogSystem : ReferenceCounted<LogSystem> {
bool remoteStorageRecovered() const;
// Checks older TLog generations and remove no longer needed generations from the log system.
// Removes no-longer-needed older TLog generations from the outgoing core state.
void purgeOldRecoveredGenerationsCoreState(DBCoreState&);
void purgeOldRecoveredGenerationsInMemory(const DBCoreState&);
Future<Void> onCoreStateChanged() const;

View File

@ -108,60 +108,6 @@ Future<Void> networkTestServer() {
co_await server.run();
}
class NetworkTestStreamingServer {
public:
NetworkTestStreamingServer() : interf(g_network) {}
Future<Void> run() { co_await race(requests(), logging()); }
private:
Future<Void> requests() {
while (true) {
try {
NetworkTestStreamingRequest req = co_await interf.testStream.getFuture();
LatencyStats::sample sample = latency.tick();
for (int i = 0; i < 100; ++i) {
co_await req.reply.onReady();
req.reply.send(NetworkTestStreamingReply{ i });
}
req.reply.sendError(end_of_stream());
latency.tock(sample);
sent++;
} catch (Error& e) {
if (e.code() != error_code_operation_obsolete) {
throw e;
}
}
}
}
Future<Void> logging() {
double lastTime = now();
while (true) {
co_await delay(1.0);
auto spd = sent / (now() - lastTime);
if (FLOW_KNOBS->NETWORK_TEST_SCRIPT_MODE) {
fprintf(stderr, "%f\t%.3f\t%.3f\n", spd, latency.mean() * 1e6, latency.stddev() * 1e6);
} else {
fprintf(stderr, "responses per second: %f (%f us)\n", spd, latency.mean() * 1e6);
}
latency.reset();
lastTime = now();
sent = 0;
}
}
NetworkTestInterface interf;
int sent = 0;
LatencyStats latency;
};
Future<Void> networkTestStreamingServer() {
NetworkTestStreamingServer server;
co_await server.run();
}
static bool moreRequestsPending(int count) {
if (count == -1) {
return false;
@ -193,31 +139,6 @@ Future<Void> testClient(std::vector<NetworkTestInterface> interfs, int* sent, in
}
}
Future<Void> testClientStream(std::vector<NetworkTestInterface> interfs,
int* sent,
int* completed,
LatencyStats* latency) {
while (moreRequestsPending(*sent)) {
(*sent)++;
LatencyStats::sample sample = latency->tick();
ReplyPromiseStream<NetworkTestStreamingReply> stream =
interfs[deterministicRandom()->randomInt(0, interfs.size())].testStream.getReplyStream(
NetworkTestStreamingRequest{});
int j = 0;
try {
while (true) {
NetworkTestStreamingReply rep = co_await stream.getFuture();
ASSERT(rep.index == j++);
}
} catch (Error& e) {
ASSERT(e.code() == error_code_end_of_stream || e.code() == error_code_connection_failed ||
e.code() == error_code_request_maybe_delivered);
}
latency->tock(sample);
(*completed)++;
}
}
Future<Void> logger(int* sent, int* completed, LatencyStats* latency) {
double lastTime = now();
int logged = 0;

View File

@ -662,7 +662,18 @@ void Ratekeeper::updateRate(RatekeeperLimits* limits) {
// ratio
for (auto i = storageQueueInfo.begin(); i != storageQueueInfo.end(); ++i) {
auto const& ss = i->value;
if (!ss.valid || !ss.acceptingRequests || (remoteDC.present() && ss.locality.dcId() == remoteDC)) {
if (!ss.valid || !ss.acceptingRequests) {
continue;
}
int64_t storageQueue = ss.getStorageQueueBytes();
worstStorageQueueStorageServer = std::max(worstStorageQueueStorageServer, storageQueue);
int64_t storageDurabilityLag = ss.getDurabilityLag();
worstDurabilityLag = std::max(worstDurabilityLag, storageDurabilityLag);
// Remote storage is reported in health metrics but is not used to rate-limit the primary region.
if (remoteDC.present() && ss.locality.dcId() == remoteDC) {
continue;
}
++sscount;
@ -697,12 +708,6 @@ void Ratekeeper::updateRate(RatekeeperLimits* limits) {
}
}
int64_t storageQueue = ss.getStorageQueueBytes();
worstStorageQueueStorageServer = std::max(worstStorageQueueStorageServer, storageQueue);
int64_t storageDurabilityLag = ss.getDurabilityLag();
worstDurabilityLag = std::max(worstDurabilityLag, storageDurabilityLag);
storageDurabilityLagReverseIndex.insert(std::make_pair(-1 * storageDurabilityLag, &ss));
double targetRateRatio = std::min((storageQueue - targetBytes + springBytes) / (double)springBytes, 2.0);

View File

@ -431,8 +431,9 @@ public:
void addConflictRanges(const Finger* fingers, int rangeCount, Version version) {
for (int r = rangeCount - 1; r >= 0; r--) {
const Finger& startF = fingers[r * 2];
const Finger& endF = fingers[r * 2 + 1];
const size_t fingerIndex = static_cast<size_t>(r) * 2;
const Finger& startF = fingers[fingerIndex];
const Finger& endF = fingers[fingerIndex + 1];
if (endF.found() == nullptr)
insert(endF, endF.finger[0]->getMaxVersion(0));
@ -1018,7 +1019,7 @@ void ConflictBatch::addConflictRanges(Version now,
int ss = stringCount - (stripes - 1) * stripeSize;
for (int s = stripes - 1; s >= 0; s--) {
part->find(&strings[s * stripeSize], fingers, temp, ss);
part->find(&strings[static_cast<size_t>(s) * stripeSize], fingers, temp, ss);
part->addConflictRanges(fingers, ss / 2, now);
ss = stripeSize;
}

View File

@ -40,7 +40,7 @@
#include "fdbserver/core/StorageMetrics.h"
#include "fdbserver/core/WaitFailure.h"
#include "fdbserver/resolver/Resolver.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "ConflictSet.h"
#include "flow/ActorCollection.h"
#include "flow/Error.h"

View File

@ -96,7 +96,7 @@
#include "fdbserver/core/ServerDBInfo.h"
#include "fdbserver/core/DataMovement.h"
#include "fdbserver/storageserver/StorageServer.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "StorageServerUtils.h"
#include "flow/CoroUtils.h"
#include "flow/TDMetric.h"
@ -1985,16 +1985,6 @@ Future<Version> waitForVersionActor(StorageServer* data, Version version, SpanCo
}
}
// If the latest commit version that mutated the shard(s) being served by the specified storage
// server is below the client specified read version then do a read at the latest commit version
// of the storage server.
Version getRealReadVersion(VersionVector& ssLatestCommitVersions, Tag& tag, Version specifiedReadVersion) {
Version realReadVersion =
ssLatestCommitVersions.hasVersion(tag) ? ssLatestCommitVersions.getVersion(tag) : specifiedReadVersion;
ASSERT(realReadVersion <= specifiedReadVersion);
return realReadVersion;
}
// Find the latest commit version of the given tag.
Version getLatestCommitVersion(VersionVector& ssLatestCommitVersions, Tag& tag) {
Version commitVersion =
@ -3496,7 +3486,8 @@ Future<Void> getKeyValuesQ(StorageServer* data, GetKeyValuesRequest req)
if (req.taskID.present() && req.taskID.get() == TaskPriority::FetchKeys) {
data->counters.kvFetchServed += r.data.size();
data->counters.kvFetchBytesServed += (totalByteSize + (8 - (int)sizeof(KeyValueRef)) * r.data.size());
data->counters.kvFetchBytesServed +=
totalByteSize + (8LL - static_cast<int64_t>(sizeof(KeyValueRef))) * r.data.size();
}
if (totalByteSize > 0 && SERVER_KNOBS->READ_SAMPLING_ENABLED) {
@ -4266,23 +4257,6 @@ Future<Void> auditStorageServerShardQ(StorageServer* data, AuditStorageRequest r
*
*/
// Helper: Issue a GetKeyValues request for a given range and return the future
static Future<ErrorOr<GetKeyValuesReply>> issueGetKeyValuesRequest(StorageServer* data,
KeyRange range,
Version version,
int limit,
int limitBytes) {
GetKeyValuesRequest req;
req.begin = firstGreaterOrEqual(range.begin);
req.end = firstGreaterOrEqual(range.end);
req.limit = limit;
req.limitBytes = limitBytes;
req.version = version;
req.tags = TagSet();
data->actors.add(getKeyValuesQ(data, req));
return errorOr(req.reply.getFuture());
}
// Helper: Read both source and restored data for a given range
//
// Restored data is stored at validateRestoreLogKeys (\xff\x02/rlog/) in system key space.
@ -6249,20 +6223,6 @@ bool changeDurableVersion(StorageServer* data, Version desiredDurableVersion) {
return nextDurableVersion == desiredDurableVersion;
}
Optional<MutationRef> clipMutation(MutationRef const& m, KeyRangeRef range) {
if (isSingleKeyMutation((MutationRef::Type)m.type)) {
if (range.contains(m.param1))
return m;
} else if (m.type == MutationRef::ClearRange) {
KeyRangeRef i = range & KeyRangeRef(m.param1, m.param2);
if (!i.empty())
return MutationRef((MutationRef::Type)m.type, i.begin, i.end);
} else {
ASSERT(false);
}
return Optional<MutationRef>();
}
bool convertAtomicOp(MutationRef& m, StorageServer::VersionedData const& data, UpdateEagerReadInfo* eager, Arena& ar) {
// After this function call, m should be copied into an arena immediately (before modifying data, shards, or eager)
if (m.type != MutationRef::ClearRange && m.type != MutationRef::SetValue) {

View File

@ -36,7 +36,7 @@
#include "fdbserver/core/Knobs.h"
#include "fdbserver/core/MoveKeys.h"
#include "fdbserver/core/QuietDatabase.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "ConsistencyChecker.h"
#include "fdbserver/tester/workloads.h"
@ -544,8 +544,9 @@ std::unordered_map<int, std::vector<KeyRange>> makeTaskAssignment(Database cx,
int batchSize = CLIENT_KNOBS->CONSISTENCY_CHECK_URGENT_BATCH_SHARD_COUNT;
int startingPoint = 0;
if (shardsToCheck.size() > batchSize * testersCount) {
startingPoint = deterministicRandom()->randomInt(0, shardsToCheck.size() - batchSize * testersCount);
const size_t batchShardCount = static_cast<size_t>(batchSize) * testersCount;
if (shardsToCheck.size() > batchShardCount) {
startingPoint = deterministicRandom()->randomInt(0, shardsToCheck.size() - batchShardCount);
// We randomly pick a set of successive shards:
// (1) We want to retry for different shards to avoid repeated failure on the same shards
// (2) We want to check successive shards to avoid inefficiency incurred by fragments

View File

@ -34,7 +34,7 @@
#include "fdbserver/core/FDBSimulatorProcessInfo.h"
#include "fdbclient/NativeAPI.actor.h"
#include "fdbserver/core/ServerDBInfo.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "TesterServer.h"
#include "fdbserver/tester/workloads.h"

View File

@ -41,7 +41,7 @@
#include "fdbclient/NativeAPI.actor.h"
#include "fdbserver/core/Knobs.h"
#include "fdbserver/core/QuietDatabase.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbserver/core/FDBSimulationPolicy.h"
#include "KnobProtectiveGroups.h"
#include "ConsistencyChecker.h"

View File

@ -36,7 +36,7 @@
#include "fdbserver/core/TLogInterface.h"
#include "fdbserver/core/WaitFailure.h"
#include "fdbserver/tlog/TLogServer.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "flow/ActorCollection.h"
#include "fdbrpc/FailureMonitor.h"
#include "fdbrpc/sim_validation.h"
@ -349,6 +349,7 @@ struct TLogData : NonCopyable {
Promise<Void> terminated;
FlowLock concurrentLogRouterReads;
Reference<FlowLock> persistentDataCommitLock;
bool retireRecoveredLogsRunning = false;
// Beginning of fields used by snapshot based backup and restore
double ignorePopDeadline; // time until which the ignorePopRequest will be
@ -541,7 +542,11 @@ struct LogData : NonCopyable, public ReferenceCounted<LogData> {
*/
AsyncTrigger stopCommit;
AsyncTrigger persistentDataUpdated;
bool initialized;
bool retirementRequested = false;
bool retirementStarted = false;
bool retired = false;
Promise<Void> stoppedPromise;
DBRecoveryCount recoveryCount;
@ -1278,6 +1283,167 @@ Future<Void> updatePersistentData(TLogData* self, Reference<LogData> logData, Ve
}
}
logData->newPersistentDataVersion = invalidVersion;
logData->persistentDataUpdated.trigger();
}
void advanceRetiredLogQueues(TLogData* self) {
// A restored queue has no committed location until its first post-recovery commit.
if (self->queueCommitEnd.get() == 0) {
return;
}
while (!self->popOrder.empty()) {
auto log = self->id_data.find(self->popOrder.front());
if (log == self->id_data.end()) {
self->popOrder.pop_front();
continue;
}
if (!log->second->retired || log->second->persistentDataDurableVersion != log->second->version.get()) {
break;
}
if (!log->second->versionLocation.empty()) {
auto lastLocation = log->second->versionLocation.lastItem();
self->persistentQueue->pop(lastLocation->value.second);
}
log->second->queuePoppedVersion = std::max(log->second->queuePoppedVersion, log->second->version.get());
self->popOrder.pop_front();
}
}
Future<bool> waitForRetirementStep(Future<Void> step, Future<Void> removed) {
if (removed.isReady()) {
co_return false;
}
auto result = co_await race(step, errorOr(removed));
co_return result.index() == 0 && !removed.isReady();
}
Future<Void> retireRecoveredLog(TLogData* self, Reference<LogData> logData) {
if (!logData->stopped()) {
if (!co_await waitForRetirementStep(logData->stoppedPromise.getFuture(), logData->removed)) {
co_return;
}
}
ASSERT(logData->stopped());
ASSERT(logData->version.get() < MAX_VERSION);
if (!co_await waitForRetirementStep(logData->queueCommittedVersion.whenAtLeast(logData->version.get()),
logData->removed)) {
co_return;
}
if (!self->id_data.contains(logData->logId)) {
co_return;
}
while (logData->persistentDataDurableVersion < logData->version.get()) {
Future<Void> persistentDataUpdated = logData->persistentDataUpdated.onTrigger();
if (logData->persistentDataDurableVersion < logData->version.get()) {
if (!co_await waitForRetirementStep(persistentDataUpdated, logData->removed)) {
co_return;
}
}
if (!self->id_data.contains(logData->logId)) {
co_return;
}
}
ASSERT(logData->persistentDataDurableVersion == logData->version.get());
Reference<FlowLock> persistentDataCommitLock = self->persistentDataCommitLock;
co_await persistentDataCommitLock->take();
FlowLock::Releaser commitLockReleaser(*persistentDataCommitLock);
if (!self->id_data.contains(logData->logId)) {
co_return;
}
ASSERT(logData->persistentDataVersion == logData->version.get());
ASSERT(logData->persistentDataDurableVersion == logData->version.get());
const Version popTo = logData->version.get() + 1;
for (int tagLocality = 0; tagLocality < logData->tag_data.size(); ++tagLocality) {
for (int tagId = 0; tagId < logData->tag_data[tagLocality].size(); ++tagId) {
Reference<LogData::TagData> tagData = logData->tag_data[tagLocality][tagId];
if (tagData && tagData->popped < popTo) {
tagData->popped = popTo;
tagData->poppedRecently = true;
co_await tagData->eraseMessagesBefore(popTo, self, logData, TaskPriority::UpdateStorage);
}
}
}
for (const auto& locality : logData->tag_data) {
for (const auto& tagData : locality) {
if (tagData) {
updatePersistentPopped(self, logData, tagData);
}
}
}
double tLogMaxCreateDuration = SERVER_KNOBS->TLOG_MAX_CREATE_DURATION;
if (g_network->isSimulated() && logData->logSpillType == TLogSpillType::VALUE) {
tLogMaxCreateDuration *= 2;
}
co_await ioTimeoutError(self->persistentData->commit(), tLogMaxCreateDuration, "TLogRetireCommit");
logData->retired = true;
advanceRetiredLogQueues(self);
}
Reference<LogData> getNextRecoveredLogToRetire(TLogData* self) {
// updateStorage owns spillOrder removals; changing its front here can skip the following generation.
if (!self->spillOrder.empty()) {
auto log = self->id_data.find(self->spillOrder.front());
if (log != self->id_data.end() && log->second->retirementRequested && !log->second->retirementStarted &&
!log->second->retired) {
return log->second;
}
}
for (const auto& entry : self->id_data) {
const auto& logData = entry.second;
if (logData->retirementRequested && !logData->retirementStarted && !logData->retired &&
logData->persistentDataDurableVersion == logData->version.get()) {
return logData;
}
}
return {};
}
Future<Void> retireRecoveredLogs(TLogData* self) {
while (Reference<LogData> logData = getNextRecoveredLogToRetire(self)) {
logData->retirementStarted = true;
co_await retireRecoveredLog(self, logData);
}
self->retireRecoveredLogsRunning = false;
}
void startRetiringRecoveredLogs(TLogData* self) {
if (!self->retireRecoveredLogsRunning) {
self->retireRecoveredLogsRunning = true;
self->sharedActors.send(retireRecoveredLogs(self));
}
}
Future<Void> monitorRetainedOldLogs(TLogData* self) {
while (true) {
Future<Void> dbInfoChange = self->dbInfo->onChange();
const auto& dbInfo = self->dbInfo->get();
bool retirementRequested = false;
if (dbInfo.recoveryState == RecoveryState::FULLY_RECOVERED) {
for (const auto& entry : self->id_data) {
const auto& logData = entry.second;
bool currentLog = false;
for (const auto& logSet : dbInfo.logSystemConfig.tLogs) {
if (std::find(logSet.tLogs.begin(), logSet.tLogs.end(), logData->logId) != logSet.tLogs.end()) {
currentLog = true;
break;
}
}
if (!currentLog && dbInfo.logSystemConfig.hasTLog(logData->logId) && !logData->retirementRequested) {
logData->retirementRequested = true;
retirementRequested = true;
}
}
}
if (retirementRequested) {
startRetiringRecoveredLogs(self);
}
co_await dbInfoChange;
}
}
Future<Void> tLogPopCore(TLogData* self, Tag inputTag, Version to, Reference<LogData> logData) {
@ -1432,8 +1598,13 @@ double getTLogStorageUpdateDelayDuration() {
// This actor is just a loop that calls updatePersistentData and popDiskQueue whenever
// (a) there's data to be spilled or (b) we should update metadata after some commits have been fully popped.
Future<Void> updateStorage(TLogData* self) {
bool removedGeneration = false;
while (!self->spillOrder.empty() && !self->id_data.contains(self->spillOrder.front())) {
self->spillOrder.pop_front();
removedGeneration = true;
}
if (removedGeneration) {
startRetiringRecoveredLogs(self);
}
if (self->spillOrder.empty()) {
@ -1449,7 +1620,7 @@ Future<Void> updateStorage(TLogData* self) {
FlowLock::Releaser commitLockReleaser;
if (logData->stopped()) {
if (self->bytesInput - self->bytesDurable >= self->targetVolatileBytes) {
if (self->bytesInput - self->bytesDurable >= self->targetVolatileBytes || logData->retirementRequested) {
while (logData->persistentDataDurableVersion != logData->version.get()) {
totalSize = 0;
Map<Version, std::pair<int, int>>::iterator sizeItr = logData->version_sizes.begin();
@ -1488,6 +1659,9 @@ Future<Void> updateStorage(TLogData* self) {
if (logData->persistentDataDurableVersion == logData->version.get()) {
self->spillOrder.pop_front();
if (logData->retirementRequested) {
startRetiringRecoveredLogs(self);
}
}
co_await delay(0.0, TaskPriority::UpdateStorage);
} else {
@ -2584,6 +2758,7 @@ Future<Void> doQueueCommit(TLogData* self,
CODE_PROBE(true, "A TLog was replaced before having a chance to commit its queue", probe::decoration::rare);
it->queueCommittedVersion.set(it->version.get());
}
advanceRetiredLogQueues(self);
}
Future<Void> commitQueue(TLogData* self) {
@ -4168,6 +4343,7 @@ Future<Void> tLog(IKeyValueStore* persistentData,
self.sharedActors.send(commitQueue(&self));
self.sharedActors.send(updateStorageLoop(&self));
self.sharedActors.send(monitorRetainedOldLogs(&self));
self.sharedActors.send(traceRole(Role::SHARED_TRANSACTION_LOG, tlogId));
Future<Void> activeSharedChange = Void();

View File

@ -30,7 +30,7 @@
#include "fdbserver/core/Knobs.h"
#include "fdbserver/core/TLogInterface.h"
#include "fdbserver/tlog/TLogServer.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include "fdbserver/logsystem/LogSystem.h"
#include "fdbserver/logsystem/LogSystemFactory.h"
#include "flow/IRandom.h"
@ -247,7 +247,7 @@ Future<Void> TLogTestContext::sendPushMessages(TLogTestContext* pTLogTestContext
TraceEvent("TestTLogServerEnterPush", pTLogTestContext->workerID);
for (uint16_t logID = 0; logID < pTLogTestContext->numLogServers; ++logID) {
for (uint32_t logID = 0; logID < pTLogTestContext->numLogServers; ++logID) {
Reference<TLogContext> pTLogContext = pTLogTestContext->pTLogContextList[logID];
bool tLogReady = co_await pTLogContext->TLogStarted.getFuture();
ASSERT_EQ(tLogReady, true);
@ -393,7 +393,7 @@ Future<Void> buildTLogSet(Reference<TLogTestContext> pTLogTestContext) {
tLogSet.isLocal = true;
tLogSet.tLogVersion = TLogVersion::V6;
tLogSet.tLogReplicationFactor = 1;
for (uint16_t processID = 0; processID < pTLogTestContext->numLogServers; ++processID) {
for (uint32_t processID = 0; processID < pTLogTestContext->numLogServers; ++processID) {
Reference<TLogContext> pTLogContext = pTLogTestContext->pTLogContextList[processID];
bool isCreated = co_await pTLogContext->TLogCreated.getFuture();
ASSERT_EQ(isCreated, true);
@ -401,7 +401,7 @@ Future<Void> buildTLogSet(Reference<TLogTestContext> pTLogTestContext) {
tLogSet.tLogs.push_back(OptionalInterface<TLogInterface>(pTLogContext->TestTLogInterface));
}
pTLogTestContext->dbInfo.logSystemConfig.tLogs.push_back(tLogSet);
for (uint16_t processID = 0; processID < pTLogTestContext->numLogServers; ++processID) {
for (uint32_t processID = 0; processID < pTLogTestContext->numLogServers; ++processID) {
Reference<TLogContext> pTLogContext = pTLogTestContext->pTLogContextList[processID];
// start transactions
pTLogContext->TLogStarted.send(true);
@ -420,7 +420,7 @@ Future<Void> startTestsTLogRecoveryActors(TestTLogOptions params) {
FlowTransport::createInstance(false, 1, WLTOKEN_RESERVED_COUNT);
uint16_t tLogIdx = 0;
uint32_t tLogIdx = 0;
TraceEvent("TestTLogServerEnterRecoveryTest");

View File

@ -22,7 +22,7 @@
#include "fdbclient/ActorLineageProfiler.h"
#include "fdbclient/ProcessClass.h"
#include "fdbserver/core/ProcessClassRecruitment.h"
#include "fdbserver/core/WorkerInterface.actor.h"
#include "fdbserver/core/WorkerInterface.h"
#include <any>
#include <optional>

View File

@ -295,8 +295,9 @@ struct AsyncFileCorrectnessWorkload : public AsyncFileWorkload {
do {
// Generate random length and offset
if (unbufferedIO) {
info.length =
deterministicRandom()->randomInt(1, maxOperationSize / _PAGE_SIZE + 1) * _PAGE_SIZE;
info.length = static_cast<uint64_t>(
deterministicRandom()->randomInt(1, maxOperationSize / _PAGE_SIZE + 1)) *
_PAGE_SIZE;
info.offset =
(int64_t)(deterministicRandom()->random01() * maxOffset / _PAGE_SIZE) * _PAGE_SIZE;
} else {

View File

@ -307,7 +307,7 @@ struct AsyncFileReadWorkload : public AsyncFileWorkload {
}
co_await waitForAll(self->readFutures);
self->bytesRead += self->readSize * self->numParallelReads;
self->bytesRead += static_cast<int64_t>(self->readSize) * self->numParallelReads;
self->readFutures.clear();

View File

@ -136,7 +136,7 @@ struct AsyncFileWriteWorkload : public AsyncFileWorkload {
self->writeFutures.clear();
self->bytesWritten += self->writeSize * self->numParallelWrites;
self->bytesWritten += static_cast<int64_t>(self->writeSize) * self->numParallelWrites;
}
}

Some files were not shown because too many files have changed in this diff Show More