From 14cca75429733f59f7b3ca05ba730533bcd9046f Mon Sep 17 00:00:00 2001 From: Alec Grieser Date: Thu, 15 Feb 2018 21:35:00 -0800 Subject: [PATCH 1/7] server components of version of alternative versionstamp op that writes to an arbitrary place in the value --- fdbclient/Atomic.h | 17 ++++++++++++++++- fdbclient/CommitTransaction.h | 8 ++++---- fdbclient/RYWIterator.h | 16 +++++++++++++++- fdbclient/ReadYourWrites.actor.cpp | 13 ++++++++++++- fdbclient/WriteMap.h | 8 ++++---- fdbserver/MasterProxyServer.actor.cpp | 2 ++ .../workloads/FuzzApiCorrectness.actor.cpp | 16 +++++++++++----- fdbserver/workloads/Unreadable.actor.cpp | 15 +++++++++++---- fdbserver/workloads/VersionStamp.actor.cpp | 14 +++++++++++++- 9 files changed, 88 insertions(+), 21 deletions(-) diff --git a/fdbclient/Atomic.h b/fdbclient/Atomic.h index 105c9c9f8f4..70143a78e44 100644 --- a/fdbclient/Atomic.h +++ b/fdbclient/Atomic.h @@ -279,4 +279,19 @@ static void transformSetVersionstampedValue( MutationRef& mutation, Version vers mutation.type = MutationRef::SetValue; } -#endif \ No newline at end of file +static void transformSetVersionstampedValuePos( MutationRef& mutation, Version version, uint16_t transactionNumber ) { + if (mutation.param2.size() >= 4) { + int32_t pos; + memcpy(&pos, mutation.param2.end() - sizeof(int32_t), sizeof(int32_t)); + pos = littleEndian32(pos); + mutation.param2 = mutation.param2.substr(0, mutation.param2.size() - 4); + + if (pos >= 0 && pos + 10 <= mutation.param2.size()) { + placeVersionstamp( mutateString(mutation.param2) + pos, version, transactionNumber ); + } + } + + mutation.type = MutationRef::SetValue; +} + +#endif diff --git a/fdbclient/CommitTransaction.h b/fdbclient/CommitTransaction.h index 8064849df19..911869bf252 100644 --- a/fdbclient/CommitTransaction.h +++ b/fdbclient/CommitTransaction.h @@ -24,11 +24,11 @@ #include "FDBTypes.h" -static const char * typeString[] = { "SetValue", "ClearRange", "AddValue", "DebugKeyRange", "DebugKey", "NoOp", "And", "Or", "Xor", "AppendIfFits", "AvailableForReuse", "Reserved_For_LogProtocolMessage", "Max", "Min", "SetVersionstampedKey", "SetVersionstampedValue", "ByteMin", "ByteMax", "MinV2", "AndV2" }; +static const char * typeString[] = { "SetValue", "ClearRange", "AddValue", "DebugKeyRange", "DebugKey", "NoOp", "And", "Or", "Xor", "AppendIfFits", "AvailableForReuse", "Reserved_For_LogProtocolMessage", "Max", "Min", "SetVersionstampedKey", "SetVersionstampedValue", "ByteMin", "ByteMax", "MinV2", "AndV2", "SetVersionstampedValuePos" }; struct MutationRef { static const int OVERHEAD_BYTES = 12; //12 is the size of Header in MutationList entries - enum Type : uint8_t { SetValue=0, ClearRange, AddValue, DebugKeyRange, DebugKey, NoOp, And, Or, Xor, AppendIfFits, AvailableForReuse, Reserved_For_LogProtocolMessage /* See fdbserver/LogProtocolMessage.h */, Max, Min, SetVersionstampedKey, SetVersionstampedValue, ByteMin, ByteMax, MinV2, AndV2, MAX_ATOMIC_OP }; + enum Type : uint8_t { SetValue=0, ClearRange, AddValue, DebugKeyRange, DebugKey, NoOp, And, Or, Xor, AppendIfFits, AvailableForReuse, Reserved_For_LogProtocolMessage /* See fdbserver/LogProtocolMessage.h */, Max, Min, SetVersionstampedKey, SetVersionstampedValue, ByteMin, ByteMax, MinV2, AndV2, SetVersionstampedValuePos, MAX_ATOMIC_OP }; // This is stored this way for serialization purposes. uint8_t type; StringRef param1, param2; @@ -55,9 +55,9 @@ struct MutationRef { // These masks define which mutation types have particular properties (they are used to implement isSingleKeyMutation() etc) enum { - ATOMIC_MASK = (1 << AddValue) | (1 << And) | (1 << Or) | (1 << Xor) | (1 << AppendIfFits) | (1 << Max) | (1 << Min) | (1 << SetVersionstampedKey) | (1 << SetVersionstampedValue) | (1 << ByteMin) | (1 << ByteMax) | (1 << MinV2) | (1 << AndV2), + ATOMIC_MASK = (1 << AddValue) | (1 << And) | (1 << Or) | (1 << Xor) | (1 << AppendIfFits) | (1 << Max) | (1 << Min) | (1 << SetVersionstampedKey) | (1 << SetVersionstampedValue) | (1 << ByteMin) | (1 << ByteMax) | (1 << MinV2) | (1 << AndV2) | (1 << SetVersionstampedValuePos), SINGLE_KEY_MASK = ATOMIC_MASK | (1<randomInt(10, 98); + std::string value = std::string(len, 'x'); + int pos = g_random->randomInt(0, len - 9); + if (g_random->random01() < 0.01) { + pos = value.size() - 10; + } + value += (char)(pos & 0xFF); + value += (char)((pos >> 8) & 0xFF); + value += (char)((pos >> 16) & 0xFF); + value += (char)((pos >> 24) & 0xFF); + return ValueRef(arena, value); + } + static ValueRef getRandomVersionstampKey(Arena& arena) { int idx = g_random->randomInt(0, 100); std::string key = format("%010d", idx / 3); @@ -132,4 +146,4 @@ class RandomTestImpl { void testESR(); void testSnapshotCache(); -#endif \ No newline at end of file +#endif diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index 5452215d941..b143b52c296 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1423,6 +1423,7 @@ void ReadYourWritesTransaction::writeRangeToNativeTransaction( KeyRangeRef const case MutationRef::Min: case MutationRef::SetVersionstampedKey: case MutationRef::SetVersionstampedValue: + case MutationRef::SetVersionstampedValuePos: case MutationRef::ByteMin: case MutationRef::ByteMax: case MutationRef::MinV2: @@ -1495,8 +1496,18 @@ void ReadYourWritesTransaction::atomicOp( const KeyRef& key, const ValueRef& ope } } - if (operationType == MutationRef::SetVersionstampedValue && operand.size() < 10) + if(operationType == MutationRef::SetVersionstampedValue && operand.size() < 10) throw client_invalid_operation(); + + if(operationType == MutationRef::SetVersionstampedValuePos) { + if(operand.size() < 4) + throw client_invalid_operation(); + int32_t pos; + memcpy(&pos, operand.end() - sizeof(int32_t), sizeof(int32_t)); + pos = littleEndian32(pos); + if (pos < 0 || pos + 10 > operand.size() - 4) + throw client_invalid_operation(); + } if (tr.apiVersionAtLeast(510)) { if (operationType == MutationRef::Min) diff --git a/fdbclient/WriteMap.h b/fdbclient/WriteMap.h index 444ed750eb9..bb930c6aad9 100644 --- a/fdbclient/WriteMap.h +++ b/fdbclient/WriteMap.h @@ -65,7 +65,7 @@ class OperationStack { bool isDependent() const { if( !size() ) return false; - return singletonOperation.type != MutationRef::SetValue && singletonOperation.type != MutationRef::ClearRange && singletonOperation.type != MutationRef::SetVersionstampedValue && singletonOperation.type != MutationRef::SetVersionstampedKey; + return singletonOperation.type != MutationRef::SetValue && singletonOperation.type != MutationRef::ClearRange && singletonOperation.type != MutationRef::SetVersionstampedValue && singletonOperation.type != MutationRef::SetVersionstampedValuePos && singletonOperation.type != MutationRef::SetVersionstampedKey; } const RYWMutation& top() const { return hasVector() ? optionalOperations.get().back() : singletonOperation; } RYWMutation& operator[] (int n) { return (n==0) ? singletonOperation : optionalOperations.get()[n-1]; } @@ -142,8 +142,8 @@ class WriteMap { bool following_conflict = it.entry().following_keys_conflict; bool is_conflict = addConflict || it.is_conflict_range(); bool following_unreadable = it.entry().following_keys_unreadable; - bool is_unreadable = it.is_unreadable() || operation == MutationRef::SetVersionstampedValue || operation == MutationRef::SetVersionstampedKey; - bool is_dependent = operation != MutationRef::SetValue && operation != MutationRef::SetVersionstampedValue && operation != MutationRef::SetVersionstampedKey; + bool is_unreadable = it.is_unreadable() || operation == MutationRef::SetVersionstampedValue || operation == MutationRef::SetVersionstampedValuePos || operation == MutationRef::SetVersionstampedKey; + bool is_dependent = operation != MutationRef::SetValue && operation != MutationRef::SetVersionstampedValue && operation!= MutationRef::SetVersionstampedValuePos && operation != MutationRef::SetVersionstampedKey; if (it.entry().key != key) { if( it.is_cleared_range() && is_dependent ) { @@ -630,4 +630,4 @@ class WriteMap { */ -#endif \ No newline at end of file +#endif diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index 1d8eaed1307..c8787c5a45f 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -248,6 +248,8 @@ struct ResolutionRequestBuilder { trIn.write_conflict_ranges.push_back( requests[0].arena, singleKeyRange( m.param1, requests[0].arena ) ); } else if (m.type == MutationRef::SetVersionstampedValue ) { transformSetVersionstampedValue( m, requests[0].version, transactionNumberInBatch ); + } else if (m.type == MutationRef::SetVersionstampedValuePos ) { + transformSetVersionstampedValuePos( m, requests[0].version, transactionNumberInBatch ); } if (isMetadataMutation(m)) { isTXNStateTransaction = true; diff --git a/fdbserver/workloads/FuzzApiCorrectness.actor.cpp b/fdbserver/workloads/FuzzApiCorrectness.actor.cpp index 6804f94a2fb..61dce7ffeff 100644 --- a/fdbserver/workloads/FuzzApiCorrectness.actor.cpp +++ b/fdbserver/workloads/FuzzApiCorrectness.actor.cpp @@ -821,7 +821,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { Key key; Value value; uint8_t op; - int16_t pos; + int16_t pos16; + int32_t pos32; TestAtomicOp(unsigned int id, FuzzApiCorrectnessWorkload *workload) : BaseTestCallback(id, workload, "TestAtomicOp") { key = makeKey(); @@ -853,9 +854,13 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { op = g_random->randomInt(minval, maxval+1); } - pos = -1; + pos16 = -1; if(op == MutationRef::SetVersionstampedKey && key.size() >= 2) { - pos = littleEndian16(*(int16_t*)&key.end()[-2]); + pos16 = littleEndian16(*(int16_t*)&key.end()[-2]); + } + pos32 = -1; + if(op == MutationRef::SetVersionstampedValuePos && value.size() >= 4) { + pos32 = littleEndian32(*(int32_t*)&value.end()[-4]); } contract = { @@ -867,7 +872,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { (key >= (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) ), std::make_pair( error_code_client_invalid_operation, ExceptionContract::requiredIf( (op == MutationRef::SetVersionstampedValue && value.size() < 10) || - (op == MutationRef::SetVersionstampedKey && (pos < 0 || pos + 10 > key.size() - 2))) ) + (op == MutationRef::SetVersionstampedKey && (pos16 < 0 || pos16 + 10 > key.size() - 2)) || + (op == MutationRef::SetVersionstampedValuePos && (pos32 < 0 || pos32 + 10 > value.size() - 4))) ) }; } @@ -877,7 +883,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { void augmentTrace(TraceEvent &e) const { base_type::augmentTrace(e); - e.detail("key", printable(key)).detail("value", printable(value)).detail("op", op).detail("pos", pos); + e.detail("key", printable(key)).detail("value", printable(value)).detail("op", op).detail("pos16", pos16).detail("pos32", pos32); } }; diff --git a/fdbserver/workloads/Unreadable.actor.cpp b/fdbserver/workloads/Unreadable.actor.cpp index b9307b3140f..3e6653c6ef9 100644 --- a/fdbserver/workloads/Unreadable.actor.cpp +++ b/fdbserver/workloads/Unreadable.actor.cpp @@ -308,7 +308,7 @@ struct UnreadableWorkload : TestWorkload { setMap[normalKeys.end] = ValueRef(); for (; opCount < 500; opCount++) { - int r = g_random->randomInt(0, 19); + int r = g_random->randomInt(0, 20); if (r <= 10) { key = RandomTestImpl::getRandomKey(arena); value = RandomTestImpl::getRandomValue(arena); @@ -341,6 +341,13 @@ struct UnreadableWorkload : TestWorkload { //TraceEvent("RYWT_setVersionstampValue").detail("key", printable(key)); } else if (r == 15) { + key = RandomTestImpl::getRandomKey(arena); + value = RandomTestImpl::getRandomVersionstampValuePos(arena); + tr.atomicOp(key, value, MutationRef::SetVersionstampedValuePos); + unreadableMap.insert(key, true); + //TraceEvent("RYWT_setVersionstampValuePos").detail("key", printable(key)); + } + else if (r == 16) { key = RandomTestImpl::getRandomVersionstampKey(arena); value = RandomTestImpl::getRandomValue(arena); tr.atomicOp(key, value, MutationRef::SetVersionstampedKey); @@ -348,7 +355,7 @@ struct UnreadableWorkload : TestWorkload { unreadableMap.insert(range, true); //TraceEvent("RYWT_setVersionstampKey").detail("range", printable(range)); } - else if (r == 16) { + else if (r == 17) { range = RandomTestImpl::getRandomRange(arena); snapshot = g_random->random01() < 0.05; reverse = g_random->random01() < 0.5; @@ -378,7 +385,7 @@ struct UnreadableWorkload : TestWorkload { setMap[normalKeys.end] = ValueRef(); } } - else if (r == 17) { + else if (r == 18) { begin = RandomTestImpl::getRandomKeySelector(arena); end = RandomTestImpl::getRandomKeySelector(arena); limit = g_random->randomInt(1, 100); // maximum number of results to return from the db @@ -425,7 +432,7 @@ struct UnreadableWorkload : TestWorkload { setMap[normalKeys.end] = ValueRef(); } } - else if (r == 18) { + else if (r == 19) { key = RandomTestImpl::getRandomKey(arena); snapshot = g_random->random01() < 0.05; diff --git a/fdbserver/workloads/VersionStamp.actor.cpp b/fdbserver/workloads/VersionStamp.actor.cpp index b9af8d73daf..a59a214f57d 100644 --- a/fdbserver/workloads/VersionStamp.actor.cpp +++ b/fdbserver/workloads/VersionStamp.actor.cpp @@ -232,11 +232,23 @@ struct VersionStampWorkload : TestWorkload { state KeyRangeRef range(prefix, endOfRange); state Standalone committedVersionStamp; state Version committedVersion; + + bool useVersionstampPos = (g_random->random01() < 0.5); + state MutationRef::Type valueMutation; + state Value versionStampValue; + if(useVersionstampPos) { + versionStampValue = LiteralStringRef("\x00\x00\x00\x00").withPrefix(value); + valueMutation = MutationRef::SetVersionstampedValuePos; + } else { + versionStampValue = value; + valueMutation = MutationRef::SetVersionstampedValue; + } + loop{ state bool error = false; //TraceEvent("VST_commit_begin").detail("key", printable(key)).detail("vsKey", printable(versionStampKey)).detail("clear", printable(range)); try { - tr.atomicOp(key, value, MutationRef::SetVersionstampedValue); + tr.atomicOp(key, versionStampValue, valueMutation); tr.clear(range); tr.atomicOp(versionStampKey, value, MutationRef::SetVersionstampedKey); state Future> fTrVs = tr.getVersionstamp(); From 2167012d83a716f3fcbb50bf5766dde043a914d7 Mon Sep 17 00:00:00 2001 From: Alec Grieser Date: Fri, 23 Feb 2018 18:17:53 -0800 Subject: [PATCH 2/7] use littleEndian16/32 instead of rolling our own --- fdbclient/RYWIterator.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/fdbclient/RYWIterator.h b/fdbclient/RYWIterator.h index 18e05d47ff7..5bff2f63a8d 100644 --- a/fdbclient/RYWIterator.h +++ b/fdbclient/RYWIterator.h @@ -88,14 +88,12 @@ class RandomTestImpl { static ValueRef getRandomVersionstampValuePos(Arena& arena) { int len = g_random->randomInt(10, 98); std::string value = std::string(len, 'x'); - int pos = g_random->randomInt(0, len - 9); + int32_t pos = g_random->randomInt(0, len - 9); if (g_random->random01() < 0.01) { pos = value.size() - 10; } - value += (char)(pos & 0xFF); - value += (char)((pos >> 8) & 0xFF); - value += (char)((pos >> 16) & 0xFF); - value += (char)((pos >> 24) & 0xFF); + pos = littleEndian32(pos); + value += std::string((const char*)&pos, sizeof(int32_t)); return ValueRef(arena, value); } @@ -106,15 +104,15 @@ class RandomTestImpl { key += '\x00'; if (idx % 3 >= 2) key += '\x00'; - int pos = key.size() - g_random->randomInt(0, 3); + int16_t pos = key.size() - g_random->randomInt(0, 3); if (g_random->random01() < 0.01) { pos = 0; } key = key.substr(0, pos); key += "XXXXXXXXYY"; key += std::string(g_random->randomInt(0, 3), 'z'); - key += (char)(pos & 0xFF); - key += (char)((pos >> 8) & 0xFF); + pos = littleEndian16(pos); + key += std::string((const char*)&pos, sizeof(int16_t)); return ValueRef(arena, key); } From 6ee14bbb934ab06c82a9484d1e12ef262faef037 Mon Sep 17 00:00:00 2001 From: Alec Grieser Date: Fri, 2 Mar 2018 16:58:51 -0800 Subject: [PATCH 3/7] expose second versionstamp value type through vexillographer and add support in bindings and bindingtester --- bindings/bindingtester/tests/api.py | 76 +++++++++++++----- bindings/flow/tester/Tester.actor.cpp | 1 + .../apple/foundationdb/subspace/Subspace.java | 35 ++++++++- .../com/apple/foundationdb/tuple/Tuple.java | 77 +++++++++++++++++-- .../apple/foundationdb/tuple/TupleUtil.java | 8 +- .../foundationdb/test/AsyncStackTester.java | 9 ++- .../foundationdb/test/StackOperation.java | 3 +- .../apple/foundationdb/test/StackTester.java | 9 ++- .../test/VersionstampSmokeTest.java | 2 +- bindings/python/fdb/tuple.py | 23 ++++-- bindings/python/tests/tester.py | 20 ++++- fdbclient/vexillographer/fdb.options | 7 +- 12 files changed, 226 insertions(+), 44 deletions(-) diff --git a/bindings/bindingtester/tests/api.py b/bindings/bindingtester/tests/api.py index e31b8636fbd..32cfee63243 100644 --- a/bindings/bindingtester/tests/api.py +++ b/bindings/bindingtester/tests/api.py @@ -40,6 +40,7 @@ def __init__(self, subspace): self.stack_subspace = self.subspace['stack'] self.versionstamped_values = self.scratch['versionstamped_values'] + self.versionstamped_values_2 = self.scratch['versionstamped_values_2'] self.versionstamped_keys = self.scratch['versionstamped_keys'] def setup(self, args): @@ -150,7 +151,7 @@ def generate(self, args, thread_number): snapshot_versions = ['GET_READ_VERSION_SNAPSHOT'] tuples = ['TUPLE_PACK', 'TUPLE_UNPACK', 'TUPLE_RANGE', 'TUPLE_SORT', 'SUB', 'ENCODE_FLOAT', 'ENCODE_DOUBLE', 'DECODE_DOUBLE', 'DECODE_FLOAT'] if 'versionstamp' in args.types: - tuples.append('TUPLE_PACK_WITH_VERSIONSTAMP') + tuples += ['TUPLE_PACK_VERSIONSTAMPED_KEY', 'TUPLE_PACK_VERSIONSTAMPED_VALUE'] resets = ['ON_ERROR', 'RESET', 'CANCEL'] read_conflicts = ['READ_CONFLICT_RANGE', 'READ_CONFLICT_KEY'] write_conflicts = ['WRITE_CONFLICT_RANGE', 'WRITE_CONFLICT_KEY', 'DISABLE_WRITE_CONFLICT'] @@ -349,17 +350,27 @@ def generate(self, args, thread_number): elif op == 'VERSIONSTAMP': rand_str1 = self.random.random_string(100) key1 = self.versionstamped_values.pack((rand_str1,)) + key2 = self.versionstamped_values_2.pack((rand_str1,)) split = random.randint(0, 70) - rand_str2 = self.random.random_string(20 + split) + fdb.tuple.Versionstamp._UNSET_TR_VERSION + self.random.random_string(70 - split) - key2 = self.versionstamped_keys.pack() + rand_str2 - index = key2.find(fdb.tuple.Versionstamp._UNSET_TR_VERSION) - key2 += chr(index % 256) + chr(index / 256) + prefix = self.random.random_string(20 + split) + if prefix.endswith('\xff'): + # Necessary to make sure that the SET_VERSIONSTAMPED_VALUE check + # correctly finds where the version is supposed to fit in. + prefix += '\x00' + suffix = self.random.random_string(70 - split) + rand_str2 = prefix + fdb.tuple.Versionstamp._UNSET_TR_VERSION + suffix + key3 = self.versionstamped_keys.pack() + rand_str2 + index = len(self.versionstamped_keys.pack()) + len(prefix) + key3 += chr(index % 256) + chr(index / 256) instructions.push_args(u'SET_VERSIONSTAMPED_VALUE', key1, fdb.tuple.Versionstamp._UNSET_TR_VERSION + rand_str2) instructions.append('ATOMIC_OP') - instructions.push_args(u'SET_VERSIONSTAMPED_KEY', key2, rand_str1) + instructions.push_args(u'SET_VERSIONSTAMPED_VALUE_POS', key2, rand_str2 + struct.pack('= 0 and second_incomplete < 0: rand_str = self.random.random_string(100) - instructions.push_args(rand_str) - test_util.to_front(instructions, 1) - instructions.push_args(u'SET_VERSIONSTAMPED_KEY') - instructions.append('ATOMIC_OP') + if op == 'TUPLE_PACK_VERSIONSTAMPED_KEY': + instructions.push_args(rand_str) + test_util.to_front(instructions, 1) + instructions.push_args(u'SET_VERSIONSTAMPED_KEY') + instructions.append('ATOMIC_OP') + + version_value_key_2 = self.versionstamped_values_2.pack((rand_str,)) + versionstamped_value = fdb.tuple.pack(tup) + struct.pack(' items, byte[] prefix) { } } - static byte[] packWithVersionstamp(List items, byte[] prefix) { + static byte[] packWithVersionstamp(List items, byte[] prefix, boolean forValue) { List encoded = new ArrayList(2 * items.size() + (prefix == null ? 1 : 2)); EncodeResult result = encodeAll(items, prefix, encoded); if(result.versionPos < 0) { @@ -587,7 +587,11 @@ static byte[] packWithVersionstamp(List items, byte[] prefix) { if(result.versionPos > 0xffff) { throw new IllegalArgumentException("Tuple has incomplete version at position " + result.versionPos + " which is greater than the maximum " + 0xffff); } - encoded.add(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort((short)result.versionPos).array()); + if (forValue) { + encoded.add(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(result.versionPos).array()); + } else { + encoded.add(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort((short)result.versionPos).array()); + } return ByteArrayUtil.join(null, encoded); } } diff --git a/bindings/java/src/test/com/apple/foundationdb/test/AsyncStackTester.java b/bindings/java/src/test/com/apple/foundationdb/test/AsyncStackTester.java index a81df36a73a..990791c668e 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/AsyncStackTester.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/AsyncStackTester.java @@ -360,7 +360,7 @@ else if(op == StackOperation.TUPLE_PACK) { }, FDB.DEFAULT_EXECUTOR); }, FDB.DEFAULT_EXECUTOR); } - else if(op == StackOperation.TUPLE_PACK_WITH_VERSIONSTAMP) { + else if(op == StackOperation.TUPLE_PACK_VERSIONSTAMPED_KEY || op == StackOperation.TUPLE_PACK_VERSIONSTAMPED_VALUE) { return inst.popParams(2).thenComposeAsync(params -> { byte[] prefix = (byte[])params.get(0); int tupleSize = StackUtils.getInt(params.get(1)); @@ -372,7 +372,12 @@ else if(op == StackOperation.TUPLE_PACK_WITH_VERSIONSTAMP) { return; } try { - byte[] coded = tuple.packWithVersionstamp(prefix); + byte[] coded; + if(op == StackOperation.TUPLE_PACK_VERSIONSTAMPED_KEY) { + coded = tuple.packVersionstampedKey(prefix); + } else { + coded = tuple.packVersionstampedValue(prefix); + } inst.push("OK".getBytes()); inst.push(coded); } catch(IllegalArgumentException e) { diff --git a/bindings/java/src/test/com/apple/foundationdb/test/StackOperation.java b/bindings/java/src/test/com/apple/foundationdb/test/StackOperation.java index 7fdc7b9ff5c..894fa3d4f25 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/StackOperation.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/StackOperation.java @@ -60,7 +60,8 @@ enum StackOperation { SUB, CONCAT, TUPLE_PACK, - TUPLE_PACK_WITH_VERSIONSTAMP, + TUPLE_PACK_VERSIONSTAMPED_KEY, + TUPLE_PACK_VERSIONSTAMPED_VALUE, TUPLE_UNPACK, TUPLE_RANGE, TUPLE_SORT, diff --git a/bindings/java/src/test/com/apple/foundationdb/test/StackTester.java b/bindings/java/src/test/com/apple/foundationdb/test/StackTester.java index 2367c1fdc08..9374c0d4d06 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/StackTester.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/StackTester.java @@ -321,7 +321,7 @@ else if(op == StackOperation.TUPLE_PACK) { //System.out.println(inst.context.preStr + " - " + " -> result '" + ByteArrayUtil.printable(coded) + "'"); inst.push(coded); } - else if(op == StackOperation.TUPLE_PACK_WITH_VERSIONSTAMP) { + else if(op == StackOperation.TUPLE_PACK_VERSIONSTAMPED_KEY || op == StackOperation.TUPLE_PACK_VERSIONSTAMPED_VALUE) { List params = inst.popParams(2).join(); byte[] prefix = (byte[])params.get(0); int tupleSize = StackUtils.getInt(params.get(1)); @@ -331,7 +331,12 @@ else if(op == StackOperation.TUPLE_PACK_WITH_VERSIONSTAMP) { inst.push("ERROR: NONE".getBytes()); } else { try { - byte[] coded = tuple.packWithVersionstamp(prefix); + byte[] coded; + if(op == StackOperation.TUPLE_PACK_VERSIONSTAMPED_KEY) { + coded = tuple.packVersionstampedKey(prefix); + } else { + coded = tuple.packVersionstampedValue(prefix); + } inst.push("OK".getBytes()); inst.push(coded); } catch (IllegalArgumentException e) { diff --git a/bindings/java/src/test/com/apple/foundationdb/test/VersionstampSmokeTest.java b/bindings/java/src/test/com/apple/foundationdb/test/VersionstampSmokeTest.java index 556e92085e8..9c6358d3756 100644 --- a/bindings/java/src/test/com/apple/foundationdb/test/VersionstampSmokeTest.java +++ b/bindings/java/src/test/com/apple/foundationdb/test/VersionstampSmokeTest.java @@ -42,7 +42,7 @@ public static void main(String[] args) { CompletableFuture trVersionFuture = db.run((Transaction tr) -> { // The incomplete Versionstamp will have tr's version information when committed. Tuple t = Tuple.from("prefix", Versionstamp.incomplete()); - tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY, t.packWithVersionstamp(), new byte[0]); + tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY, t.packVersionstampedKey(), new byte[0]); return tr.getVersionstamp(); }); diff --git a/bindings/python/fdb/tuple.py b/bindings/python/fdb/tuple.py index 766e6590852..52237f6accf 100644 --- a/bindings/python/fdb/tuple.py +++ b/bindings/python/fdb/tuple.py @@ -374,7 +374,7 @@ def _encode(value, nested=False): # * if there is exactly one incomplete versionstamp member, it returns the tuple with the # two extra version bytes and the position of the version start # * if there is more than one incomplete versionstamp member, it throws an error -def _pack_maybe_with_versionstamp(t, prefix=None): +def _pack_maybe_with_versionstamp(t, prefix=None, for_value=False): if not isinstance(t, tuple): raise Exception("fdbtuple pack() expects a tuple, got a " + str(type(t))) @@ -384,7 +384,10 @@ def _pack_maybe_with_versionstamp(t, prefix=None): if version_pos >= 0: version_pos += len(prefix) if prefix is not None else 0 bytes_list.extend(child_bytes) - bytes_list.append(struct.pack('