foundationdb/fdbclient/ReadYourWrites.actor.cpp

2681 lines
94 KiB
C++
Raw Normal View History

2017-05-26 04:48:44 +08:00
/*
* ReadYourWrites.actor.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
2017-05-26 04:48:44 +08:00
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
2017-05-26 04:48:44 +08:00
* http://www.apache.org/licenses/LICENSE-2.0
*
2017-05-26 04:48:44 +08:00
* 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 "fdbclient/ReadYourWrites.h"
#include "fdbclient/Atomic.h"
#include "fdbclient/DatabaseContext.h"
#include "fdbclient/SpecialKeySpace.actor.h"
#include "fdbclient/StatusClient.h"
#include "fdbclient/MonitorLeader.h"
#include "flow/Util.h"
#include "flow/actorcompiler.h" // This must be the last #include.
2017-05-26 04:48:44 +08:00
class RYWImpl {
public:
template <class Iter>
static void dump(Iter it) {
2017-05-26 04:48:44 +08:00
it.skip(allKeys.begin);
Arena arena;
while (true) {
Optional<StringRef> key = StringRef();
if (it.is_kv()) {
auto kv = it.kv(arena);
if (kv)
key = kv->key;
}
TraceEvent("RYWDump")
2019-04-06 04:11:50 +08:00
.detail("Begin", it.beginKey())
.detail("End", it.endKey())
.detail("Unknown", it.is_unknown_range())
.detail("Empty", it.is_empty_range())
.detail("KV", it.is_kv())
.detail("Key", key.get());
if (it.endKey() == allKeys.end)
2017-05-26 04:48:44 +08:00
break;
++it;
}
}
struct GetValueReq {
explicit GetValueReq(Key key) : key(key) {}
2017-05-26 04:48:44 +08:00
Key key;
typedef Optional<Value> Result;
};
struct GetKeyReq {
explicit GetKeyReq(KeySelector key) : key(key) {}
2017-05-26 04:48:44 +08:00
KeySelector key;
typedef Key Result;
};
2021-07-03 12:41:50 +08:00
template <bool reverse>
2017-05-26 04:48:44 +08:00
struct GetRangeReq {
GetRangeReq(KeySelector begin, KeySelector end, GetRangeLimits limits)
: begin(begin), end(end), limits(limits) {}
2017-05-26 04:48:44 +08:00
KeySelector begin, end;
GetRangeLimits limits;
2021-05-04 04:14:16 +08:00
using Result = RangeResult;
2017-05-26 04:48:44 +08:00
};
template <bool reverse>
struct GetMappedRangeReq {
GetMappedRangeReq(KeySelector begin, KeySelector end, Key mapper, GetRangeLimits limits)
: begin(begin), end(end), mapper(mapper), limits(limits) {}
KeySelector begin, end;
Key mapper;
GetRangeLimits limits;
using Result = MappedRangeResult;
};
// read() Performs a read (get, getKey, getRange, etc), in the context of the given transaction. Snapshot or RYW
// reads are distingushed by the type Iter being SnapshotCache::iterator or RYWIterator. Fills in the snapshot cache
// as a side effect but does not affect conflict ranges. Some (indicated) overloads of read are required to update
// the given *it to point to the key that was read, so that the corresponding overload of addConflictRange() can
// make use of it.
2017-05-26 04:48:44 +08:00
ACTOR template <class Iter>
static Future<Optional<Value>> read(ReadYourWritesTransaction* ryw, GetValueReq read, Iter* it) {
2017-05-26 04:48:44 +08:00
// This overload is required to provide postcondition: it->extractWriteMapIterator().segmentContains(read.key)
if (ryw->options.bypassUnreadable) {
it->bypassUnreadableProtection();
}
2017-05-26 04:48:44 +08:00
it->skip(read.key);
state bool dependent = it->is_dependent();
if (it->is_kv()) {
const KeyValueRef* result = it->kv(ryw->arena);
if (result != nullptr) {
return result->value;
} else {
return Optional<Value>();
}
} else if (it->is_empty_range()) {
2017-05-26 04:48:44 +08:00
return Optional<Value>();
} else {
Optional<Value> res = wait(ryw->tr.get(read.key, Snapshot::True));
KeyRef k(ryw->arena, read.key);
if (res.present()) {
if (ryw->cache.insert(k, res.get()))
2017-05-26 04:48:44 +08:00
ryw->arena.dependsOn(res.get().arena());
if (!dependent)
2017-05-26 04:48:44 +08:00
return res;
} else {
ryw->cache.insert(k, Optional<ValueRef>());
if (!dependent)
2017-05-26 04:48:44 +08:00
return Optional<Value>();
}
// There was a dependent write at the key, so we need to lookup the iterator again
2017-05-26 04:48:44 +08:00
it->skip(k);
ASSERT(it->is_kv());
const KeyValueRef* result = it->kv(ryw->arena);
if (result != nullptr) {
return result->value;
} else {
return Optional<Value>();
}
2017-05-26 04:48:44 +08:00
}
}
ACTOR template <class Iter>
static Future<Key> read(ReadYourWritesTransaction* ryw, GetKeyReq read, Iter* it) {
if (read.key.offset > 0) {
2021-05-04 04:14:16 +08:00
RangeResult result =
wait(getRangeValue(ryw, read.key, firstGreaterOrEqual(ryw->getMaxReadKey()), GetRangeLimits(1), it));
if (result.readToBegin)
2017-05-26 04:48:44 +08:00
return allKeys.begin;
if (result.readThroughEnd || !result.size())
2017-05-26 04:48:44 +08:00
return ryw->getMaxReadKey();
return result[0].key;
} else {
read.key.offset++;
2021-05-04 04:14:16 +08:00
RangeResult result =
wait(getRangeValueBack(ryw, firstGreaterOrEqual(allKeys.begin), read.key, GetRangeLimits(1), it));
if (result.readThroughEnd)
2017-05-26 04:48:44 +08:00
return ryw->getMaxReadKey();
if (result.readToBegin || !result.size())
2017-05-26 04:48:44 +08:00
return allKeys.begin;
return result[0].key;
}
};
template <class Iter>
2021-05-04 04:14:16 +08:00
static Future<RangeResult> read(ReadYourWritesTransaction* ryw, GetRangeReq<false> read, Iter* it) {
return getRangeValue(ryw, read.begin, read.end, read.limits, it);
2017-05-26 04:48:44 +08:00
};
template <class Iter>
2021-05-04 04:14:16 +08:00
static Future<RangeResult> read(ReadYourWritesTransaction* ryw, GetRangeReq<true> read, Iter* it) {
return getRangeValueBack(ryw, read.begin, read.end, read.limits, it);
2017-05-26 04:48:44 +08:00
};
// readThrough() performs a read in the RYW disabled case, passing it on relatively directly to the underlying
// transaction. Responsible for clipping results to the non-system keyspace when appropriate, since NativeAPI
// doesn't do that.
2017-05-26 04:48:44 +08:00
2021-07-03 12:41:50 +08:00
static Future<Optional<Value>> readThrough(ReadYourWritesTransaction* ryw, GetValueReq read, Snapshot snapshot) {
return ryw->tr.get(read.key, snapshot);
2017-05-26 04:48:44 +08:00
}
2021-07-03 12:41:50 +08:00
ACTOR static Future<Key> readThrough(ReadYourWritesTransaction* ryw, GetKeyReq read, Snapshot snapshot) {
Key key = wait(ryw->tr.getKey(read.key, snapshot));
if (ryw->getMaxReadKey() < key)
return ryw->getMaxReadKey(); // Filter out results in the system keys if they are not accessible
2017-05-26 04:48:44 +08:00
return key;
}
2021-07-03 12:41:50 +08:00
ACTOR template <bool backwards>
static Future<RangeResult> readThrough(ReadYourWritesTransaction* ryw,
GetRangeReq<backwards> read,
Snapshot snapshot) {
if (backwards && read.end.offset > 1) {
// FIXME: Optimistically assume that this will not run into the system keys, and only reissue if the result
// actually does.
Key key = wait(ryw->tr.getKey(read.end, snapshot));
if (key > ryw->getMaxReadKey())
2017-05-26 04:48:44 +08:00
read.end = firstGreaterOrEqual(ryw->getMaxReadKey());
else
2017-10-21 00:46:35 +08:00
read.end = KeySelector(firstGreaterOrEqual(key), key.arena());
2017-05-26 04:48:44 +08:00
}
2021-07-03 12:41:50 +08:00
RangeResult v = wait(
ryw->tr.getRange(read.begin, read.end, read.limits, snapshot, backwards ? Reverse::True : Reverse::False));
2017-05-26 04:48:44 +08:00
KeyRef maxKey = ryw->getMaxReadKey();
if (v.size() > 0) {
2021-07-03 12:41:50 +08:00
if (!backwards && v[v.size() - 1].key >= maxKey) {
2021-05-04 04:14:16 +08:00
state RangeResult _v = v;
2017-05-26 04:48:44 +08:00
int i = _v.size() - 2;
for (; i >= 0 && _v[i].key >= maxKey; --i) {
}
2021-05-04 04:14:16 +08:00
return RangeResult(RangeResultRef(VectorRef<KeyValueRef>(&_v[0], i + 1), false), _v.arena());
2017-05-26 04:48:44 +08:00
}
}
2017-05-26 04:48:44 +08:00
return v;
}
// addConflictRange(ryw,read,result) is called after a serializable read and is responsible for adding the relevant
// conflict range
2017-05-26 04:48:44 +08:00
template <bool mustUnmodified = false>
static void addConflictRange(ReadYourWritesTransaction* ryw,
GetValueReq read,
WriteMap::iterator& it,
Optional<Value> result) {
2017-05-26 04:48:44 +08:00
// it will already point to the right segment (see the calling code in read()), so we don't need to skip
// read.key will be copied into ryw->arena inside of updateConflictMap if it is being added
updateConflictMap<mustUnmodified>(ryw, read.key, it);
2017-05-26 04:48:44 +08:00
}
static void addConflictRange(ReadYourWritesTransaction* ryw, GetKeyReq read, WriteMap::iterator& it, Key result) {
2017-05-26 04:48:44 +08:00
KeyRangeRef readRange;
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()));
2017-05-26 04:48:44 +08:00
else
readRange = KeyRangeRef(read.key.orEqual ? keyAfter(read.key.getKey(), ryw->arena)
: KeyRef(ryw->arena, read.key.getKey()),
keyAfter(result, ryw->arena));
2017-05-26 04:48:44 +08:00
it.skip(readRange.begin);
2017-05-26 04:48:44 +08:00
ryw->updateConflictMap(readRange, it);
}
template <bool mustUnmodified = false, class RangeResultFamily = RangeResult>
static void addConflictRange(ReadYourWritesTransaction* ryw,
GetRangeReq<false> read,
WriteMap::iterator& it,
RangeResultFamily& result) {
2017-05-26 04:48:44 +08:00
KeyRef rangeBegin, rangeEnd;
bool endInArena = false;
if (read.begin.getKey() < read.end.getKey()) {
2017-05-26 04:48:44 +08:00
rangeBegin = read.begin.getKey();
rangeEnd = read.end.offset > 0 && result.more ? read.begin.getKey() : read.end.getKey();
} else {
2017-05-26 04:48:44 +08:00
rangeBegin = read.end.getKey();
rangeEnd = read.begin.getKey();
}
if (result.readToBegin && read.begin.offset <= 0)
rangeBegin = allKeys.begin;
if (result.readThroughEnd && read.end.offset > 0)
rangeEnd = ryw->getMaxReadKey();
2017-05-26 04:48:44 +08:00
if (result.size()) {
if (read.begin.offset <= 0)
rangeBegin = std::min(rangeBegin, result[0].key);
if (rangeEnd <= result.end()[-1].key) {
rangeEnd = keyAfter(result.end()[-1].key, ryw->arena);
2017-05-26 04:48:44 +08:00
endInArena = true;
}
}
KeyRangeRef readRange =
KeyRangeRef(KeyRef(ryw->arena, rangeBegin), endInArena ? rangeEnd : KeyRef(ryw->arena, rangeEnd));
it.skip(readRange.begin);
updateConflictMap<mustUnmodified>(ryw, readRange, it);
2017-05-26 04:48:44 +08:00
}
// In the case where RangeResultFamily is MappedRangeResult, it only adds the primary range to conflict.
template <bool mustUnmodified = false, class RangeResultFamily = RangeResult>
static void addConflictRange(ReadYourWritesTransaction* ryw,
GetRangeReq<true> read,
WriteMap::iterator& it,
RangeResultFamily& result) {
2017-05-26 04:48:44 +08:00
KeyRef rangeBegin, rangeEnd;
bool endInArena = false;
if (read.begin.getKey() < read.end.getKey()) {
2017-05-26 04:48:44 +08:00
rangeBegin = read.begin.offset <= 0 && result.more ? read.end.getKey() : read.begin.getKey();
rangeEnd = read.end.getKey();
} else {
2017-05-26 04:48:44 +08:00
rangeBegin = read.end.getKey();
rangeEnd = read.begin.getKey();
}
if (result.readToBegin && read.begin.offset <= 0)
rangeBegin = allKeys.begin;
if (result.readThroughEnd && read.end.offset > 0)
rangeEnd = ryw->getMaxReadKey();
2017-05-26 04:48:44 +08:00
if (result.size()) {
rangeBegin = std::min(rangeBegin, result.end()[-1].key);
if (read.end.offset > 0 && rangeEnd <= result[0].key) {
rangeEnd = keyAfter(result[0].key, ryw->arena);
2017-05-26 04:48:44 +08:00
endInArena = true;
}
}
KeyRangeRef readRange =
KeyRangeRef(KeyRef(ryw->arena, rangeBegin), endInArena ? rangeEnd : KeyRef(ryw->arena, rangeEnd));
it.skip(readRange.begin);
updateConflictMap<mustUnmodified>(ryw, readRange, it);
}
template <bool mustUnmodified = false>
static void updateConflictMap(ReadYourWritesTransaction* ryw, KeyRef const& key, WriteMap::iterator& it) {
// it.skip( key );
// ASSERT( it.beginKey() <= key && key < it.endKey() );
if (mustUnmodified && !it.is_unmodified_range()) {
throw get_mapped_range_reads_your_writes();
}
if (it.is_unmodified_range() || (it.is_operation() && !it.is_independent())) {
ryw->approximateSize += 2 * key.expectedSize() + 1 + sizeof(KeyRangeRef);
ryw->readConflicts.insert(singleKeyRange(key, ryw->arena), true);
}
}
template <bool mustUnmodified = false>
static void updateConflictMap(ReadYourWritesTransaction* ryw, KeyRangeRef const& keys, WriteMap::iterator& it) {
// it.skip( keys.begin );
// ASSERT( it.beginKey() <= keys.begin && keys.begin < it.endKey() );
for (; it.beginKey() < keys.end; ++it) {
if (mustUnmodified && !it.is_unmodified_range()) {
throw get_mapped_range_reads_your_writes();
}
if (it.is_unmodified_range() || (it.is_operation() && !it.is_independent())) {
KeyRangeRef insert_range = KeyRangeRef(std::max(keys.begin, it.beginKey().toArenaOrRef(ryw->arena)),
std::min(keys.end, it.endKey().toArenaOrRef(ryw->arena)));
if (!insert_range.empty()) {
ryw->approximateSize += keys.expectedSize() + sizeof(KeyRangeRef);
ryw->readConflicts.insert(insert_range, true);
}
}
}
2017-05-26 04:48:44 +08:00
}
ACTOR template <class Req>
static Future<typename Req::Result> readWithConflictRangeThrough(ReadYourWritesTransaction* ryw,
Req req,
2021-07-03 12:41:50 +08:00
Snapshot snapshot) {
2017-05-26 04:48:44 +08:00
choose {
when(typename Req::Result result = wait(readThrough(ryw, req, snapshot))) { return result; }
when(wait(ryw->resetPromise.getFuture())) { throw internal_error(); }
2017-05-26 04:48:44 +08:00
}
}
ACTOR template <class Req>
static Future<typename Req::Result> readWithConflictRangeSnapshot(ReadYourWritesTransaction* ryw, Req req) {
2017-05-26 04:48:44 +08:00
state SnapshotCache::iterator it(&ryw->cache, &ryw->writes);
choose {
when(typename Req::Result result = wait(read(ryw, req, &it))) { return result; }
when(wait(ryw->resetPromise.getFuture())) { throw internal_error(); }
2017-05-26 04:48:44 +08:00
}
}
ACTOR template <class Req>
static Future<typename Req::Result> readWithConflictRangeRYW(ReadYourWritesTransaction* ryw,
Req req,
2021-07-03 12:41:50 +08:00
Snapshot snapshot) {
state RYWIterator it(&ryw->cache, &ryw->writes);
2017-05-26 04:48:44 +08:00
choose {
when(typename Req::Result result = wait(read(ryw, req, &it))) {
// Some overloads of addConflictRange() require it to point to the "right" key and others don't. The
// corresponding overloads of read() have to provide that guarantee!
if (!snapshot)
addConflictRange(ryw, req, it.extractWriteMapIterator(), result);
2017-05-26 04:48:44 +08:00
return result;
}
when(wait(ryw->resetPromise.getFuture())) { throw internal_error(); }
2017-05-26 04:48:44 +08:00
}
}
template <class Req>
static inline Future<typename Req::Result> readWithConflictRange(ReadYourWritesTransaction* ryw,
Req const& req,
2021-07-03 12:41:50 +08:00
Snapshot snapshot) {
2017-05-26 04:48:44 +08:00
if (ryw->options.readYourWritesDisabled) {
return readWithConflictRangeThrough(ryw, req, snapshot);
} else if (snapshot && ryw->options.snapshotRywEnabled <= 0) {
return readWithConflictRangeSnapshot(ryw, req);
}
return readWithConflictRangeRYW(ryw, req, snapshot);
}
template <class Iter>
static void resolveKeySelectorFromCache(KeySelector& key,
Iter& it,
KeyRef const& maxKey,
bool* readToBegin,
bool* readThroughEnd,
int* actualOffset) {
// If the key indicated by `key` can be determined without reading unknown data from the snapshot, then
// it.kv().key is the resolved key. If the indicated key is determined to be "off the beginning or end" of the
// database, it points to the first or last segment in the DB,
2017-05-26 04:48:44 +08:00
// and key is an equivalent key selector relative to the beginning or end of the database.
// Otherwise it points to an unknown segment, and key is an equivalent key selector whose base key is in or
// adjoining the segment.
2017-05-26 04:48:44 +08:00
key.removeOrEqual(key.arena());
bool alreadyExhausted = key.offset == 1;
it.skip(key.getKey()); // TODO: or precondition?
2017-05-26 04:48:44 +08:00
if (key.offset <= 0 && it.beginKey() == key.getKey() && key.getKey() != allKeys.begin)
2017-05-26 04:48:44 +08:00
--it;
2017-05-26 04:48:44 +08:00
ExtStringRef keykey = key.getKey();
bool keyNeedsCopy = false;
// Invariant: it.beginKey() <= keykey && keykey <= it.endKey() && (key.isBackward() ? it.beginKey() != keykey :
// it.endKey() != keykey) Maintaining this invariant, we transform the key selector toward firstGreaterOrEqual
// form until we reach an unknown range or the result
while (key.offset > 1 && !it.is_unreadable() && !it.is_unknown_range() && it.endKey() < maxKey) {
2017-05-26 04:48:44 +08:00
if (it.is_kv())
--key.offset;
++it;
keykey = it.beginKey();
keyNeedsCopy = true;
2017-05-26 04:48:44 +08:00
}
while (key.offset < 1 && !it.is_unreadable() && !it.is_unknown_range() && it.beginKey() != allKeys.begin) {
if (it.is_kv()) {
++key.offset;
if (key.offset == 1) {
keykey = it.beginKey();
keyNeedsCopy = true;
2017-05-26 04:48:44 +08:00
break;
}
}
--it;
keykey = it.endKey();
keyNeedsCopy = true;
2017-05-26 04:48:44 +08:00
}
if (!alreadyExhausted) {
2017-05-26 04:48:44 +08:00
*actualOffset = key.offset;
}
if (!it.is_unreadable() && !it.is_unknown_range() && key.offset < 1) {
*readToBegin = true;
key.setKey(allKeys.begin);
key.offset = 1;
return;
}
2017-05-26 04:48:44 +08:00
if (!it.is_unreadable() && !it.is_unknown_range() && key.offset > 1) {
*readThroughEnd = true;
key.setKey(maxKey); // maxKey is a KeyRef, but points to a LiteralStringRef. TODO: how can we ASSERT this?
2017-05-26 04:48:44 +08:00
key.offset = 1;
return;
}
while (!it.is_unreadable() && it.is_empty_range() && it.endKey() < maxKey) {
++it;
keykey = it.beginKey();
keyNeedsCopy = true;
2017-05-26 04:48:44 +08:00
}
if (keyNeedsCopy) {
key.setKey(keykey.toArena(key.arena()));
}
2017-05-26 04:48:44 +08:00
}
static KeyRangeRef getKnownKeyRange(RangeResultRef data, KeySelector begin, KeySelector end, Arena& arena) {
StringRef beginKey = begin.offset <= 1 ? begin.getKey() : allKeys.end;
ExtStringRef endKey = !data.more && end.offset >= 1 ? end.getKey() : allKeys.begin;
2017-05-26 04:48:44 +08:00
if (data.readToBegin)
beginKey = allKeys.begin;
if (data.readThroughEnd)
endKey = allKeys.end;
2017-05-26 04:48:44 +08:00
if (data.size()) {
beginKey = std::min(beginKey, data[0].key);
if (data.readThrough.present()) {
endKey = std::max<ExtStringRef>(endKey, data.readThrough.get());
} else {
endKey = !data.more && data.end()[-1].key < endKey ? endKey : ExtStringRef(data.end()[-1].key, 1);
}
2017-05-26 04:48:44 +08:00
}
if (beginKey >= endKey)
return KeyRangeRef();
2017-05-26 04:48:44 +08:00
return KeyRangeRef(StringRef(arena, beginKey), endKey.toArena(arena));
2017-05-26 04:48:44 +08:00
}
// Pre: it points to an unknown range
// Increments it to point to the unknown range just before the next nontrivial known range (skips over trivial known
// ranges), but not more than iterationLimit ranges away
template <class Iter>
static int skipUncached(Iter& it, Iter const& end, int iterationLimit) {
2017-05-26 04:48:44 +08:00
ExtStringRef b = it.beginKey();
ExtStringRef e = it.endKey();
int singleEmpty = 0;
ASSERT(!it.is_unreadable() && it.is_unknown_range());
2017-05-26 04:48:44 +08:00
// b is the beginning of the most recent contiguous *empty* range
// e is it.endKey()
while (it != end && --iterationLimit >= 0) {
2017-05-26 04:48:44 +08:00
if (it.is_unreadable() || it.is_empty_range()) {
if (it.is_unreadable() || !e.isKeyAfter(b)) { // Assumes no degenerate ranges
2017-05-26 04:48:44 +08:00
while (it.is_unreadable() || !it.is_unknown_range())
--it;
return singleEmpty;
}
singleEmpty++;
} else
b = e;
++it;
e = it.endKey();
}
while (it.is_unreadable() || !it.is_unknown_range())
--it;
return singleEmpty;
}
// Pre: it points to an unknown range
// Returns the number of following empty single-key known ranges between it and the next nontrivial known range, but
// no more than maxClears Leaves `it` in an indeterminate state
template <class Iter>
static int countUncached(Iter&& it, KeyRef maxKey, int maxClears) {
if (maxClears <= 0)
return 0;
2017-05-26 04:48:44 +08:00
ExtStringRef b = it.beginKey();
ExtStringRef e = it.endKey();
int singleEmpty = 0;
while (e < maxKey) {
2017-05-26 04:48:44 +08:00
if (it.is_unreadable() || it.is_empty_range()) {
if (it.is_unreadable() || !e.isKeyAfter(b)) { // Assumes no degenerate ranges
2017-05-26 04:48:44 +08:00
return singleEmpty;
}
singleEmpty++;
if (singleEmpty >= maxClears)
2017-05-26 04:48:44 +08:00
return maxClears;
} else
b = e;
++it;
e = it.endKey();
}
return singleEmpty;
}
static void setRequestLimits(GetRangeLimits& requestLimit, int64_t additionalRows, int offset, int requestCount) {
requestLimit.minRows =
(int)std::min(std::max(1 + additionalRows, (int64_t)offset), (int64_t)std::numeric_limits<int>::max());
if (requestLimit.hasRowLimit()) {
requestLimit.rows =
(int)std::min(std::max(std::max(1, requestLimit.rows) + additionalRows, (int64_t)offset),
(int64_t)std::numeric_limits<int>::max());
2017-05-26 04:48:44 +08:00
}
// Calculating request byte limit
if (requestLimit.bytes == 0) {
requestLimit.bytes = GetRangeLimits::BYTE_LIMIT_UNLIMITED;
if (!requestLimit.hasRowLimit()) {
requestLimit.rows =
(int)std::min(std::max(std::max(1, requestLimit.rows) + additionalRows, (int64_t)offset),
(int64_t)std::numeric_limits<int>::max());
2017-05-26 04:48:44 +08:00
}
} else if (requestLimit.hasByteLimit()) {
requestLimit.bytes = std::min(int64_t(requestLimit.bytes) << std::min(requestCount, 20),
(int64_t)CLIENT_KNOBS->REPLY_BYTE_LIMIT);
2017-05-26 04:48:44 +08:00
}
}
// TODO: read to begin, read through end flags for result
ACTOR template <class Iter>
2021-05-04 04:14:16 +08:00
static Future<RangeResult> getRangeValue(ReadYourWritesTransaction* ryw,
KeySelector begin,
KeySelector end,
GetRangeLimits limits,
Iter* pit) {
2017-05-26 04:48:44 +08:00
state Iter& it(*pit);
state Iter itEnd(*pit);
2021-05-04 04:14:16 +08:00
state RangeResult result;
2017-05-26 04:48:44 +08:00
state int64_t additionalRows = 0;
state int itemsPastEnd = 0;
state int requestCount = 0;
state bool readToBegin = false;
state bool readThroughEnd = false;
state int actualBeginOffset = begin.offset;
state int actualEndOffset = end.offset;
// state UID randomID = nondeterministicRandom()->randomUniqueID();
2017-05-26 04:48:44 +08:00
resolveKeySelectorFromCache(begin, it, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualBeginOffset);
resolveKeySelectorFromCache(end, itEnd, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualEndOffset);
2017-05-26 04:48:44 +08:00
if (actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(false, false);
} else if ((begin.isFirstGreaterOrEqual() && begin.getKey() == ryw->getMaxReadKey()) ||
(end.isFirstGreaterOrEqual() && end.getKey() == allKeys.begin)) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(readToBegin, readThroughEnd);
}
if (!end.isFirstGreaterOrEqual() && begin.getKey() > end.getKey()) {
Key resolvedEnd = wait(read(ryw, GetKeyReq(end), pit));
if (resolvedEnd == allKeys.begin)
2017-05-26 04:48:44 +08:00
readToBegin = true;
if (resolvedEnd == ryw->getMaxReadKey())
2017-05-26 04:48:44 +08:00
readThroughEnd = true;
if (begin.getKey() >= resolvedEnd && !begin.isBackward()) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(false, false);
} else if (resolvedEnd == allKeys.begin) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(readToBegin, readThroughEnd);
}
resolveKeySelectorFromCache(
begin, it, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualBeginOffset);
resolveKeySelectorFromCache(
end, itEnd, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualEndOffset);
2017-05-26 04:48:44 +08:00
}
//TraceEvent("RYWSelectorsStartForward", randomID).detail("ByteLimit", limits.bytes).detail("RowLimit", limits.rows);
loop {
/*TraceEvent("RYWSelectors", randomID).detail("Begin", begin.toString())
.detail("End", end.toString())
.detail("Reached", limits.isReached())
.detail("ItemsPastEnd", itemsPastEnd)
.detail("EndOffset", -end.offset)
.detail("ItBegin", it.beginKey())
.detail("ItEnd", itEnd.beginKey())
.detail("Unknown", it.is_unknown_range())
.detail("Requests", requestCount);*/
if (!result.size() && actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(false, false);
}
if (end.offset <= 1 && end.getKey() == allKeys.begin) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(readToBegin, readThroughEnd);
}
if ((begin.offset >= end.offset && begin.getKey() >= end.getKey()) ||
(begin.offset >= 1 && begin.getKey() >= ryw->getMaxReadKey())) {
if (end.isFirstGreaterOrEqual())
break;
if (!result.size())
break;
Key resolvedEnd =
wait(read(ryw,
GetKeyReq(end),
pit)); // do not worry about iterator invalidation, because we are breaking for the loop
if (resolvedEnd == allKeys.begin)
2017-05-26 04:48:44 +08:00
readToBegin = true;
if (resolvedEnd == ryw->getMaxReadKey())
2017-05-26 04:48:44 +08:00
readThroughEnd = true;
end = firstGreaterOrEqual(resolvedEnd);
2017-05-26 04:48:44 +08:00
break;
}
if (!it.is_unreadable() && !it.is_unknown_range() && it.beginKey() > itEnd.beginKey()) {
if (end.isFirstGreaterOrEqual())
break;
2017-05-26 04:48:44 +08:00
return RangeResultRef(readToBegin, readThroughEnd);
}
if (limits.isReached() && itemsPastEnd >= 1 - end.offset)
break;
2017-05-26 04:48:44 +08:00
if (it == itEnd && ((!it.is_unreadable() && !it.is_unknown_range()) ||
(begin.offset > 0 && end.isFirstGreaterOrEqual() && end.getKey() == it.beginKey())))
break;
2017-05-26 04:48:44 +08:00
if (it.is_unknown_range()) {
if (limits.hasByteLimit() && result.size() && itemsPastEnd >= 1 - end.offset) {
2017-05-26 04:48:44 +08:00
result.more = true;
break;
}
Iter ucEnd(it);
int singleClears = 0;
int clearLimit = requestCount ? 1 << std::min(requestCount, 20) : 0;
if (it.beginKey() < itEnd.beginKey())
2017-05-26 04:48:44 +08:00
singleClears = std::min(skipUncached(ucEnd, itEnd, BUGGIFY ? 0 : clearLimit + 100), clearLimit);
2017-05-26 04:48:44 +08:00
state KeySelector read_end;
if (ucEnd != itEnd) {
Key k = ucEnd.endKey().toStandaloneStringRef();
read_end = KeySelector(firstGreaterOrEqual(k), k.arena());
if (end.offset < 1)
additionalRows += 1 - end.offset; // extra for items past end
} else if (end.offset < 1) {
read_end = KeySelector(firstGreaterOrEqual(end.getKey()), end.arena());
2017-05-26 04:48:44 +08:00
additionalRows += 1 - end.offset;
} else {
read_end = end;
if (end.offset > 1) {
singleClears +=
countUncached(std::move(ucEnd), ryw->getMaxReadKey(), clearLimit - singleClears);
2017-05-26 04:48:44 +08:00
read_end.offset += singleClears;
}
}
additionalRows += singleClears;
state KeySelector read_begin;
if (begin.isFirstGreaterOrEqual()) {
Key k = it.beginKey() > begin.getKey() ? it.beginKey().toStandaloneStringRef()
: Key(begin.getKey(), begin.arena());
begin = KeySelector(firstGreaterOrEqual(k), k.arena());
2017-05-26 04:48:44 +08:00
read_begin = begin;
} else if (begin.offset > 1) {
read_begin = KeySelector(firstGreaterOrEqual(begin.getKey()), begin.arena());
2017-05-26 04:48:44 +08:00
additionalRows += begin.offset - 1;
} else {
read_begin = begin;
ucEnd = it;
singleClears = countUncachedBack(std::move(ucEnd), clearLimit);
read_begin.offset -= singleClears;
additionalRows += singleClears;
}
if (read_end.getKey() < read_begin.getKey()) {
read_end.setKey(read_begin.getKey());
read_end.arena().dependsOn(read_begin.arena());
}
2017-05-26 04:48:44 +08:00
state GetRangeLimits requestLimit = limits;
setRequestLimits(requestLimit, additionalRows, 2 - read_begin.offset, requestCount);
2017-05-26 04:48:44 +08:00
requestCount++;
ASSERT(!requestLimit.hasRowLimit() || requestLimit.rows > 0);
ASSERT(requestLimit.hasRowLimit() || requestLimit.hasByteLimit());
2019-04-06 04:11:50 +08:00
//TraceEvent("RYWIssuing", randomID).detail("Begin", read_begin.toString()).detail("End", read_end.toString()).detail("Bytes", requestLimit.bytes).detail("Rows", requestLimit.rows).detail("Limits", limits.bytes).detail("Reached", limits.isReached()).detail("RequestCount", requestCount).detail("SingleClears", singleClears).detail("UcEnd", ucEnd.beginKey()).detail("MinRows", requestLimit.minRows);
2017-05-26 04:48:44 +08:00
additionalRows = 0;
2021-07-03 12:41:50 +08:00
RangeResult snapshot_read =
wait(ryw->tr.getRange(read_begin, read_end, requestLimit, Snapshot::True, Reverse::False));
KeyRangeRef range = getKnownKeyRange(snapshot_read, read_begin, read_end, ryw->arena);
//TraceEvent("RYWCacheInsert", randomID).detail("Range", range).detail("ExpectedSize", snapshot_read.expectedSize()).detail("Rows", snapshot_read.size()).detail("Results", snapshot_read).detail("More", snapshot_read.more).detail("ReadToBegin", snapshot_read.readToBegin).detail("ReadThroughEnd", snapshot_read.readThroughEnd).detail("ReadThrough", snapshot_read.readThrough);
2017-05-26 04:48:44 +08:00
if (ryw->cache.insert(range, snapshot_read))
2017-05-26 04:48:44 +08:00
ryw->arena.dependsOn(snapshot_read.arena());
// TODO: Is there a more efficient way to deal with invalidation?
resolveKeySelectorFromCache(
begin, it, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualBeginOffset);
resolveKeySelectorFromCache(
end, itEnd, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualEndOffset);
2017-05-26 04:48:44 +08:00
} else if (it.is_kv()) {
KeyValueRef const* start = it.kv(ryw->arena);
if (start == nullptr) {
++it;
continue;
}
it.skipContiguous(end.isFirstGreaterOrEqual()
? end.getKey()
: ryw->getMaxReadKey()); // not technically correct since this would add
// end.getKey(), but that is protected above
int maxCount = it.kv(ryw->arena) - start + 1;
2017-05-26 04:48:44 +08:00
int count = 0;
for (; count < maxCount && !limits.isReached(); count++) {
2017-05-26 04:48:44 +08:00
limits.decrement(start[count]);
}
itemsPastEnd += maxCount - count;
2019-04-06 04:11:50 +08:00
//TraceEvent("RYWaddKV", randomID).detail("Key", it.beginKey()).detail("Count", count).detail("MaxCount", maxCount).detail("ItemsPastEnd", itemsPastEnd);
if (count)
result.append(result.arena(), start, count);
2017-05-26 04:48:44 +08:00
++it;
} else
++it;
}
result.more = result.more || limits.isReached();
if (end.isFirstGreaterOrEqual()) {
int keepItems = std::lower_bound(result.begin(), result.end(), end.getKey(), KeyValueRef::OrderByKey()) -
result.begin();
if (keepItems < result.size())
2017-05-26 04:48:44 +08:00
result.more = false;
result.resize(result.arena(), keepItems);
2017-05-26 04:48:44 +08:00
}
result.readToBegin = readToBegin;
result.readThroughEnd = !result.more && readThroughEnd;
result.arena().dependsOn(ryw->arena);
2017-05-26 04:48:44 +08:00
return result;
}
static KeyRangeRef getKnownKeyRangeBack(RangeResultRef data, KeySelector begin, KeySelector end, Arena& arena) {
StringRef beginKey = !data.more && begin.offset <= 1 ? begin.getKey() : allKeys.end;
ExtStringRef endKey = end.offset >= 1 ? end.getKey() : allKeys.begin;
2017-05-26 04:48:44 +08:00
if (data.readToBegin)
beginKey = allKeys.begin;
if (data.readThroughEnd)
endKey = allKeys.end;
2017-05-26 04:48:44 +08:00
if (data.size()) {
if (data.readThrough.present()) {
beginKey = std::min(data.readThrough.get(), beginKey);
} else {
beginKey = !data.more && data.end()[-1].key > beginKey ? beginKey : data.end()[-1].key;
}
endKey = data[0].key < endKey ? endKey : ExtStringRef(data[0].key, 1);
2017-05-26 04:48:44 +08:00
}
if (beginKey >= endKey)
return KeyRangeRef();
2017-05-26 04:48:44 +08:00
return KeyRangeRef(StringRef(arena, beginKey), endKey.toArena(arena));
2017-05-26 04:48:44 +08:00
}
// Pre: it points to an unknown range
// Decrements it to point to the unknown range just before the last nontrivial known range (skips over trivial known
// ranges), but not more than iterationLimit ranges away Returns the number of single-key empty ranges skipped
template <class Iter>
static int skipUncachedBack(Iter& it, Iter const& end, int iterationLimit) {
2017-05-26 04:48:44 +08:00
ExtStringRef b = it.beginKey();
ExtStringRef e = it.endKey();
int singleEmpty = 0;
ASSERT(!it.is_unreadable() && it.is_unknown_range());
// b == it.beginKey()
// e is the end of the contiguous empty range containing it
while (it != end && --iterationLimit >= 0) {
2017-05-26 04:48:44 +08:00
if (it.is_unreadable() || it.is_empty_range()) {
if (it.is_unreadable() || !e.isKeyAfter(b)) { // Assumes no degenerate ranges
2017-05-26 04:48:44 +08:00
while (it.is_unreadable() || !it.is_unknown_range())
++it;
return singleEmpty;
}
singleEmpty++;
} else
e = b;
--it;
b = it.beginKey();
}
while (it.is_unreadable() || !it.is_unknown_range())
++it;
return singleEmpty;
}
// Pre: it points to an unknown range
// Returns the number of preceding empty single-key known ranges between it and the previous nontrivial known range,
// but no more than maxClears Leaves it in an indeterminate state
template <class Iter>
static int countUncachedBack(Iter&& it, int maxClears) {
if (maxClears <= 0)
return 0;
2017-05-26 04:48:44 +08:00
ExtStringRef b = it.beginKey();
ExtStringRef e = it.endKey();
int singleEmpty = 0;
while (b > allKeys.begin) {
2017-05-26 04:48:44 +08:00
if (it.is_unreadable() || it.is_empty_range()) {
if (it.is_unreadable() || !e.isKeyAfter(b)) { // Assumes no degenerate ranges
2017-05-26 04:48:44 +08:00
return singleEmpty;
}
singleEmpty++;
if (singleEmpty >= maxClears)
2017-05-26 04:48:44 +08:00
return maxClears;
} else
e = b;
--it;
b = it.beginKey();
}
return singleEmpty;
}
ACTOR template <class Iter>
2021-05-04 04:14:16 +08:00
static Future<RangeResult> getRangeValueBack(ReadYourWritesTransaction* ryw,
KeySelector begin,
KeySelector end,
GetRangeLimits limits,
Iter* pit) {
2017-05-26 04:48:44 +08:00
state Iter& it(*pit);
state Iter itEnd(*pit);
2021-05-04 04:14:16 +08:00
state RangeResult result;
2017-05-26 04:48:44 +08:00
state int64_t additionalRows = 0;
state int itemsPastBegin = 0;
state int requestCount = 0;
state bool readToBegin = false;
state bool readThroughEnd = false;
state int actualBeginOffset = begin.offset;
state int actualEndOffset = end.offset;
// state UID randomID = nondeterministicRandom()->randomUniqueID();
2017-05-26 04:48:44 +08:00
resolveKeySelectorFromCache(end, it, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualEndOffset);
resolveKeySelectorFromCache(
begin, itEnd, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualBeginOffset);
2017-05-26 04:48:44 +08:00
if (actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(false, false);
} else if ((begin.isFirstGreaterOrEqual() && begin.getKey() == ryw->getMaxReadKey()) ||
(end.isFirstGreaterOrEqual() && end.getKey() == allKeys.begin)) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(readToBegin, readThroughEnd);
}
if (!begin.isFirstGreaterOrEqual() && begin.getKey() > end.getKey()) {
Key resolvedBegin = wait(read(ryw, GetKeyReq(begin), pit));
if (resolvedBegin == allKeys.begin)
2017-05-26 04:48:44 +08:00
readToBegin = true;
if (resolvedBegin == ryw->getMaxReadKey())
2017-05-26 04:48:44 +08:00
readThroughEnd = true;
if (resolvedBegin >= end.getKey() && end.offset <= 1) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(false, false);
} else if (resolvedBegin == ryw->getMaxReadKey()) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(readToBegin, readThroughEnd);
}
resolveKeySelectorFromCache(end, it, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualEndOffset);
resolveKeySelectorFromCache(
begin, itEnd, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualBeginOffset);
2017-05-26 04:48:44 +08:00
}
//TraceEvent("RYWSelectorsStartReverse", randomID).detail("ByteLimit", limits.bytes).detail("RowLimit", limits.rows);
2017-05-26 04:48:44 +08:00
loop {
/*TraceEvent("RYWSelectors", randomID).detail("Begin", begin.toString())
.detail("End", end.toString())
.detail("Reached", limits.isReached())
.detail("ItemsPastBegin", itemsPastBegin)
.detail("EndOffset", end.offset)
.detail("ItBegin", it.beginKey())
.detail("ItEnd", itEnd.beginKey())
.detail("Unknown", it.is_unknown_range())
.detail("Kv", it.is_kv())
.detail("Requests", requestCount);*/
if (!result.size() && actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(false, false);
}
if (!begin.isBackward() && begin.getKey() >= ryw->getMaxReadKey()) {
2017-05-26 04:48:44 +08:00
return RangeResultRef(readToBegin, readThroughEnd);
}
if ((begin.offset >= end.offset && begin.getKey() >= end.getKey()) ||
(end.offset <= 1 && end.getKey() == allKeys.begin)) {
if (begin.isFirstGreaterOrEqual())
break;
if (!result.size())
break;
Key resolvedBegin =
wait(read(ryw,
GetKeyReq(begin),
pit)); // do not worry about iterator invalidation, because we are breaking for the loop
if (resolvedBegin == allKeys.begin)
2017-05-26 04:48:44 +08:00
readToBegin = true;
if (resolvedBegin == ryw->getMaxReadKey())
2017-05-26 04:48:44 +08:00
readThroughEnd = true;
begin = firstGreaterOrEqual(resolvedBegin);
2017-05-26 04:48:44 +08:00
break;
}
if (itemsPastBegin >= begin.offset - 1 && !it.is_unreadable() && !it.is_unknown_range() &&
it.beginKey() < itEnd.beginKey()) {
if (begin.isFirstGreaterOrEqual())
break;
2017-05-26 04:48:44 +08:00
return RangeResultRef(readToBegin, readThroughEnd);
}
if (limits.isReached() && itemsPastBegin >= begin.offset - 1)
break;
2017-05-26 04:48:44 +08:00
if (end.isFirstGreaterOrEqual() && end.getKey() == it.beginKey()) {
if (itemsPastBegin >= begin.offset - 1 && it == itEnd)
break;
2017-05-26 04:48:44 +08:00
--it;
}
if (it.is_unknown_range()) {
if (limits.hasByteLimit() && result.size() && itemsPastBegin >= begin.offset - 1) {
2017-05-26 04:48:44 +08:00
result.more = true;
break;
}
Iter ucEnd(it);
int singleClears = 0;
int clearLimit = requestCount ? 1 << std::min(requestCount, 20) : 0;
if (it.beginKey() > itEnd.beginKey())
singleClears = std::min(skipUncachedBack(ucEnd, itEnd, BUGGIFY ? 0 : clearLimit + 100), clearLimit);
2017-05-26 04:48:44 +08:00
state KeySelector read_begin;
if (ucEnd != itEnd) {
Key k = ucEnd.beginKey().toStandaloneStringRef();
read_begin = KeySelector(firstGreaterOrEqual(k), k.arena());
if (begin.offset > 1)
additionalRows += begin.offset - 1; // extra for items past end
} else if (begin.offset > 1) {
read_begin = KeySelector(firstGreaterOrEqual(begin.getKey()), begin.arena());
2017-05-26 04:48:44 +08:00
additionalRows += begin.offset - 1;
} else {
read_begin = begin;
if (begin.offset < 1) {
singleClears += countUncachedBack(std::move(ucEnd), clearLimit - singleClears);
2017-05-26 04:48:44 +08:00
read_begin.offset -= singleClears;
}
}
additionalRows += singleClears;
state KeySelector read_end;
if (end.isFirstGreaterOrEqual()) {
Key k = it.endKey() < end.getKey() ? it.endKey().toStandaloneStringRef() : end.getKey();
end = KeySelector(firstGreaterOrEqual(k), k.arena());
2017-05-26 04:48:44 +08:00
read_end = end;
} else if (end.offset < 1) {
read_end = KeySelector(firstGreaterOrEqual(end.getKey()), end.arena());
2017-05-26 04:48:44 +08:00
additionalRows += 1 - end.offset;
} else {
read_end = end;
ucEnd = it;
2017-05-26 04:48:44 +08:00
singleClears = countUncached(std::move(ucEnd), ryw->getMaxReadKey(), clearLimit);
read_end.offset += singleClears;
additionalRows += singleClears;
}
if (read_begin.getKey() > read_end.getKey()) {
read_begin.setKey(read_end.getKey());
read_begin.arena().dependsOn(read_end.arena());
}
2017-05-26 04:48:44 +08:00
state GetRangeLimits requestLimit = limits;
setRequestLimits(requestLimit, additionalRows, read_end.offset, requestCount);
requestCount++;
ASSERT(!requestLimit.hasRowLimit() || requestLimit.rows > 0);
ASSERT(requestLimit.hasRowLimit() || requestLimit.hasByteLimit());
2017-05-26 04:48:44 +08:00
2019-04-06 04:11:50 +08:00
//TraceEvent("RYWIssuing", randomID).detail("Begin", read_begin.toString()).detail("End", read_end.toString()).detail("Bytes", requestLimit.bytes).detail("Rows", requestLimit.rows).detail("Limits", limits.bytes).detail("Reached", limits.isReached()).detail("RequestCount", requestCount).detail("SingleClears", singleClears).detail("UcEnd", ucEnd.beginKey()).detail("MinRows", requestLimit.minRows);
2017-05-26 04:48:44 +08:00
additionalRows = 0;
2021-07-03 12:41:50 +08:00
RangeResult snapshot_read =
wait(ryw->tr.getRange(read_begin, read_end, requestLimit, Snapshot::True, Reverse::True));
KeyRangeRef range = getKnownKeyRangeBack(snapshot_read, read_begin, read_end, ryw->arena);
2017-05-26 04:48:44 +08:00
//TraceEvent("RYWCacheInsert", randomID).detail("Range", range).detail("ExpectedSize", snapshot_read.expectedSize()).detail("Rows", snapshot_read.size()).detail("Results", snapshot_read).detail("More", snapshot_read.more).detail("ReadToBegin", snapshot_read.readToBegin).detail("ReadThroughEnd", snapshot_read.readThroughEnd).detail("ReadThrough", snapshot_read.readThrough);
2017-05-26 04:48:44 +08:00
RangeResultRef reversed;
reversed.resize(ryw->arena, snapshot_read.size());
for (int i = 0; i < snapshot_read.size(); i++) {
reversed[snapshot_read.size() - i - 1] = snapshot_read[i];
2017-05-26 04:48:44 +08:00
}
if (ryw->cache.insert(range, reversed))
2017-05-26 04:48:44 +08:00
ryw->arena.dependsOn(snapshot_read.arena());
// TODO: Is there a more efficient way to deal with invalidation?
resolveKeySelectorFromCache(
end, it, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualEndOffset);
resolveKeySelectorFromCache(
begin, itEnd, ryw->getMaxReadKey(), &readToBegin, &readThroughEnd, &actualBeginOffset);
2017-05-26 04:48:44 +08:00
} else {
KeyValueRef const* end = it.is_kv() ? it.kv(ryw->arena) : nullptr;
if (end != nullptr) {
it.skipContiguousBack(begin.isFirstGreaterOrEqual() ? begin.getKey() : allKeys.begin);
KeyValueRef const* start = it.kv(ryw->arena);
ASSERT(start != nullptr);
2017-05-26 04:48:44 +08:00
int maxCount = end - start + 1;
int count = 0;
for (; count < maxCount && !limits.isReached(); count++) {
limits.decrement(start[maxCount - count - 1]);
2017-05-26 04:48:44 +08:00
}
itemsPastBegin += maxCount - count;
2019-04-06 04:11:50 +08:00
//TraceEvent("RYWaddKV", randomID).detail("Key", it.beginKey()).detail("Count", count).detail("MaxCount", maxCount).detail("ItemsPastBegin", itemsPastBegin);
if (count) {
2017-05-26 04:48:44 +08:00
int size = result.size();
result.resize(result.arena(), size + count);
for (int i = 0; i < count; i++) {
result[size + i] = start[maxCount - i - 1];
2017-05-26 04:48:44 +08:00
}
}
}
if (it == itEnd)
break;
2017-05-26 04:48:44 +08:00
--it;
}
}
result.more = result.more || limits.isReached();
if (begin.isFirstGreaterOrEqual()) {
int keepItems = result.rend() -
std::lower_bound(result.rbegin(), result.rend(), begin.getKey(), KeyValueRef::OrderByKey());
if (keepItems < result.size())
2017-05-26 04:48:44 +08:00
result.more = false;
result.resize(result.arena(), keepItems);
2017-05-26 04:48:44 +08:00
}
result.readToBegin = !result.more && readToBegin;
result.readThroughEnd = readThroughEnd;
result.arena().dependsOn(ryw->arena);
2017-05-26 04:48:44 +08:00
return result;
}
#ifndef __INTEL_COMPILER
#pragma region GetMappedRange
#endif
template <class Iter>
static Future<MappedRangeResult> read(ReadYourWritesTransaction* ryw, GetMappedRangeReq<false> read, Iter* it) {
return getMappedRangeValue(ryw, read.begin, read.end, read.mapper, read.limits, it);
};
template <class Iter>
static Future<MappedRangeResult> read(ReadYourWritesTransaction* ryw, GetMappedRangeReq<true> read, Iter* it) {
throw unsupported_operation();
// TODO: Support reverse. return getMappedRangeValueBack(ryw, read.begin, read.end, read.mapper,
// read.limits, it);
};
ACTOR template <bool backwards>
static Future<MappedRangeResult> readThrough(ReadYourWritesTransaction* ryw,
GetMappedRangeReq<backwards> read,
Snapshot snapshot) {
if (backwards && read.end.offset > 1) {
// FIXME: Optimistically assume that this will not run into the system keys, and only reissue if the result
// actually does.
Key key = wait(ryw->tr.getKey(read.end, snapshot));
if (key > ryw->getMaxReadKey())
read.end = firstGreaterOrEqual(ryw->getMaxReadKey());
else
read.end = KeySelector(firstGreaterOrEqual(key), key.arena());
}
MappedRangeResult v = wait(ryw->tr.getMappedRange(
read.begin, read.end, read.mapper, read.limits, snapshot, backwards ? Reverse::True : Reverse::False));
return v;
}
template <bool backwards>
static void addConflictRangeAndMustUnmodified(ReadYourWritesTransaction* ryw,
GetMappedRangeReq<backwards> read,
WriteMap::iterator& it,
MappedRangeResult result) {
// Primary getRange.
addConflictRange<true, MappedRangeResult>(
ryw, GetRangeReq<backwards>(read.begin, read.end, read.limits), it, result);
// Secondary getValue/getRanges.
for (const auto& mappedKeyValue : result) {
const auto& reqAndResult = mappedKeyValue.reqAndResult;
if (std::holds_alternative<GetValueReqAndResultRef>(reqAndResult)) {
auto getValue = std::get<GetValueReqAndResultRef>(reqAndResult);
// GetValueReq variation of addConflictRange require it to point at the right segment.
it.skip(getValue.key);
// The result is not used in GetValueReq variation of addConflictRange. Let's just pass in a
// placeholder.
addConflictRange<true>(ryw, GetValueReq(getValue.key), it, Optional<Value>());
} else if (std::holds_alternative<GetRangeReqAndResultRef>(reqAndResult)) {
auto getRange = std::get<GetRangeReqAndResultRef>(reqAndResult);
// We only support forward scan for secondary getRange requests.
// The limits are not used in addConflictRange. Let's just pass in a placeholder.
addConflictRange<true>(
ryw, GetRangeReq<false>(getRange.begin, getRange.end, GetRangeLimits()), it, getRange.result);
} else {
throw internal_error();
}
}
}
// For Snapshot::True and NOT readYourWritesDisabled.
ACTOR template <bool backwards>
static Future<MappedRangeResult> readWithConflictRangeRYW(ReadYourWritesTransaction* ryw,
GetMappedRangeReq<backwards> req,
Snapshot snapshot) {
choose {
when(MappedRangeResult result = wait(readThrough(ryw, req, Snapshot::True))) {
// Insert read conflicts (so that it supported Snapshot::True) and check it is not modified (so it masks
// sure not break RYW semantic while not implementing RYW) for both the primary getRange and all
// underlying getValue/getRanges.
WriteMap::iterator writes(&ryw->writes);
addConflictRangeAndMustUnmodified<backwards>(ryw, req, writes, result);
return result;
}
when(wait(ryw->resetPromise.getFuture())) { throw internal_error(); }
}
}
template <bool backwards>
static inline Future<MappedRangeResult> readWithConflictRangeForGetMappedRange(
ReadYourWritesTransaction* ryw,
GetMappedRangeReq<backwards> const& req,
Snapshot snapshot) {
// For now, getMappedRange requires serializable isolation. (Technically it is trivial to add snapshot
// isolation support. But it is not default and is rarely used. So we disallow it until we have thorough test
// coverage for it.)
if (snapshot) {
TEST(true); // getMappedRange not supported for snapshot.
throw unsupported_operation();
}
// For now, getMappedRange requires read-your-writes being NOT disabled. But the support of RYW is limited
// to throwing get_mapped_range_reads_your_writes error when getMappedRange actually reads your own writes.
// Applications should fall back in their own ways. This is different from what is usually expected from RYW,
// which returns the written value transparently. In another word, it makes sure not break RYW semantics without
// actually implementing reading from the writes.
if (ryw->options.readYourWritesDisabled) {
TEST(true); // getMappedRange not supported for read-your-writes disabled.
throw unsupported_operation();
}
return readWithConflictRangeRYW(ryw, req, snapshot);
}
#ifndef __INTEL_COMPILER
#pragma endregion
#endif
static void triggerWatches(ReadYourWritesTransaction* ryw,
KeyRangeRef range,
Optional<ValueRef> val,
bool valueKnown = true) {
for (auto it = ryw->watchMap.lower_bound(range.begin); it != ryw->watchMap.end() && it->key < range.end;) {
2017-05-26 04:48:44 +08:00
auto itCopy = it;
++it;
ASSERT(itCopy->value.size());
TEST(itCopy->value.size() > 1); // Multiple watches on the same key triggered by RYOW
for (int i = 0; i < itCopy->value.size(); i++) {
if (itCopy->value[i]->onChangeTrigger.isSet()) {
swapAndPop(&itCopy->value, i--);
} else if (!valueKnown ||
(itCopy->value[i]->setPresent &&
(itCopy->value[i]->setValue.present() != val.present() ||
(val.present() && itCopy->value[i]->setValue.get() != val.get()))) ||
(itCopy->value[i]->valuePresent &&
(itCopy->value[i]->value.present() != val.present() ||
(val.present() && itCopy->value[i]->value.get() != val.get())))) {
2017-05-26 04:48:44 +08:00
itCopy->value[i]->onChangeTrigger.send(Void());
swapAndPop(&itCopy->value, i--);
2017-05-26 04:48:44 +08:00
} else {
itCopy->value[i]->setPresent = true;
itCopy->value[i]->setValue = val.castTo<Value>();
2017-05-26 04:48:44 +08:00
}
}
if (itCopy->value.size() == 0)
2017-05-26 04:48:44 +08:00
ryw->watchMap.erase(itCopy);
}
}
static void triggerWatches(ReadYourWritesTransaction* ryw,
KeyRef key,
Optional<ValueRef> val,
bool valueKnown = true) {
2017-05-26 04:48:44 +08:00
triggerWatches(ryw, singleKeyRange(key), val, valueKnown);
}
ACTOR static Future<Void> watch(ReadYourWritesTransaction* ryw, Key key) {
2017-05-26 04:48:44 +08:00
state Future<Optional<Value>> val;
state Future<Void> watchFuture;
state Reference<Watch> watch(new Watch(key));
state Promise<Void> done;
ryw->reading.add(done.getFuture());
2017-05-26 04:48:44 +08:00
if (!ryw->options.readYourWritesDisabled) {
2017-05-26 04:48:44 +08:00
ryw->watchMap[key].push_back(watch);
val = readWithConflictRange(ryw, GetValueReq(key), Snapshot::False);
} else {
ryw->approximateSize += 2 * key.expectedSize() + 1;
2017-05-26 04:48:44 +08:00
val = ryw->tr.get(key);
}
2017-05-26 04:48:44 +08:00
try {
wait(ryw->resetPromise.getFuture() || success(val) || watch->onChangeTrigger.getFuture());
} catch (Error& e) {
2017-05-26 04:48:44 +08:00
done.send(Void());
throw;
}
if (watch->onChangeTrigger.getFuture().isReady()) {
2017-05-26 04:48:44 +08:00
done.send(Void());
if (watch->onChangeTrigger.getFuture().isError())
2017-05-26 04:48:44 +08:00
throw watch->onChangeTrigger.getFuture().getError();
return Void();
}
watch->valuePresent = true;
watch->value = val.get();
if (watch->setPresent && (watch->setValue.present() != watch->value.present() ||
(watch->value.present() && watch->setValue.get() != watch->value.get()))) {
2017-05-26 04:48:44 +08:00
watch->onChangeTrigger.send(Void());
done.send(Void());
return Void();
}
try {
watchFuture = ryw->tr.watch(watch); // throws if there are too many outstanding watches
} catch (Error& e) {
done.send(Void());
throw;
}
2017-05-26 04:48:44 +08:00
done.send(Void());
wait(watchFuture);
2017-05-26 04:48:44 +08:00
return Void();
}
ACTOR static void simulateTimeoutInFlightCommit(ReadYourWritesTransaction* ryw_) {
state Reference<ReadYourWritesTransaction> ryw = Reference<ReadYourWritesTransaction>::addRef(ryw_);
ASSERT(ryw->options.timeoutInSeconds > 0);
if (!ryw->resetPromise.isSet())
ryw->resetPromise.sendError(transaction_timed_out());
wait(delay(deterministicRandom()->random01() * 5));
TraceEvent("ClientBuggifyInFlightCommit").log();
wait(ryw->tr.commit());
}
ACTOR static Future<Void> commit(ReadYourWritesTransaction* ryw) {
2017-05-26 04:48:44 +08:00
try {
ryw->commitStarted = true;
if (ryw->options.specialKeySpaceChangeConfiguration)
wait(ryw->getDatabase()->specialKeySpace->commit(ryw));
2017-05-26 04:48:44 +08:00
Future<Void> ready = ryw->reading;
wait(ryw->resetPromise.getFuture() || ready);
2017-05-26 04:48:44 +08:00
if (ryw->options.readYourWritesDisabled) {
// Stash away conflict ranges to read after commit
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)
ryw->nativeReadRanges.push_back(
ryw->nativeReadRanges.arena(),
KeyRangeRef(f.get().first, f.get().second)
.withPrefix(readConflictRangeKeysRange.begin, ryw->nativeReadRanges.arena()));
}
2017-05-26 04:48:44 +08:00
if (ryw->resetPromise.isSet())
throw ryw->resetPromise.getFuture().getError();
if (CLIENT_BUGGIFY && ryw->options.timeoutInSeconds > 0) {
simulateTimeoutInFlightCommit(ryw);
throw transaction_timed_out();
}
wait(ryw->resetPromise.getFuture() || ryw->tr.commit());
2017-05-26 04:48:44 +08:00
ryw->debugLogRetries();
if (!ryw->tr.apiVersionAtLeast(410)) {
2017-05-26 04:48:44 +08:00
ryw->reset();
}
return Void();
}
2019-07-13 07:53:37 +08:00
ryw->writeRangeToNativeTransaction(KeyRangeRef(StringRef(), allKeys.end));
2017-05-26 04:48:44 +08:00
auto conflictRanges = ryw->readConflicts.ranges();
for (auto iter = conflictRanges.begin(); iter != conflictRanges.end(); ++iter) {
if (iter->value()) {
ryw->tr.addReadConflictRange(iter->range());
2017-05-26 04:48:44 +08:00
}
}
if (CLIENT_BUGGIFY && ryw->options.timeoutInSeconds > 0) {
simulateTimeoutInFlightCommit(ryw);
throw transaction_timed_out();
}
wait(ryw->resetPromise.getFuture() || ryw->tr.commit());
2017-05-26 04:48:44 +08:00
ryw->debugLogRetries();
if (!ryw->tr.apiVersionAtLeast(410)) {
2017-05-26 04:48:44 +08:00
ryw->reset();
}
2017-05-26 04:48:44 +08:00
return Void();
} catch (Error& e) {
if (!ryw->tr.apiVersionAtLeast(410)) {
2017-05-26 04:48:44 +08:00
ryw->commitStarted = false;
if (!ryw->resetPromise.isSet()) {
2017-05-26 04:48:44 +08:00
ryw->tr.reset();
ryw->resetRyow();
}
}
throw;
}
}
ACTOR static Future<Void> onError(ReadYourWritesTransaction* ryw, Error e) {
2017-05-26 04:48:44 +08:00
try {
if (ryw->resetPromise.isSet()) {
2017-05-26 04:48:44 +08:00
throw ryw->resetPromise.getFuture().getError();
}
bool retry_limit_hit = ryw->options.maxRetries != -1 && ryw->retries >= ryw->options.maxRetries;
if (ryw->retries < std::numeric_limits<int>::max())
ryw->retries++;
if (retry_limit_hit) {
2017-05-26 04:48:44 +08:00
throw e;
}
wait(ryw->resetPromise.getFuture() || ryw->tr.onError(e));
2017-05-26 04:48:44 +08:00
ryw->debugLogRetries(e);
ryw->resetRyow();
return Void();
} catch (Error& e) {
if (!ryw->resetPromise.isSet()) {
if (ryw->tr.apiVersionAtLeast(610)) {
ryw->resetPromise.sendError(transaction_cancelled());
} else {
ryw->resetRyow();
}
2017-05-26 04:48:44 +08:00
}
if (e.code() == error_code_broken_promise)
2017-05-26 04:48:44 +08:00
throw transaction_cancelled();
throw;
}
}
ACTOR static Future<Version> getReadVersion(ReadYourWritesTransaction* ryw) {
choose {
when(Version v = wait(ryw->tr.getReadVersion())) { return v; }
2017-05-26 04:48:44 +08:00
when(wait(ryw->resetPromise.getFuture())) { throw internal_error(); }
2017-05-26 04:48:44 +08:00
}
}
};
ReadYourWritesTransaction::ReadYourWritesTransaction(Database const& cx, Optional<TenantName> tenantName)
: ISingleThreadTransaction(cx->deferredError), tr(cx, tenantName), cache(&arena), writes(&arena), retries(0),
approximateSize(0), creationTime(now()), commitStarted(false), versionStampFuture(tr.getVersionstamp()),
specialKeySpaceWriteMap(std::make_pair(false, Optional<Value>()), specialKeys.end), options(tr) {
std::copy(
cx.getTransactionDefaults().begin(), cx.getTransactionDefaults().end(), std::back_inserter(persistentOptions));
applyPersistentOptions();
}
2017-05-26 04:48:44 +08:00
void ReadYourWritesTransaction::construct(Database const& cx) {
*this = ReadYourWritesTransaction(cx, Optional<TenantName>());
}
void ReadYourWritesTransaction::construct(Database const& cx, TenantName const& tenantName) {
*this = ReadYourWritesTransaction(cx, tenantName);
}
ACTOR Future<Void> timebomb(double endTime, Promise<Void> resetPromise) {
while (now() < endTime) {
wait(delayUntil(std::min(endTime + 0.0001, now() + CLIENT_KNOBS->TRANSACTION_TIMEOUT_DELAY_INTERVAL)));
2017-05-26 04:48:44 +08:00
}
if (!resetPromise.isSet())
2017-05-26 04:48:44 +08:00
resetPromise.sendError(transaction_timed_out());
throw transaction_timed_out();
2017-05-26 04:48:44 +08:00
}
void ReadYourWritesTransaction::resetTimeout() {
timeoutActor =
options.timeoutInSeconds == 0.0 ? Void() : timebomb(options.timeoutInSeconds + creationTime, resetPromise);
}
2017-05-26 04:48:44 +08:00
Future<Version> ReadYourWritesTransaction::getReadVersion() {
if (tr.apiVersionAtLeast(101)) {
if (resetPromise.isSet())
return resetPromise.getFuture().getError();
return RYWImpl::getReadVersion(this);
}
return tr.getReadVersion();
}
Optional<Value> getValueFromJSON(StatusObject statusObj) {
try {
Value output =
StringRef(json_spirit::write_string(json_spirit::mValue(statusObj), json_spirit::Output_options::none));
2017-05-26 04:48:44 +08:00
return output;
} catch (std::exception& e) {
2017-05-26 04:48:44 +08:00
TraceEvent(SevError, "UnableToUnparseStatusJSON").detail("What", e.what());
throw internal_error();
}
}
ACTOR Future<Optional<Value>> getJSON(Database db) {
StatusObject statusObj = wait(StatusClient::statusFetcher(db));
2017-05-26 04:48:44 +08:00
return getValueFromJSON(statusObj);
}
ACTOR Future<RangeResult> getWorkerInterfaces(Reference<IClusterConnectionRecord> connRecord) {
2017-05-26 04:48:44 +08:00
state Reference<AsyncVar<Optional<ClusterInterface>>> clusterInterface(new AsyncVar<Optional<ClusterInterface>>);
state Future<Void> leaderMon = monitorLeader<ClusterInterface>(connRecord, clusterInterface);
2017-05-26 04:48:44 +08:00
loop {
2017-05-26 04:48:44 +08:00
choose {
when(std::vector<ClientWorkerInterface> workers =
wait(clusterInterface->get().present()
? brokenPromiseToNever(
clusterInterface->get().get().getClientWorkers.getReply(GetClientWorkersRequest()))
: Never())) {
2021-05-04 04:14:16 +08:00
RangeResult result;
for (auto& it : workers) {
result.push_back_deep(
result.arena(),
KeyValueRef(it.address().toString(), BinaryWriter::toValue(it, IncludeVersion())));
2017-05-26 04:48:44 +08:00
}
2017-05-26 04:48:44 +08:00
return result;
}
when(wait(clusterInterface->onChange())) {}
2017-05-26 04:48:44 +08:00
}
}
}
2021-07-03 12:41:50 +08:00
Future<Optional<Value>> ReadYourWritesTransaction::get(const Key& key, Snapshot snapshot) {
2020-11-12 05:07:54 +08:00
TEST(true); // ReadYourWritesTransaction::get
if (getDatabase()->apiVersionAtLeast(630)) {
2020-05-08 04:26:41 +08:00
if (specialKeys.contains(key)) {
TEST(true); // Special keys get
return getDatabase()->specialKeySpace->get(this, key);
2020-05-08 04:26:41 +08:00
}
} else {
if (key == LiteralStringRef("\xff\xff/status/json")) {
if (tr.getDatabase().getPtr() && tr.getDatabase()->getConnectionRecord()) {
2021-01-15 04:13:29 +08:00
++tr.getDatabase()->transactionStatusRequests;
return getJSON(tr.getDatabase());
} else {
return Optional<Value>();
2017-05-26 04:48:44 +08:00
}
}
if (key == LiteralStringRef("\xff\xff/cluster_file_path")) {
try {
if (tr.getDatabase().getPtr() && tr.getDatabase()->getConnectionRecord()) {
Optional<Value> output = StringRef(tr.getDatabase()->getConnectionRecord()->getLocation());
return output;
}
} catch (Error& e) {
return e;
2017-05-26 04:48:44 +08:00
}
return Optional<Value>();
2017-05-26 04:48:44 +08:00
}
if (key == LiteralStringRef("\xff\xff/connection_string")) {
try {
if (tr.getDatabase().getPtr() && tr.getDatabase()->getConnectionRecord()) {
Reference<IClusterConnectionRecord> f = tr.getDatabase()->getConnectionRecord();
Optional<Value> output = StringRef(f->getConnectionString().toString());
return output;
}
} catch (Error& e) {
return e;
}
return Optional<Value>();
2017-05-26 04:48:44 +08:00
}
}
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
return used_during_commit();
}
if (resetPromise.isSet())
2017-05-26 04:48:44 +08:00
return resetPromise.getFuture().getError();
if (key >= getMaxReadKey() && key != metadataVersionKey)
2017-05-26 04:48:44 +08:00
return key_outside_legal_range();
// There are no keys in the database with size greater than KEY_SIZE_LIMIT
if (key.size() >
(key.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
2017-05-26 04:48:44 +08:00
return Optional<Value>();
Future<Optional<Value>> result = RYWImpl::readWithConflictRange(this, RYWImpl::GetValueReq(key), snapshot);
reading.add(success(result));
2017-05-26 04:48:44 +08:00
return result;
}
2021-07-03 12:41:50 +08:00
Future<Key> ReadYourWritesTransaction::getKey(const KeySelector& key, Snapshot snapshot) {
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
return used_during_commit();
}
if (resetPromise.isSet())
2017-05-26 04:48:44 +08:00
return resetPromise.getFuture().getError();
if (key.getKey() > getMaxReadKey())
2017-05-26 04:48:44 +08:00
return key_outside_legal_range();
Future<Key> result = RYWImpl::readWithConflictRange(this, RYWImpl::GetKeyReq(key), snapshot);
reading.add(success(result));
2017-05-26 04:48:44 +08:00
return result;
}
2021-05-04 04:14:16 +08:00
Future<RangeResult> ReadYourWritesTransaction::getRange(KeySelector begin,
KeySelector end,
GetRangeLimits limits,
2021-07-03 12:41:50 +08:00
Snapshot snapshot,
Reverse reverse) {
if (getDatabase()->apiVersionAtLeast(630)) {
if (specialKeys.contains(begin.getKey()) && specialKeys.begin <= end.getKey() &&
end.getKey() <= specialKeys.end) {
2020-05-08 04:26:41 +08:00
TEST(true); // Special key space get range
return getDatabase()->specialKeySpace->getRange(this, begin, end, limits, reverse);
2020-05-08 04:26:41 +08:00
}
} else {
if (begin.getKey() == LiteralStringRef("\xff\xff/worker_interfaces")) {
if (tr.getDatabase().getPtr() && tr.getDatabase()->getConnectionRecord()) {
return getWorkerInterfaces(tr.getDatabase()->getConnectionRecord());
} else {
2021-05-04 04:14:16 +08:00
return RangeResult();
}
2017-05-26 04:48:44 +08:00
}
}
2020-02-21 05:52:48 +08:00
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
return used_during_commit();
}
if (resetPromise.isSet())
2017-05-26 04:48:44 +08:00
return resetPromise.getFuture().getError();
2017-05-26 04:48:44 +08:00
KeyRef maxKey = getMaxReadKey();
if (begin.getKey() > maxKey || end.getKey() > maxKey)
2017-05-26 04:48:44 +08:00
return key_outside_legal_range();
// This optimization prevents nullptr operations from being added to the conflict range
if (limits.isReached()) {
2017-05-26 04:48:44 +08:00
TEST(true); // RYW range read limit 0
2021-05-04 04:14:16 +08:00
return RangeResult();
2017-05-26 04:48:44 +08:00
}
if (!limits.isValid())
2017-05-26 04:48:44 +08:00
return range_limits_invalid();
if (begin.orEqual)
2017-05-26 04:48:44 +08:00
begin.removeOrEqual(begin.arena());
if (end.orEqual)
2017-05-26 04:48:44 +08:00
end.removeOrEqual(end.arena());
if (begin.offset >= end.offset && begin.getKey() >= end.getKey()) {
2017-05-26 04:48:44 +08:00
TEST(true); // RYW range inverted
2021-05-04 04:14:16 +08:00
return RangeResult();
2017-05-26 04:48:44 +08:00
}
2021-05-04 04:14:16 +08:00
Future<RangeResult> result =
reverse ? RYWImpl::readWithConflictRange(this, RYWImpl::GetRangeReq<true>(begin, end, limits), snapshot)
: RYWImpl::readWithConflictRange(this, RYWImpl::GetRangeReq<false>(begin, end, limits), snapshot);
2017-05-26 04:48:44 +08:00
reading.add(success(result));
2017-05-26 04:48:44 +08:00
return result;
}
2021-05-04 04:14:16 +08:00
Future<RangeResult> ReadYourWritesTransaction::getRange(const KeySelector& begin,
const KeySelector& end,
int limit,
2021-07-03 12:41:50 +08:00
Snapshot snapshot,
Reverse reverse) {
return getRange(begin, end, GetRangeLimits(limit), snapshot, reverse);
2017-05-26 04:48:44 +08:00
}
Future<MappedRangeResult> ReadYourWritesTransaction::getMappedRange(KeySelector begin,
KeySelector end,
Key mapper,
GetRangeLimits limits,
Snapshot snapshot,
Reverse reverse) {
if (getDatabase()->apiVersionAtLeast(630)) {
if (specialKeys.contains(begin.getKey()) && specialKeys.begin <= end.getKey() &&
end.getKey() <= specialKeys.end) {
TEST(true); // Special key space get range (getMappedRange)
throw client_invalid_operation(); // Not support special keys.
}
} else {
if (begin.getKey() == LiteralStringRef("\xff\xff/worker_interfaces")) {
throw client_invalid_operation(); // Not support special keys.
}
}
if (checkUsedDuringCommit()) {
return used_during_commit();
}
if (resetPromise.isSet())
return resetPromise.getFuture().getError();
KeyRef maxKey = getMaxReadKey();
if (begin.getKey() > maxKey || end.getKey() > maxKey)
return key_outside_legal_range();
// This optimization prevents nullptr operations from being added to the conflict range
if (limits.isReached()) {
TEST(true); // RYW range read limit 0 (getMappedRange)
return MappedRangeResult();
}
if (!limits.isValid())
return range_limits_invalid();
if (begin.orEqual)
begin.removeOrEqual(begin.arena());
if (end.orEqual)
end.removeOrEqual(end.arena());
if (begin.offset >= end.offset && begin.getKey() >= end.getKey()) {
TEST(true); // RYW range inverted (getMappedRange)
return MappedRangeResult();
}
Future<MappedRangeResult> result =
reverse ? RYWImpl::readWithConflictRangeForGetMappedRange(
this, RYWImpl::GetMappedRangeReq<true>(begin, end, mapper, limits), snapshot)
: RYWImpl::readWithConflictRangeForGetMappedRange(
this, RYWImpl::GetMappedRangeReq<false>(begin, end, mapper, limits), snapshot);
return result;
}
Future<Standalone<VectorRef<const char*>>> ReadYourWritesTransaction::getAddressesForKey(const Key& key) {
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
return used_during_commit();
}
if (resetPromise.isSet())
2017-05-26 04:48:44 +08:00
return resetPromise.getFuture().getError();
// If key >= allKeys.end, then our resulting address vector will be empty.
Future<Standalone<VectorRef<const char*>>> result =
waitOrError(tr.getAddressesForKey(key), resetPromise.getFuture());
reading.add(success(result));
2017-05-26 04:48:44 +08:00
return result;
}
2020-08-10 13:11:29 +08:00
Future<int64_t> ReadYourWritesTransaction::getEstimatedRangeSizeBytes(const KeyRange& keys) {
if (checkUsedDuringCommit()) {
throw used_during_commit();
}
if (resetPromise.isSet())
return resetPromise.getFuture().getError();
return map(waitOrError(tr.getDatabase()->getStorageMetrics(keys, -1), resetPromise.getFuture()),
[](const StorageMetrics& m) { return m.bytes; });
}
2020-08-15 05:07:08 +08:00
Future<Standalone<VectorRef<KeyRef>>> ReadYourWritesTransaction::getRangeSplitPoints(const KeyRange& range,
int64_t chunkSize) {
if (checkUsedDuringCommit()) {
2020-09-12 07:17:23 +08:00
return used_during_commit();
}
if (resetPromise.isSet())
return resetPromise.getFuture().getError();
2020-09-12 07:17:23 +08:00
KeyRef maxKey = getMaxReadKey();
if (range.begin > maxKey || range.end > maxKey)
2020-09-12 07:17:23 +08:00
return key_outside_legal_range();
return waitOrError(tr.getRangeSplitPoints(range, chunkSize), resetPromise.getFuture());
}
Future<Standalone<VectorRef<KeyRangeRef>>> ReadYourWritesTransaction::getBlobGranuleRanges(const KeyRange& range) {
if (checkUsedDuringCommit()) {
return used_during_commit();
}
if (resetPromise.isSet())
return resetPromise.getFuture().getError();
KeyRef maxKey = getMaxReadKey();
if (range.begin > maxKey || range.end > maxKey)
return key_outside_legal_range();
return waitOrError(tr.getBlobGranuleRanges(range), resetPromise.getFuture());
}
Future<Standalone<VectorRef<BlobGranuleChunkRef>>> ReadYourWritesTransaction::readBlobGranules(
const KeyRange& range,
Version begin,
Optional<Version> readVersion,
Version* readVersionOut) {
// Remove in V2 of API
ASSERT(begin == 0);
if (!options.readYourWritesDisabled) {
return blob_granule_no_ryw();
}
if (checkUsedDuringCommit()) {
return used_during_commit();
}
if (resetPromise.isSet())
return resetPromise.getFuture().getError();
KeyRef maxKey = getMaxReadKey();
if (range.begin > maxKey || range.end > maxKey)
return key_outside_legal_range();
return waitOrError(tr.readBlobGranules(range, begin, readVersion, readVersionOut), resetPromise.getFuture());
}
void ReadYourWritesTransaction::addReadConflictRange(KeyRangeRef const& keys) {
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
throw used_during_commit();
}
2017-05-26 04:48:44 +08:00
if (tr.apiVersionAtLeast(300)) {
if ((keys.begin > getMaxReadKey() || keys.end > getMaxReadKey()) &&
(keys.begin != metadataVersionKey || keys.end != metadataVersionKeyEnd)) {
2017-05-26 04:48:44 +08:00
throw key_outside_legal_range();
}
}
// There aren't any keys in the database with size larger than KEY_SIZE_LIMIT, so if range contains large keys
// we can translate it to an equivalent one with smaller keys
2017-05-26 04:48:44 +08:00
KeyRef begin = keys.begin;
KeyRef end = keys.end;
if (begin.size() >
(begin.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
begin = begin.substr(
0,
(begin.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT) +
1);
if (end.size() >
(end.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
end = end.substr(
0,
(end.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT) +
1);
2017-05-26 04:48:44 +08:00
KeyRangeRef r = KeyRangeRef(begin, end);
if (r.empty()) {
2017-05-26 04:48:44 +08:00
return;
}
if (options.readYourWritesDisabled) {
2019-07-13 07:53:37 +08:00
approximateSize += r.expectedSize() + sizeof(KeyRangeRef);
2017-05-26 04:48:44 +08:00
tr.addReadConflictRange(r);
return;
}
WriteMap::iterator it(&writes);
KeyRangeRef readRange(arena, r);
it.skip(readRange.begin);
2017-05-26 04:48:44 +08:00
updateConflictMap(readRange, it);
}
void ReadYourWritesTransaction::updateConflictMap(KeyRef const& key, WriteMap::iterator& it) {
RYWImpl::updateConflictMap(this, key, it);
2017-05-26 04:48:44 +08:00
}
void ReadYourWritesTransaction::updateConflictMap(KeyRangeRef const& keys, WriteMap::iterator& it) {
RYWImpl::updateConflictMap(this, keys, it);
2017-05-26 04:48:44 +08:00
}
void ReadYourWritesTransaction::writeRangeToNativeTransaction(KeyRangeRef const& keys) {
WriteMap::iterator it(&writes);
2017-05-26 04:48:44 +08:00
it.skip(keys.begin);
bool inClearRange = false;
ExtStringRef clearBegin;
// Clear ranges must be done first because of keys that are both cleared and set to a new value
for (; it.beginKey() < keys.end; ++it) {
if (it.is_cleared_range() && !inClearRange) {
2017-05-26 04:48:44 +08:00
clearBegin = std::max(ExtStringRef(keys.begin), it.beginKey());
inClearRange = true;
} else if (!it.is_cleared_range() && inClearRange) {
2021-07-03 12:41:50 +08:00
tr.clear(KeyRangeRef(clearBegin.toArenaOrRef(arena), it.beginKey().toArenaOrRef(arena)),
AddConflictRange::False);
2017-05-26 04:48:44 +08:00
inClearRange = false;
}
}
2019-07-13 07:53:37 +08:00
if (inClearRange) {
tr.clear(KeyRangeRef(clearBegin.toArenaOrRef(arena), keys.end), AddConflictRange::False);
2017-05-26 04:48:44 +08:00
}
it.skip(keys.begin);
bool inConflictRange = false;
ExtStringRef conflictBegin;
for (; it.beginKey() < keys.end; ++it) {
if (it.is_conflict_range() && !inConflictRange) {
2017-05-26 04:48:44 +08:00
conflictBegin = std::max(ExtStringRef(keys.begin), it.beginKey());
inConflictRange = true;
} else if (!it.is_conflict_range() && inConflictRange) {
2019-07-13 07:53:37 +08:00
tr.addWriteConflictRange(KeyRangeRef(conflictBegin.toArenaOrRef(arena), it.beginKey().toArenaOrRef(arena)));
2017-05-26 04:48:44 +08:00
inConflictRange = false;
}
// SOMEDAY: make atomicOp take set to avoid switch
if (it.is_operation()) {
2017-05-26 04:48:44 +08:00
auto op = it.op();
for (int i = 0; i < op.size(); ++i) {
switch (op[i].type) {
case MutationRef::SetValue:
if (op[i].value.present()) {
tr.set(it.beginKey().assertRef(), op[i].value.get(), AddConflictRange::False);
} else {
tr.clear(it.beginKey().assertRef(), AddConflictRange::False);
}
break;
case MutationRef::AddValue:
case MutationRef::AppendIfFits:
case MutationRef::And:
case MutationRef::Or:
case MutationRef::Xor:
case MutationRef::Max:
case MutationRef::Min:
case MutationRef::SetVersionstampedKey:
case MutationRef::SetVersionstampedValue:
case MutationRef::ByteMin:
case MutationRef::ByteMax:
case MutationRef::MinV2:
case MutationRef::AndV2:
case MutationRef::CompareAndClear:
tr.atomicOp(it.beginKey().assertRef(), op[i].value.get(), op[i].type, AddConflictRange::False);
break;
default:
break;
2017-05-26 04:48:44 +08:00
}
}
}
}
if (inConflictRange) {
tr.addWriteConflictRange(KeyRangeRef(conflictBegin.toArenaOrRef(arena), keys.end));
2017-05-26 04:48:44 +08:00
}
}
ReadYourWritesTransactionOptions::ReadYourWritesTransactionOptions(Transaction const& tr) {
reset(tr);
}
void ReadYourWritesTransactionOptions::reset(Transaction const& tr) {
memset(this, 0, sizeof(*this));
timeoutInSeconds = 0.0;
maxRetries = -1;
snapshotRywEnabled = tr.getDatabase()->snapshotRywEnabled;
}
2017-05-26 04:48:44 +08:00
bool ReadYourWritesTransactionOptions::getAndResetWriteConflictDisabled() {
bool disabled = nextWriteDisableConflictRange;
nextWriteDisableConflictRange = false;
return disabled;
}
void ReadYourWritesTransaction::getWriteConflicts(KeyRangeMap<bool>* result) {
WriteMap::iterator it(&writes);
2017-05-26 04:48:44 +08:00
it.skip(allKeys.begin);
bool inConflictRange = false;
ExtStringRef conflictBegin;
for (; it.beginKey() < getMaxWriteKey(); ++it) {
if (it.is_conflict_range() && !inConflictRange) {
2017-05-26 04:48:44 +08:00
conflictBegin = it.beginKey();
inConflictRange = true;
} else if (!it.is_conflict_range() && inConflictRange) {
result->insert(KeyRangeRef(conflictBegin.toArenaOrRef(arena), it.beginKey().toArenaOrRef(arena)), true);
2017-05-26 04:48:44 +08:00
inConflictRange = false;
}
}
if (inConflictRange) {
result->insert(KeyRangeRef(conflictBegin.toArenaOrRef(arena), getMaxWriteKey()), true);
2017-05-26 04:48:44 +08:00
}
}
2020-12-04 06:06:11 +08:00
void ReadYourWritesTransaction::setTransactionID(uint64_t id) {
tr.setTransactionID(id);
}
void ReadYourWritesTransaction::setToken(uint64_t token) {
tr.setToken(token);
}
2021-05-04 04:14:16 +08:00
RangeResult ReadYourWritesTransaction::getReadConflictRangeIntersecting(KeyRangeRef kr) {
2020-05-08 04:26:41 +08:00
TEST(true); // Special keys read conflict range
2020-04-30 05:43:37 +08:00
ASSERT(readConflictRangeKeysRange.contains(kr));
ASSERT(!tr.trState->options.checkWritesEnabled);
2021-05-04 04:14:16 +08:00
RangeResult result;
if (!options.readYourWritesDisabled) {
2020-04-30 05:43:37 +08:00
kr = kr.removePrefix(readConflictRangeKeysRange.begin);
auto iter = readConflicts.rangeContainingKeyBefore(kr.begin);
if (iter->begin() == allKeys.begin && !iter->value()) {
++iter; // Conventionally '' is missing from the result range if it's not part of a read conflict
}
for (; iter->begin() < kr.end; ++iter) {
if (kr.begin <= iter->begin() && iter->begin() < kr.end) {
2020-04-30 05:43:37 +08:00
result.push_back(result.arena(),
KeyValueRef(iter->begin().withPrefix(readConflictRangeKeysRange.begin, result.arena()),
iter->value() ? LiteralStringRef("1") : LiteralStringRef("0")));
}
}
} else {
CoalescedKeyRefRangeMap<ValueRef> readConflicts{ LiteralStringRef("0"), specialKeys.end };
for (const auto& range : tr.readConflictRanges())
2020-04-30 05:43:37 +08:00
readConflicts.insert(range.withPrefix(readConflictRangeKeysRange.begin, result.arena()),
LiteralStringRef("1"));
for (const auto& range : nativeReadRanges)
readConflicts.insert(range.withPrefix(readConflictRangeKeysRange.begin, result.arena()),
LiteralStringRef("1"));
for (const auto& f : tr.getExtraReadConflictRanges()) {
if (f.isReady() && f.get().first < f.get().second)
readConflicts.insert(KeyRangeRef(f.get().first, f.get().second)
.withPrefix(readConflictRangeKeysRange.begin, result.arena()),
LiteralStringRef("1"));
}
auto beginIter = readConflicts.rangeContaining(kr.begin);
if (beginIter->begin() != kr.begin)
++beginIter;
for (auto it = beginIter; it->begin() < kr.end; ++it) {
result.push_back(result.arena(), KeyValueRef(it->begin(), it->value()));
2020-04-29 01:34:10 +08:00
}
}
return result;
}
2021-05-04 04:14:16 +08:00
RangeResult ReadYourWritesTransaction::getWriteConflictRangeIntersecting(KeyRangeRef kr) {
2020-05-08 04:26:41 +08:00
TEST(true); // Special keys write conflict range
2020-04-30 05:43:37 +08:00
ASSERT(writeConflictRangeKeysRange.contains(kr));
2021-05-04 04:14:16 +08:00
RangeResult result;
// Memory owned by result
CoalescedKeyRefRangeMap<ValueRef> writeConflicts{ LiteralStringRef("0"), specialKeys.end };
if (!options.readYourWritesDisabled) {
KeyRangeRef strippedWriteRangePrefix = kr.removePrefix(writeConflictRangeKeysRange.begin);
WriteMap::iterator it(&writes);
it.skip(strippedWriteRangePrefix.begin);
if (it.beginKey() > allKeys.begin)
--it;
for (; it.beginKey() < strippedWriteRangePrefix.end; ++it) {
if (it.is_conflict_range())
writeConflicts.insert(
KeyRangeRef(it.beginKey().toArena(result.arena()), it.endKey().toArena(result.arena()))
.withPrefix(writeConflictRangeKeysRange.begin, result.arena()),
LiteralStringRef("1"));
}
} else {
for (const auto& range : tr.writeConflictRanges())
writeConflicts.insert(range.withPrefix(writeConflictRangeKeysRange.begin, result.arena()),
LiteralStringRef("1"));
for (const auto& range : nativeWriteRanges)
writeConflicts.insert(range.withPrefix(writeConflictRangeKeysRange.begin, result.arena()),
LiteralStringRef("1"));
}
for (const auto& k : versionStampKeys) {
KeyRange range;
if (versionStampFuture.isValid() && versionStampFuture.isReady() && !versionStampFuture.isError()) {
const auto& stamp = versionStampFuture.get();
StringRef key(range.arena(), k); // Copy
ASSERT(k.size() >= 4);
int32_t pos;
memcpy(&pos, k.end() - sizeof(int32_t), sizeof(int32_t));
pos = littleEndian32(pos);
ASSERT(pos >= 0 && pos + stamp.size() <= key.size());
memcpy(mutateString(key) + pos, stamp.begin(), stamp.size());
*(mutateString(key) + key.size() - 4) = '\x00';
// singleKeyRange, but share begin and end's memory
range = KeyRangeRef(key.substr(0, key.size() - 4), key.substr(0, key.size() - 3));
} else {
range = getVersionstampKeyRange(result.arena(), k, tr.getCachedReadVersion().orDefault(0), getMaxReadKey());
}
writeConflicts.insert(range.withPrefix(writeConflictRangeKeysRange.begin, result.arena()),
LiteralStringRef("1"));
}
auto beginIter = writeConflicts.rangeContaining(kr.begin);
if (beginIter->begin() != kr.begin)
++beginIter;
for (auto it = beginIter; it->begin() < kr.end; ++it) {
result.push_back(result.arena(), KeyValueRef(it->begin(), it->value()));
}
return result;
}
void ReadYourWritesTransaction::atomicOp(const KeyRef& key, const ValueRef& operand, uint32_t operationType) {
2021-07-03 12:41:50 +08:00
AddConflictRange addWriteConflict{ !options.getAndResetWriteConflictDisabled() };
2017-05-26 04:48:44 +08:00
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
throw used_during_commit();
}
if (key == metadataVersionKey) {
if (operationType != MutationRef::SetVersionstampedValue || operand != metadataVersionRequiredValue) {
throw client_invalid_operation();
}
} else if (key >= getMaxWriteKey()) {
2017-05-26 04:48:44 +08:00
throw key_outside_legal_range();
}
2017-05-26 04:48:44 +08:00
if (!isValidMutationType(operationType) || !isAtomicOp((MutationRef::Type)operationType))
2017-05-26 04:48:44 +08:00
throw invalid_mutation_type();
if (key.size() >
(key.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
2017-05-26 04:48:44 +08:00
throw key_too_large();
if (operand.size() > CLIENT_KNOBS->VALUE_SIZE_LIMIT)
2017-05-26 04:48:44 +08:00
throw value_too_large();
if (tr.apiVersionAtLeast(510)) {
if (operationType == MutationRef::Min)
operationType = MutationRef::MinV2;
else if (operationType == MutationRef::And)
operationType = MutationRef::AndV2;
}
KeyRef k;
if (!tr.apiVersionAtLeast(520) && operationType == MutationRef::SetVersionstampedKey) {
k = key.withSuffix(LiteralStringRef("\x00\x00"), arena);
} else {
k = KeyRef(arena, key);
}
ValueRef v;
if (!tr.apiVersionAtLeast(520) && operationType == MutationRef::SetVersionstampedValue) {
v = operand.withSuffix(LiteralStringRef("\x00\x00\x00\x00"), arena);
} else {
v = ValueRef(arena, operand);
}
if (operationType == MutationRef::SetVersionstampedKey) {
TEST(options.readYourWritesDisabled); // SetVersionstampedKey without ryw enabled
// this does validation of the key and needs to be performed before the readYourWritesDisabled path
KeyRangeRef range = getVersionstampKeyRange(arena, k, tr.getCachedReadVersion().orDefault(0), getMaxReadKey());
versionStampKeys.push_back(arena, k);
addWriteConflict = AddConflictRange::False;
if (!options.readYourWritesDisabled) {
2017-05-26 04:48:44 +08:00
writeRangeToNativeTransaction(range);
writes.addUnmodifiedAndUnreadableRange(range);
}
// k is the unversionstamped key provided by the user. If we've filled in a minimum bound
// for the versionstamp, we need to make sure that's reflected when we insert it into the
// WriteMap below.
transformVersionstampKey(k, tr.getCachedReadVersion().orDefault(0), 0);
2017-05-26 04:48:44 +08:00
}
if (operationType == MutationRef::SetVersionstampedValue) {
if (v.size() < 4)
throw client_invalid_operation();
int32_t pos;
2018-05-24 06:32:56 +08:00
memcpy(&pos, v.end() - sizeof(int32_t), sizeof(int32_t));
pos = littleEndian32(pos);
2018-05-24 06:32:56 +08:00
if (pos < 0 || pos + 10 > v.size() - 4)
throw client_invalid_operation();
}
approximateSize += k.expectedSize() + v.expectedSize() + sizeof(MutationRef) +
(addWriteConflict ? sizeof(KeyRangeRef) + 2 * key.expectedSize() + 1 : 0);
if (options.readYourWritesDisabled) {
return tr.atomicOp(k, v, (MutationRef::Type)operationType, addWriteConflict);
2017-05-26 04:48:44 +08:00
}
writes.mutate(k, (MutationRef::Type)operationType, v, addWriteConflict);
2018-05-24 06:32:56 +08:00
RYWImpl::triggerWatches(this, k, Optional<ValueRef>(), false);
2017-05-26 04:48:44 +08:00
}
void ReadYourWritesTransaction::set(const KeyRef& key, const ValueRef& value) {
if (key == metadataVersionKey) {
throw client_invalid_operation();
}
if (specialKeys.contains(key)) {
if (getDatabase()->apiVersionAtLeast(700)) {
return getDatabase()->specialKeySpace->set(this, key, value);
} else {
// These three special keys are deprecated in 7.0 and an alternative C API is added
2020-08-01 16:09:45 +08:00
// TODO : Rewrite related code using C api
if (key == LiteralStringRef("\xff\xff/reboot_worker")) {
BinaryReader::fromStringRef<ClientWorkerInterface>(value, IncludeVersion())
.reboot.send(RebootRequest());
return;
}
if (key == LiteralStringRef("\xff\xff/suspend_worker")) {
2020-08-01 16:09:45 +08:00
BinaryReader::fromStringRef<ClientWorkerInterface>(value, IncludeVersion())
.reboot.send(RebootRequest(false, false, options.timeoutInSeconds));
return;
}
if (key == LiteralStringRef("\xff\xff/reboot_and_check_worker")) {
BinaryReader::fromStringRef<ClientWorkerInterface>(value, IncludeVersion())
.reboot.send(RebootRequest(false, true));
return;
}
}
}
2021-07-03 12:41:50 +08:00
AddConflictRange addWriteConflict{ !options.getAndResetWriteConflictDisabled() };
2017-05-26 04:48:44 +08:00
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
throw used_during_commit();
}
if (key >= getMaxWriteKey())
2017-05-26 04:48:44 +08:00
throw key_outside_legal_range();
2019-07-13 07:53:37 +08:00
approximateSize += key.expectedSize() + value.expectedSize() + sizeof(MutationRef) +
(addWriteConflict ? sizeof(KeyRangeRef) + 2 * key.expectedSize() + 1 : 0);
2019-07-10 07:09:03 +08:00
if (options.readYourWritesDisabled) {
2017-05-26 04:48:44 +08:00
return tr.set(key, value, addWriteConflict);
}
// TODO: check transaction size here
if (key.size() >
(key.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
2017-05-26 04:48:44 +08:00
throw key_too_large();
if (value.size() > CLIENT_KNOBS->VALUE_SIZE_LIMIT)
2017-05-26 04:48:44 +08:00
throw value_too_large();
KeyRef k = KeyRef(arena, key);
ValueRef v = ValueRef(arena, value);
2017-05-26 04:48:44 +08:00
writes.mutate(k, MutationRef::SetValue, v, addWriteConflict);
RYWImpl::triggerWatches(this, key, value);
}
void ReadYourWritesTransaction::clear(const KeyRangeRef& range) {
2021-07-03 12:41:50 +08:00
AddConflictRange addWriteConflict{ !options.getAndResetWriteConflictDisabled() };
2017-05-26 04:48:44 +08:00
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
throw used_during_commit();
}
if (specialKeys.contains(range)) {
if (getDatabase()->apiVersionAtLeast(700)) {
return getDatabase()->specialKeySpace->clear(this, range);
}
}
2017-05-26 04:48:44 +08:00
KeyRef maxKey = getMaxWriteKey();
if (range.begin > maxKey || range.end > maxKey)
2017-05-26 04:48:44 +08:00
throw key_outside_legal_range();
2019-07-13 07:53:37 +08:00
approximateSize += range.expectedSize() + sizeof(MutationRef) +
(addWriteConflict ? sizeof(KeyRangeRef) + range.expectedSize() : 0);
if (options.readYourWritesDisabled) {
2017-05-26 04:48:44 +08:00
return tr.clear(range, addWriteConflict);
}
2019-07-13 07:53:37 +08:00
// There aren't any keys in the database with size larger than KEY_SIZE_LIMIT, so if range contains large keys
// we can translate it to an equivalent one with smaller keys
2017-05-26 04:48:44 +08:00
KeyRef begin = range.begin;
KeyRef end = range.end;
if (begin.size() >
(begin.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
begin = begin.substr(
0,
(begin.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT) +
1);
if (end.size() >
(end.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
end = end.substr(
0,
(end.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT) +
1);
2017-05-26 04:48:44 +08:00
KeyRangeRef r = KeyRangeRef(begin, end);
if (r.empty()) {
2017-05-26 04:48:44 +08:00
return;
}
r = KeyRangeRef(arena, r);
2017-05-26 04:48:44 +08:00
writes.clear(r, addWriteConflict);
RYWImpl::triggerWatches(this, r, Optional<ValueRef>());
}
void ReadYourWritesTransaction::clear(const KeyRef& key) {
2021-07-03 12:41:50 +08:00
AddConflictRange addWriteConflict{ !options.getAndResetWriteConflictDisabled() };
2017-05-26 04:48:44 +08:00
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
throw used_during_commit();
}
if (specialKeys.contains(key)) {
if (getDatabase()->apiVersionAtLeast(700)) {
return getDatabase()->specialKeySpace->clear(this, key);
}
}
if (key >= getMaxWriteKey())
2017-05-26 04:48:44 +08:00
throw key_outside_legal_range();
if (key.size() >
(key.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
2017-05-26 04:48:44 +08:00
return;
if (options.readYourWritesDisabled) {
2017-05-26 04:48:44 +08:00
return tr.clear(key, addWriteConflict);
}
KeyRangeRef r = singleKeyRange(key, arena);
approximateSize +=
r.expectedSize() + sizeof(KeyRangeRef) + (addWriteConflict ? sizeof(KeyRangeRef) + r.expectedSize() : 0);
// SOMEDAY: add an optimized single key clear to write map
2017-05-26 04:48:44 +08:00
writes.clear(r, addWriteConflict);
RYWImpl::triggerWatches(this, r, Optional<ValueRef>());
}
Future<Void> ReadYourWritesTransaction::watch(const Key& key) {
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
return used_during_commit();
}
if (resetPromise.isSet())
2017-05-26 04:48:44 +08:00
return resetPromise.getFuture().getError();
if (options.readYourWritesDisabled)
2017-05-26 04:48:44 +08:00
return watches_disabled();
if (key >= allKeys.end || (key >= getMaxReadKey() && key != metadataVersionKey && tr.apiVersionAtLeast(300)))
2017-05-26 04:48:44 +08:00
return key_outside_legal_range();
if (key.size() >
(key.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
2017-05-26 04:48:44 +08:00
return key_too_large();
return RYWImpl::watch(this, key);
}
void ReadYourWritesTransaction::addWriteConflictRange(KeyRangeRef const& keys) {
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
throw used_during_commit();
}
if (tr.apiVersionAtLeast(300)) {
if (keys.begin > getMaxWriteKey() || keys.end > getMaxWriteKey()) {
throw key_outside_legal_range();
}
}
// There aren't any keys in the database with size larger than KEY_SIZE_LIMIT, so if range contains large keys
// we can translate it to an equivalent one with smaller keys
2017-05-26 04:48:44 +08:00
KeyRef begin = keys.begin;
KeyRef end = keys.end;
if (begin.size() >
(begin.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
begin = begin.substr(
0,
(begin.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT) +
1);
if (end.size() >
(end.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT))
end = end.substr(
0,
(end.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT) +
1);
2017-05-26 04:48:44 +08:00
KeyRangeRef r = KeyRangeRef(begin, end);
if (r.empty()) {
2017-05-26 04:48:44 +08:00
return;
}
2019-07-13 07:53:37 +08:00
approximateSize += r.expectedSize() + sizeof(KeyRangeRef);
if (options.readYourWritesDisabled) {
2017-05-26 04:48:44 +08:00
tr.addWriteConflictRange(r);
return;
}
r = KeyRangeRef(arena, r);
2017-05-26 04:48:44 +08:00
writes.addConflictRange(r);
}
Future<Void> ReadYourWritesTransaction::commit() {
if (checkUsedDuringCommit()) {
2017-05-26 04:48:44 +08:00
return used_during_commit();
}
if (resetPromise.isSet())
2017-05-26 04:48:44 +08:00
return resetPromise.getFuture().getError();
return RYWImpl::commit(this);
2017-05-26 04:48:44 +08:00
}
Future<Standalone<StringRef>> ReadYourWritesTransaction::getVersionstamp() {
if (checkUsedDuringCommit()) {
return used_during_commit();
}
return waitOrError(tr.getVersionstamp(), resetPromise.getFuture());
}
void ReadYourWritesTransaction::setOption(FDBTransactionOptions::Option option, Optional<StringRef> value) {
setOptionImpl(option, value);
if (FDBTransactionOptions::optionInfo.getMustExist(option).persistent) {
persistentOptions.emplace_back(option, value.castTo<Standalone<StringRef>>());
}
}
void ReadYourWritesTransaction::setOptionImpl(FDBTransactionOptions::Option option, Optional<StringRef> value) {
switch (option) {
case FDBTransactionOptions::READ_YOUR_WRITES_DISABLE:
validateOptionValueNotPresent(value);
2017-05-26 04:48:44 +08:00
if (!reading.isReady() || !cache.empty() || !writes.empty())
throw client_invalid_operation();
2017-05-26 04:48:44 +08:00
options.readYourWritesDisabled = true;
break;
2017-05-26 04:48:44 +08:00
case FDBTransactionOptions::READ_AHEAD_DISABLE:
validateOptionValueNotPresent(value);
2017-05-26 04:48:44 +08:00
options.readAheadDisabled = true;
break;
2017-05-26 04:48:44 +08:00
case FDBTransactionOptions::NEXT_WRITE_NO_WRITE_CONFLICT_RANGE:
validateOptionValueNotPresent(value);
2017-05-26 04:48:44 +08:00
options.nextWriteDisableConflictRange = true;
break;
2017-05-26 04:48:44 +08:00
case FDBTransactionOptions::ACCESS_SYSTEM_KEYS:
validateOptionValueNotPresent(value);
2017-05-26 04:48:44 +08:00
options.readSystemKeys = true;
options.writeSystemKeys = true;
break;
2017-05-26 04:48:44 +08:00
case FDBTransactionOptions::READ_SYSTEM_KEYS:
validateOptionValueNotPresent(value);
2017-05-26 04:48:44 +08:00
options.readSystemKeys = true;
break;
2017-05-26 04:48:44 +08:00
case FDBTransactionOptions::TIMEOUT:
options.timeoutInSeconds = extractIntOption(value, 0, std::numeric_limits<int>::max()) / 1000.0;
resetTimeout();
break;
2017-05-26 04:48:44 +08:00
case FDBTransactionOptions::RETRY_LIMIT:
options.maxRetries = (int)extractIntOption(value, -1, std::numeric_limits<int>::max());
break;
case FDBTransactionOptions::DEBUG_RETRY_LOGGING:
options.debugRetryLogging = true;
if (!transactionDebugInfo) {
transactionDebugInfo = Reference<TransactionDebugInfo>::addRef(new TransactionDebugInfo());
transactionDebugInfo->lastRetryLogTime = creationTime;
}
transactionDebugInfo->transactionName = value.present() ? value.get().toString() : "";
break;
case FDBTransactionOptions::SNAPSHOT_RYW_ENABLE:
validateOptionValueNotPresent(value);
options.snapshotRywEnabled++;
break;
case FDBTransactionOptions::SNAPSHOT_RYW_DISABLE:
validateOptionValueNotPresent(value);
options.snapshotRywEnabled--;
break;
case FDBTransactionOptions::USED_DURING_COMMIT_PROTECTION_DISABLE:
validateOptionValueNotPresent(value);
2017-05-26 04:48:44 +08:00
options.disableUsedDuringCommitProtection = true;
break;
case FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED:
validateOptionValueNotPresent(value);
options.specialKeySpaceRelaxed = true;
break;
case FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES:
validateOptionValueNotPresent(value);
options.specialKeySpaceChangeConfiguration = true;
break;
case FDBTransactionOptions::BYPASS_UNREADABLE:
validateOptionValueNotPresent(value);
options.bypassUnreadable = true;
break;
default:
break;
}
tr.setOption(option, value);
2017-05-26 04:48:44 +08:00
}
2020-06-10 08:33:41 +08:00
void ReadYourWritesTransaction::operator=(ReadYourWritesTransaction&& r) noexcept {
cache = std::move(r.cache);
writes = std::move(r.writes);
arena = std::move(r.arena);
tr = std::move(r.tr);
readConflicts = std::move(r.readConflicts);
watchMap = std::move(r.watchMap);
reading = std::move(r.reading);
resetPromise = std::move(r.resetPromise);
2017-05-26 04:48:44 +08:00
r.resetPromise = Promise<Void>();
deferredError = std::move(r.deferredError);
2017-05-26 04:48:44 +08:00
retries = r.retries;
approximateSize = r.approximateSize;
2017-05-26 04:48:44 +08:00
timeoutActor = r.timeoutActor;
creationTime = r.creationTime;
commitStarted = r.commitStarted;
options = r.options;
transactionDebugInfo = r.transactionDebugInfo;
cache.arena = &arena;
writes.arena = &arena;
persistentOptions = std::move(r.persistentOptions);
nativeReadRanges = std::move(r.nativeReadRanges);
nativeWriteRanges = std::move(r.nativeWriteRanges);
versionStampKeys = std::move(r.versionStampKeys);
specialKeySpaceWriteMap = std::move(r.specialKeySpaceWriteMap);
2017-05-26 04:48:44 +08:00
}
2020-06-10 08:33:41 +08:00
ReadYourWritesTransaction::ReadYourWritesTransaction(ReadYourWritesTransaction&& r) noexcept
: ISingleThreadTransaction(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) {
2017-05-26 04:48:44 +08:00
cache.arena = &arena;
writes.arena = &arena;
tr = std::move(r.tr);
2017-05-26 04:48:44 +08:00
readConflicts = std::move(r.readConflicts);
watchMap = std::move(r.watchMap);
2017-05-26 04:48:44 +08:00
r.resetPromise = Promise<Void>();
persistentOptions = std::move(r.persistentOptions);
nativeReadRanges = std::move(r.nativeReadRanges);
nativeWriteRanges = std::move(r.nativeWriteRanges);
versionStampKeys = std::move(r.versionStampKeys);
specialKeySpaceWriteMap = std::move(r.specialKeySpaceWriteMap);
2017-05-26 04:48:44 +08:00
}
Future<Void> ReadYourWritesTransaction::onError(Error const& e) {
return RYWImpl::onError(this, e);
2017-05-26 04:48:44 +08:00
}
void ReadYourWritesTransaction::applyPersistentOptions() {
Optional<StringRef> timeout;
for (auto option : persistentOptions) {
if (option.first == FDBTransactionOptions::TIMEOUT) {
timeout = option.second.castTo<StringRef>();
} else {
setOptionImpl(option.first, option.second.castTo<StringRef>());
}
}
// Setting a timeout can immediately cause a transaction to fail. The only timeout
2019-06-29 05:17:25 +08:00
// that matters is the one most recently set, so we ignore any earlier set timeouts
// that might inadvertently fail the transaction.
if (timeout.present()) {
setOptionImpl(FDBTransactionOptions::TIMEOUT, timeout);
}
}
2017-05-26 04:48:44 +08:00
void ReadYourWritesTransaction::resetRyow() {
Promise<Void> oldReset = resetPromise;
resetPromise = Promise<Void>();
2017-05-26 04:48:44 +08:00
timeoutActor.cancel();
arena = Arena();
cache = SnapshotCache(&arena);
writes = WriteMap(&arena);
readConflicts = CoalescedKeyRefRangeMap<bool>();
versionStampKeys = VectorRef<KeyRef>();
nativeReadRanges = Standalone<VectorRef<KeyRangeRef>>();
nativeWriteRanges = Standalone<VectorRef<KeyRangeRef>>();
specialKeySpaceWriteMap =
KeyRangeMap<std::pair<bool, Optional<Value>>>(std::make_pair(false, Optional<Value>()), specialKeys.end);
specialKeySpaceErrorMsg.reset();
2017-05-26 04:48:44 +08:00
watchMap.clear();
reading = AndFuture();
approximateSize = 0;
2017-05-26 04:48:44 +08:00
commitStarted = false;
deferredError = Error();
2017-05-26 04:48:44 +08:00
if (tr.apiVersionAtLeast(16)) {
2017-05-26 04:48:44 +08:00
options.reset(tr);
applyPersistentOptions();
2017-05-26 04:48:44 +08:00
}
if (!oldReset.isSet())
2017-05-26 04:48:44 +08:00
oldReset.sendError(transaction_cancelled());
}
void ReadYourWritesTransaction::cancel() {
if (!resetPromise.isSet())
2017-05-26 04:48:44 +08:00
resetPromise.sendError(transaction_cancelled());
}
void ReadYourWritesTransaction::reset() {
retries = 0;
approximateSize = 0;
2017-05-26 04:48:44 +08:00
creationTime = now();
timeoutActor.cancel();
persistentOptions.clear();
options.reset(tr);
2017-05-26 04:48:44 +08:00
transactionDebugInfo.clear();
tr.fullReset();
versionStampFuture = tr.getVersionstamp();
std::copy(tr.getDatabase().getTransactionDefaults().begin(),
tr.getDatabase().getTransactionDefaults().end(),
std::back_inserter(persistentOptions));
2017-05-26 04:48:44 +08:00
resetRyow();
}
KeyRef ReadYourWritesTransaction::getMaxReadKey() {
if (options.readSystemKeys)
2017-05-26 04:48:44 +08:00
return systemKeys.end;
else
return normalKeys.end;
}
KeyRef ReadYourWritesTransaction::getMaxWriteKey() {
if (options.writeSystemKeys)
2017-05-26 04:48:44 +08:00
return systemKeys.end;
else
return normalKeys.end;
}
ReadYourWritesTransaction::~ReadYourWritesTransaction() {
if (!resetPromise.isSet())
2017-05-26 04:48:44 +08:00
resetPromise.sendError(transaction_cancelled());
}
bool ReadYourWritesTransaction::checkUsedDuringCommit() {
if (commitStarted && !resetPromise.isSet() && !options.disableUsedDuringCommitProtection) {
2017-05-26 04:48:44 +08:00
resetPromise.sendError(used_during_commit());
}
return commitStarted;
}
void ReadYourWritesTransaction::debugLogRetries(Optional<Error> error) {
bool committed = !error.present();
if (options.debugRetryLogging) {
2017-05-26 04:48:44 +08:00
double timeSinceLastLog = now() - transactionDebugInfo->lastRetryLogTime;
double elapsed = now() - creationTime;
if (timeSinceLastLog >= 1 || (committed && elapsed > 1)) {
2017-05-26 04:48:44 +08:00
std::string transactionNameStr = "";
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
fprintf(stderr,
"fdb WARNING: long transaction (%.2fs elapsed%s, %d retries, %s)\n",
elapsed,
transactionNameStr.c_str(),
retries,
committed ? "committed" : error.get().what());
2017-05-26 04:48:44 +08:00
{
TraceEvent trace = TraceEvent("LongTransaction");
if (error.present())
trace.errorUnsuppressed(error.get());
if (!transactionDebugInfo->transactionName.empty())
2019-04-06 04:11:50 +08:00
trace.detail("TransactionName", transactionDebugInfo->transactionName);
2017-05-26 04:48:44 +08:00
trace.detail("Elapsed", elapsed).detail("Retries", retries).detail("Committed", committed);
}
transactionDebugInfo->lastRetryLogTime = now();
}
}
}