Skip to content

[k2] rpc client API - #1635

Open
T-y-c-o-o-n wants to merge 45 commits into
masterfrom
n.siniachenko/k2/rpc-client-boost
Open

[k2] rpc client API#1635
T-y-c-o-o-n wants to merge 45 commits into
masterfrom
n.siniachenko/k2/rpc-client-boost

Conversation

@T-y-c-o-o-n

Copy link
Copy Markdown
Contributor

No description provided.

@T-y-c-o-o-n
T-y-c-o-o-n force-pushed the n.siniachenko/k2/rpc-client-boost branch from 156e9ec to 222adae Compare June 15, 2026 11:57
Comment thread runtime-light/stdlib/rpc/rpc-api.cpp
Comment thread runtime-light/stdlib/rpc/rpc-queue-functions.h Outdated
Comment thread builtin-functions/kphp-light/stdlib/rpc.txt Outdated
Comment thread runtime-light/k2-platform/k2-header.h Outdated
Comment thread runtime-light/k2-platform/k2-header.h Outdated
Comment thread runtime-light/k2-platform/k2-header.h Outdated
Comment thread runtime-light/k2-platform/k2-header.h
Comment thread runtime-light/k2-platform/k2-header.h Outdated
Comment thread runtime-light/stdlib/rpc/rpc-api.cpp Outdated
Comment thread runtime-light/stdlib/rpc/rpc-api.cpp Outdated
Comment thread runtime-light/stdlib/rpc/rpc-api.cpp Outdated
Comment thread runtime-light/stdlib/rpc/rpc-api.cpp Outdated
@T-y-c-o-o-n
T-y-c-o-o-n force-pushed the n.siniachenko/k2/rpc-client-boost branch from a342aa9 to e77636c Compare June 21, 2026 19:19
Comment thread runtime-light/k2-platform/k2-header.h Outdated
Comment thread runtime-light/k2-platform/k2-api.h Outdated
Comment thread runtime-light/stdlib/time/util.h Outdated
@T-y-c-o-o-n
T-y-c-o-o-n force-pushed the n.siniachenko/k2/rpc-client-boost branch 2 times, most recently from 0c1f4cc to 44d348e Compare July 17, 2026 13:14
Comment thread runtime-light/k2-platform/k2-header.h Outdated
Comment thread runtime-light/k2-platform/k2-api.h Outdated
Comment thread runtime-light/stdlib/rpc/rpc-client-state.h Outdated
Comment thread runtime-light/stdlib/rpc/rpc-client-state.h Outdated
Comment thread runtime-light/stdlib/rpc/rpc-query.h
Comment thread runtime-light/stdlib/rpc/rpc-query-handle.h Outdated
Comment thread runtime-light/stdlib/rpc/rpc-query-handle.h Outdated
Comment thread runtime-light/stdlib/rpc/rpc-query-handle.h Outdated
Comment thread runtime-light/stdlib/rpc/rpc-queue-functions.h Outdated
Comment thread runtime-light/stdlib/time/time-functions.h Outdated
@T-y-c-o-o-n T-y-c-o-o-n changed the title [k2] rpc client boost [k2] rpc client API Jul 22, 2026
Comment thread runtime-light/stdlib/rpc/rpc-query-handle.h Outdated
Comment thread runtime-light/stdlib/rpc/rpc-query-handle.h Outdated
@T-y-c-o-o-n
T-y-c-o-o-n force-pushed the n.siniachenko/k2/rpc-client-boost branch from df743db to 6435238 Compare July 24, 2026 15:48
Comment thread runtime-light/k2-platform/k2-api.h Outdated
return {descriptor};
}

inline std::expected<size_t, int32_t> rpc_get_response_size(uint64_t rpc_d) noexcept {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use k2::descriptor instead of uint64_t

Comment thread runtime-light/k2-platform/k2-header.h Outdated
int32_t k2_component_access(size_t name_len, const char* name);

/**
* Try to send rpc request. In case of success write descriptor of rpc request to `rpc_d`, otherwise return `errno` != 0,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please make these docs more clear?

For example, for k2_rpc_send_request it might look like:

* Try to send an RPC request. On success, write the request descriptor to `rpc_d`;
* this descriptor is later used when calling `k2_rpc_fetch_response`.
* Otherwise, return a non-zero `errno`.
...

Please, take a look at other API functions you've added and check their docs too

int64_t current_query_id{kphp::rpc::VALID_QUERY_ID_RANGE_START};

kphp::stl::unordered_map<int64_t, kphp::coro::shared_task<std::optional<string>>, kphp::memory::script_allocator> response_awaiter_tasks;
kphp::stl::unordered_map<int64_t, kphp::coro::shared_task<std::expected<kphp::stl::vector<std::byte, kphp::memory::script_allocator>, int32_t>>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a good idea to have shared_task returning something like vector. shared_task returns a copy of its result on each co_await, so you'll end up with multiple copies of the vector

Comment thread runtime-light/stdlib/rpc/rpc-query.h Outdated
m_deadline{deadline} {}

template<std::invocable<size_t> B>
requires std::is_convertible_v<std::invoke_result_t<B, size_t>, std::span<std::byte>>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make it std::same_as<std::span<byte>>

Comment thread runtime-light/stdlib/rpc/rpc-query.h Outdated

template<std::invocable<size_t> B>
requires std::is_convertible_v<std::invoke_result_t<B, size_t>, std::span<std::byte>>
auto get_ready_response(B response_buffer_provider) noexcept -> std::expected<std::span<std::byte>, int32_t>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to accept this parameter by value?

Comment thread runtime-light/stdlib/rpc/rpc-query.h Outdated

template<std::invocable<size_t> B>
requires std::is_convertible_v<std::invoke_result_t<B, size_t>, std::span<std::byte>>
auto get_response(B response_buffer_provider) noexcept -> kphp::coro::task<std::expected<std::span<std::byte>, int32_t>>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note to think about: we can get rid of this method. Instead, we might have an interface like this:

kphp::rpc::query q{create_some_query()};
co_await q(my_response_buffer_provider);

What do you think?

Comment thread runtime-light/stdlib/rpc/rpc-query.h Outdated
}
size_t response_size{*response_size_exp};

std::span<std::byte> response_buffer{std::invoke(response_buffer_provider, response_size)};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to check response_buffer's size here

Comment thread runtime-light/stdlib/rpc/rpc-query.h Outdated
requires std::is_convertible_v<std::invoke_result_t<B, size_t>, std::span<std::byte>>
auto query::get_response(B response_buffer_provider) noexcept -> kphp::coro::task<std::expected<std::span<std::byte>, int32_t>> {
if (m_descriptor == k2::INVALID_PLATFORM_DESCRIPTOR) {
co_return std::unexpected{TL_ERROR_INTERNAL};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use TL_ERROR_INVALID_CONNECTION_ID to improve observability

Comment thread runtime-light/stdlib/rpc/rpc-query.h Outdated
kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()};
std::chrono::nanoseconds timeout{kphp::time::remaining(m_deadline)};

// TODO DISCUSS: may be completely remove timeout monitoring in kphp and leave it in k2-node's rpc client ???

@apolyakov apolyakov Jul 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable now. There are two ways the platform can notify us about RPC timeout:

  1. close the stream -> aren't there other errors that can lead to stream being closed?
  2. write some "there was a timeout" answer to the stream -> requires a bit more complex processing since such an answer will vary depending on RPC kind we use

/**
* Calculate time remaining to the deadline.
*/
inline std::chrono::nanoseconds remaining(std::chrono::steady_clock::time_point deadline) noexcept {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about following implementation:

/**             
   * Returns the current steady-clock time point from the K2 platform.                                                                                                                                                                                         
   */             
  inline std::chrono::steady_clock::time_point now() noexcept /* iff k2::instant is noexcept */ {
    k2::TimePoint now_instant{};
    k2::instant(std::addressof(now_instant));
    return std::chrono::steady_clock::time_point{std::chrono::nanoseconds{now_instant.time_point_ns}};
  }

  /**
   * Calculate time remaining until the deadline.
   */
  template <typename Duration>
  inline auto remaining(std::chrono::time_point<std::chrono::steady_clock, Duration> deadline) noexcept {
    return deadline - now();
  }

  /**
   * Converts a timeout to the time point at which it elapses, i.e. the deadline.
   */
  template <typename Rep, typename Period>
  inline auto expires_at(std::chrono::duration<Rep, Period> timeout) noexcept {
    return now() + timeout;
  }

It makes client code easier by removing mandatory conversions from any duration to nanoseconds

inline kphp::coro::task<array<int64_t>> f$rpc_send_requests(string actor, array<mixed> tl_objects, Optional<double> timeout, bool ignore_answer,
class_instance<C$KphpRpcRequestsExtraInfo> requests_extra_info,
bool need_responses_extra_info) noexcept {
inline array<int64_t> f$rpc_send_requests(string actor, array<mixed> tl_objects, Optional<double> timeout, bool ignore_answer,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it's not longer a coroutine, we don't need to pass parameters by value

m_deadline{deadline} {}

template<std::invocable<size_t> B>
requires std::is_same_v<std::invoke_result_t<B, size_t>, std::span<std::byte>>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing includes for concepts and type_traits


inline auto query::send(std::string_view actor, std::chrono::milliseconds timeout,
std::span<const std::byte> request_buffer) noexcept -> std::expected<query, int32_t> {
auto descriptor_exp{k2::rpc_send_request(actor, request_buffer, RpcKind::TL_RPC)};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think send needs to accept RPC kind as a parameter

co_return std::unexpected{TL_ERROR_INVALID_CONNECTION_ID};
}

kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no need to get the scheduler unless it's really needed

auto timeout{kphp::time::remaining(m_deadline)};

if (timeout <= std::chrono::nanoseconds::zero()) {
co_return std::move(*this).get_ready_response<B>(std::move(response_buffer_provider));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think it's better to declare get_ready_response and static constexpr lambda inside response

std::swap(tl_storer, rpc_server_instance_st.tl_storer);
}
}};
// if we have to allocate memory for request buffer, it will be in this vector, and it will be freed at the end of the function

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to use the tl_storer buffer for this

auto fetch_task{kphp::component::fetch_response(stream, kphp::component::read_ext::append(*opt_response))};
if (auto expected{co_await kphp::coro::io_scheduler::get().schedule(std::move(fetch_task), timeout)}; !expected) [[unlikely]] {
opt_response = std::nullopt;
static constexpr auto awaiter_coroutine{[](query q, int64_t query_id, bool collect_responses_extra_info) mutable noexcept

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it mutable?

-> kphp::coro::shared_task<std::expected<string, int32_t>> {
string resp_buf{};
const auto response_buffer_provider{[&resp_buf](size_t size) noexcept -> std::span<std::byte> {
resp_buf.assign(size, true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reserve_at_least better expresses what you do here

return {reinterpret_cast<std::byte*>(resp_buf.buffer()), size};
}};

auto fetch_task{std::move(q).response<decltype(response_buffer_provider)>(response_buffer_provider)};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for explicit decltype here

auto fetch_task{kphp::component::fetch_response(stream, [](std::span<const std::byte>) noexcept {})};
std::ignore = co_await kphp::coro::io_scheduler::get().schedule(std::move(fetch_task), timeout);
}};
static constexpr auto ignore_answer_awaiter_coroutine{[](query q) noexcept -> kphp::coro::shared_task<> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we even need it now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants