From 49dfda066169bd09c12960bbbcb72762dd7b1a35 Mon Sep 17 00:00:00 2001 From: HuangWei Date: Fri, 17 Aug 2018 15:45:02 +0800 Subject: [PATCH] server: add check_and_mutate() interface and implementation (#161) Former-commit-id: bd5573b269ebe3f88e1409b5871b83dcc1e1e6de [formerly 64766397cd85f3053269f5c0648770b8f3620f0a] Former-commit-id: 05a2d0d6d9576352686585153a35a8ca550dea17 --- rdsn | 2 +- src/base/pegasus_rpc_types.h | 3 + src/base/rrdb_types.cpp | 1225 ++++++++--- src/client_lib/pegasus_client_impl.cpp | 129 +- src/client_lib/pegasus_client_impl.h | 23 +- src/idl/rrdb.thrift | 39 + src/include/pegasus/client.h | 152 +- src/include/rrdb/rrdb.client.h | 44 + src/include/rrdb/rrdb.code.definition.h | 1 + src/include/rrdb/rrdb.server.h | 20 +- src/include/rrdb/rrdb.types.h | 3 + src/include/rrdb/rrdb_types.h | 278 ++- src/server/config-server.ini | 14 + src/server/config.ini | 8 + src/server/info_collector.cpp | 11 +- src/server/info_collector.h | 5 +- src/server/pegasus_server_write.cpp | 5 + src/server/pegasus_write_service.cpp | 24 + src/server/pegasus_write_service.h | 7 + src/server/pegasus_write_service_impl.h | 168 +- src/shell/command_helper.h | 4 + src/shell/commands.h | 257 ++- src/shell/main.cpp | 17 + src/test/function_test/run.sh | 4 +- .../function_test/test_check_and_mutate.cpp | 1834 +++++++++++++++++ src/test/function_test/test_restore.cpp | 6 +- 26 files changed, 3960 insertions(+), 323 deletions(-) create mode 100644 src/test/function_test/test_check_and_mutate.cpp diff --git a/rdsn b/rdsn index ae92a2d229..b613aad324 160000 --- a/rdsn +++ b/rdsn @@ -1 +1 @@ -Subproject commit ae92a2d229ecfd2c89ea3585b9f1a98b25ebff4d +Subproject commit b613aad324ac95532de4c795a76f43064d6b4b17 diff --git a/src/base/pegasus_rpc_types.h b/src/base/pegasus_rpc_types.h index 0f1324ecc8..3d7373413d 100644 --- a/src/base/pegasus_rpc_types.h +++ b/src/base/pegasus_rpc_types.h @@ -22,4 +22,7 @@ using incr_rpc = dsn::rpc_holder; +using check_and_mutate_rpc = + dsn::rpc_holder; + } // namespace pegasus diff --git a/src/base/rrdb_types.cpp b/src/base/rrdb_types.cpp index 9c74c1b338..e000aa08ec 100644 --- a/src/base/rrdb_types.cpp +++ b/src/base/rrdb_types.cpp @@ -64,6 +64,12 @@ const std::map _cas_check_type_VALUES_TO_NAMES( ::apache::thrift::TEnumIterator(18, _kcas_check_typeValues, _kcas_check_typeNames), ::apache::thrift::TEnumIterator(-1, NULL, NULL)); +int _kmutate_operationValues[] = {mutate_operation::MO_PUT, mutate_operation::MO_DELETE}; +const char *_kmutate_operationNames[] = {"MO_PUT", "MO_DELETE"}; +const std::map _mutate_operation_VALUES_TO_NAMES( + ::apache::thrift::TEnumIterator(2, _kmutate_operationValues, _kmutate_operationNames), + ::apache::thrift::TEnumIterator(-1, NULL, NULL)); + update_request::~update_request() throw() {} void update_request::__set_key(const ::dsn::blob &val) { this->key = val; } @@ -2959,41 +2965,17 @@ void check_and_set_response::printTo(std::ostream &out) const out << ")"; } -get_scanner_request::~get_scanner_request() throw() {} - -void get_scanner_request::__set_start_key(const ::dsn::blob &val) { this->start_key = val; } - -void get_scanner_request::__set_stop_key(const ::dsn::blob &val) { this->stop_key = val; } - -void get_scanner_request::__set_start_inclusive(const bool val) { this->start_inclusive = val; } - -void get_scanner_request::__set_stop_inclusive(const bool val) { this->stop_inclusive = val; } - -void get_scanner_request::__set_batch_size(const int32_t val) { this->batch_size = val; } - -void get_scanner_request::__set_no_value(const bool val) { this->no_value = val; } +mutate::~mutate() throw() {} -void get_scanner_request::__set_hash_key_filter_type(const filter_type::type val) -{ - this->hash_key_filter_type = val; -} +void mutate::__set_operation(const mutate_operation::type val) { this->operation = val; } -void get_scanner_request::__set_hash_key_filter_pattern(const ::dsn::blob &val) -{ - this->hash_key_filter_pattern = val; -} +void mutate::__set_sort_key(const ::dsn::blob &val) { this->sort_key = val; } -void get_scanner_request::__set_sort_key_filter_type(const filter_type::type val) -{ - this->sort_key_filter_type = val; -} +void mutate::__set_value(const ::dsn::blob &val) { this->value = val; } -void get_scanner_request::__set_sort_key_filter_pattern(const ::dsn::blob &val) -{ - this->sort_key_filter_pattern = val; -} +void mutate::__set_set_expire_ts_seconds(const int32_t val) { this->set_expire_ts_seconds = val; } -uint32_t get_scanner_request::read(::apache::thrift::protocol::TProtocol *iprot) +uint32_t mutate::read(::apache::thrift::protocol::TProtocol *iprot) { apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); @@ -3013,85 +2995,35 @@ uint32_t get_scanner_request::read(::apache::thrift::protocol::TProtocol *iprot) } switch (fid) { case 1: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->start_key.read(iprot); - this->__isset.start_key = true; + if (ftype == ::apache::thrift::protocol::T_I32) { + int32_t ecast86; + xfer += iprot->readI32(ecast86); + this->operation = (mutate_operation::type)ecast86; + this->__isset.operation = true; } else { xfer += iprot->skip(ftype); } break; case 2: if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->stop_key.read(iprot); - this->__isset.stop_key = true; + xfer += this->sort_key.read(iprot); + this->__isset.sort_key = true; } else { xfer += iprot->skip(ftype); } break; case 3: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->start_inclusive); - this->__isset.start_inclusive = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 4: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->stop_inclusive); - this->__isset.stop_inclusive = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 5: - if (ftype == ::apache::thrift::protocol::T_I32) { - xfer += iprot->readI32(this->batch_size); - this->__isset.batch_size = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 6: - if (ftype == ::apache::thrift::protocol::T_BOOL) { - xfer += iprot->readBool(this->no_value); - this->__isset.no_value = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 7: - if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast86; - xfer += iprot->readI32(ecast86); - this->hash_key_filter_type = (filter_type::type)ecast86; - this->__isset.hash_key_filter_type = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 8: if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->hash_key_filter_pattern.read(iprot); - this->__isset.hash_key_filter_pattern = true; + xfer += this->value.read(iprot); + this->__isset.value = true; } else { xfer += iprot->skip(ftype); } break; - case 9: + case 4: if (ftype == ::apache::thrift::protocol::T_I32) { - int32_t ecast87; - xfer += iprot->readI32(ecast87); - this->sort_key_filter_type = (filter_type::type)ecast87; - this->__isset.sort_key_filter_type = true; - } else { - xfer += iprot->skip(ftype); - } - break; - case 10: - if (ftype == ::apache::thrift::protocol::T_STRUCT) { - xfer += this->sort_key_filter_pattern.read(iprot); - this->__isset.sort_key_filter_pattern = true; + xfer += iprot->readI32(this->set_expire_ts_seconds); + this->__isset.set_expire_ts_seconds = true; } else { xfer += iprot->skip(ftype); } @@ -3108,52 +3040,26 @@ uint32_t get_scanner_request::read(::apache::thrift::protocol::TProtocol *iprot) return xfer; } -uint32_t get_scanner_request::write(::apache::thrift::protocol::TProtocol *oprot) const +uint32_t mutate::write(::apache::thrift::protocol::TProtocol *oprot) const { uint32_t xfer = 0; apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("get_scanner_request"); - - xfer += oprot->writeFieldBegin("start_key", ::apache::thrift::protocol::T_STRUCT, 1); - xfer += this->start_key.write(oprot); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("stop_key", ::apache::thrift::protocol::T_STRUCT, 2); - xfer += this->stop_key.write(oprot); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("start_inclusive", ::apache::thrift::protocol::T_BOOL, 3); - xfer += oprot->writeBool(this->start_inclusive); - xfer += oprot->writeFieldEnd(); + xfer += oprot->writeStructBegin("mutate"); - xfer += oprot->writeFieldBegin("stop_inclusive", ::apache::thrift::protocol::T_BOOL, 4); - xfer += oprot->writeBool(this->stop_inclusive); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("batch_size", ::apache::thrift::protocol::T_I32, 5); - xfer += oprot->writeI32(this->batch_size); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("no_value", ::apache::thrift::protocol::T_BOOL, 6); - xfer += oprot->writeBool(this->no_value); - xfer += oprot->writeFieldEnd(); - - xfer += oprot->writeFieldBegin("hash_key_filter_type", ::apache::thrift::protocol::T_I32, 7); - xfer += oprot->writeI32((int32_t)this->hash_key_filter_type); + xfer += oprot->writeFieldBegin("operation", ::apache::thrift::protocol::T_I32, 1); + xfer += oprot->writeI32((int32_t)this->operation); xfer += oprot->writeFieldEnd(); - xfer += - oprot->writeFieldBegin("hash_key_filter_pattern", ::apache::thrift::protocol::T_STRUCT, 8); - xfer += this->hash_key_filter_pattern.write(oprot); + xfer += oprot->writeFieldBegin("sort_key", ::apache::thrift::protocol::T_STRUCT, 2); + xfer += this->sort_key.write(oprot); xfer += oprot->writeFieldEnd(); - xfer += oprot->writeFieldBegin("sort_key_filter_type", ::apache::thrift::protocol::T_I32, 9); - xfer += oprot->writeI32((int32_t)this->sort_key_filter_type); + xfer += oprot->writeFieldBegin("value", ::apache::thrift::protocol::T_STRUCT, 3); + xfer += this->value.write(oprot); xfer += oprot->writeFieldEnd(); - xfer += - oprot->writeFieldBegin("sort_key_filter_pattern", ::apache::thrift::protocol::T_STRUCT, 10); - xfer += this->sort_key_filter_pattern.write(oprot); + xfer += oprot->writeFieldBegin("set_expire_ts_seconds", ::apache::thrift::protocol::T_I32, 4); + xfer += oprot->writeI32(this->set_expire_ts_seconds); xfer += oprot->writeFieldEnd(); xfer += oprot->writeFieldStop(); @@ -3161,111 +3067,94 @@ uint32_t get_scanner_request::write(::apache::thrift::protocol::TProtocol *oprot return xfer; } -void swap(get_scanner_request &a, get_scanner_request &b) +void swap(mutate &a, mutate &b) { using ::std::swap; - swap(a.start_key, b.start_key); - swap(a.stop_key, b.stop_key); - swap(a.start_inclusive, b.start_inclusive); - swap(a.stop_inclusive, b.stop_inclusive); - swap(a.batch_size, b.batch_size); - swap(a.no_value, b.no_value); - swap(a.hash_key_filter_type, b.hash_key_filter_type); - swap(a.hash_key_filter_pattern, b.hash_key_filter_pattern); - swap(a.sort_key_filter_type, b.sort_key_filter_type); - swap(a.sort_key_filter_pattern, b.sort_key_filter_pattern); + swap(a.operation, b.operation); + swap(a.sort_key, b.sort_key); + swap(a.value, b.value); + swap(a.set_expire_ts_seconds, b.set_expire_ts_seconds); swap(a.__isset, b.__isset); } -get_scanner_request::get_scanner_request(const get_scanner_request &other88) -{ - start_key = other88.start_key; - stop_key = other88.stop_key; - start_inclusive = other88.start_inclusive; - stop_inclusive = other88.stop_inclusive; - batch_size = other88.batch_size; - no_value = other88.no_value; - hash_key_filter_type = other88.hash_key_filter_type; - hash_key_filter_pattern = other88.hash_key_filter_pattern; - sort_key_filter_type = other88.sort_key_filter_type; - sort_key_filter_pattern = other88.sort_key_filter_pattern; - __isset = other88.__isset; -} -get_scanner_request::get_scanner_request(get_scanner_request &&other89) -{ - start_key = std::move(other89.start_key); - stop_key = std::move(other89.stop_key); - start_inclusive = std::move(other89.start_inclusive); - stop_inclusive = std::move(other89.stop_inclusive); - batch_size = std::move(other89.batch_size); - no_value = std::move(other89.no_value); - hash_key_filter_type = std::move(other89.hash_key_filter_type); - hash_key_filter_pattern = std::move(other89.hash_key_filter_pattern); - sort_key_filter_type = std::move(other89.sort_key_filter_type); - sort_key_filter_pattern = std::move(other89.sort_key_filter_pattern); - __isset = std::move(other89.__isset); -} -get_scanner_request &get_scanner_request::operator=(const get_scanner_request &other90) -{ - start_key = other90.start_key; - stop_key = other90.stop_key; - start_inclusive = other90.start_inclusive; - stop_inclusive = other90.stop_inclusive; - batch_size = other90.batch_size; - no_value = other90.no_value; - hash_key_filter_type = other90.hash_key_filter_type; - hash_key_filter_pattern = other90.hash_key_filter_pattern; - sort_key_filter_type = other90.sort_key_filter_type; - sort_key_filter_pattern = other90.sort_key_filter_pattern; - __isset = other90.__isset; +mutate::mutate(const mutate &other87) +{ + operation = other87.operation; + sort_key = other87.sort_key; + value = other87.value; + set_expire_ts_seconds = other87.set_expire_ts_seconds; + __isset = other87.__isset; +} +mutate::mutate(mutate &&other88) +{ + operation = std::move(other88.operation); + sort_key = std::move(other88.sort_key); + value = std::move(other88.value); + set_expire_ts_seconds = std::move(other88.set_expire_ts_seconds); + __isset = std::move(other88.__isset); +} +mutate &mutate::operator=(const mutate &other89) +{ + operation = other89.operation; + sort_key = other89.sort_key; + value = other89.value; + set_expire_ts_seconds = other89.set_expire_ts_seconds; + __isset = other89.__isset; return *this; } -get_scanner_request &get_scanner_request::operator=(get_scanner_request &&other91) -{ - start_key = std::move(other91.start_key); - stop_key = std::move(other91.stop_key); - start_inclusive = std::move(other91.start_inclusive); - stop_inclusive = std::move(other91.stop_inclusive); - batch_size = std::move(other91.batch_size); - no_value = std::move(other91.no_value); - hash_key_filter_type = std::move(other91.hash_key_filter_type); - hash_key_filter_pattern = std::move(other91.hash_key_filter_pattern); - sort_key_filter_type = std::move(other91.sort_key_filter_type); - sort_key_filter_pattern = std::move(other91.sort_key_filter_pattern); - __isset = std::move(other91.__isset); +mutate &mutate::operator=(mutate &&other90) +{ + operation = std::move(other90.operation); + sort_key = std::move(other90.sort_key); + value = std::move(other90.value); + set_expire_ts_seconds = std::move(other90.set_expire_ts_seconds); + __isset = std::move(other90.__isset); return *this; } -void get_scanner_request::printTo(std::ostream &out) const +void mutate::printTo(std::ostream &out) const { using ::apache::thrift::to_string; - out << "get_scanner_request("; - out << "start_key=" << to_string(start_key); - out << ", " - << "stop_key=" << to_string(stop_key); + out << "mutate("; + out << "operation=" << to_string(operation); out << ", " - << "start_inclusive=" << to_string(start_inclusive); - out << ", " - << "stop_inclusive=" << to_string(stop_inclusive); - out << ", " - << "batch_size=" << to_string(batch_size); - out << ", " - << "no_value=" << to_string(no_value); - out << ", " - << "hash_key_filter_type=" << to_string(hash_key_filter_type); - out << ", " - << "hash_key_filter_pattern=" << to_string(hash_key_filter_pattern); + << "sort_key=" << to_string(sort_key); out << ", " - << "sort_key_filter_type=" << to_string(sort_key_filter_type); + << "value=" << to_string(value); out << ", " - << "sort_key_filter_pattern=" << to_string(sort_key_filter_pattern); + << "set_expire_ts_seconds=" << to_string(set_expire_ts_seconds); out << ")"; } -scan_request::~scan_request() throw() {} +check_and_mutate_request::~check_and_mutate_request() throw() {} -void scan_request::__set_context_id(const int64_t val) { this->context_id = val; } +void check_and_mutate_request::__set_hash_key(const ::dsn::blob &val) { this->hash_key = val; } -uint32_t scan_request::read(::apache::thrift::protocol::TProtocol *iprot) +void check_and_mutate_request::__set_check_sort_key(const ::dsn::blob &val) +{ + this->check_sort_key = val; +} + +void check_and_mutate_request::__set_check_type(const cas_check_type::type val) +{ + this->check_type = val; +} + +void check_and_mutate_request::__set_check_operand(const ::dsn::blob &val) +{ + this->check_operand = val; +} + +void check_and_mutate_request::__set_mutate_list(const std::vector &val) +{ + this->mutate_list = val; +} + +void check_and_mutate_request::__set_return_check_value(const bool val) +{ + this->return_check_value = val; +} + +uint32_t check_and_mutate_request::read(::apache::thrift::protocol::TProtocol *iprot) { apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); @@ -3285,9 +3174,62 @@ uint32_t scan_request::read(::apache::thrift::protocol::TProtocol *iprot) } switch (fid) { case 1: - if (ftype == ::apache::thrift::protocol::T_I64) { - xfer += iprot->readI64(this->context_id); - this->__isset.context_id = true; + if (ftype == ::apache::thrift::protocol::T_STRUCT) { + xfer += this->hash_key.read(iprot); + this->__isset.hash_key = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 2: + if (ftype == ::apache::thrift::protocol::T_STRUCT) { + xfer += this->check_sort_key.read(iprot); + this->__isset.check_sort_key = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 3: + if (ftype == ::apache::thrift::protocol::T_I32) { + int32_t ecast91; + xfer += iprot->readI32(ecast91); + this->check_type = (cas_check_type::type)ecast91; + this->__isset.check_type = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 4: + if (ftype == ::apache::thrift::protocol::T_STRUCT) { + xfer += this->check_operand.read(iprot); + this->__isset.check_operand = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 5: + if (ftype == ::apache::thrift::protocol::T_LIST) { + { + this->mutate_list.clear(); + uint32_t _size92; + ::apache::thrift::protocol::TType _etype95; + xfer += iprot->readListBegin(_etype95, _size92); + this->mutate_list.resize(_size92); + uint32_t _i96; + for (_i96 = 0; _i96 < _size92; ++_i96) { + xfer += this->mutate_list[_i96].read(iprot); + } + xfer += iprot->readListEnd(); + } + this->__isset.mutate_list = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 6: + if (ftype == ::apache::thrift::protocol::T_BOOL) { + xfer += iprot->readBool(this->return_check_value); + this->__isset.return_check_value = true; } else { xfer += iprot->skip(ftype); } @@ -3304,48 +3246,765 @@ uint32_t scan_request::read(::apache::thrift::protocol::TProtocol *iprot) return xfer; } -uint32_t scan_request::write(::apache::thrift::protocol::TProtocol *oprot) const +uint32_t check_and_mutate_request::write(::apache::thrift::protocol::TProtocol *oprot) const { uint32_t xfer = 0; apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); - xfer += oprot->writeStructBegin("scan_request"); + xfer += oprot->writeStructBegin("check_and_mutate_request"); - xfer += oprot->writeFieldBegin("context_id", ::apache::thrift::protocol::T_I64, 1); - xfer += oprot->writeI64(this->context_id); + xfer += oprot->writeFieldBegin("hash_key", ::apache::thrift::protocol::T_STRUCT, 1); + xfer += this->hash_key.write(oprot); xfer += oprot->writeFieldEnd(); - xfer += oprot->writeFieldStop(); - xfer += oprot->writeStructEnd(); - return xfer; -} - -void swap(scan_request &a, scan_request &b) + xfer += oprot->writeFieldBegin("check_sort_key", ::apache::thrift::protocol::T_STRUCT, 2); + xfer += this->check_sort_key.write(oprot); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("check_type", ::apache::thrift::protocol::T_I32, 3); + xfer += oprot->writeI32((int32_t)this->check_type); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("check_operand", ::apache::thrift::protocol::T_STRUCT, 4); + xfer += this->check_operand.write(oprot); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("mutate_list", ::apache::thrift::protocol::T_LIST, 5); + { + xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, + static_cast(this->mutate_list.size())); + std::vector::const_iterator _iter97; + for (_iter97 = this->mutate_list.begin(); _iter97 != this->mutate_list.end(); ++_iter97) { + xfer += (*_iter97).write(oprot); + } + xfer += oprot->writeListEnd(); + } + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("return_check_value", ::apache::thrift::protocol::T_BOOL, 6); + xfer += oprot->writeBool(this->return_check_value); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldStop(); + xfer += oprot->writeStructEnd(); + return xfer; +} + +void swap(check_and_mutate_request &a, check_and_mutate_request &b) +{ + using ::std::swap; + swap(a.hash_key, b.hash_key); + swap(a.check_sort_key, b.check_sort_key); + swap(a.check_type, b.check_type); + swap(a.check_operand, b.check_operand); + swap(a.mutate_list, b.mutate_list); + swap(a.return_check_value, b.return_check_value); + swap(a.__isset, b.__isset); +} + +check_and_mutate_request::check_and_mutate_request(const check_and_mutate_request &other98) +{ + hash_key = other98.hash_key; + check_sort_key = other98.check_sort_key; + check_type = other98.check_type; + check_operand = other98.check_operand; + mutate_list = other98.mutate_list; + return_check_value = other98.return_check_value; + __isset = other98.__isset; +} +check_and_mutate_request::check_and_mutate_request(check_and_mutate_request &&other99) +{ + hash_key = std::move(other99.hash_key); + check_sort_key = std::move(other99.check_sort_key); + check_type = std::move(other99.check_type); + check_operand = std::move(other99.check_operand); + mutate_list = std::move(other99.mutate_list); + return_check_value = std::move(other99.return_check_value); + __isset = std::move(other99.__isset); +} +check_and_mutate_request &check_and_mutate_request:: +operator=(const check_and_mutate_request &other100) +{ + hash_key = other100.hash_key; + check_sort_key = other100.check_sort_key; + check_type = other100.check_type; + check_operand = other100.check_operand; + mutate_list = other100.mutate_list; + return_check_value = other100.return_check_value; + __isset = other100.__isset; + return *this; +} +check_and_mutate_request &check_and_mutate_request::operator=(check_and_mutate_request &&other101) +{ + hash_key = std::move(other101.hash_key); + check_sort_key = std::move(other101.check_sort_key); + check_type = std::move(other101.check_type); + check_operand = std::move(other101.check_operand); + mutate_list = std::move(other101.mutate_list); + return_check_value = std::move(other101.return_check_value); + __isset = std::move(other101.__isset); + return *this; +} +void check_and_mutate_request::printTo(std::ostream &out) const +{ + using ::apache::thrift::to_string; + out << "check_and_mutate_request("; + out << "hash_key=" << to_string(hash_key); + out << ", " + << "check_sort_key=" << to_string(check_sort_key); + out << ", " + << "check_type=" << to_string(check_type); + out << ", " + << "check_operand=" << to_string(check_operand); + out << ", " + << "mutate_list=" << to_string(mutate_list); + out << ", " + << "return_check_value=" << to_string(return_check_value); + out << ")"; +} + +check_and_mutate_response::~check_and_mutate_response() throw() {} + +void check_and_mutate_response::__set_error(const int32_t val) { this->error = val; } + +void check_and_mutate_response::__set_check_value_returned(const bool val) +{ + this->check_value_returned = val; +} + +void check_and_mutate_response::__set_check_value_exist(const bool val) +{ + this->check_value_exist = val; +} + +void check_and_mutate_response::__set_check_value(const ::dsn::blob &val) +{ + this->check_value = val; +} + +void check_and_mutate_response::__set_app_id(const int32_t val) { this->app_id = val; } + +void check_and_mutate_response::__set_partition_index(const int32_t val) +{ + this->partition_index = val; +} + +void check_and_mutate_response::__set_decree(const int64_t val) { this->decree = val; } + +void check_and_mutate_response::__set_server(const std::string &val) { this->server = val; } + +uint32_t check_and_mutate_response::read(::apache::thrift::protocol::TProtocol *iprot) +{ + + apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); + uint32_t xfer = 0; + std::string fname; + ::apache::thrift::protocol::TType ftype; + int16_t fid; + + xfer += iprot->readStructBegin(fname); + + using ::apache::thrift::protocol::TProtocolException; + + while (true) { + xfer += iprot->readFieldBegin(fname, ftype, fid); + if (ftype == ::apache::thrift::protocol::T_STOP) { + break; + } + switch (fid) { + case 1: + if (ftype == ::apache::thrift::protocol::T_I32) { + xfer += iprot->readI32(this->error); + this->__isset.error = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 2: + if (ftype == ::apache::thrift::protocol::T_BOOL) { + xfer += iprot->readBool(this->check_value_returned); + this->__isset.check_value_returned = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 3: + if (ftype == ::apache::thrift::protocol::T_BOOL) { + xfer += iprot->readBool(this->check_value_exist); + this->__isset.check_value_exist = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 4: + if (ftype == ::apache::thrift::protocol::T_STRUCT) { + xfer += this->check_value.read(iprot); + this->__isset.check_value = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 5: + if (ftype == ::apache::thrift::protocol::T_I32) { + xfer += iprot->readI32(this->app_id); + this->__isset.app_id = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 6: + if (ftype == ::apache::thrift::protocol::T_I32) { + xfer += iprot->readI32(this->partition_index); + this->__isset.partition_index = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 7: + if (ftype == ::apache::thrift::protocol::T_I64) { + xfer += iprot->readI64(this->decree); + this->__isset.decree = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 8: + if (ftype == ::apache::thrift::protocol::T_STRING) { + xfer += iprot->readString(this->server); + this->__isset.server = true; + } else { + xfer += iprot->skip(ftype); + } + break; + default: + xfer += iprot->skip(ftype); + break; + } + xfer += iprot->readFieldEnd(); + } + + xfer += iprot->readStructEnd(); + + return xfer; +} + +uint32_t check_and_mutate_response::write(::apache::thrift::protocol::TProtocol *oprot) const +{ + uint32_t xfer = 0; + apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); + xfer += oprot->writeStructBegin("check_and_mutate_response"); + + xfer += oprot->writeFieldBegin("error", ::apache::thrift::protocol::T_I32, 1); + xfer += oprot->writeI32(this->error); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("check_value_returned", ::apache::thrift::protocol::T_BOOL, 2); + xfer += oprot->writeBool(this->check_value_returned); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("check_value_exist", ::apache::thrift::protocol::T_BOOL, 3); + xfer += oprot->writeBool(this->check_value_exist); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("check_value", ::apache::thrift::protocol::T_STRUCT, 4); + xfer += this->check_value.write(oprot); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("app_id", ::apache::thrift::protocol::T_I32, 5); + xfer += oprot->writeI32(this->app_id); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("partition_index", ::apache::thrift::protocol::T_I32, 6); + xfer += oprot->writeI32(this->partition_index); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("decree", ::apache::thrift::protocol::T_I64, 7); + xfer += oprot->writeI64(this->decree); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("server", ::apache::thrift::protocol::T_STRING, 8); + xfer += oprot->writeString(this->server); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldStop(); + xfer += oprot->writeStructEnd(); + return xfer; +} + +void swap(check_and_mutate_response &a, check_and_mutate_response &b) +{ + using ::std::swap; + swap(a.error, b.error); + swap(a.check_value_returned, b.check_value_returned); + swap(a.check_value_exist, b.check_value_exist); + swap(a.check_value, b.check_value); + swap(a.app_id, b.app_id); + swap(a.partition_index, b.partition_index); + swap(a.decree, b.decree); + swap(a.server, b.server); + swap(a.__isset, b.__isset); +} + +check_and_mutate_response::check_and_mutate_response(const check_and_mutate_response &other102) +{ + error = other102.error; + check_value_returned = other102.check_value_returned; + check_value_exist = other102.check_value_exist; + check_value = other102.check_value; + app_id = other102.app_id; + partition_index = other102.partition_index; + decree = other102.decree; + server = other102.server; + __isset = other102.__isset; +} +check_and_mutate_response::check_and_mutate_response(check_and_mutate_response &&other103) +{ + error = std::move(other103.error); + check_value_returned = std::move(other103.check_value_returned); + check_value_exist = std::move(other103.check_value_exist); + check_value = std::move(other103.check_value); + app_id = std::move(other103.app_id); + partition_index = std::move(other103.partition_index); + decree = std::move(other103.decree); + server = std::move(other103.server); + __isset = std::move(other103.__isset); +} +check_and_mutate_response &check_and_mutate_response:: +operator=(const check_and_mutate_response &other104) +{ + error = other104.error; + check_value_returned = other104.check_value_returned; + check_value_exist = other104.check_value_exist; + check_value = other104.check_value; + app_id = other104.app_id; + partition_index = other104.partition_index; + decree = other104.decree; + server = other104.server; + __isset = other104.__isset; + return *this; +} +check_and_mutate_response &check_and_mutate_response:: +operator=(check_and_mutate_response &&other105) +{ + error = std::move(other105.error); + check_value_returned = std::move(other105.check_value_returned); + check_value_exist = std::move(other105.check_value_exist); + check_value = std::move(other105.check_value); + app_id = std::move(other105.app_id); + partition_index = std::move(other105.partition_index); + decree = std::move(other105.decree); + server = std::move(other105.server); + __isset = std::move(other105.__isset); + return *this; +} +void check_and_mutate_response::printTo(std::ostream &out) const +{ + using ::apache::thrift::to_string; + out << "check_and_mutate_response("; + out << "error=" << to_string(error); + out << ", " + << "check_value_returned=" << to_string(check_value_returned); + out << ", " + << "check_value_exist=" << to_string(check_value_exist); + out << ", " + << "check_value=" << to_string(check_value); + out << ", " + << "app_id=" << to_string(app_id); + out << ", " + << "partition_index=" << to_string(partition_index); + out << ", " + << "decree=" << to_string(decree); + out << ", " + << "server=" << to_string(server); + out << ")"; +} + +get_scanner_request::~get_scanner_request() throw() {} + +void get_scanner_request::__set_start_key(const ::dsn::blob &val) { this->start_key = val; } + +void get_scanner_request::__set_stop_key(const ::dsn::blob &val) { this->stop_key = val; } + +void get_scanner_request::__set_start_inclusive(const bool val) { this->start_inclusive = val; } + +void get_scanner_request::__set_stop_inclusive(const bool val) { this->stop_inclusive = val; } + +void get_scanner_request::__set_batch_size(const int32_t val) { this->batch_size = val; } + +void get_scanner_request::__set_no_value(const bool val) { this->no_value = val; } + +void get_scanner_request::__set_hash_key_filter_type(const filter_type::type val) +{ + this->hash_key_filter_type = val; +} + +void get_scanner_request::__set_hash_key_filter_pattern(const ::dsn::blob &val) +{ + this->hash_key_filter_pattern = val; +} + +void get_scanner_request::__set_sort_key_filter_type(const filter_type::type val) +{ + this->sort_key_filter_type = val; +} + +void get_scanner_request::__set_sort_key_filter_pattern(const ::dsn::blob &val) +{ + this->sort_key_filter_pattern = val; +} + +uint32_t get_scanner_request::read(::apache::thrift::protocol::TProtocol *iprot) +{ + + apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); + uint32_t xfer = 0; + std::string fname; + ::apache::thrift::protocol::TType ftype; + int16_t fid; + + xfer += iprot->readStructBegin(fname); + + using ::apache::thrift::protocol::TProtocolException; + + while (true) { + xfer += iprot->readFieldBegin(fname, ftype, fid); + if (ftype == ::apache::thrift::protocol::T_STOP) { + break; + } + switch (fid) { + case 1: + if (ftype == ::apache::thrift::protocol::T_STRUCT) { + xfer += this->start_key.read(iprot); + this->__isset.start_key = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 2: + if (ftype == ::apache::thrift::protocol::T_STRUCT) { + xfer += this->stop_key.read(iprot); + this->__isset.stop_key = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 3: + if (ftype == ::apache::thrift::protocol::T_BOOL) { + xfer += iprot->readBool(this->start_inclusive); + this->__isset.start_inclusive = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 4: + if (ftype == ::apache::thrift::protocol::T_BOOL) { + xfer += iprot->readBool(this->stop_inclusive); + this->__isset.stop_inclusive = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 5: + if (ftype == ::apache::thrift::protocol::T_I32) { + xfer += iprot->readI32(this->batch_size); + this->__isset.batch_size = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 6: + if (ftype == ::apache::thrift::protocol::T_BOOL) { + xfer += iprot->readBool(this->no_value); + this->__isset.no_value = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 7: + if (ftype == ::apache::thrift::protocol::T_I32) { + int32_t ecast106; + xfer += iprot->readI32(ecast106); + this->hash_key_filter_type = (filter_type::type)ecast106; + this->__isset.hash_key_filter_type = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 8: + if (ftype == ::apache::thrift::protocol::T_STRUCT) { + xfer += this->hash_key_filter_pattern.read(iprot); + this->__isset.hash_key_filter_pattern = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 9: + if (ftype == ::apache::thrift::protocol::T_I32) { + int32_t ecast107; + xfer += iprot->readI32(ecast107); + this->sort_key_filter_type = (filter_type::type)ecast107; + this->__isset.sort_key_filter_type = true; + } else { + xfer += iprot->skip(ftype); + } + break; + case 10: + if (ftype == ::apache::thrift::protocol::T_STRUCT) { + xfer += this->sort_key_filter_pattern.read(iprot); + this->__isset.sort_key_filter_pattern = true; + } else { + xfer += iprot->skip(ftype); + } + break; + default: + xfer += iprot->skip(ftype); + break; + } + xfer += iprot->readFieldEnd(); + } + + xfer += iprot->readStructEnd(); + + return xfer; +} + +uint32_t get_scanner_request::write(::apache::thrift::protocol::TProtocol *oprot) const +{ + uint32_t xfer = 0; + apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); + xfer += oprot->writeStructBegin("get_scanner_request"); + + xfer += oprot->writeFieldBegin("start_key", ::apache::thrift::protocol::T_STRUCT, 1); + xfer += this->start_key.write(oprot); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("stop_key", ::apache::thrift::protocol::T_STRUCT, 2); + xfer += this->stop_key.write(oprot); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("start_inclusive", ::apache::thrift::protocol::T_BOOL, 3); + xfer += oprot->writeBool(this->start_inclusive); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("stop_inclusive", ::apache::thrift::protocol::T_BOOL, 4); + xfer += oprot->writeBool(this->stop_inclusive); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("batch_size", ::apache::thrift::protocol::T_I32, 5); + xfer += oprot->writeI32(this->batch_size); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("no_value", ::apache::thrift::protocol::T_BOOL, 6); + xfer += oprot->writeBool(this->no_value); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("hash_key_filter_type", ::apache::thrift::protocol::T_I32, 7); + xfer += oprot->writeI32((int32_t)this->hash_key_filter_type); + xfer += oprot->writeFieldEnd(); + + xfer += + oprot->writeFieldBegin("hash_key_filter_pattern", ::apache::thrift::protocol::T_STRUCT, 8); + xfer += this->hash_key_filter_pattern.write(oprot); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldBegin("sort_key_filter_type", ::apache::thrift::protocol::T_I32, 9); + xfer += oprot->writeI32((int32_t)this->sort_key_filter_type); + xfer += oprot->writeFieldEnd(); + + xfer += + oprot->writeFieldBegin("sort_key_filter_pattern", ::apache::thrift::protocol::T_STRUCT, 10); + xfer += this->sort_key_filter_pattern.write(oprot); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldStop(); + xfer += oprot->writeStructEnd(); + return xfer; +} + +void swap(get_scanner_request &a, get_scanner_request &b) +{ + using ::std::swap; + swap(a.start_key, b.start_key); + swap(a.stop_key, b.stop_key); + swap(a.start_inclusive, b.start_inclusive); + swap(a.stop_inclusive, b.stop_inclusive); + swap(a.batch_size, b.batch_size); + swap(a.no_value, b.no_value); + swap(a.hash_key_filter_type, b.hash_key_filter_type); + swap(a.hash_key_filter_pattern, b.hash_key_filter_pattern); + swap(a.sort_key_filter_type, b.sort_key_filter_type); + swap(a.sort_key_filter_pattern, b.sort_key_filter_pattern); + swap(a.__isset, b.__isset); +} + +get_scanner_request::get_scanner_request(const get_scanner_request &other108) +{ + start_key = other108.start_key; + stop_key = other108.stop_key; + start_inclusive = other108.start_inclusive; + stop_inclusive = other108.stop_inclusive; + batch_size = other108.batch_size; + no_value = other108.no_value; + hash_key_filter_type = other108.hash_key_filter_type; + hash_key_filter_pattern = other108.hash_key_filter_pattern; + sort_key_filter_type = other108.sort_key_filter_type; + sort_key_filter_pattern = other108.sort_key_filter_pattern; + __isset = other108.__isset; +} +get_scanner_request::get_scanner_request(get_scanner_request &&other109) +{ + start_key = std::move(other109.start_key); + stop_key = std::move(other109.stop_key); + start_inclusive = std::move(other109.start_inclusive); + stop_inclusive = std::move(other109.stop_inclusive); + batch_size = std::move(other109.batch_size); + no_value = std::move(other109.no_value); + hash_key_filter_type = std::move(other109.hash_key_filter_type); + hash_key_filter_pattern = std::move(other109.hash_key_filter_pattern); + sort_key_filter_type = std::move(other109.sort_key_filter_type); + sort_key_filter_pattern = std::move(other109.sort_key_filter_pattern); + __isset = std::move(other109.__isset); +} +get_scanner_request &get_scanner_request::operator=(const get_scanner_request &other110) +{ + start_key = other110.start_key; + stop_key = other110.stop_key; + start_inclusive = other110.start_inclusive; + stop_inclusive = other110.stop_inclusive; + batch_size = other110.batch_size; + no_value = other110.no_value; + hash_key_filter_type = other110.hash_key_filter_type; + hash_key_filter_pattern = other110.hash_key_filter_pattern; + sort_key_filter_type = other110.sort_key_filter_type; + sort_key_filter_pattern = other110.sort_key_filter_pattern; + __isset = other110.__isset; + return *this; +} +get_scanner_request &get_scanner_request::operator=(get_scanner_request &&other111) +{ + start_key = std::move(other111.start_key); + stop_key = std::move(other111.stop_key); + start_inclusive = std::move(other111.start_inclusive); + stop_inclusive = std::move(other111.stop_inclusive); + batch_size = std::move(other111.batch_size); + no_value = std::move(other111.no_value); + hash_key_filter_type = std::move(other111.hash_key_filter_type); + hash_key_filter_pattern = std::move(other111.hash_key_filter_pattern); + sort_key_filter_type = std::move(other111.sort_key_filter_type); + sort_key_filter_pattern = std::move(other111.sort_key_filter_pattern); + __isset = std::move(other111.__isset); + return *this; +} +void get_scanner_request::printTo(std::ostream &out) const +{ + using ::apache::thrift::to_string; + out << "get_scanner_request("; + out << "start_key=" << to_string(start_key); + out << ", " + << "stop_key=" << to_string(stop_key); + out << ", " + << "start_inclusive=" << to_string(start_inclusive); + out << ", " + << "stop_inclusive=" << to_string(stop_inclusive); + out << ", " + << "batch_size=" << to_string(batch_size); + out << ", " + << "no_value=" << to_string(no_value); + out << ", " + << "hash_key_filter_type=" << to_string(hash_key_filter_type); + out << ", " + << "hash_key_filter_pattern=" << to_string(hash_key_filter_pattern); + out << ", " + << "sort_key_filter_type=" << to_string(sort_key_filter_type); + out << ", " + << "sort_key_filter_pattern=" << to_string(sort_key_filter_pattern); + out << ")"; +} + +scan_request::~scan_request() throw() {} + +void scan_request::__set_context_id(const int64_t val) { this->context_id = val; } + +uint32_t scan_request::read(::apache::thrift::protocol::TProtocol *iprot) +{ + + apache::thrift::protocol::TInputRecursionTracker tracker(*iprot); + uint32_t xfer = 0; + std::string fname; + ::apache::thrift::protocol::TType ftype; + int16_t fid; + + xfer += iprot->readStructBegin(fname); + + using ::apache::thrift::protocol::TProtocolException; + + while (true) { + xfer += iprot->readFieldBegin(fname, ftype, fid); + if (ftype == ::apache::thrift::protocol::T_STOP) { + break; + } + switch (fid) { + case 1: + if (ftype == ::apache::thrift::protocol::T_I64) { + xfer += iprot->readI64(this->context_id); + this->__isset.context_id = true; + } else { + xfer += iprot->skip(ftype); + } + break; + default: + xfer += iprot->skip(ftype); + break; + } + xfer += iprot->readFieldEnd(); + } + + xfer += iprot->readStructEnd(); + + return xfer; +} + +uint32_t scan_request::write(::apache::thrift::protocol::TProtocol *oprot) const +{ + uint32_t xfer = 0; + apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot); + xfer += oprot->writeStructBegin("scan_request"); + + xfer += oprot->writeFieldBegin("context_id", ::apache::thrift::protocol::T_I64, 1); + xfer += oprot->writeI64(this->context_id); + xfer += oprot->writeFieldEnd(); + + xfer += oprot->writeFieldStop(); + xfer += oprot->writeStructEnd(); + return xfer; +} + +void swap(scan_request &a, scan_request &b) { using ::std::swap; swap(a.context_id, b.context_id); swap(a.__isset, b.__isset); } -scan_request::scan_request(const scan_request &other92) +scan_request::scan_request(const scan_request &other112) { - context_id = other92.context_id; - __isset = other92.__isset; + context_id = other112.context_id; + __isset = other112.__isset; } -scan_request::scan_request(scan_request &&other93) +scan_request::scan_request(scan_request &&other113) { - context_id = std::move(other93.context_id); - __isset = std::move(other93.__isset); + context_id = std::move(other113.context_id); + __isset = std::move(other113.__isset); } -scan_request &scan_request::operator=(const scan_request &other94) +scan_request &scan_request::operator=(const scan_request &other114) { - context_id = other94.context_id; - __isset = other94.__isset; + context_id = other114.context_id; + __isset = other114.__isset; return *this; } -scan_request &scan_request::operator=(scan_request &&other95) +scan_request &scan_request::operator=(scan_request &&other115) { - context_id = std::move(other95.context_id); - __isset = std::move(other95.__isset); + context_id = std::move(other115.context_id); + __isset = std::move(other115.__isset); return *this; } void scan_request::printTo(std::ostream &out) const @@ -3401,13 +4060,13 @@ uint32_t scan_response::read(::apache::thrift::protocol::TProtocol *iprot) if (ftype == ::apache::thrift::protocol::T_LIST) { { this->kvs.clear(); - uint32_t _size96; - ::apache::thrift::protocol::TType _etype99; - xfer += iprot->readListBegin(_etype99, _size96); - this->kvs.resize(_size96); - uint32_t _i100; - for (_i100 = 0; _i100 < _size96; ++_i100) { - xfer += this->kvs[_i100].read(iprot); + uint32_t _size116; + ::apache::thrift::protocol::TType _etype119; + xfer += iprot->readListBegin(_etype119, _size116); + this->kvs.resize(_size116); + uint32_t _i120; + for (_i120 = 0; _i120 < _size116; ++_i120) { + xfer += this->kvs[_i120].read(iprot); } xfer += iprot->readListEnd(); } @@ -3474,9 +4133,9 @@ uint32_t scan_response::write(::apache::thrift::protocol::TProtocol *oprot) cons { xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast(this->kvs.size())); - std::vector::const_iterator _iter101; - for (_iter101 = this->kvs.begin(); _iter101 != this->kvs.end(); ++_iter101) { - xfer += (*_iter101).write(oprot); + std::vector::const_iterator _iter121; + for (_iter121 = this->kvs.begin(); _iter121 != this->kvs.end(); ++_iter121) { + xfer += (*_iter121).write(oprot); } xfer += oprot->writeListEnd(); } @@ -3515,46 +4174,46 @@ void swap(scan_response &a, scan_response &b) swap(a.__isset, b.__isset); } -scan_response::scan_response(const scan_response &other102) +scan_response::scan_response(const scan_response &other122) { - error = other102.error; - kvs = other102.kvs; - context_id = other102.context_id; - app_id = other102.app_id; - partition_index = other102.partition_index; - server = other102.server; - __isset = other102.__isset; + error = other122.error; + kvs = other122.kvs; + context_id = other122.context_id; + app_id = other122.app_id; + partition_index = other122.partition_index; + server = other122.server; + __isset = other122.__isset; } -scan_response::scan_response(scan_response &&other103) +scan_response::scan_response(scan_response &&other123) { - error = std::move(other103.error); - kvs = std::move(other103.kvs); - context_id = std::move(other103.context_id); - app_id = std::move(other103.app_id); - partition_index = std::move(other103.partition_index); - server = std::move(other103.server); - __isset = std::move(other103.__isset); + error = std::move(other123.error); + kvs = std::move(other123.kvs); + context_id = std::move(other123.context_id); + app_id = std::move(other123.app_id); + partition_index = std::move(other123.partition_index); + server = std::move(other123.server); + __isset = std::move(other123.__isset); } -scan_response &scan_response::operator=(const scan_response &other104) +scan_response &scan_response::operator=(const scan_response &other124) { - error = other104.error; - kvs = other104.kvs; - context_id = other104.context_id; - app_id = other104.app_id; - partition_index = other104.partition_index; - server = other104.server; - __isset = other104.__isset; + error = other124.error; + kvs = other124.kvs; + context_id = other124.context_id; + app_id = other124.app_id; + partition_index = other124.partition_index; + server = other124.server; + __isset = other124.__isset; return *this; } -scan_response &scan_response::operator=(scan_response &&other105) +scan_response &scan_response::operator=(scan_response &&other125) { - error = std::move(other105.error); - kvs = std::move(other105.kvs); - context_id = std::move(other105.context_id); - app_id = std::move(other105.app_id); - partition_index = std::move(other105.partition_index); - server = std::move(other105.server); - __isset = std::move(other105.__isset); + error = std::move(other125.error); + kvs = std::move(other125.kvs); + context_id = std::move(other125.context_id); + app_id = std::move(other125.app_id); + partition_index = std::move(other125.partition_index); + server = std::move(other125.server); + __isset = std::move(other125.__isset); return *this; } void scan_response::printTo(std::ostream &out) const @@ -3574,5 +4233,5 @@ void scan_response::printTo(std::ostream &out) const << "server=" << to_string(server); out << ")"; } -} -} // namespace +} // namespace apps +} // namespace dsn diff --git a/src/client_lib/pegasus_client_impl.cpp b/src/client_lib/pegasus_client_impl.cpp index 66bc946b15..da0b5c4fa2 100644 --- a/src/client_lib/pegasus_client_impl.cpp +++ b/src/client_lib/pegasus_client_impl.cpp @@ -876,6 +876,14 @@ void pegasus_client_impl::async_check_and_set(const std::string &hash_key, return; } + if (dsn::apps::_cas_check_type_VALUES_TO_NAMES.find(check_type) == + dsn::apps::_cas_check_type_VALUES_TO_NAMES.end()) { + derror("invalid check type: %d", (int)check_type); + if (callback != nullptr) + callback(PERR_INVALID_ARGUMENT, check_and_set_results(), internal_info()); + return; + } + ::dsn::apps::check_and_set_request req; req.hash_key.assign(hash_key.c_str(), 0, hash_key.size()); req.check_sort_key.assign(check_sort_key.c_str(), 0, check_sort_key.size()); @@ -938,6 +946,123 @@ void pegasus_client_impl::async_check_and_set(const std::string &hash_key, partition_hash); } +int pegasus_client_impl::check_and_mutate(const std::string &hash_key, + const std::string &check_sort_key, + cas_check_type check_type, + const std::string &check_operand, + const mutations &mutations, + const check_and_mutate_options &options, + check_and_mutate_results &results, + int timeout_milliseconds, + internal_info *info) +{ + ::dsn::utils::notify_event op_completed; + int ret = -1; + auto callback = [&](int _err, check_and_mutate_results &&_results, internal_info &&_info) { + ret = _err; + results = std::move(_results); + if (info != nullptr) + (*info) = std::move(_info); + op_completed.notify(); + }; + async_check_and_mutate(hash_key, + check_sort_key, + check_type, + check_operand, + mutations, + options, + std::move(callback), + timeout_milliseconds); + op_completed.wait(); + return ret; +} + +void pegasus_client_impl::async_check_and_mutate(const std::string &hash_key, + const std::string &check_sort_key, + cas_check_type check_type, + const std::string &check_operand, + const mutations &mutations, + const check_and_mutate_options &options, + async_check_and_mutate_callback_t &&callback, + int timeout_milliseconds) +{ + // check params + if (hash_key.size() >= UINT16_MAX) { + derror("invalid hash key: hash key length should be less than UINT16_MAX, but %d", + (int)hash_key.size()); + if (callback != nullptr) + callback(PERR_INVALID_HASH_KEY, check_and_mutate_results(), internal_info()); + return; + } + + if (dsn::apps::_cas_check_type_VALUES_TO_NAMES.find(check_type) == + dsn::apps::_cas_check_type_VALUES_TO_NAMES.end()) { + derror("invalid check type: %d", (int)check_type); + if (callback != nullptr) + callback(PERR_INVALID_ARGUMENT, check_and_mutate_results(), internal_info()); + return; + } + if (mutations.is_empty()) { + derror("invalid mutations: mutations should not be empty."); + if (callback != nullptr) + callback(PERR_INVALID_ARGUMENT, check_and_mutate_results(), internal_info()); + return; + } + + ::dsn::apps::check_and_mutate_request req; + req.hash_key.assign(hash_key.c_str(), 0, hash_key.size()); + req.check_sort_key.assign(check_sort_key.c_str(), 0, check_sort_key.size()); + req.check_type = (dsn::apps::cas_check_type::type)check_type; + req.check_operand.assign(check_operand.c_str(), 0, check_operand.size()); + mutations.get_mutations(req.mutate_list); + req.return_check_value = options.return_check_value; + + ::dsn::blob tmp_key; + pegasus_generate_key(tmp_key, req.hash_key, ::dsn::blob()); + auto partition_hash = pegasus_key_hash(tmp_key); + auto new_callback = [user_callback = std::move(callback)]( + ::dsn::error_code err, dsn_message_t req, dsn_message_t resp) + { + if (user_callback == nullptr) { + return; + } + check_and_mutate_results results; + internal_info info; + ::dsn::apps::check_and_mutate_response response; + if (err == ::dsn::ERR_OK) { + ::dsn::unmarshall(resp, response); + if (response.error == 0) { + results.mutate_succeed = true; + } else if (response.error == 13) { // kTryAgain + results.mutate_succeed = false; + response.error = 0; + } else { + results.mutate_succeed = false; + } + if (response.check_value_returned) { + results.check_value_returned = true; + if (response.check_value_exist) { + results.check_value_exist = true; + results.check_value.assign(response.check_value.data(), + response.check_value.length()); + } + } + info.app_id = response.app_id; + info.partition_index = response.partition_index; + info.decree = response.decree; + info.server = response.server; + } + int ret = + get_client_error(err == ERR_OK ? get_rocksdb_server_error(response.error) : int(err)); + user_callback(ret, std::move(results), std::move(info)); + }; + _client->check_and_mutate(req, + std::move(new_callback), + std::chrono::milliseconds(timeout_milliseconds), + 0, + partition_hash); +} + int pegasus_client_impl::ttl(const std::string &hash_key, const std::string &sort_key, int &ttl_seconds, @@ -1171,5 +1296,5 @@ const char *pegasus_client_impl::get_error_string(int error_code) const { return (rocskdb_error == 0) ? 0 : ROCSKDB_ERROR_START - rocskdb_error; } -} -} // namespace +} // namespace client +} // namespace pegasus diff --git a/src/client_lib/pegasus_client_impl.h b/src/client_lib/pegasus_client_impl.h index ee2df704cf..7724c808b7 100644 --- a/src/client_lib/pegasus_client_impl.h +++ b/src/client_lib/pegasus_client_impl.h @@ -172,6 +172,25 @@ class pegasus_client_impl : public pegasus_client async_check_and_set_callback_t &&callback = nullptr, int timeout_milliseconds = 5000) override; + virtual int check_and_mutate(const std::string &hash_key, + const std::string &check_sort_key, + cas_check_type check_type, + const std::string &check_operand, + const mutations &mutations, + const check_and_mutate_options &options, + check_and_mutate_results &results, + int timeout_milliseconds = 5000, + internal_info *info = nullptr) override; + + virtual void async_check_and_mutate(const std::string &hash_key, + const std::string &check_sort_key, + cas_check_type check_type, + const std::string &check_operand, + const mutations &mutations, + const check_and_mutate_options &options, + async_check_and_mutate_callback_t &&callback = nullptr, + int timeout_milliseconds = 5000) override; + virtual int ttl(const std::string &hashkey, const std::string &sortkey, int &ttl_seconds, @@ -300,5 +319,5 @@ class pegasus_client_impl : public pegasus_client /// static std::unordered_map _server_error_to_client; }; -} -} // namespace +} // namespace client +} // namespace pegasus diff --git a/src/idl/rrdb.thrift b/src/idl/rrdb.thrift index 93349edb3a..ad2c6eae18 100644 --- a/src/idl/rrdb.thrift +++ b/src/idl/rrdb.thrift @@ -40,6 +40,12 @@ enum cas_check_type CT_VALUE_INT_GREATER // int compare: value > operand } +enum mutate_operation +{ + MO_PUT, + MO_DELETE +} + struct update_request { 1:dsn.blob key; @@ -181,6 +187,38 @@ struct check_and_set_response 8:string server; } +struct mutate +{ + 1:mutate_operation operation; + 2:dsn.blob sort_key; + 3:dsn.blob value; // set null if operation is MO_DELETE + 4:i32 set_expire_ts_seconds; // set 0 if operation is MO_DELETE +} + +struct check_and_mutate_request +{ + 1:dsn.blob hash_key; + 2:dsn.blob check_sort_key; + 3:cas_check_type check_type; + 4:dsn.blob check_operand; + 5:list mutate_list; + 6:bool return_check_value; +} + +struct check_and_mutate_response +{ + 1:i32 error; // return kTryAgain if check not passed. + // return kInvalidArgument if check type is int compare and + // check_operand/check_value is not integer or out of range. + 2:bool check_value_returned; + 3:bool check_value_exist; // used only if check_value_returned is true + 4:dsn.blob check_value; // used only if check_value_returned and check_value_exist is true + 5:i32 app_id; + 6:i32 partition_index; + 7:i64 decree; + 8:string server; +} + struct get_scanner_request { 1:dsn.blob start_key; @@ -218,6 +256,7 @@ service rrdb multi_remove_response multi_remove(1:multi_remove_request request); incr_response incr(1:incr_request request); check_and_set_response check_and_set(1:check_and_set_request request); + check_and_mutate_response check_and_mutate(1:check_and_mutate_request request); read_response get(1:dsn.blob key); multi_get_response multi_get(1:multi_get_request request); count_response sortkey_count(1:dsn.blob hash_key); diff --git a/src/include/pegasus/client.h b/src/include/pegasus/client.h index a76313b70d..36d17d3fe4 100644 --- a/src/include/pegasus/client.h +++ b/src/include/pegasus/client.h @@ -13,6 +13,8 @@ #include #include +#include +#include namespace pegasus { class rrdb_client; @@ -159,12 +161,81 @@ class pegasus_client } }; + struct mutations + { + private: + std::vector<::dsn::apps::mutate> _mu_list; + std::vector> _ttl_list; + + public: + void set(dsn::string_view sort_key, dsn::string_view value, const int ttl_seconds = 0) + { + ::dsn::apps::mutate mu; + mu.operation = ::dsn::apps::mutate_operation::MO_PUT; + mu.sort_key = ::dsn::blob::create_from_bytes(sort_key.data(), sort_key.size()); + mu.value = ::dsn::blob::create_from_bytes(value.data(), value.size()); + // set_expire_ts_seconds will be set when check_and_mutate() gets the mutations (by + // calling get_mutations()) + mu.set_expire_ts_seconds = 0; + _mu_list.emplace_back(std::move(mu)); + if (ttl_seconds != 0) { + _ttl_list.emplace_back(std::make_pair(_mu_list.size() - 1, ttl_seconds)); + } + } + void del(dsn::string_view sort_key) + { + ::dsn::apps::mutate mu; + mu.operation = ::dsn::apps::mutate_operation::MO_DELETE; + mu.sort_key = ::dsn::blob::create_from_bytes(sort_key.data(), sort_key.size()); + mu.set_expire_ts_seconds = 0; + _mu_list.emplace_back(std::move(mu)); + } + // TODO HW: distinguish copy from deepcopy + void get_mutations(std::vector<::dsn::apps::mutate> &mutations) const + { + int current_time = ::pegasus::utils::epoch_now(); + mutations = _mu_list; + for (auto &pair : _ttl_list) { + mutations[pair.first].set_expire_ts_seconds = pair.second + current_time; + } + } + bool is_empty() const { return _mu_list.empty(); } + }; + + struct check_and_mutate_options + { + bool return_check_value; // if return the check value in results. + check_and_mutate_options() : return_check_value(false) {} + check_and_mutate_options(const check_and_mutate_options &o) + : return_check_value(o.return_check_value) + { + } + }; + + struct check_and_mutate_results + { + bool mutate_succeed; // if mutate succeed. + bool check_value_returned; // if the check value is returned. + bool check_value_exist; // can be used only when check_value_returned is true. + std::string check_value; // can be used only when check_value_exist is true. + check_and_mutate_results() + : mutate_succeed(false), check_value_returned(false), check_value_exist(false) + { + } + check_and_mutate_results(const check_and_mutate_results &o) + : mutate_succeed(o.mutate_succeed), + check_value_returned(o.check_value_returned), + check_value_exist(o.check_value_exist) + { + } + }; + struct scan_options { - int timeout_ms; // RPC call timeout param, in milliseconds - int batch_size; // max k-v count one RPC call - bool start_inclusive; // will be ingored when get_unordered_scanners() - bool stop_inclusive; // will be ingored when get_unordered_scanners() + int timeout_ms; // RPC call timeout param, in milliseconds + int batch_size; // max k-v count one RPC call + bool start_inclusive; // will be ingored when get_unordered_scanners() + bool stop_inclusive; // will be ingored when get_unordered_scanners() filter_type hash_key_filter_type; std::string hash_key_filter_pattern; filter_type sort_key_filter_type; @@ -220,6 +291,9 @@ class pegasus_client typedef std::function async_check_and_set_callback_t; + typedef std::function + async_check_and_mutate_callback_t; typedef std::function + check_and_mutate_sync(const check_and_mutate_request &args, + std::chrono::milliseconds timeout = std::chrono::milliseconds(0), + int thread_hash = 0, // if thread_hash == 0 && partition_hash != 0, + // thread_hash is computed from partition_hash + uint64_t partition_hash = 0, + dsn::optional<::dsn::rpc_address> server_addr = dsn::none) + { + return ::dsn::rpc::wait_and_unwrap( + ::dsn::rpc::call(server_addr.unwrap_or(_server), + RPC_RRDB_RRDB_CHECK_AND_MUTATE, + args, + &_tracker, + empty_rpc_handler, + timeout, + thread_hash, + partition_hash)); + } + + // - asynchronous with on-stack check_and_mutate_request and check_and_mutate_response + template + ::dsn::task_ptr check_and_mutate(const check_and_mutate_request &args, + TCallback &&callback, + std::chrono::milliseconds timeout = std::chrono::milliseconds(0), + int request_thread_hash = 0, // if thread_hash == 0 && + // partition_hash != 0, thread_hash + // is computed from partition_hash + uint64_t request_partition_hash = 0, + int reply_thread_hash = 0, + dsn::optional<::dsn::rpc_address> server_addr = dsn::none) + { + return ::dsn::rpc::call(server_addr.unwrap_or(_server), + RPC_RRDB_RRDB_CHECK_AND_MUTATE, + args, + &_tracker, + std::forward(callback), + timeout, + request_thread_hash, + request_partition_hash, + reply_thread_hash); + } + // ---------- call RPC_RRDB_RRDB_GET ------------ // - synchronous std::pair<::dsn::error_code, read_response> diff --git a/src/include/rrdb/rrdb.code.definition.h b/src/include/rrdb/rrdb.code.definition.h index bf9acc75d6..4d4a1bbd9f 100644 --- a/src/include/rrdb/rrdb.code.definition.h +++ b/src/include/rrdb/rrdb.code.definition.h @@ -9,6 +9,7 @@ DEFINE_STORAGE_WRITE_RPC_CODE(RPC_RRDB_RRDB_REMOVE, ALLOW_BATCH, IS_IDEMPOTENT) DEFINE_STORAGE_WRITE_RPC_CODE(RPC_RRDB_RRDB_MULTI_REMOVE, NOT_ALLOW_BATCH, IS_IDEMPOTENT) DEFINE_STORAGE_WRITE_RPC_CODE(RPC_RRDB_RRDB_INCR, NOT_ALLOW_BATCH, NOT_IDEMPOTENT) DEFINE_STORAGE_WRITE_RPC_CODE(RPC_RRDB_RRDB_CHECK_AND_SET, NOT_ALLOW_BATCH, NOT_IDEMPOTENT) +DEFINE_STORAGE_WRITE_RPC_CODE(RPC_RRDB_RRDB_CHECK_AND_MUTATE, NOT_ALLOW_BATCH, NOT_IDEMPOTENT) DEFINE_STORAGE_READ_RPC_CODE(RPC_RRDB_RRDB_GET) DEFINE_STORAGE_READ_RPC_CODE(RPC_RRDB_RRDB_MULTI_GET) DEFINE_STORAGE_READ_RPC_CODE(RPC_RRDB_RRDB_SORTKEY_COUNT) diff --git a/src/include/rrdb/rrdb.server.h b/src/include/rrdb/rrdb.server.h index 33762ac015..88aeaefba7 100644 --- a/src/include/rrdb/rrdb.server.h +++ b/src/include/rrdb/rrdb.server.h @@ -65,6 +65,14 @@ class rrdb_service : public replication::replication_app_base, check_and_set_response resp; reply(resp); } + // RPC_RRDB_RRDB_CHECK_AND_MUTATE + virtual void on_check_and_mutate(const check_and_mutate_request &args, + ::dsn::rpc_replier &reply) + { + std::cout << "... exec RPC_RRDB_RRDB_CHECK_AND_MUTATE ... (not implemented) " << std::endl; + check_and_mutate_response resp; + reply(resp); + } // RPC_RRDB_RRDB_GET virtual void on_get(const ::dsn::blob &args, ::dsn::rpc_replier &reply) { @@ -123,6 +131,8 @@ class rrdb_service : public replication::replication_app_base, register_async_rpc_handler(RPC_RRDB_RRDB_REMOVE, "remove", on_multi_remove); register_async_rpc_handler(RPC_RRDB_RRDB_INCR, "incr", on_incr); register_async_rpc_handler(RPC_RRDB_RRDB_CHECK_AND_SET, "check_and_set", on_check_and_set); + register_async_rpc_handler( + RPC_RRDB_RRDB_CHECK_AND_MUTATE, "check_and_mutate", on_check_and_mutate); register_async_rpc_handler(RPC_RRDB_RRDB_GET, "get", on_get); register_async_rpc_handler(RPC_RRDB_RRDB_MULTI_GET, "multi_get", on_multi_get); register_async_rpc_handler(RPC_RRDB_RRDB_SORTKEY_COUNT, "sortkey_count", on_sortkey_count); @@ -168,6 +178,12 @@ class rrdb_service : public replication::replication_app_base, { svc->on_check_and_set(args, reply); } + static void on_check_and_mutate(rrdb_service *svc, + const check_and_mutate_request &args, + ::dsn::rpc_replier &reply) + { + svc->on_check_and_mutate(args, reply); + } static void on_get(rrdb_service *svc, const ::dsn::blob &args, ::dsn::rpc_replier &reply) { @@ -206,5 +222,5 @@ class rrdb_service : public replication::replication_app_base, svc->on_clear_scanner(args); } }; -} -} +} // namespace apps +} // namespace dsn diff --git a/src/include/rrdb/rrdb.types.h b/src/include/rrdb/rrdb.types.h index 505db4186b..66d5b83142 100644 --- a/src/include/rrdb/rrdb.types.h +++ b/src/include/rrdb/rrdb.types.h @@ -21,6 +21,9 @@ GENERATED_TYPE_SERIALIZATION(incr_request, THRIFT) GENERATED_TYPE_SERIALIZATION(incr_response, THRIFT) GENERATED_TYPE_SERIALIZATION(check_and_set_request, THRIFT) GENERATED_TYPE_SERIALIZATION(check_and_set_response, THRIFT) +GENERATED_TYPE_SERIALIZATION(mutate, THRIFT) +GENERATED_TYPE_SERIALIZATION(check_and_mutate_request, THRIFT) +GENERATED_TYPE_SERIALIZATION(check_and_mutate_response, THRIFT) GENERATED_TYPE_SERIALIZATION(get_scanner_request, THRIFT) GENERATED_TYPE_SERIALIZATION(scan_request, THRIFT) GENERATED_TYPE_SERIALIZATION(scan_response, THRIFT) diff --git a/src/include/rrdb/rrdb_types.h b/src/include/rrdb/rrdb_types.h index 52bc4d8963..3ed92cd97e 100644 --- a/src/include/rrdb/rrdb_types.h +++ b/src/include/rrdb/rrdb_types.h @@ -60,6 +60,17 @@ struct cas_check_type extern const std::map _cas_check_type_VALUES_TO_NAMES; +struct mutate_operation +{ + enum type + { + MO_PUT = 0, + MO_DELETE = 1 + }; +}; + +extern const std::map _mutate_operation_VALUES_TO_NAMES; + class update_request; class update_response; @@ -90,6 +101,12 @@ class check_and_set_request; class check_and_set_response; +class mutate; + +class check_and_mutate_request; + +class check_and_mutate_response; + class get_scanner_request; class scan_request; @@ -1262,6 +1279,263 @@ inline std::ostream &operator<<(std::ostream &out, const check_and_set_response return out; } +typedef struct _mutate__isset +{ + _mutate__isset() : operation(false), sort_key(false), value(false), set_expire_ts_seconds(false) + { + } + bool operation : 1; + bool sort_key : 1; + bool value : 1; + bool set_expire_ts_seconds : 1; +} _mutate__isset; + +class mutate +{ +public: + mutate(const mutate &); + mutate(mutate &&); + mutate &operator=(const mutate &); + mutate &operator=(mutate &&); + mutate() : operation((mutate_operation::type)0), set_expire_ts_seconds(0) {} + + virtual ~mutate() throw(); + mutate_operation::type operation; + ::dsn::blob sort_key; + ::dsn::blob value; + int32_t set_expire_ts_seconds; + + _mutate__isset __isset; + + void __set_operation(const mutate_operation::type val); + + void __set_sort_key(const ::dsn::blob &val); + + void __set_value(const ::dsn::blob &val); + + void __set_set_expire_ts_seconds(const int32_t val); + + bool operator==(const mutate &rhs) const + { + if (!(operation == rhs.operation)) + return false; + if (!(sort_key == rhs.sort_key)) + return false; + if (!(value == rhs.value)) + return false; + if (!(set_expire_ts_seconds == rhs.set_expire_ts_seconds)) + return false; + return true; + } + bool operator!=(const mutate &rhs) const { return !(*this == rhs); } + + bool operator<(const mutate &) const; + + uint32_t read(::apache::thrift::protocol::TProtocol *iprot); + uint32_t write(::apache::thrift::protocol::TProtocol *oprot) const; + + virtual void printTo(std::ostream &out) const; +}; + +void swap(mutate &a, mutate &b); + +inline std::ostream &operator<<(std::ostream &out, const mutate &obj) +{ + obj.printTo(out); + return out; +} + +typedef struct _check_and_mutate_request__isset +{ + _check_and_mutate_request__isset() + : hash_key(false), + check_sort_key(false), + check_type(false), + check_operand(false), + mutate_list(false), + return_check_value(false) + { + } + bool hash_key : 1; + bool check_sort_key : 1; + bool check_type : 1; + bool check_operand : 1; + bool mutate_list : 1; + bool return_check_value : 1; +} _check_and_mutate_request__isset; + +class check_and_mutate_request +{ +public: + check_and_mutate_request(const check_and_mutate_request &); + check_and_mutate_request(check_and_mutate_request &&); + check_and_mutate_request &operator=(const check_and_mutate_request &); + check_and_mutate_request &operator=(check_and_mutate_request &&); + check_and_mutate_request() : check_type((cas_check_type::type)0), return_check_value(0) {} + + virtual ~check_and_mutate_request() throw(); + ::dsn::blob hash_key; + ::dsn::blob check_sort_key; + cas_check_type::type check_type; + ::dsn::blob check_operand; + std::vector mutate_list; + bool return_check_value; + + _check_and_mutate_request__isset __isset; + + void __set_hash_key(const ::dsn::blob &val); + + void __set_check_sort_key(const ::dsn::blob &val); + + void __set_check_type(const cas_check_type::type val); + + void __set_check_operand(const ::dsn::blob &val); + + void __set_mutate_list(const std::vector &val); + + void __set_return_check_value(const bool val); + + bool operator==(const check_and_mutate_request &rhs) const + { + if (!(hash_key == rhs.hash_key)) + return false; + if (!(check_sort_key == rhs.check_sort_key)) + return false; + if (!(check_type == rhs.check_type)) + return false; + if (!(check_operand == rhs.check_operand)) + return false; + if (!(mutate_list == rhs.mutate_list)) + return false; + if (!(return_check_value == rhs.return_check_value)) + return false; + return true; + } + bool operator!=(const check_and_mutate_request &rhs) const { return !(*this == rhs); } + + bool operator<(const check_and_mutate_request &) const; + + uint32_t read(::apache::thrift::protocol::TProtocol *iprot); + uint32_t write(::apache::thrift::protocol::TProtocol *oprot) const; + + virtual void printTo(std::ostream &out) const; +}; + +void swap(check_and_mutate_request &a, check_and_mutate_request &b); + +inline std::ostream &operator<<(std::ostream &out, const check_and_mutate_request &obj) +{ + obj.printTo(out); + return out; +} + +typedef struct _check_and_mutate_response__isset +{ + _check_and_mutate_response__isset() + : error(false), + check_value_returned(false), + check_value_exist(false), + check_value(false), + app_id(false), + partition_index(false), + decree(false), + server(false) + { + } + bool error : 1; + bool check_value_returned : 1; + bool check_value_exist : 1; + bool check_value : 1; + bool app_id : 1; + bool partition_index : 1; + bool decree : 1; + bool server : 1; +} _check_and_mutate_response__isset; + +class check_and_mutate_response +{ +public: + check_and_mutate_response(const check_and_mutate_response &); + check_and_mutate_response(check_and_mutate_response &&); + check_and_mutate_response &operator=(const check_and_mutate_response &); + check_and_mutate_response &operator=(check_and_mutate_response &&); + check_and_mutate_response() + : error(0), + check_value_returned(0), + check_value_exist(0), + app_id(0), + partition_index(0), + decree(0), + server() + { + } + + virtual ~check_and_mutate_response() throw(); + int32_t error; + bool check_value_returned; + bool check_value_exist; + ::dsn::blob check_value; + int32_t app_id; + int32_t partition_index; + int64_t decree; + std::string server; + + _check_and_mutate_response__isset __isset; + + void __set_error(const int32_t val); + + void __set_check_value_returned(const bool val); + + void __set_check_value_exist(const bool val); + + void __set_check_value(const ::dsn::blob &val); + + void __set_app_id(const int32_t val); + + void __set_partition_index(const int32_t val); + + void __set_decree(const int64_t val); + + void __set_server(const std::string &val); + + bool operator==(const check_and_mutate_response &rhs) const + { + if (!(error == rhs.error)) + return false; + if (!(check_value_returned == rhs.check_value_returned)) + return false; + if (!(check_value_exist == rhs.check_value_exist)) + return false; + if (!(check_value == rhs.check_value)) + return false; + if (!(app_id == rhs.app_id)) + return false; + if (!(partition_index == rhs.partition_index)) + return false; + if (!(decree == rhs.decree)) + return false; + if (!(server == rhs.server)) + return false; + return true; + } + bool operator!=(const check_and_mutate_response &rhs) const { return !(*this == rhs); } + + bool operator<(const check_and_mutate_response &) const; + + uint32_t read(::apache::thrift::protocol::TProtocol *iprot); + uint32_t write(::apache::thrift::protocol::TProtocol *oprot) const; + + virtual void printTo(std::ostream &out) const; +}; + +void swap(check_and_mutate_response &a, check_and_mutate_response &b); + +inline std::ostream &operator<<(std::ostream &out, const check_and_mutate_response &obj) +{ + obj.printTo(out); + return out; +} + typedef struct _get_scanner_request__isset { _get_scanner_request__isset() @@ -1511,7 +1785,7 @@ inline std::ostream &operator<<(std::ostream &out, const scan_response &obj) obj.printTo(out); return out; } -} -} // namespace +} // namespace apps +} // namespace dsn #endif diff --git a/src/server/config-server.ini b/src/server/config-server.ini index ae4b85015f..e40573f4e7 100644 --- a/src/server/config-server.ini +++ b/src/server/config-server.ini @@ -502,6 +502,20 @@ profiler::cancelled = false [task.RPC_RRDB_RRDB_CHECK_AND_SET_ACK] is_profile = true +[task.RPC_RRDB_RRDB_CHECK_AND_MUTATE] +rpc_request_throttling_mode = TM_DELAY +rpc_request_delays_milliseconds = 1000, 1000, 1000, 1000, 1000, 10000 +is_profile = true +profiler::inqueue = false +;profiler::queue = false +;profiler::exec = false +;profiler::qps = false +profiler::cancelled = false +;profiler::latency.server = false + +[task.RPC_RRDB_RRDB_CHECK_AND_MUTATE_ACK] +is_profile = true + [task.RPC_RRDB_RRDB_GET] rpc_request_throttling_mode = TM_DELAY rpc_request_delays_milliseconds = 1000, 1000, 1000, 1000, 1000, 10000 diff --git a/src/server/config.ini b/src/server/config.ini index 1b19061a64..950a6e9bdd 100644 --- a/src/server/config.ini +++ b/src/server/config.ini @@ -499,6 +499,14 @@ [task.RPC_RRDB_RRDB_CHECK_AND_SET_ACK] is_profile = true +[task.RPC_RRDB_RRDB_CHECK_AND_MUTATE] + rpc_request_throttling_mode = TM_DELAY + rpc_request_delays_milliseconds = 1000, 1000, 1000, 1000, 1000, 10000 + is_profile = true + +[task.RPC_RRDB_RRDB_CHECK_AND_MUTATE_ACK] + is_profile = true + [task.RPC_RRDB_RRDB_GET] rpc_request_throttling_mode = TM_DELAY rpc_request_delays_milliseconds = 1000, 1000, 1000, 1000, 1000, 10000 diff --git a/src/server/info_collector.cpp b/src/server/info_collector.cpp index eb49e47b0f..224a2f0caa 100644 --- a/src/server/info_collector.cpp +++ b/src/server/info_collector.cpp @@ -90,6 +90,7 @@ void info_collector::on_app_stat() all.multi_remove_qps += row.multi_remove_qps; all.incr_qps += row.incr_qps; all.check_and_set_qps += row.check_and_set_qps; + all.check_and_mutate_qps += row.check_and_mutate_qps; all.scan_qps += row.scan_qps; all.recent_expire_count += row.recent_expire_count; all.recent_filter_count += row.recent_filter_count; @@ -98,12 +99,12 @@ void info_collector::on_app_stat() all.storage_count += row.storage_count; read_qps[i] = row.get_qps + row.multi_get_qps + row.scan_qps; write_qps[i] = row.put_qps + row.multi_put_qps + row.remove_qps + row.multi_remove_qps + - row.incr_qps + row.check_and_set_qps; + row.incr_qps + row.check_and_set_qps + row.check_and_mutate_qps; } read_qps[read_qps.size() - 1] = all.get_qps + all.multi_get_qps + all.scan_qps; write_qps[read_qps.size() - 1] = all.put_qps + all.multi_put_qps + all.remove_qps + all.multi_remove_qps + all.incr_qps + - all.check_and_set_qps; + all.check_and_set_qps + all.check_and_mutate_qps; for (int i = 0; i < rows.size(); ++i) { row_data &row = rows[i]; AppStatCounters *counters = get_app_counters(row.row_name); @@ -115,6 +116,7 @@ void info_collector::on_app_stat() counters->multi_remove_qps->set(row.multi_remove_qps); counters->incr_qps->set(row.incr_qps); counters->check_and_set_qps->set(row.check_and_set_qps); + counters->check_and_mutate_qps->set(row.check_and_mutate_qps); counters->scan_qps->set(row.scan_qps); counters->recent_expire_count->set(row.recent_expire_count); counters->recent_filter_count->set(row.recent_filter_count); @@ -157,6 +159,7 @@ info_collector::AppStatCounters *info_collector::get_app_counters(const std::str INIT_COUNER(multi_remove_qps); INIT_COUNER(incr_qps); INIT_COUNER(check_and_set_qps); + INIT_COUNER(check_and_mutate_qps); INIT_COUNER(scan_qps); INIT_COUNER(recent_expire_count); INIT_COUNER(recent_filter_count); @@ -168,5 +171,5 @@ info_collector::AppStatCounters *info_collector::get_app_counters(const std::str _app_stat_counters[app_name] = counters; return counters; } -} -} // namespace +} // namespace server +} // namespace pegasus diff --git a/src/server/info_collector.h b/src/server/info_collector.h index 69a785a5e2..52299081d5 100644 --- a/src/server/info_collector.h +++ b/src/server/info_collector.h @@ -36,6 +36,7 @@ class info_collector ::dsn::perf_counter_wrapper multi_remove_qps; ::dsn::perf_counter_wrapper incr_qps; ::dsn::perf_counter_wrapper check_and_set_qps; + ::dsn::perf_counter_wrapper check_and_mutate_qps; ::dsn::perf_counter_wrapper scan_qps; ::dsn::perf_counter_wrapper recent_expire_count; ::dsn::perf_counter_wrapper recent_filter_count; @@ -66,5 +67,5 @@ class info_collector ::dsn::utils::ex_lock_nr _app_stat_counter_lock; std::map _app_stat_counters; }; -} -} // namespace +} // namespace server +} // namespace pegasus diff --git a/src/server/pegasus_server_write.cpp b/src/server/pegasus_server_write.cpp index fff3f292cc..00ad4ef8a7 100644 --- a/src/server/pegasus_server_write.cpp +++ b/src/server/pegasus_server_write.cpp @@ -54,6 +54,11 @@ int pegasus_server_write::on_batched_write_requests(dsn_message_t *requests, auto rpc = check_and_set_rpc::auto_reply(requests[0]); return _write_svc->check_and_set(_decree, rpc.request(), rpc.response()); } + if (rpc_code == dsn::apps::RPC_RRDB_RRDB_CHECK_AND_MUTATE) { + dassert(count == 1, "count = %d", count); + auto rpc = check_and_mutate_rpc::auto_reply(requests[0]); + return _write_svc->check_and_mutate(_decree, rpc.request(), rpc.response()); + } return on_batched_writes(requests, count); } diff --git a/src/server/pegasus_write_service.cpp b/src/server/pegasus_write_service.cpp index 1abdb03274..07944c0a60 100644 --- a/src/server/pegasus_write_service.cpp +++ b/src/server/pegasus_write_service.cpp @@ -43,6 +43,12 @@ pegasus_write_service::pegasus_write_service(pegasus_server_impl *server) COUNTER_TYPE_RATE, "statistic the qps of CHECK_AND_SET request"); + name = fmt::format("check_and_mutate_qps@{}", str_gpid); + _pfc_check_and_mutate_qps.init_app_counter("app.pegasus", + name.c_str(), + COUNTER_TYPE_RATE, + "statistic the qps of CHECK_AND_MUTATE request"); + name = fmt::format("put_latency@{}", str_gpid); _pfc_put_latency.init_app_counter("app.pegasus", name.c_str(), @@ -78,6 +84,13 @@ pegasus_write_service::pegasus_write_service(pegasus_server_impl *server) name.c_str(), COUNTER_TYPE_NUMBER_PERCENTILES, "statistic the latency of CHECK_AND_SET request"); + + name = fmt::format("check_and_mutate_latency@{}", str_gpid); + _pfc_check_and_mutate_latency.init_app_counter( + "app.pegasus", + name.c_str(), + COUNTER_TYPE_NUMBER_PERCENTILES, + "statistic the latency of CHECK_AND_MUTATE request"); } pegasus_write_service::~pegasus_write_service() {} @@ -128,6 +141,17 @@ int pegasus_write_service::check_and_set(int64_t decree, return err; } +int pegasus_write_service::check_and_mutate(int64_t decree, + const dsn::apps::check_and_mutate_request &update, + dsn::apps::check_and_mutate_response &resp) +{ + uint64_t start_time = dsn_now_ns(); + _pfc_check_and_mutate_qps->increment(); + int err = _impl->check_and_mutate(decree, update, resp); + _pfc_check_and_mutate_latency->set(dsn_now_ns() - start_time); + return err; +} + void pegasus_write_service::batch_prepare(int64_t decree) { dassert(_batch_start_time == 0, diff --git a/src/server/pegasus_write_service.h b/src/server/pegasus_write_service.h index e621bf32e7..3c859ac9ea 100644 --- a/src/server/pegasus_write_service.h +++ b/src/server/pegasus_write_service.h @@ -50,6 +50,11 @@ class pegasus_write_service const dsn::apps::check_and_set_request &update, dsn::apps::check_and_set_response &resp); + // Write CHECK_AND_MUTATE record. + int check_and_mutate(int64_t decree, + const dsn::apps::check_and_mutate_request &update, + dsn::apps::check_and_mutate_response &resp); + /// For batch write. /// NOTE: A batch write may incur a database read for consistency check of timetag. /// (see pegasus::pegasus_value_generator::generate_value_v1 for more info about timetag) @@ -97,6 +102,7 @@ class pegasus_write_service ::dsn::perf_counter_wrapper _pfc_multi_remove_qps; ::dsn::perf_counter_wrapper _pfc_incr_qps; ::dsn::perf_counter_wrapper _pfc_check_and_set_qps; + ::dsn::perf_counter_wrapper _pfc_check_and_mutate_qps; ::dsn::perf_counter_wrapper _pfc_put_latency; ::dsn::perf_counter_wrapper _pfc_multi_put_latency; @@ -104,6 +110,7 @@ class pegasus_write_service ::dsn::perf_counter_wrapper _pfc_multi_remove_latency; ::dsn::perf_counter_wrapper _pfc_incr_latency; ::dsn::perf_counter_wrapper _pfc_check_and_set_latency; + ::dsn::perf_counter_wrapper _pfc_check_and_mutate_latency; // Records all requests. std::vector<::dsn::perf_counter *> _batch_qps_perfcounters; diff --git a/src/server/pegasus_write_service_impl.h b/src/server/pegasus_write_service_impl.h index 84318c8b74..5bd1262b90 100644 --- a/src/server/pegasus_write_service_impl.h +++ b/src/server/pegasus_write_service_impl.h @@ -229,37 +229,52 @@ class pegasus_write_service::impl : public dsn::replication::replica_base pegasus_generate_key(check_key, update.hash_key, update.check_sort_key); rocksdb::Slice check_raw_key(check_key.data(), check_key.length()); std::string check_raw_value; - rocksdb::Status s = _db->Get(_rd_opts, check_raw_key, &check_raw_value); - if (s.ok()) { + rocksdb::Status check_status = _db->Get(_rd_opts, check_raw_key, &check_raw_value); + if (check_status.ok()) { // read check value succeed if (check_if_record_expired( _value_schema_version, utils::epoch_now(), check_raw_value)) { // check value ttl timeout _pfc_recent_expire_count->increment(); - s = rocksdb::Status::NotFound(); + check_status = rocksdb::Status::NotFound(); } - } else if (!s.IsNotFound()) { + } else if (!check_status.IsNotFound()) { // read check value failed derror_rocksdb("GetCheckValue for CheckAndSet", - s.ToString(), + check_status.ToString(), "decree: {}, hash_key: {}, check_sort_key: {}", decree, utils::c_escape_string(update.hash_key), utils::c_escape_string(update.check_sort_key)); - resp.error = s.code(); + resp.error = check_status.code(); return resp.error; } - dassert(s.ok() || s.IsNotFound(), "status = %s", s.ToString().c_str()); + dassert_f(check_status.ok() || check_status.IsNotFound(), + "status = %s", + check_status.ToString().c_str()); ::dsn::blob check_value; - if (s.ok()) { + if (check_status.ok()) { pegasus_extract_user_data( _value_schema_version, std::move(check_raw_value), check_value); } + if (update.return_check_value) { + resp.check_value_returned = true; + if (check_status.ok()) { + resp.check_value_exist = true; + resp.check_value = check_value; + } + } + bool invalid_argument = false; - bool passed = validate_check( - decree, update.check_type, update.check_operand, s.ok(), check_value, invalid_argument); + bool passed = validate_check(decree, + update.check_type, + update.check_operand, + check_status.ok(), + check_value, + invalid_argument); + if (passed) { // check passed, write new value ::dsn::blob set_key; @@ -293,12 +308,141 @@ class pegasus_write_service::impl : public dsn::replication::replica_base invalid_argument ? rocksdb::Status::kInvalidArgument : rocksdb::Status::kTryAgain; } + clear_up_batch_states(decree, resp.error); + return 0; + } + + int check_and_mutate(int64_t decree, + const dsn::apps::check_and_mutate_request &update, + dsn::apps::check_and_mutate_response &resp) + { + resp.app_id = get_gpid().get_app_id(); + resp.partition_index = get_gpid().get_partition_index(); + resp.decree = decree; + resp.server = _primary_address; + + if (update.mutate_list.empty()) { + derror_replica("invalid argument for check_and_mutate: decree = {}, error = {}", + decree, + "mutate list is empty"); + resp.error = rocksdb::Status::kInvalidArgument; + // we should write empty record to update rocksdb's last flushed decree + return empty_put(decree); + } + + for (int i = 0; i < update.mutate_list.size(); ++i) { + auto &mu = update.mutate_list[i]; + if (mu.operation != ::dsn::apps::mutate_operation::MO_PUT && + mu.operation != ::dsn::apps::mutate_operation::MO_DELETE) { + derror_replica("invalid argument for check_and_mutate: decree = {}, error = " + "mutation[{}] uses invalid operation {}", + decree, + i, + mu.operation); + resp.error = rocksdb::Status::kInvalidArgument; + // we should write empty record to update rocksdb's last flushed decree + return empty_put(decree); + } + } + + if (!is_check_type_supported(update.check_type)) { + derror_replica("invalid argument for check_and_mutate: decree = {}, error = {}", + decree, + "check type {} not supported", + update.check_type); + resp.error = rocksdb::Status::kInvalidArgument; + // we should write empty record to update rocksdb's last flushed decree + return empty_put(decree); + } + + ::dsn::blob check_key; + pegasus_generate_key(check_key, update.hash_key, update.check_sort_key); + rocksdb::Slice check_raw_key(check_key.data(), check_key.length()); + std::string check_raw_value; + rocksdb::Status check_status = _db->Get(_rd_opts, check_raw_key, &check_raw_value); + if (check_status.ok()) { + // read check value succeed + if (check_if_record_expired( + _value_schema_version, utils::epoch_now(), check_raw_value)) { + // check value ttl timeout + _pfc_recent_expire_count->increment(); + check_status = rocksdb::Status::NotFound(); + } + } else if (!check_status.IsNotFound()) { + // read check value failed + derror_rocksdb("GetCheckValue for CheckAndMutate", + check_status.ToString(), + "decree: {}, hash_key: {}, check_sort_key: {}", + decree, + utils::c_escape_string(update.hash_key), + utils::c_escape_string(update.check_sort_key)); + resp.error = check_status.code(); + return resp.error; + } + dassert_f(check_status.ok() || check_status.IsNotFound(), + "status = %s", + check_status.ToString().c_str()); + + ::dsn::blob check_value; + if (check_status.ok()) { + pegasus_extract_user_data( + _value_schema_version, std::move(check_raw_value), check_value); + } + if (update.return_check_value) { resp.check_value_returned = true; - if (s.ok()) { + if (check_status.ok()) { resp.check_value_exist = true; - resp.check_value = std::move(check_value); + resp.check_value = check_value; + } + } + + bool invalid_argument = false; + bool passed = validate_check(decree, + update.check_type, + update.check_operand, + check_status.ok(), + check_value, + invalid_argument); + + if (passed) { + for (auto &m : update.mutate_list) { + ::dsn::blob key; + pegasus_generate_key(key, update.hash_key, m.sort_key); + if (m.operation == ::dsn::apps::mutate_operation::MO_PUT) { + resp.error = db_write_batch_put( + decree, key, m.value, static_cast(m.set_expire_ts_seconds)); + } else { + dassert_f(m.operation == ::dsn::apps::mutate_operation::MO_DELETE, + "m.operation = %d", + m.operation); + resp.error = db_write_batch_delete(decree, key); + } + + // in case of failure, cancel mutations + if (resp.error) + break; } + } else { + // check not passed, write empty record to update rocksdb's last flushed decree + resp.error = db_write_batch_put(decree, dsn::string_view(), dsn::string_view(), 0); + } + + if (resp.error) { + clear_up_batch_states(decree, resp.error); + return resp.error; + } + + resp.error = db_write(decree); + if (resp.error) { + clear_up_batch_states(decree, resp.error); + return resp.error; + } + + if (!passed) { + // check not passed, return proper error code to user + resp.error = + invalid_argument ? rocksdb::Status::kInvalidArgument : rocksdb::Status::kTryAgain; } clear_up_batch_states(decree, resp.error); diff --git a/src/shell/command_helper.h b/src/shell/command_helper.h index 8a473fc215..ff71b3c4a3 100644 --- a/src/shell/command_helper.h +++ b/src/shell/command_helper.h @@ -376,6 +376,7 @@ struct row_data double multi_remove_qps; double incr_qps; double check_and_set_qps; + double check_and_mutate_qps; double scan_qps; double recent_expire_count; double recent_filter_count; @@ -391,6 +392,7 @@ struct row_data multi_remove_qps(0), incr_qps(0), check_and_set_qps(0), + check_and_mutate_qps(0), scan_qps(0), recent_expire_count(0), recent_filter_count(0), @@ -419,6 +421,8 @@ update_app_pegasus_perf_counter(row_data &row, const std::string &counter_name, row.incr_qps += value; else if (counter_name == "check_and_set_qps") row.check_and_set_qps += value; + else if (counter_name == "check_and_mutate_qps") + row.check_and_mutate_qps += value; else if (counter_name == "scan_qps") row.scan_qps += value; else if (counter_name == "recent.expire.count") diff --git a/src/shell/commands.h b/src/shell/commands.h index 562e9811cf..2de5de617a 100644 --- a/src/shell/commands.h +++ b/src/shell/commands.h @@ -1454,6 +1454,251 @@ inline bool check_and_set(command_executor *e, shell_context *sc, arguments args return true; } +inline int mutation_check(int args_count, sds *args) +{ + int ret = -2; + if (args_count > 0) { + std::string op = unescape_str(args[0]); + if (op == "abort") + ret = -1; + else if (op == "ok") + ret = 0; + else if (op == "set" && (args_count == 3 || args_count == 4)) + ret = 1; + else if (op == "del" && args_count == 2) + ret = 2; + } + return ret; +} + +inline int load_mutations(shell_context *sc, pegasus::pegasus_client::mutations &mutations) +{ + while (true) { + int arg_count = 0; + sds *args = scanfCommand(&arg_count); + auto cleanup = dsn::defer([args, arg_count] { sdsfreesplitres(args, arg_count); }); + escape_sds_argv(arg_count, args); + + std::string sort_key, value; + int ttl = 0; + int status = mutation_check(arg_count, args); + switch (status) { + case -1: + fprintf(stderr, "INFO: abort loading\n"); + return -1; + case 0: + fprintf(stderr, "INFO: load mutations done.\n\n"); + return 0; + case 1: // SET + ttl = 0; + if (arg_count == 4) { + if (!dsn::buf2int32(args[3], ttl)) { + fprintf(stderr, + "ERROR: parse \"%s\" as ttl failed, " + "print \"ok\" to finish loading, print \"abort\" to abort this " + "command\n", + args[3]); + break; + } + if (ttl <= 0) { + fprintf(stderr, + "ERROR: invalid ttl %s, " + "print \"ok\" to finish loading, print \"abort\" to abort this " + "command\n", + args[3]); + break; + } + } + sort_key = unescape_str(args[1]); + value = unescape_str(args[2]); + fprintf(stderr, + "LOAD: set sortkey \"%s\", value \"%s\", ttl %d\n", + pegasus::utils::c_escape_string(sort_key, sc->escape_all).c_str(), + pegasus::utils::c_escape_string(value, sc->escape_all).c_str(), + ttl); + mutations.set(sort_key, value, ttl); + break; + case 2: // DEL + sort_key = unescape_str(args[1]); + fprintf(stderr, + "LOAD: del sortkey \"%s\"\n", + pegasus::utils::c_escape_string(sort_key, sc->escape_all).c_str()); + mutations.del(sort_key); + break; + default: + fprintf(stderr, "ERROR: invalid mutation, print \"ok\" to finish loading\n"); + break; + } + } + return 0; +} + +inline bool check_and_mutate(command_executor *e, shell_context *sc, arguments args) +{ + if (args.argc < 2) + return false; + + std::string hash_key = sds_to_string(args.argv[1]); + bool check_sort_key_provided = false; + std::string check_sort_key; + ::dsn::apps::cas_check_type::type check_type = ::dsn::apps::cas_check_type::CT_NO_CHECK; + std::string check_type_name; + bool check_operand_provided = false; + std::string check_operand; + pegasus::pegasus_client::mutations mutations; + + pegasus::pegasus_client::check_and_mutate_options options; + static struct option long_options[] = {{"check_sort_key", required_argument, 0, 'c'}, + {"check_type", required_argument, 0, 't'}, + {"check_operand", required_argument, 0, 'o'}, + {"return_check_value", no_argument, 0, 'r'}, + {0, 0, 0, 0}}; + + escape_sds_argv(args.argc, args.argv); + std::string str; + optind = 0; + while (true) { + int option_index = 0; + int c; + c = getopt_long(args.argc, args.argv, "c:t:o:r", long_options, &option_index); + if (c == -1) + break; + switch (c) { + case 'c': + check_sort_key_provided = true; + check_sort_key = unescape_str(optarg); + break; + case 't': + check_type = type_from_string(::dsn::apps::_cas_check_type_VALUES_TO_NAMES, + std::string("ct_value_") + optarg, + ::dsn::apps::cas_check_type::CT_NO_CHECK); + if (check_type == ::dsn::apps::cas_check_type::CT_NO_CHECK) { + fprintf(stderr, "ERROR: invalid check_type param\n"); + return false; + } + check_type_name = optarg; + break; + case 'o': + check_operand_provided = true; + check_operand = unescape_str(optarg); + break; + case 'r': + options.return_check_value = true; + break; + default: + return false; + } + } + + if (!check_sort_key_provided) { + fprintf(stderr, "ERROR: check_sort_key not provided\n"); + return false; + } + if (check_type == ::dsn::apps::cas_check_type::CT_NO_CHECK) { + fprintf(stderr, "ERROR: check_type not provided\n"); + return false; + } + if (!check_operand_provided && + check_type >= ::dsn::apps::cas_check_type::CT_VALUE_MATCH_ANYWHERE) { + fprintf(stderr, "ERROR: check_operand not provided\n"); + return false; + } + + fprintf(stderr, + "Load mutations, like\n" + " set [ttl]\n" + " del \n" + "Print \"ok\" to finish loading, \"abort\" to abort this command\n"); + if (load_mutations(sc, mutations)) { + fprintf(stderr, "INFO: abort check_and_mutate command\n"); + return true; + } + if (mutations.is_empty()) { + fprintf(stderr, "ERROR: mutations not provided\n"); + return false; + } + + fprintf(stderr, "hash_key: \"%s\"\n", pegasus::utils::c_escape_string(hash_key).c_str()); + fprintf(stderr, + "check_sort_key: \"%s\"\n", + pegasus::utils::c_escape_string(check_sort_key).c_str()); + fprintf(stderr, "check_type: %s\n", check_type_name.c_str()); + if (check_type >= ::dsn::apps::cas_check_type::CT_VALUE_MATCH_ANYWHERE) { + fprintf(stderr, + "check_operand: \"%s\"\n", + pegasus::utils::c_escape_string(check_operand).c_str()); + } + fprintf(stderr, "return_check_value: %s\n", options.return_check_value ? "true" : "false"); + + std::vector<::dsn::apps::mutate> copy_of_mutations; + mutations.get_mutations(copy_of_mutations); + fprintf(stderr, "mutations:\n"); + for (int i = 0; i < copy_of_mutations.size(); ++i) { + if (copy_of_mutations[i].operation == ::dsn::apps::mutate_operation::MO_PUT) { + fprintf( + stderr, + " mutation[%d].type: SET\n mutation[%d].sort_key: \"%s\"\n " + "mutation[%d].value: " + "\"%s\"\n mutation[%d].expire_seconds: %d\n", + i, + i, + pegasus::utils::c_escape_string(copy_of_mutations[i].sort_key.to_string()).c_str(), + i, + pegasus::utils::c_escape_string(copy_of_mutations[i].value.to_string()).c_str(), + i, + copy_of_mutations[i].set_expire_ts_seconds); + } else { + fprintf( + stderr, + " mutation[%d].type: DEL\n mutation[%d].sort_key: \"%s\"\n", + i, + i, + pegasus::utils::c_escape_string(copy_of_mutations[i].sort_key.to_string()).c_str()); + } + } + fprintf(stderr, "\n"); + + pegasus::pegasus_client::check_and_mutate_results results; + pegasus::pegasus_client::internal_info info; + int ret = sc->pg_client->check_and_mutate(hash_key, + check_sort_key, + (pegasus::pegasus_client::cas_check_type)check_type, + check_operand, + mutations, + options, + results, + sc->timeout_ms, + &info); + if (ret != pegasus::PERR_OK) { + fprintf(stderr, "ERROR: %s\n", sc->pg_client->get_error_string(ret)); + } else { + if (results.mutate_succeed) { + fprintf(stderr, "Mutate succeed.\n"); + } else { + fprintf(stderr, "Mutate failed, because check not passed.\n"); + } + if (results.check_value_returned) { + fprintf(stderr, "\n"); + if (results.check_value_exist) { + fprintf( + stderr, + "Check value: \"%s\"\n", + pegasus::utils::c_escape_string(results.check_value, sc->escape_all).c_str()); + } else { + fprintf(stderr, "Check value not exist.\n"); + } + } + } + + fprintf(stderr, "\n"); + fprintf(stderr, "app_id : %d\n", info.app_id); + fprintf(stderr, "partition_index : %d\n", info.partition_index); + fprintf(stderr, "decree : %ld\n", info.decree); + fprintf(stderr, "server : %s\n", info.server.c_str()); + + return true; +} + inline bool get_ttl(command_executor *e, shell_context *sc, arguments args) { if (args.argc != 3) { @@ -2593,6 +2838,7 @@ inline bool data_operations(command_executor *e, shell_context *sc, arguments ar {"multi_del_range", multi_del_range}, {"incr", incr}, {"check_and_set", check_and_set}, + {"check_and_mutate", check_and_mutate}, {"exist", exist}, {"count", sortkey_count}, {"ttl", get_ttl}, @@ -3406,11 +3652,12 @@ inline bool app_stat(command_executor *e, shell_context *sc, arguments args) << std::setw(w) << std::right << "PUT" << std::setw(w) << std::right << "MPUT" << std::setw(w) << std::right << "DEL" << std::setw(w) << std::right << "MDEL" << std::setw(w) << std::right << "INCR" << std::setw(w) << std::right << "CAS" - << std::setw(w) << std::right << "SCAN"; + << std::setw(w) << std::right << "CAM" << std::setw(w) << std::right << "SCAN"; if (!only_qps) { - out << std::setw(w) << std::right << "expired" << std::setw(w) << std::right << "filtered" - << std::setw(w) << std::right << "abnormal" << std::setw(w) << std::right << "file_mb" - << std::setw(w) << std::right << "file_num"; + out << std::setw(w) << std::right << "storage_mb" << std::setw(w) << std::right + << "file_count" << std::setw(w) << std::right << "expired" << std::setw(w) << std::right + << "filtered" << std::setw(w) << std::right << "abnormal" << std::setw(w) << std::right + << "file_mb" << std::setw(w) << std::right << "file_num"; } out << std::endl; rows.resize(rows.size() + 1); @@ -3425,6 +3672,7 @@ inline bool app_stat(command_executor *e, shell_context *sc, arguments args) sum.multi_remove_qps += row.multi_remove_qps; sum.incr_qps += row.incr_qps; sum.check_and_set_qps += row.check_and_set_qps; + sum.check_and_mutate_qps += row.check_and_mutate_qps; sum.scan_qps += row.scan_qps; sum.recent_expire_count += row.recent_expire_count; sum.recent_filter_count += row.recent_filter_count; @@ -3450,6 +3698,7 @@ inline bool app_stat(command_executor *e, shell_context *sc, arguments args) PRINT_QPS(multi_remove_qps); PRINT_QPS(incr_qps); PRINT_QPS(check_and_set_qps); + PRINT_QPS(check_and_mutate_qps); PRINT_QPS(scan_qps); if (!only_qps) { out << std::setw(w) << std::right << (int64_t)row.recent_expire_count << std::setw(w) diff --git a/src/shell/main.cpp b/src/shell/main.cpp index ebae506685..ba84d3850b 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -194,6 +194,19 @@ static command_executor commands[] = { "[-r|--return_check_value]", data_operations, }, + { + "check_and_mutate", + "atomically check and mutate", + " " + "[-c|--check_sort_key str] " + "[-t|--check_type not_exist|not_exist_or_empty|exist|not_empty] " + "[match_anywhere|match_prefix|match_postfix] " + "[bytes_less|bytes_less_or_equal|bytes_equal|bytes_greater_or_equal|bytes_greater] " + "[int_less|int_less_or_equal|int_equal|int_greater_or_equal|int_greater] " + "[-o|--check_operand str] " + "[-r|--return_check_value]", + data_operations, + }, { "exist", "check value exist", " ", data_operations, }, @@ -546,7 +559,11 @@ void run() if (arg_count > 0) { auto iter = s_commands_map.find(args[0]); if (iter != s_commands_map.end()) { + // command executions(e.g. check_and_mutate) may have the different hints, so cancel + // the commands hints temporarily + linenoiseSetHintsCallback(nullptr); execute_command(iter->second, arg_count, args); + linenoiseSetHintsCallback(hintsCallback); } else { std::cout << "ERROR: invalid subcommand '" << args[0] << "'" << std::endl; print_help(); diff --git a/src/test/function_test/run.sh b/src/test/function_test/run.sh index f4c4211f30..e598a275ff 100755 --- a/src/test/function_test/run.sh +++ b/src/test/function_test/run.sh @@ -17,10 +17,12 @@ table_name=temp GTEST_OUTPUT="xml:$REPORT_DIR/basic.xml" GTEST_FILTER="basic.*" ./$test_case $config_file $table_name exit_if_fail $? "run test basic failed: $test_case $config_file $table_name" -GTEST_OUTPUT="xml:$REPORT_DIR/incr" GTEST_FILTER="incr.*" ./$test_case $config_file $table_name +GTEST_OUTPUT="xml:$REPORT_DIR/incr.xml" GTEST_FILTER="incr.*" ./$test_case $config_file $table_name exit_if_fail $? "run test incr failed: $test_case $config_file $table_name" GTEST_OUTPUT="xml:$REPORT_DIR/check_and_set.xml" GTEST_FILTER="check_and_set.*" ./$test_case $config_file $table_name exit_if_fail $? "run test check_and_set failed: $test_case $config_file $table_name" +GTEST_OUTPUT="xml:$REPORT_DIR/check_and_mutate.xml" GTEST_FILTER="check_and_mutate.*" ./$test_case $config_file $table_name +exit_if_fail $? "run test check_and_mutate failed: $test_case $config_file $table_name" GTEST_OUTPUT="xml:$REPORT_DIR/scan.xml" GTEST_FILTER="scan.*" ./$test_case $config_file $table_name exit_if_fail $? "run test scan failed: $test_case $config_file $table_name" GTEST_OUTPUT="xml:$REPORT_DIR/slog_log.xml" GTEST_FILTER="lost_log.*" ./$test_case $config_file $table_name diff --git a/src/test/function_test/test_check_and_mutate.cpp b/src/test/function_test/test_check_and_mutate.cpp new file mode 100644 index 0000000000..1ee67e2864 --- /dev/null +++ b/src/test/function_test/test_check_and_mutate.cpp @@ -0,0 +1,1834 @@ +// Copyright (c) 2017, Xiaomi, Inc. All rights reserved. +// This source code is licensed under the Apache License Version 2.0, which +// can be found in the LICENSE file in the root directory of this source tree. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::pegasus; + +extern pegasus_client *client; + +TEST(check_and_mutate, value_not_exist) +{ + std::string hash_key("check_and_mutate_test_value_not_exist"); + + { + int ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + + options.return_check_value = false; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_FALSE(results.check_value_returned); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->del(hash_key, "k2"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k2", ""); + ret = client->check_and_mutate(hash_key, + "k2", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k2", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("", value); + + options.return_check_value = true; + mutations.set("k2", "v2"); + ret = client->check_and_mutate(hash_key, + "k2", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k2", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("", value); + + ret = client->del(hash_key, "k2"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->del(hash_key, "k3"); + ASSERT_EQ(0, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k4", "v4"); + ret = client->check_and_mutate(hash_key, + "k3", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k4", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v4", value); + + ret = client->del(hash_key, "k3"); + ASSERT_EQ(0, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + } +} + +TEST(check_and_mutate, value_exist) +{ + std::string hash_key("check_and_mutate_test_value_exist"); + + { + int ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_NOT_FOUND, ret); + + ret = client->set(hash_key, "k1", ""); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->set(hash_key, "k3", "v3"); + ASSERT_EQ(PERR_OK, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k4", "v4"); + ret = client->check_and_mutate(hash_key, + "k3", + pegasus_client::cas_check_type::CT_VALUE_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v3", results.check_value); + ret = client->get(hash_key, "k4", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v4", value); + + ret = client->del(hash_key, "k3"); + ASSERT_EQ(0, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + } +} + +TEST(check_and_mutate, value_not_empty) +{ + std::string hash_key("check_and_mutate_test_value_not_empty"); + + { + int ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EMPTY, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_NOT_FOUND, ret); + + ret = client->set(hash_key, "k1", ""); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EMPTY, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("", value); + + ret = client->set(hash_key, "k1", "v1"); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EMPTY, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->set(hash_key, "k3", "v3"); + ASSERT_EQ(PERR_OK, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k4", "v4"); + ret = client->check_and_mutate(hash_key, + "k3", + pegasus_client::cas_check_type::CT_VALUE_NOT_EMPTY, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v3", results.check_value); + ret = client->get(hash_key, "k4", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v4", value); + + ret = client->del(hash_key, "k3"); + ASSERT_EQ(0, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + } +} +TEST(check_and_mutate, value_match_anywhere) +{ + std::string hash_key("check_and_mutate_test_value_match_anywhere"); + + { + int ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_NOT_FOUND, ret); + + ret = client->set(hash_key, "k1", ""); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("", value); + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v111v"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v111v", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "111", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v111v", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "y", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "v2v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "v2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v3", value); + + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->set(hash_key, "k3", "v333v"); + ASSERT_EQ(PERR_OK, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k4", "v4"); + ret = client->check_and_mutate(hash_key, + "k3", + pegasus_client::cas_check_type::CT_VALUE_MATCH_ANYWHERE, + "333", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v333v", results.check_value); + ret = client->get(hash_key, "k4", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v4", value); + + ret = client->del(hash_key, "k3"); + ASSERT_EQ(0, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + } +} + +TEST(check_and_mutate, value_match_prefix) +{ + std::string hash_key("check_and_mutate_test_value_match_prefix"); + + { + int ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_NOT_FOUND, ret); + + ret = client->set(hash_key, "k1", ""); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("", value); + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v111v"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v111v", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "111", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v111v", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v111v", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "v111", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v111v", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "y", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "v2v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "v2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v3", value); + + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->set(hash_key, "k3", "v333v"); + ASSERT_EQ(PERR_OK, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k4", "v4"); + ret = client->check_and_mutate(hash_key, + "k3", + pegasus_client::cas_check_type::CT_VALUE_MATCH_PREFIX, + "v333", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v333v", results.check_value); + ret = client->get(hash_key, "k4", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v4", value); + + ret = client->del(hash_key, "k3"); + ASSERT_EQ(0, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + } +} + +TEST(check_and_mutate, value_match_postfix) +{ + std::string hash_key("check_and_mutate_test_value_match_postfix"); + + { + int ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_NOT_FOUND, ret); + + ret = client->set(hash_key, "k1", ""); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("", value); + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v111v"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v111v", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "111", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v111v", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v111v", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "111v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v111v", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "y", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "2v2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + options.return_check_value = true; + mutations.set("k1", "v3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "v2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v3", value); + + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->set(hash_key, "k3", "v333v"); + ASSERT_EQ(PERR_OK, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k4", "v4"); + ret = client->check_and_mutate(hash_key, + "k3", + pegasus_client::cas_check_type::CT_VALUE_MATCH_POSTFIX, + "333v", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v333v", results.check_value); + ret = client->get(hash_key, "k4", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v4", value); + + ret = client->del(hash_key, "k3"); + ASSERT_EQ(0, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + } +} + +TEST(check_and_mutate, value_bytes_compare) +{ + std::string hash_key("check_and_mutate_test_value_bytes_compare"); + + { + int ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_BYTES_EQUAL, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_NOT_FOUND, ret); + + ret = client->set(hash_key, "k1", ""); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_BYTES_EQUAL, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_BYTES_EQUAL, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + + options.return_check_value = true; + mutations.set("k1", "v2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_BYTES_EQUAL, + "v1", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->set(hash_key, "k3", "v3"); + ASSERT_EQ(PERR_OK, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k4", "v4"); + ret = client->check_and_mutate(hash_key, + "k3", + pegasus_client::cas_check_type::CT_VALUE_BYTES_EQUAL, + "v3", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v3", results.check_value); + ret = client->get(hash_key, "k4", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v4", value); + + ret = client->del(hash_key, "k3"); + ASSERT_EQ(0, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->set(hash_key, "k5", "v1"); + ASSERT_EQ(PERR_OK, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + // v1 < v2 + options.return_check_value = true; + mutations.set("k5", "v2"); + ret = client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_BYTES_LESS, + "v2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v1", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v2", value); + + // v2 <= v2 + options.return_check_value = true; + mutations.set("k5", "v3"); + ret = client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_BYTES_LESS_OR_EQUAL, + "v2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v2", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v3", value); + + // v3 <= v4 + options.return_check_value = true; + mutations.set("k5", "v4"); + ret = client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_BYTES_LESS_OR_EQUAL, + "v4", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v3", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v4", value); + + // v4 >= v4 + options.return_check_value = true; + mutations.set("k5", "v5"); + ret = client->check_and_mutate( + hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_BYTES_GREATER_OR_EQUAL, + "v4", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v4", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v5", value); + + // v5 >= v4 + options.return_check_value = true; + mutations.set("k5", "v6"); + ret = client->check_and_mutate( + hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_BYTES_GREATER_OR_EQUAL, + "v4", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v5", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v6", value); + + // v6 > v5 + options.return_check_value = true; + mutations.set("k5", "v7"); + ret = client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_BYTES_GREATER, + "v5", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("v6", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v7", value); + + ret = client->del(hash_key, "k5"); + ASSERT_EQ(0, ret); + } +} + +TEST(check_and_mutate, value_int_compare) +{ + std::string hash_key("check_and_mutate_test_value_int_compare"); + + { + int ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_INT_EQUAL, + "1", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_NOT_FOUND, ret); + + ret = client->set(hash_key, "k1", ""); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_INT_EQUAL, + "1", + mutations, + options, + results); + ASSERT_EQ(PERR_INVALID_ARGUMENT, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("", value); + + ret = client->set(hash_key, "k1", "1"); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_INT_EQUAL, + "1", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("2", value); + + options.return_check_value = true; + mutations.set("k1", "3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_INT_EQUAL, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_INVALID_ARGUMENT, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("2", value); + + options.return_check_value = true; + mutations.set("k1", "3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_INT_EQUAL, + "v1", + mutations, + options, + results); + ASSERT_EQ(PERR_INVALID_ARGUMENT, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("2", value); + + options.return_check_value = true; + mutations.set("k1", "3"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_INT_EQUAL, + "88888888888888888888888888888888888888888888888", + mutations, + options, + results); + ASSERT_EQ(PERR_INVALID_ARGUMENT, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("2", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("2", value); + + ret = client->set(hash_key, "k1", "0"); + ASSERT_EQ(PERR_OK, ret); + + options.return_check_value = true; + mutations.set("k1", "-1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_INT_EQUAL, + "0", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("0", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("-1", value); + + options.return_check_value = true; + mutations.set("k1", "-2"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_INT_EQUAL, + "-1", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("-1", results.check_value); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("-2", value); + + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->set(hash_key, "k3", "3"); + ASSERT_EQ(PERR_OK, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k4", "4"); + ret = client->check_and_mutate(hash_key, + "k3", + pegasus_client::cas_check_type::CT_VALUE_INT_EQUAL, + "3", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("3", results.check_value); + ret = client->get(hash_key, "k4", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("4", value); + + ret = client->del(hash_key, "k3"); + ASSERT_EQ(0, ret); + ret = client->del(hash_key, "k4"); + ASSERT_EQ(0, ret); + } + + { + int ret = client->set(hash_key, "k5", "1"); + ASSERT_EQ(PERR_OK, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + // 1 < 2 + options.return_check_value = true; + mutations.set("k5", "2"); + ret = client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_INT_LESS, + "2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("1", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("2", value); + + // 2 <= 2 + options.return_check_value = true; + mutations.set("k5", "3"); + ret = client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_INT_LESS_OR_EQUAL, + "2", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("2", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("3", value); + + // 3 <= 4 + options.return_check_value = true; + mutations.set("k5", "4"); + ret = client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_INT_LESS_OR_EQUAL, + "4", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("3", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("4", value); + + // 4 >= 4 + options.return_check_value = true; + mutations.set("k5", "5"); + ret = + client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_INT_GREATER_OR_EQUAL, + "4", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("4", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("5", value); + + // 5 >= 4 + options.return_check_value = true; + mutations.set("k5", "6"); + ret = + client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_INT_GREATER_OR_EQUAL, + "4", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("5", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("6", value); + + // 6 > 5 + options.return_check_value = true; + mutations.set("k5", "7"); + ret = client->check_and_mutate(hash_key, + "k5", + pegasus_client::cas_check_type::CT_VALUE_INT_GREATER, + "5", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_TRUE(results.check_value_exist); + ASSERT_EQ("6", results.check_value); + ret = client->get(hash_key, "k5", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("7", value); + + ret = client->del(hash_key, "k5"); + ASSERT_EQ(0, ret); + } +} + +TEST(check_and_mutate, invalid_type) +{ + std::string hash_key("check_and_mutate_test_value_invalid_type"); + + { + int ret = 0; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1"); + ret = client->check_and_mutate( + hash_key, "k1", (pegasus_client::cas_check_type)100, "v", mutations, options, results); + ASSERT_EQ(PERR_INVALID_ARGUMENT, ret); + ASSERT_FALSE(results.mutate_succeed); + ASSERT_FALSE(results.check_value_returned); + } +} + +TEST(check_and_mutate, set_del) +{ + std::string hash_key("check_and_mutate_test_set_del"); + + { + int ret = 0; + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1"); + mutations.del("k1"); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_NOT_FOUND, ret); + } +} + +TEST(check_and_mutate, multi_get_mutations) +{ + std::string hash_key("check_and_mutate_test_multi_get_mutations"); + + { + int ret = 0; + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1", 10); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + + ::sleep(12); + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_NOT_FOUND, ret); + + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + ASSERT_FALSE(results.check_value_exist); + + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + } +} + +TEST(check_and_mutate, expire_seconds) +{ + std::string hash_key("check_and_mutate_test_expire_seconds"); + + { + int ret = 0; + ret = client->del(hash_key, "k1"); + ASSERT_EQ(0, ret); + + std::string value; + pegasus_client::mutations mutations; + pegasus_client::check_and_mutate_options options; + pegasus_client::check_and_mutate_results results; + + options.return_check_value = true; + mutations.set("k1", "v1", 10); + ::sleep(12); + mutations.set("k2", "v2", 10); + ret = client->check_and_mutate(hash_key, + "k1", + pegasus_client::cas_check_type::CT_VALUE_NOT_EXIST, + "", + mutations, + options, + results); + ASSERT_EQ(PERR_OK, ret); + ASSERT_TRUE(results.mutate_succeed); + ASSERT_TRUE(results.check_value_returned); + + ret = client->get(hash_key, "k1", value); + ASSERT_EQ(PERR_OK, ret); + ASSERT_EQ("v1", value); + } +} diff --git a/src/test/function_test/test_restore.cpp b/src/test/function_test/test_restore.cpp index e36e2eb0a5..51badb0b3d 100644 --- a/src/test/function_test/test_restore.cpp +++ b/src/test/function_test/test_restore.cpp @@ -21,7 +21,7 @@ class restore_test : public testing::Test working_root_dir = global_env::instance()._working_dir; chdir(pegasus_root_dir.c_str()); - cluster_name = utils::filesystem::path_combine(pegasus_root_dir, backup_data_dir); + cluster_name = dsn::utils::filesystem::path_combine(pegasus_root_dir, backup_data_dir); system("pwd"); // modify the config to enable backup, and restart onebox system("sed -i \"/^cold_backup_disabled/c cold_backup_disabled = false\" " @@ -226,7 +226,7 @@ class restore_test : public testing::Test int64_t get_first_backup_timestamp() { - std::string policy_dir = utils::filesystem::path_combine(cluster_name, policy_name); + std::string policy_dir = dsn::utils::filesystem::path_combine(cluster_name, policy_name); std::string cmd = "cd " + policy_dir + "; " "ls -c > restore_app_from_backup_test_tmp; " "tail -n 1 restore_app_from_backup_test_tmp; " @@ -250,7 +250,7 @@ class restore_test : public testing::Test bool find_second_backup_timestamp() { - std::string policy_dir = utils::filesystem::path_combine(cluster_name, policy_name); + std::string policy_dir = dsn::utils::filesystem::path_combine(cluster_name, policy_name); std::vector dirs; ::dsn::utils::filesystem::get_subdirectories(policy_dir, dirs, false); return (dirs.size() >= 2);