Merge origin/main into native CDC C bindings
This commit is contained in:
commit
200931f7e3
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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=$(
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
*
|
||||
|
|
|
|||
|
|
@ -790,4 +790,3 @@ public class StackTester {
|
|||
|
||||
private StackTester() {}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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|
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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``.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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``)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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&);
|
||||
|
|
@ -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]));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue