storage_proxy: handler responses, use pointers to default constructed values instead of nulls

The current Seastar RPC infrastructure lacks support
for null values in tuples in handler responses.
In this commit we add the make_default_rpc_tuple function,
which solves the problem by returning pointers to
default-constructed values for smart pointer types
rather than nulls.

The problem was introduced in this commit
 2d791a5ed4. The
 function `encode_replica_exception_for_rpc` used
 `default_tuple_maker` callback to create tuples
 containing exceptions. Callers returned pointers
 to default-constructed values in this callback,
 e.g. `foreign_ptr(make_lw_shared<reconcilable_result>())`.
 The commit changed this to just `SourceTuple{}`,
 which means nullptr for pointer types.

Fixes: #14282

Closes #14352
This commit is contained in:
Petr Gusev
2023-06-22 14:48:23 +04:00
committed by Avi Kivity
parent 74fc69c825
commit 1e851262f2
2 changed files with 64 additions and 1 deletions

View File

@@ -94,6 +94,7 @@
#include "utils/error_injection.hh"
#include "utils/exceptions.hh"
#include "utils/tuple_utils.hh"
#include "utils/rpc_utils.hh"
#include "replica/exceptions.hh"
#include "db/operation_type.hh"
#include "locator/util.hh"
@@ -134,7 +135,7 @@ static future<ResultTuple> encode_replica_exception_for_rpc(gms::feature_service
std::exception_ptr eptr = f.get_exception();
if (features.typed_errors_in_read_rpc) {
if (auto ex = replica::try_encode_replica_exception(eptr); ex) {
return make_ready_future<ResultTuple>(utils::tuple_insert<ResultTuple>(SourceTuple{}, std::move(ex)));
return make_ready_future<ResultTuple>(utils::tuple_insert<ResultTuple>(utils::make_default_rpc_tuple<SourceTuple>(), std::move(ex)));
}
}
return make_exception_future<ResultTuple>(std::move(eptr));

62
utils/rpc_utils.hh Normal file
View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2023-present ScyllaDB
*
*/
/*
* SPDX-License-Identifier: (AGPL-3.0-or-later and Apache-2.0)
*/
#pragma once
#include "utils/tuple_utils.hh"
#include <seastar/core/shared_ptr.hh>
#include <seastar/core/sharded.hh>
namespace utils {
namespace internal {
class rpc_default_value {
template <typename T>
struct marker {};
template <typename T>
static auto create(marker<std::unique_ptr<T>>) {
return std::make_unique<T>();
}
template <typename T>
static auto create(marker<std::shared_ptr<T>>) {
return std::make_shared<T>();
}
template <typename T>
static auto create(marker<seastar::lw_shared_ptr<T>>) {
return make_lw_shared<T>();
}
template <typename T>
static auto create(marker<seastar::shared_ptr<T>>) {
return make_shared<T>();
}
template <typename T>
static auto create(marker<seastar::foreign_ptr<T>>) {
return make_foreign(create<T>());
}
template <typename T>
static auto create(marker<T>) {
return T{};
}
public:
template <typename T>
static T create() {
return create(marker<T>{});
}
};
}
// The Seastar RPC infrastructure does not support null values in tuples in handler responses.
// This function generates a tuple by default constructing all of its elements, except for
// smart pointer types. For those, it returns a pointer to the default constructed value.
template <Tuple T>
T make_default_rpc_tuple() {
return std::invoke([&]<std::size_t... Is>(std::index_sequence<Is...>) {
return T { internal::rpc_default_value::create<std::tuple_element_t<Is, T>>()... };
}, std::make_index_sequence<tuple_ex_size_v<T>>());
}
}