From c82a52a780806a0f26df71b0b704b6112f44ab4f Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Wed, 29 Apr 2026 17:04:15 +0300 Subject: [PATCH 01/17] first impl --- runtime-light/k2-platform/k2-api.h | 17 ++++ runtime-light/k2-platform/k2-header.h | 9 ++ runtime-light/stdlib/rpc/rpc-api.cpp | 121 ++++++++++++++++++-------- 3 files changed, 111 insertions(+), 36 deletions(-) diff --git a/runtime-light/k2-platform/k2-api.h b/runtime-light/k2-platform/k2-api.h index 617dad5e3f..1a08d8ec82 100644 --- a/runtime-light/k2-platform/k2-api.h +++ b/runtime-light/k2-platform/k2-api.h @@ -210,6 +210,23 @@ inline int32_t component_access(std::string_view component_name) noexcept { return k2_component_access(component_name.size(), component_name.data()); } +inline std::expected rpc_send_request(std::string_view actor_name, std::span request_buffer) noexcept { + uint64_t rpc_d{}; + if (auto error_code{k2_rpc_send_request(actor_name.data(), actor_name.size(), request_buffer.data(), request_buffer.size(), std::addressof(rpc_d))}; + error_code != k2::errno_ok) { + return std::unexpected{error_code}; + } + return {rpc_d}; +} + +inline size_t rpc_get_response_size(uint64_t rpc_d) noexcept { + return k2_rpc_get_response_size(rpc_d); +} + +inline size_t rpc_fetch_response(uint64_t rpc_d, std::span buffer) noexcept { + return k2_rpc_fetch_response(rpc_d, buffer.data(), buffer.size()); +} + inline void stream_status(k2::descriptor descriptor, StreamStatus* status) noexcept { k2_stream_status(descriptor, status); } diff --git a/runtime-light/k2-platform/k2-header.h b/runtime-light/k2-platform/k2-header.h index 25b8789f7f..38a818b0d3 100644 --- a/runtime-light/k2-platform/k2-header.h +++ b/runtime-light/k2-platform/k2-header.h @@ -316,6 +316,15 @@ int32_t k2_unlink(const char* path, size_t path_len); */ int32_t k2_component_access(size_t name_len, const char* name); +/** + * @return returns descriptor of rpc request, which should be later used to call `k2_rpc_fetch_response`. + */ +int32_t k2_rpc_send_request(const char *actor_name, size_t actor_name_len, const void* request_ptr, size_t request_size, uint64_t *rpc_d); + +size_t k2_rpc_get_response_size(uint64_t rpc_d); + +size_t k2_rpc_fetch_response(uint64_t rpc_d, void* buf, size_t buf_size); + /** * If the write or read status is `Blocked` - then the platform ensures that * the component receives this `stream_d` via `k2_take_update` when the status is diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index d881fa3c11..ac5e6c6845 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -279,38 +279,52 @@ kphp::coro::task send_request(std::string_view actor, std auto& rpc_server_instance_st{RpcServerInstanceState::get()}; const size_t request_size{rpc_server_instance_st.tl_storer.view().size_bytes()}; const auto timestamp{std::chrono::duration{std::chrono::system_clock::now().time_since_epoch()}.count()}; + /* - auto expected_stream{kphp::component::stream::open(actor, k2::stream_kind::component)}; - if (!expected_stream) [[unlikely]] { - co_return kphp::rpc::query_info{ - .id = kphp::rpc::INVALID_QUERY_ID, .request_size = rpc_server_instance_st.tl_storer.view().size_bytes(), .timestamp = timestamp}; - } - - auto stream{*std::move(expected_stream)}; - { - auto tl_storer{std::exchange(rpc_server_instance_st.tl_storer, tl::storer{0})}; - const vk::final_action finalizer{[&tl_storer, &rpc_server_instance_st] noexcept { - if (tl_storer.capacity() > rpc_server_instance_st.tl_storer.capacity()) { - std::swap(tl_storer, rpc_server_instance_st.tl_storer); - } - }}; + auto expected_stream{kphp::component::stream::open(actor, k2::stream_kind::component)}; + if (!expected_stream) [[unlikely]] { + co_return kphp::rpc::query_info{ + .id = kphp::rpc::INVALID_QUERY_ID, .request_size = rpc_server_instance_st.tl_storer.view().size_bytes(), .timestamp = timestamp}; + } - // prepare and send RPC request - // 'request_buf' will look like this: - // [ RpcExtraHeaders (optional) ] [ payload ] - if (const auto& [opt_new_extra_header, cur_extra_header_size]{kphp::rpc::regularize_extra_headers(tl_storer.view(), ignore_answer)}; opt_new_extra_header) { - std::span request_body{tl_storer.view().subspan(cur_extra_header_size)}; - std::span new_header{reinterpret_cast(std::addressof(*opt_new_extra_header)), - sizeof(std::remove_cvref_t)}; + auto stream{*std::move(expected_stream)}; + { + auto tl_storer{std::exchange(rpc_server_instance_st.tl_storer, tl::storer{0})}; + const vk::final_action finalizer{[&tl_storer, &rpc_server_instance_st] noexcept { + if (tl_storer.capacity() > rpc_server_instance_st.tl_storer.capacity()) { + std::swap(tl_storer, rpc_server_instance_st.tl_storer); + } + }}; - if (!co_await stream.write_all(new_header) || !co_await kphp::component::send_request(stream, request_body)) [[unlikely]] { + // prepare and send RPC request + // 'request_buf' will look like this: + // [ RpcExtraHeaders (optional) ] [ payload ] + if (const auto& [opt_new_extra_header, cur_extra_header_size]{kphp::rpc::regularize_extra_headers(tl_storer.view(), ignore_answer)}; opt_new_extra_header) + { std::span request_body{tl_storer.view().subspan(cur_extra_header_size)}; std::span new_header{reinterpret_cast(std::addressof(*opt_new_extra_header)), sizeof(std::remove_cvref_t)}; + + if (!co_await stream.write_all(new_header) || !co_await kphp::component::send_request(stream, request_body)) [[unlikely]] { + co_return kphp::rpc::query_info{.id = kphp::rpc::INVALID_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; + } + } else if (!co_await kphp::component::send_request(stream, tl_storer.view())) [[unlikely]] { co_return kphp::rpc::query_info{.id = kphp::rpc::INVALID_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; } - } else if (!co_await kphp::component::send_request(stream, tl_storer.view())) [[unlikely]] { - co_return kphp::rpc::query_info{.id = kphp::rpc::INVALID_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; } + */ + + auto tl_storer{std::exchange(rpc_server_instance_st.tl_storer, tl::storer{0})}; + const vk::final_action finalizer{[&tl_storer, &rpc_server_instance_st] noexcept { + if (tl_storer.capacity() > rpc_server_instance_st.tl_storer.capacity()) { + std::swap(tl_storer, rpc_server_instance_st.tl_storer); + } + }}; + // TODO make special (may be resource) type for rpc request; + auto rpc_d_exp{k2::rpc_send_request(actor, tl_storer.view())}; + if (!rpc_d_exp) { + k2::exit(123); } + // TODO why is query_id incremented here but not before the request ??? const auto query_id{rpc_client_instance_st.current_query_id++}; // create response extra info @@ -318,15 +332,49 @@ kphp::coro::task send_request(std::string_view actor, std rpc_client_instance_st.rpc_responses_extra_info.emplace(query_id, std::make_pair(response_extra_info_status::not_ready, response_extra_info{0, timestamp})); } + k2::descriptor rpc_d{*rpc_d_exp}; + // create a task to wait for RPC response. we need to do it even if 'ignore_answer' is 'true' to make sure // that the stream will not be closed too early. otherwise, platform may even not send RPC request - static constexpr auto awaiter_coroutine{[](int64_t query_id, kphp::component::stream stream, std::chrono::milliseconds timeout, + static constexpr auto awaiter_coroutine{[](int64_t query_id, k2::descriptor rpc_d, std::chrono::milliseconds timeout, bool collect_responses_extra_info) noexcept -> kphp::coro::shared_task> { - std::optional opt_response{std::in_place}; - 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; + std::optional opt_response{std::nullopt}; + + kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; + switch (co_await m_scheduler.poll(rpc_d, kphp::coro::poll_op::read, timeout)) { + case kphp::coro::poll_status::event: { + size_t response_size{k2::rpc_get_response_size(rpc_d)}; + if (response_size == 0) { + // TODO ERROR + k2::exit(148); + } + std::span response_buf{reinterpret_cast(k2::alloc(response_size)), response_size}; + size_t new_resonse_size = k2::rpc_fetch_response(rpc_d, response_buf); + if (new_resonse_size != response_size) { + // TODO ERROR + k2::exit(69); + } + opt_response = {reinterpret_cast(response_buf.data()), static_cast(response_buf.size())}; } + case kphp::coro::poll_status::closed: + // TODO really nothing ? + // fallthrough + case kphp::coro::poll_status::error: + // TODO ERROR ??? + // nothing + // fallthrough + case kphp::coro::poll_status::timeout: + // nothing + // fallthrough + } + + /* + std::optional opt_response{std::in_place}; + 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; + } + */ // update response extra info if needed if (collect_responses_extra_info) { @@ -344,11 +392,10 @@ kphp::coro::task send_request(std::string_view actor, std co_return std::move(opt_response); }}; - static constexpr auto ignore_answer_awaiter_coroutine{ - [](kphp::component::stream stream, std::chrono::milliseconds timeout) noexcept -> kphp::coro::shared_task<> { - auto fetch_task{kphp::component::fetch_response(stream, [](std::span) noexcept {})}; - std::ignore = co_await kphp::coro::io_scheduler::get().schedule(std::move(fetch_task), timeout); - }}; + static constexpr auto ignore_answer_awaiter_coroutine{[](k2::descriptor rpc_d, std::chrono::milliseconds timeout) noexcept -> kphp::coro::shared_task<> { + auto fetch_task{kphp::coro::io_scheduler::get().poll(rpc_d, kphp::coro::poll_op::read, timeout)}; + std::ignore = co_await kphp::coro::io_scheduler::get().schedule(std::move(fetch_task), timeout); + }}; // normalize timeout using namespace std::chrono_literals; @@ -364,14 +411,16 @@ kphp::coro::task send_request(std::string_view actor, std MIN_TIMEOUT, MAX_TIMEOUT)}; if (ignore_answer) { // start ignore answer awaiter task - auto ignore_answer_awaiter_task{ignore_answer_awaiter_coroutine(std::move(stream), timeout)}; + // TODO need to move rpc_d ? + auto ignore_answer_awaiter_task{ignore_answer_awaiter_coroutine(std::move(rpc_d), timeout)}; kphp::log::assertion(kphp::coro::io_scheduler::get().start(ignore_answer_awaiter_task)); rpc_client_instance_st.ignore_answer_request_awaiter_tasks.push(std::move(ignore_answer_awaiter_task)); co_return kphp::rpc::query_info{.id = kphp::rpc::IGNORED_ANSWER_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; } // start awaiter task - auto awaiter_task{awaiter_coroutine(query_id, std::move(stream), timeout, collect_responses_extra_info)}; + // TODO need to move rpc_d ? + auto awaiter_task{awaiter_coroutine(query_id, std::move(rpc_d), timeout, collect_responses_extra_info)}; kphp::log::assertion(kphp::coro::io_scheduler::get().start(awaiter_task)); rpc_client_instance_st.response_awaiter_tasks.emplace(query_id, std::move(awaiter_task)); From 9758cab2957858aa7a07bae744cd9ae92d72c266 Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Thu, 30 Apr 2026 13:13:08 +0300 Subject: [PATCH 02/17] added kphp rpc client bench --- common.tl | 124 +++++++++++++++++++++++++++++++++++ common.tlo | Bin 0 -> 9084 bytes rpc-client-latency-bench.php | 94 ++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 common.tl create mode 100644 common.tlo create mode 100644 rpc-client-latency-bench.php diff --git a/common.tl b/common.tl new file mode 100644 index 0000000000..9cf3f1de25 --- /dev/null +++ b/common.tl @@ -0,0 +1,124 @@ +///// +// +// Common Types +// +///// + +// Builtin types +int#a8509bda ? = Int; +long#22076cba ? = Long; +float#824dab22 ? = Float; // 4 bytes -- single precision +double#2210c154 ? = Double; // 8 bytes -- double precision +string#b5286e24 ? = String; + +// Boolean emulation +boolFalse#bc799737 = Bool; +boolTrue#997275b5 = Bool; + +// Boolean for diagonal queries +boolStat statTrue:int statFalse:int statUnknown:int = BoolStat; + +// Vector +vector#1cb5c415 {t:Type} # [t] = Vector t; +tuple#9770768a {t:Type} {n:#} [t] = Tuple t n; +vectorTotal {t:Type} total_count:int vector:%(Vector t) = VectorTotal t; + +// Dictionaries +dictionaryField {t:Type} key:string value:t = DictionaryField t; +dictionary#1f4c618f {t:Type} %(Vector %(DictionaryField t)) = Dictionary t; + +intKeyDictionaryField {t:Type} key:int value:t = IntKeyDictionaryField t; +intKeyDictionary#07bafc42 {t:Type} %(Vector %(intKeyDictionaryField t)) = IntKeyDictionary t; + +longKeyDictionaryField {t:Type} key:long value:t = LongKeyDictionaryField t; +longKeyDictionary#b424d8f1 {t:Type} %(Vector %(longKeyDictionaryField t)) = LongKeyDictionary t; + +// Maybe +resultFalse#27930a7b {t:Type} = Maybe t; +resultTrue#3f9c8ef8 {t:Type} result:t = Maybe t; + + +pair {X:Type} {Y:Type} a:X b:Y = Pair X Y; + +true = True; // this can be used as void type and serialized to empty array in PHP + +stat#9d56e6b2 %(Dictionary string) = Stat; + +rpcInvokeReqExtra#f3ef81a9 {flags:#} + return_binlog_pos:flags.0?%True + return_binlog_time:flags.1?%True + return_pid:flags.2?%True + return_request_sizes:flags.3?%True + return_failed_subqueries:flags.4?%True + return_query_stats:flags.6?%True + no_result:flags.7?%True // Currently for proxy only. Client goes to proxy, it clears this bit and sends query to engine. Client does not wait for answer. + // Bits 17, 22, 24 was used in before, but their support was dropped + wait_binlog_pos:flags.16?long // Perform query only after position in binlog is at least this + string_forward_keys:flags.18?%(Vector string) // For cluster that are split by string (like pmemcached in some modes) - first specified string is used to choose target, then it is deleted from vector + int_forward_keys:flags.19?%(Vector long) // First long is used to choose target. Then it is deleted from vector + string_forward:flags.20?string // Same as string_forward_keys, but it is not deleted + int_forward:flags.21?long // Same as int_forward_keys, but it is not deleted + custom_timeout_ms:flags.23?int // Custom timeout for query + supported_compression_version:flags.25?int // note, that client support compression, to possibly compress answers + random_delay:flags.26?double // starting query would be delayed by random number, not grater than given + return_view_number:flags.27?%True // Barsic related parameter: return view number in response + = RpcInvokeReqExtra flags; + +rpcReqResultExtra#c5011709 {flags:#} + binlog_pos:flags.0?long binlog_time:flags.1?long + engine_pid:flags.2?%net.Pid + request_size:flags.3?int response_size:flags.3?int + failed_subqueries:flags.4?int + compression_version:flags.5?int + stats:flags.6?%(Dictionary string) + epoch_number:flags.27?%long view_number:flags.27?%long + = RpcReqResultExtra flags; + + +// ReqResult +reqError#b527877d {X:Type} error_code:int error:string = ReqResult X; +reqResultHeader#8cc84ce1 {X:Type} flags:# extra:%(RpcReqResultExtra flags) result:X = ReqResult X; +//reqResultFalse {X:Type} = ReqResult X; +_ {X:Type} result:X = ReqResult X; + +rpcReqResult#63aeda4e {X:Type} query_id:long result:(ReqResult X) = RpcReqResult X; +rpcReqError#7ae432f5 {X:Type} query_id:long error_code:int error:string = RpcReqResult X; + +rpcPong#8430eaa7 ping_id:long = RpcPong; + +net.pid ip:int port_pid:int utime:int = net.Pid; + +left {X:Type} {Y:Type} value:X = Either X Y; +right {X:Type} {Y:Type} value:Y = Either X Y; + +memcache.not_found = memcache.Value; +memcache.strvalue value:string flags:int = memcache.Value; + +---functions--- + +@any rpcDestActor#7568aabd {X:Type} actor_id:long query:!X = X; +@any rpcDestActorFlags#f0a5acf7 {X:Type} actor_id:long flags:# extra:%(RpcInvokeReqExtra flags) query:!X = X; +@any rpcDestFlags#e352035e {X:Type} flags:# extra:%(RpcInvokeReqExtra flags) query:!X = X; + +@any rpcInvokeReq#2374df3d {X:Type} query_id:long query:!X = RpcReqResult X; + +//NEVER use this. Only for debugging engines. +@any @internal engine.setVerbosity verbosity:int = True; +@any @internal engine.setVerbosityType type:string verbosity:int = True; +@any @internal engine.sendSignal signal:int = True; +@any @internal engine.sleep time_ms:int = Bool; + +@any engine.nop = True; +@read engine.readNop = True; // same as nop, but assumed as read in proxy +@write engine.writeNop = True; // same as nop, but assumed as write in proxy +@any engine.stat = Stat; +@any engine.filteredStat stat_names:%(Vector string) = Stat; +@any engine.count = BoolStat; +@any engine.pid = net.Pid; +@any engine.version = String; + +@kphp kphp.pingRpcCluster#a677ee41 id:int = Int; + + + +@kphp memcache.get#d33b13ae key:string = memcache.Value; diff --git a/common.tlo b/common.tlo new file mode 100644 index 0000000000000000000000000000000000000000..ce69379adf2309440a343ca876ee30102152aa89 GIT binary patch literal 9084 zcmcJVe{38_702zwcm8oEj_o9F5=h!4P%0`5^$&h1gwP~SQ%EDrNu%};IIZv2_R8no zV{gx{gOH#^rHT-sf>clw_ybW|TICODgh(l(qOCw7fK(OQ2oV)f#g9UR08tern9uCK zz43VMy`z&(qyR^}N z&+kfa@7fatHPyXJ{jIfNU|v6W?*1Myha)Fa{hbzuM{r+o4sPw+=T#yv@SSi$<9aMS zxQWdtw-2;%`#rZ-Wy~z>k*C&fEA9)L6E#PZK#ap~++6_}1utge}ZI9l5 zPho#8zyNy;a9}PxIQH=B+x&=r8$D(K-P^hj2L4nst~sW9rFs7Sg&mI%g0YAQRz3w^+EU-RVF^AaA{=a)&m14Prf|En zFyXf1!p1y$>jwt9#vCt9xT4SY``9xtJ=r_%etg_*G;7NDs})AY*eZLhM?U@PKL*F^ zmD~KeV8&Gfx7;0tPV6U%3z5N3-}>{rk9RBlm~w->N53>MPyF%28~YW;P@l$!DFe25 zr1Kf{r`R0*-+B4ysji&$u)}}nnd0Fn^weyk&(sM|^uGVOzwYij5;m2abw9S64a|wT z`U8a{&AM6w=@|G~d<|ygwO4NJIg^yL~qr9rMz4ZgM0&J%9g( zKfU*Xg&*`zs64aZsWp@#)VBXKdh;8VnUmCXQoLX(= zRG#68uhHMB*Hjz{@FNaHo__gGAM+1){_ei;(^q&$E;iqt{`np;FZDS(8As$y4DaEo zb9#%XPAVNr*yHg9e=;v{powqL$#WKl_wd!Vg3ULn73f~Tt{}k%8NU6<#E+DN-eHY z_-@Vx&&W%|HeIiPwR*x4{pYy!}Dq6PY|cuYLaH?!H)$-VsF)`6*8i{l!=oHJSOl9S*wQ ztjF~x-?6hx^uNJ;-05g)i@0gKCR*=h?!<3!pcfz5YhRv41{`bQlb^l*Oi!rF+b~pB zSVsr-@uGOjv7^sLuv`hMaiz!JvSf{G3>ug2rNSM(^utfM@+=LmFCF~J=LQtlxOP9_ zI#o9mzU}(GFj;e^8nKVi1z*mjq6Z%r+_=`~9sSheVEKFD!N-4dY2ArK^zqzIEVX0X z&(^Ft-dS$>KaJxY|L4VVJG%8*CtP~g;XSc1nAj=4GwTf7cY~oDHABBV;rX>-s$370 zubCTZ_>(gRzN6v4p~W(hH|wS{;Id@Y+ShvOUNyzyvn=eo1gk=)={BNr!#n0GTB#iM zb(Y*UiQJ^))!b^i(VS57p|?mD+p=WECL|Wh6x@weUvU|kUOy;HS`weJQD==mFzp>W)d9DPrbu|qQ zb!$|fb3;B`HnIWYUt7LEbo{E)S#@g;hX&!eDZx|EXT|)S=gyb?=In&JD-xN_iHx~U zBu}y?Rriuy#5)>!Qr|_W^~_xa-_8BEyRJ_EWwmHjv*Sc`AVa@2#yoW4bfsU7nchdJ zPVO@#)!Dr##;+XXR+_ zV9fQD&yl85lh!pb@!HWY;wbBDf1#0G`qyO}$XrNKSV z9?{7du~hsoxdgk5asKb%yLn#Y*~aTT$tb>))SoL4(l zzNf~KxVCE?(e>MF|MQ|*3xX$?NWXbUB3$7@e^!ld*OuV$m%b;Ri|rM)AXwy+%?-V$UDZ9hV7f4_~Ao?sk{pKt;c?|;{x7h4i>#x zIqTW)&1^{avr#{&ceI;_-s7LTL*&>8WgbjBZ9n0ot;gSWYE5Eis7|_v+R#ZXBm0e> z`Avg~Y{T`dhrKD^Q9q~(XK=dKY9t-%dAT_>b1QocgrJzq4%iC!FP9%Pu`SEvOEs2dr4CAYnV)6Cj=q8B`IiL{7c23ILOaIxo;m(0X z=X~fzt_m#ir>WPRcl^w}-~6}vhlXeB)Admf5##mB-kLgSxgoU~y2T%>k_vyh`38?J z8ow_x_UYlB&-JIda>^yyOP-W*&3ojmb!+B88TZL=Pd7QY>{AE-oAob^Xzl1Qb75}n z?;w~Qnv^Vd*|k_fpKTbqYtfw@^JwlrAh7q}kA3sfpvh~0d@n^m+^8>!-K)aw3hul( zXX1DM?79SAmuv6GR*(N)=D{#k+sb-q$5z<~iAQ)b4|Z*4-`!npJ9b@+wqr;7{{j>b WIZMj#w7GNE#yNN;|6+IE8S(!~gxvc8 literal 0 HcmV?d00001 diff --git a/rpc-client-latency-bench.php b/rpc-client-latency-bench.php new file mode 100644 index 0000000000..cda544139a --- /dev/null +++ b/rpc-client-latency-bench.php @@ -0,0 +1,94 @@ + " . number_format(($end_time - $start_time) * 1000, 2) . ", requests time -> " . number_format(($requests_time_end - $requests_time_start) * 1000, 2) . +// ", responses time -> " . number_format(($responses_time_end - $responses_time_start) * 1000, 2) . "\n"; + + return $responses[0]; +} + +function fibonacciRecursive($n) { + if ($n <= 1) { + return $n; + } else { + return fibonacciRecursive($n - 1) + fibonacciRecursive($n - 2); + } +} + +$fibonacci_num = (int) $_ENV['FIBONACCI_NUM']; +// $fibonacci_num = 30; +// echo $fibonacci_num; + +// CPU WORK +$a = fibonacciRecursive($fibonacci_num); + +$sum_num = (int) $_ENV['SUM_NUM']; +$b = 0; +for ($i = 1; $i <= $sum_num; ++$i) { + $b += $i; +} + +$requests_num = (int) $_ENV['REQUESTS_NUM']; +// $requests_num = 0; +// echo $requests_num; + +// $key_len = (int) $_ENV['KEY_LEN']; + +// NET WORK +$query = rpc_server_fetch_request(); +$queries = array_fill(0, $requests_num, $query); + +if ($requests_num >= 0) { + $response = proxy_queries($queries); +} + +// $start = microtime(true); +// $queries = array_map(function ($i) { return $query; }, range(0, $requests_num)); +// $end = microtime(true); +// echo "It took " . number_format(($end - $start) * 1000, 2) . " to create an array\n"; + + +// rpc_server_store_response($response->getResult()); + +$responsee = VK\TL\memcache\Functions\memcache_get::createRpcServerResponse(new VK\TL\memcache\Types\memcache_not_found()); +rpc_server_store_response($responsee); + +// wait(fork(benchmark($queries)), 0.15); +// exit(0); From 7ef70d13c5927a3ac9c0d5d4e9a297bf6efacb8c Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Tue, 5 May 2026 21:28:16 +0300 Subject: [PATCH 03/17] break in switch + different debug exit codes --- runtime-light/stdlib/rpc/rpc-api.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index ac5e6c6845..fde9fe4c89 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -355,15 +355,19 @@ kphp::coro::task send_request(std::string_view actor, std k2::exit(69); } opt_response = {reinterpret_cast(response_buf.data()), static_cast(response_buf.size())}; + break; } case kphp::coro::poll_status::closed: + k2::exit(70); // TODO really nothing ? // fallthrough case kphp::coro::poll_status::error: + k2::exit(71); // TODO ERROR ??? // nothing // fallthrough case kphp::coro::poll_status::timeout: + k2::exit(79); // nothing // fallthrough } From f12712707a370307fc80615e61d91b1ccd3c0f69 Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Tue, 5 May 2026 21:29:46 +0300 Subject: [PATCH 04/17] null timeout in bench --- cmake/utils.cmake | 22 +++++++++++----------- rpc-client-latency-bench.php | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 56c09db09a..62a8639b44 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -91,17 +91,17 @@ function(update_git_submodule SUBMODULE_PATH) message(STATUS "Updating Git submodule ${SUBMODULE_PATH} ...") # Update submodules - execute_process( - COMMAND ${GIT_EXECUTABLE} submodule update --init ${ARGN} ${SUBMODULE_PATH} - WORKING_DIRECTORY ${BASE_DIR} - RESULT_VARIABLE return_code - OUTPUT_VARIABLE stdout - ERROR_VARIABLE stderr - ) - - if(NOT return_code EQUAL 0) - message(FATAL_ERROR "Failed to update Git submodule ${SUBMODULE_PATH}: ${stdout} ${stderr}") - endif() +# execute_process( +# COMMAND ${GIT_EXECUTABLE} submodule update --init ${ARGN} ${SUBMODULE_PATH} +# WORKING_DIRECTORY ${BASE_DIR} +# RESULT_VARIABLE return_code +# OUTPUT_VARIABLE stdout +# ERROR_VARIABLE stderr +# ) + +# if(NOT return_code EQUAL 0) +# message(FATAL_ERROR "Failed to update Git submodule ${SUBMODULE_PATH}: ${stdout} ${stderr}") +# endif() endfunction() function(detect_xcode_sdk_path SDK_PATH INCLUDE_DIRS) diff --git a/rpc-client-latency-bench.php b/rpc-client-latency-bench.php index cda544139a..2b0d1b6b72 100644 --- a/rpc-client-latency-bench.php +++ b/rpc-client-latency-bench.php @@ -13,10 +13,10 @@ function proxy_queries($queries) { // $requests_time_start = $start_time; // if ($is_k2 === 1) { - $query_ids = rpc_send_typed_query_requests("rpc", $queries, 0, false, null, false); + $query_ids = rpc_send_typed_query_requests("rpc", $queries, null, false, null, false); // } else { // $connection = new_rpc_connection("127.0.0.1", 9999); -// $query_ids = typed_rpc_tl_query($connection, $queries, 0, false, null, false); +// $query_ids = typed_rpc_tl_query($connection, $queries, null, false, null, false); // } // $requests_time_end = microtime(true); From 2ca69bd98712fd3ce4d9f4e2035ecefb60c1befc Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Tue, 5 May 2026 23:59:48 +0300 Subject: [PATCH 05/17] rpc send functions now common functions instead of coroutines --- builtin-functions/kphp-light/stdlib/rpc.txt | 4 +-- runtime-light/stdlib/rpc/rpc-api.cpp | 36 ++++++++++----------- runtime-light/stdlib/rpc/rpc-api.h | 22 ++++++------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/builtin-functions/kphp-light/stdlib/rpc.txt b/builtin-functions/kphp-light/stdlib/rpc.txt index c88b464163..e82e507e12 100644 --- a/builtin-functions/kphp-light/stdlib/rpc.txt +++ b/builtin-functions/kphp-light/stdlib/rpc.txt @@ -40,7 +40,7 @@ final class KphpRpcRequestsExtraInfo { public function get (); } -/** @kphp-extern-func-info interruptible */ +/** @kphp-extern-func-info */ function rpc_send_requests($actor ::: string, $arr ::: array, $timeout ::: ?float, @@ -48,7 +48,7 @@ function rpc_send_requests($actor ::: string, \KphpRpcRequestsExtraInfo $requests_extra_info, $need_responses_extra_info ::: bool) ::: int[]; -/** @kphp-extern-func-info tl_common_h_dep interruptible */ +/** @kphp-extern-func-info tl_common_h_dep */ function rpc_send_typed_query_requests($actor ::: string, @tl\RpcFunction[] $query_functions, $timeout ::: ?float, $ignore_answer ::: bool, diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index fde9fe4c89..7bd8f411e2 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -111,42 +111,42 @@ class_instance store_function(const mixed& tl_object) noexcept { return rpc_tl_query; } -kphp::coro::task rpc_tl_query_one_impl(std::string_view actor, mixed tl_object, std::optional opt_timeout, +kphp::rpc::query_info rpc_tl_query_one_impl(std::string_view actor, mixed tl_object, std::optional opt_timeout, bool collect_resp_extra_info, bool ignore_answer) noexcept { if (!tl_object.is_array()) [[unlikely]] { kphp::log::warning("not an array passed to function rpc_tl_query"); - co_return kphp::rpc::query_info{}; + return kphp::rpc::query_info{}; } f$rpc_clean(); auto rpc_tl_query{store_function(tl_object)}; // THROWING // handle exceptions that could arise during store_function if (!TlRpcError::transform_exception_into_error_if_possible().empty() || rpc_tl_query.is_null()) [[unlikely]] { - co_return kphp::rpc::query_info{}; + return kphp::rpc::query_info{}; } - const auto query_info{co_await kphp::rpc::send_request(actor, opt_timeout, ignore_answer, collect_resp_extra_info)}; + const auto query_info{kphp::rpc::send_request(actor, opt_timeout, ignore_answer, collect_resp_extra_info)}; if (!ignore_answer) { RpcClientInstanceState::get().response_fetcher_instances.emplace(query_info.id, std::move(rpc_tl_query)); } - co_return query_info; + return query_info; } -kphp::coro::task typed_rpc_tl_query_one_impl(std::string_view actor, const RpcRequest& rpc_request, std::optional opt_timeout, +kphp::rpc::query_info typed_rpc_tl_query_one_impl(std::string_view actor, const RpcRequest& rpc_request, std::optional opt_timeout, bool collect_responses_extra_info, bool ignore_answer) noexcept { if (rpc_request.empty()) [[unlikely]] { kphp::log::warning("query function is null"); - co_return kphp::rpc::query_info{}; + return kphp::rpc::query_info{}; } f$rpc_clean(); auto fetcher{rpc_request.store_request()}; // THROWING // handle exceptions that could arise during store_request if (!TlRpcError::transform_exception_into_error_if_possible().empty() || !static_cast(fetcher)) [[unlikely]] { - co_return kphp::rpc::query_info{}; + return kphp::rpc::query_info{}; } - const auto query_info{co_await kphp::rpc::send_request(actor, opt_timeout, ignore_answer, collect_responses_extra_info)}; + const auto query_info{kphp::rpc::send_request(actor, opt_timeout, ignore_answer, collect_responses_extra_info)}; if (!ignore_answer) { auto rpc_tl_query{make_instance()}; rpc_tl_query.get()->result_fetcher = std::move(fetcher); @@ -154,7 +154,7 @@ kphp::coro::task typed_rpc_tl_query_one_impl(std::string_ RpcClientInstanceState::get().response_fetcher_instances.emplace(query_info.id, std::move(rpc_tl_query)); } - co_return query_info; + return query_info; } kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) noexcept { @@ -273,7 +273,7 @@ kphp::coro::task> typed_rpc_tl_query_result_ } // namespace detail -kphp::coro::task send_request(std::string_view actor, std::optional opt_timeout, bool ignore_answer, +kphp::rpc::query_info send_request(std::string_view actor, std::optional opt_timeout, bool ignore_answer, bool collect_responses_extra_info) noexcept { auto& rpc_client_instance_st{RpcClientInstanceState::get()}; auto& rpc_server_instance_st{RpcServerInstanceState::get()}; @@ -348,13 +348,13 @@ kphp::coro::task send_request(std::string_view actor, std // TODO ERROR k2::exit(148); } - std::span response_buf{reinterpret_cast(k2::alloc(response_size)), response_size}; - size_t new_resonse_size = k2::rpc_fetch_response(rpc_d, response_buf); - if (new_resonse_size != response_size) { + string response{reinterpret_cast(k2::alloc(response_size)), static_cast(response_size)}; + size_t new_response_size = k2::rpc_fetch_response(rpc_d, {reinterpret_cast(response.buffer()), response.size()}); + if (new_response_size != response_size) { // TODO ERROR k2::exit(69); } - opt_response = {reinterpret_cast(response_buf.data()), static_cast(response_buf.size())}; + opt_response = std::move(response); break; } case kphp::coro::poll_status::closed: @@ -403,7 +403,7 @@ kphp::coro::task send_request(std::string_view actor, std // normalize timeout using namespace std::chrono_literals; - static constexpr auto DEFAULT_TIMEOUT{300ms}; + static constexpr auto DEFAULT_TIMEOUT{/*TODO RETURN 300ms BEFORE MERGE*/3000ms}; static constexpr auto MIN_TIMEOUT{1ms}; static constexpr auto MAX_TIMEOUT{std::chrono::duration_cast(24h)}; static_assert(MIN_TIMEOUT <= MAX_TIMEOUT, "calling std::clamp will lead to undefined behavior"); @@ -420,7 +420,7 @@ kphp::coro::task send_request(std::string_view actor, std kphp::log::assertion(kphp::coro::io_scheduler::get().start(ignore_answer_awaiter_task)); rpc_client_instance_st.ignore_answer_request_awaiter_tasks.push(std::move(ignore_answer_awaiter_task)); - co_return kphp::rpc::query_info{.id = kphp::rpc::IGNORED_ANSWER_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; + return kphp::rpc::query_info{.id = kphp::rpc::IGNORED_ANSWER_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; } // start awaiter task // TODO need to move rpc_d ? @@ -428,7 +428,7 @@ kphp::coro::task send_request(std::string_view actor, std kphp::log::assertion(kphp::coro::io_scheduler::get().start(awaiter_task)); rpc_client_instance_st.response_awaiter_tasks.emplace(query_id, std::move(awaiter_task)); - co_return kphp::rpc::query_info{.id = query_id, .request_size = request_size, .timestamp = timestamp}; + return kphp::rpc::query_info{.id = query_id, .request_size = request_size, .timestamp = timestamp}; } } // namespace kphp::rpc diff --git a/runtime-light/stdlib/rpc/rpc-api.h b/runtime-light/stdlib/rpc/rpc-api.h index 5a9a43872b..405d7bdaa0 100644 --- a/runtime-light/stdlib/rpc/rpc-api.h +++ b/runtime-light/stdlib/rpc/rpc-api.h @@ -40,7 +40,7 @@ struct query_info { double timestamp{0.0}; }; -kphp::coro::task send_request(std::string_view actor, std::optional timeout, bool ignore_answer, +kphp::rpc::query_info send_request(std::string_view actor, std::optional timeout, bool ignore_answer, bool collect_responses_extra_info) noexcept; inline kphp::coro::task> send_response(std::span response) noexcept { @@ -59,12 +59,12 @@ inline kphp::coro::task> send_response(std::span rpc_tl_query_one_impl(std::string_view actor, mixed tl_object, std::optional opt_timeout, +kphp::rpc::query_info rpc_tl_query_one_impl(std::string_view actor, mixed tl_object, std::optional opt_timeout, bool collect_resp_extra_info, bool ignore_answer) noexcept; kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) noexcept; -kphp::coro::task typed_rpc_tl_query_one_impl(std::string_view actor, const RpcRequest& rpc_request, std::optional opt_timeout, +kphp::rpc::query_info typed_rpc_tl_query_one_impl(std::string_view actor, const RpcRequest& rpc_request, std::optional opt_timeout, bool collect_responses_extra_info, bool ignore_answer) noexcept; kphp::coro::task> typed_rpc_tl_query_result_one_impl(int64_t query_id, const RpcErrorFactory& error_factory) noexcept; @@ -287,7 +287,7 @@ inline int64_t f$rpc_tl_pending_queries_count() noexcept { // === client untyped ============================================================================= -inline kphp::coro::task> f$rpc_send_requests(string actor, array tl_objects, Optional timeout, bool ignore_answer, +inline array f$rpc_send_requests(string actor, array tl_objects, Optional timeout, bool ignore_answer, class_instance requests_extra_info, bool need_responses_extra_info) noexcept { if (ignore_answer && need_responses_extra_info) [[unlikely]] { @@ -300,8 +300,8 @@ inline kphp::coro::task> f$rpc_send_requests(string actor, array< auto opt_timeout{timeout.has_value() ? std::optional{timeout.val()} : std::optional{}}; for (const auto& it : std::as_const(tl_objects)) { - const auto query_info{co_await kphp::forks::id_managed( - kphp::rpc::detail::rpc_tl_query_one_impl({actor.c_str(), actor.size()}, it.get_value(), opt_timeout, collect_resp_extra_info, ignore_answer))}; + const auto query_info{ + kphp::rpc::detail::rpc_tl_query_one_impl({actor.c_str(), actor.size()}, it.get_value(), opt_timeout, collect_resp_extra_info, ignore_answer)}; query_ids.set_value(it.get_key(), query_info.id); req_extra_info_arr.set_value(it.get_key(), kphp::rpc::request_extra_info{query_info.request_size}); } @@ -309,7 +309,7 @@ inline kphp::coro::task> f$rpc_send_requests(string actor, array< if (!requests_extra_info.is_null()) { requests_extra_info->extra_info_arr = std::move(req_extra_info_arr); } - co_return std::move(query_ids); + return query_ids; } inline kphp::coro::task>> f$rpc_fetch_responses(array query_ids) noexcept { @@ -337,7 +337,7 @@ kphp::coro::task>> f$rpc_fetch_responses_synchronously(array< // === client typed =============================================================================== template rpc_function_type, std::same_as rpc_request_type = KphpRpcRequest> -kphp::coro::task> +array f$rpc_send_typed_query_requests(string actor, array> query_functions, Optional timeout, bool ignore_answer, class_instance requests_extra_info, bool need_responses_extra_info) noexcept { if (ignore_answer && need_responses_extra_info) [[unlikely]] { @@ -350,8 +350,8 @@ f$rpc_send_typed_query_requests(string actor, array{timeout.val()} : std::optional{}}; for (const auto& it : std::as_const(query_functions)) { - const auto query_info{co_await kphp::forks::id_managed(kphp::rpc::detail::typed_rpc_tl_query_one_impl( - {actor.c_str(), actor.size()}, rpc_request_type{it.get_value()}, opt_timeout, collect_resp_extra_info, ignore_answer))}; + const auto query_info{kphp::rpc::detail::typed_rpc_tl_query_one_impl( + {actor.c_str(), actor.size()}, rpc_request_type{it.get_value()}, opt_timeout, collect_resp_extra_info, ignore_answer)}; query_ids.set_value(it.get_key(), query_info.id); req_extra_info_arr.set_value(it.get_key(), kphp::rpc::request_extra_info{query_info.request_size}); } @@ -359,7 +359,7 @@ f$rpc_send_typed_query_requests(string actor, arrayextra_info_arr = std::move(req_extra_info_arr); } - co_return std::move(query_ids); + return query_ids; } template query_id_type = int64_t, std::same_as error_factory_type = RpcResponseErrorFactory> From 894a467a30171b8ea34b681d962182ac3b5d8927 Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Wed, 6 May 2026 11:37:03 +0300 Subject: [PATCH 06/17] remove one coroutine on rpc response fetch --- rpc-client-latency-bench.php | 3 + runtime-light/stdlib/rpc/rpc-api.cpp | 144 +++++++++++++++----- runtime-light/stdlib/rpc/rpc-client-state.h | 10 ++ 3 files changed, 126 insertions(+), 31 deletions(-) diff --git a/rpc-client-latency-bench.php b/rpc-client-latency-bench.php index 2b0d1b6b72..96604920c1 100644 --- a/rpc-client-latency-bench.php +++ b/rpc-client-latency-bench.php @@ -36,6 +36,9 @@ function proxy_queries($queries) { if (is_null($response)) { critical_error("null response"); } + if ($response->isError()) { + critical_error("error response"); + } } // echo "Wall time -> " . number_format(($end_time - $start_time) * 1000, 2) . ", requests time -> " . number_format(($requests_time_end - $requests_time_start) * 1000, 2) . diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index 7bd8f411e2..08f07b6785 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -111,8 +111,8 @@ class_instance store_function(const mixed& tl_object) noexcept { return rpc_tl_query; } -kphp::rpc::query_info rpc_tl_query_one_impl(std::string_view actor, mixed tl_object, std::optional opt_timeout, - bool collect_resp_extra_info, bool ignore_answer) noexcept { +kphp::rpc::query_info rpc_tl_query_one_impl(std::string_view actor, mixed tl_object, std::optional opt_timeout, bool collect_resp_extra_info, + bool ignore_answer) noexcept { if (!tl_object.is_array()) [[unlikely]] { kphp::log::warning("not an array passed to function rpc_tl_query"); return kphp::rpc::query_info{}; @@ -133,7 +133,7 @@ kphp::rpc::query_info rpc_tl_query_one_impl(std::string_view actor, mixed tl_obj } kphp::rpc::query_info typed_rpc_tl_query_one_impl(std::string_view actor, const RpcRequest& rpc_request, std::optional opt_timeout, - bool collect_responses_extra_info, bool ignore_answer) noexcept { + bool collect_responses_extra_info, bool ignore_answer) noexcept { if (rpc_request.empty()) [[unlikely]] { kphp::log::warning("query function is null"); return kphp::rpc::query_info{}; @@ -157,6 +157,40 @@ kphp::rpc::query_info typed_rpc_tl_query_one_impl(std::string_view actor, const return query_info; } +namespace { + +static std::optional get_rpc_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) { + std::optional opt_response{std::nullopt}; + + size_t first_response_size{k2::rpc_get_response_size(rpc_d)}; + if (first_response_size == 0) { + // TODO ERROR + k2::exit(148); + } + string response{reinterpret_cast(k2::alloc(first_response_size)), static_cast(first_response_size)}; + size_t new_response_size = k2::rpc_fetch_response(rpc_d, {reinterpret_cast(response.buffer()), response.size()}); + if (new_response_size != first_response_size) { + // TODO ERROR + k2::exit(69); + } + opt_response = std::move(response); + // update response extra info if needed + if (collect_responses_extra_info) { + auto& extra_info_map{RpcClientInstanceState::get().rpc_responses_extra_info}; + if (const auto it_extra_info{extra_info_map.find(query_id)}; it_extra_info != extra_info_map.end()) [[likely]] { + const auto timestamp{std::chrono::duration{std::chrono::system_clock::now().time_since_epoch()}.count()}; + const auto response_size{opt_response.transform([](const string& response) noexcept { return static_cast(response.size()); }).value_or(0)}; + it_extra_info->second.second = std::make_tuple(response_size, timestamp - std::get<1>(it_extra_info->second.second)); + it_extra_info->second.first = response_extra_info_status::ready; + } else { + kphp::log::warning("can't find extra info for RPC query {}", query_id); + } + } + + return opt_response; +} +} // namespace + kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) noexcept { if (query_id < kphp::rpc::VALID_QUERY_ID_RANGE_START) [[unlikely]] { co_return TlRpcError::make_error(TL_ERROR_WRONG_QUERY_ID, string{"wrong query_id"}); @@ -164,26 +198,30 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no auto& rpc_client_instance_st{RpcClientInstanceState::get()}; class_instance rpc_query{}; - std::optional>> opt_awaiter_task{}; + std::optional<уберите_меня_отсюда::rpc_request_info> opt_rpc_request_info{}; { const auto it_response_fetcher{rpc_client_instance_st.response_fetcher_instances.find(query_id)}; - const auto it_fork_task{rpc_client_instance_st.response_awaiter_tasks.find(query_id)}; - const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_fork_task] noexcept { +// const auto it_fork_task{rpc_client_instance_st.response_awaiter_tasks.find(query_id)}; + const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(query_id)}; + const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher/*, it_fork_task*/, it_rpc_request_info] noexcept { if (it_response_fetcher != rpc_client_instance_st.response_fetcher_instances.end()) [[likely]] { rpc_client_instance_st.response_fetcher_instances.erase(it_response_fetcher); } - if (it_fork_task != rpc_client_instance_st.response_awaiter_tasks.end()) [[likely]] { - rpc_client_instance_st.response_awaiter_tasks.erase(it_fork_task); +// if (it_fork_task != rpc_client_instance_st.response_awaiter_tasks.end()) [[likely]] { +// rpc_client_instance_st.response_awaiter_tasks.erase(it_fork_task); +// } + if (it_rpc_request_info != rpc_client_instance_st.rpc_requests_infos.end()) [[likely]] { + rpc_client_instance_st.rpc_requests_infos.erase(it_rpc_request_info); } }}; - if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end() || it_fork_task == rpc_client_instance_st.response_awaiter_tasks.end()) + if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end() /*|| it_fork_task == rpc_client_instance_st.response_awaiter_tasks.end()*/ || it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) [[unlikely]] { co_return TlRpcError::make_error(TL_ERROR_INTERNAL, string{"unexpectedly could not find query in pending queries"}); } rpc_query = std::move(it_response_fetcher->second); - opt_awaiter_task.emplace(std::move(it_fork_task->second)); + opt_rpc_request_info.emplace(std::move(it_rpc_request_info->second)); } if (rpc_query.is_null()) [[unlikely]] { @@ -196,8 +234,24 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no co_return TlRpcError::make_error(TL_ERROR_INTERNAL, string{"can't get untyped result from typed TL query. Use consistent API for that"}); } - kphp::log::assertion(opt_awaiter_task.has_value()); - auto opt_response{co_await kphp::forks::id_managed(*std::exchange(opt_awaiter_task, std::nullopt))}; + std::optional opt_response{std::nullopt}; + + kphp::log::assertion(opt_rpc_request_info.has_value()); + auto rpc_request_info{*opt_rpc_request_info}; + kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; + switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, std::chrono::milliseconds(1000))) { + case kphp::coro::poll_status::event: { + opt_response = get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info); + break; + } + case kphp::coro::poll_status::closed: + k2::exit(70); + case kphp::coro::poll_status::error: + k2::exit(71); + case kphp::coro::poll_status::timeout: + k2::exit(79); + } +// auto opt_response{co_await kphp::forks::id_managed(*std::exchange(opt_awaiter_task, std::nullopt))}; if (!opt_response) [[unlikely]] { co_return TlRpcError::make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); } @@ -221,41 +275,67 @@ kphp::coro::task> typed_rpc_tl_query_result_ auto& rpc_client_instance_st{RpcClientInstanceState::get()}; class_instance rpc_query{}; - std::optional>> opt_awaiter_task{}; + std::optional<уберите_меня_отсюда::rpc_request_info> opt_rpc_request_info{}; { const auto it_response_fetcher{rpc_client_instance_st.response_fetcher_instances.find(query_id)}; - const auto it_fork_task{rpc_client_instance_st.response_awaiter_tasks.find(query_id)}; - const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_fork_task] noexcept { +// const auto it_fork_task{rpc_client_instance_st.response_awaiter_tasks.find(query_id)}; + const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(query_id)}; + const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher/*, it_fork_task*/, it_rpc_request_info] noexcept { if (it_response_fetcher != rpc_client_instance_st.response_fetcher_instances.end()) [[likely]] { rpc_client_instance_st.response_fetcher_instances.erase(it_response_fetcher); } - if (it_fork_task != rpc_client_instance_st.response_awaiter_tasks.end()) [[likely]] { - rpc_client_instance_st.response_awaiter_tasks.erase(it_fork_task); +// if (it_fork_task != rpc_client_instance_st.response_awaiter_tasks.end()) [[likely]] { +// rpc_client_instance_st.response_awaiter_tasks.erase(it_fork_task); +// } + if (it_rpc_request_info != rpc_client_instance_st.rpc_requests_infos.end()) [[likely]] { + rpc_client_instance_st.rpc_requests_infos.erase(it_rpc_request_info); } }}; - if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end() || it_fork_task == rpc_client_instance_st.response_awaiter_tasks.end()) +// kphp::log::warning("AAA"); + if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end()/* || it_fork_task == rpc_client_instance_st.response_awaiter_tasks.end()*/ || it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) [[unlikely]] { +// kphp::log::warning("A"); co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"unexpectedly could not find query in pending queries"}); } rpc_query = std::move(it_response_fetcher->second); - opt_awaiter_task.emplace(std::move(it_fork_task->second)); + opt_rpc_request_info.emplace(std::move(it_rpc_request_info->second)); } if (rpc_query.is_null()) [[unlikely]] { +// kphp::log::warning("B"); co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"can't use rpc_tl_query_result for non-TL query"}); } if (!rpc_query.get()->result_fetcher || rpc_query.get()->result_fetcher->empty()) [[unlikely]] { +// kphp::log::warning("C"); co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"rpc query has empty result fetcher"}); } if (!rpc_query.get()->result_fetcher->is_typed) [[unlikely]] { +// kphp::log::warning("D"); co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"can't get typed result from untyped TL query. Use consistent API for that"}); } - kphp::log::assertion(opt_awaiter_task.has_value()); - auto opt_response{co_await kphp::forks::id_managed(*std::exchange(opt_awaiter_task, std::nullopt))}; + std::optional opt_response{std::nullopt}; + + kphp::log::assertion(opt_rpc_request_info.has_value()); + auto rpc_request_info{*opt_rpc_request_info}; + kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; + switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, std::chrono::milliseconds(1000))) { + case kphp::coro::poll_status::event: { + opt_response = get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info); + break; + } + case kphp::coro::poll_status::closed: + k2::exit(70); + case kphp::coro::poll_status::error: + k2::exit(71); + case kphp::coro::poll_status::timeout: + k2::exit(79); + } + // auto opt_response{co_await kphp::forks::id_managed(*std::exchange(opt_awaiter_task, std::nullopt))}; if (!opt_response) [[unlikely]] { +// kphp::log::warning("E"); co_return error_factory.make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); } @@ -266,6 +346,7 @@ kphp::coro::task> typed_rpc_tl_query_result_ auto res{fetch_function_typed(rpc_query, error_factory)}; // THROWING // handle exceptions that could arise during fetch_function_typed if (auto err{error_factory.transform_exception_into_error_if_possible()}; !err.is_null()) [[unlikely]] { +// kphp::log::warning("F"); co_return std::move(err); } co_return std::move(res); @@ -273,8 +354,7 @@ kphp::coro::task> typed_rpc_tl_query_result_ } // namespace detail -kphp::rpc::query_info send_request(std::string_view actor, std::optional opt_timeout, bool ignore_answer, - bool collect_responses_extra_info) noexcept { +kphp::rpc::query_info send_request(std::string_view actor, std::optional opt_timeout, bool ignore_answer, bool collect_responses_extra_info) noexcept { auto& rpc_client_instance_st{RpcClientInstanceState::get()}; auto& rpc_server_instance_st{RpcServerInstanceState::get()}; const size_t request_size{rpc_server_instance_st.tl_storer.view().size_bytes()}; @@ -336,7 +416,7 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional // create a task to wait for RPC response. we need to do it even if 'ignore_answer' is 'true' to make sure // that the stream will not be closed too early. otherwise, platform may even not send RPC request - static constexpr auto awaiter_coroutine{[](int64_t query_id, k2::descriptor rpc_d, std::chrono::milliseconds timeout, + /*static constexpr auto awaiter_coroutine{[](int64_t query_id, k2::descriptor rpc_d, std::chrono::milliseconds timeout, bool collect_responses_extra_info) noexcept -> kphp::coro::shared_task> { std::optional opt_response{std::nullopt}; @@ -372,13 +452,13 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional // fallthrough } - /* + *//* std::optional opt_response{std::in_place}; 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; } - */ + *//* // update response extra info if needed if (collect_responses_extra_info) { @@ -394,7 +474,7 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional } co_return std::move(opt_response); - }}; + }};*/ static constexpr auto ignore_answer_awaiter_coroutine{[](k2::descriptor rpc_d, std::chrono::milliseconds timeout) noexcept -> kphp::coro::shared_task<> { auto fetch_task{kphp::coro::io_scheduler::get().poll(rpc_d, kphp::coro::poll_op::read, timeout)}; @@ -403,7 +483,7 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional // normalize timeout using namespace std::chrono_literals; - static constexpr auto DEFAULT_TIMEOUT{/*TODO RETURN 300ms BEFORE MERGE*/3000ms}; + static constexpr auto DEFAULT_TIMEOUT{/*TODO RETURN 300ms BEFORE MERGE*/ 3000ms}; static constexpr auto MIN_TIMEOUT{1ms}; static constexpr auto MAX_TIMEOUT{std::chrono::duration_cast(24h)}; static_assert(MIN_TIMEOUT <= MAX_TIMEOUT, "calling std::clamp will lead to undefined behavior"); @@ -424,10 +504,12 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional } // start awaiter task // TODO need to move rpc_d ? - auto awaiter_task{awaiter_coroutine(query_id, std::move(rpc_d), timeout, collect_responses_extra_info)}; - kphp::log::assertion(kphp::coro::io_scheduler::get().start(awaiter_task)); +// auto awaiter_task{awaiter_coroutine(query_id, std::move(rpc_d), timeout, collect_responses_extra_info)}; +// kphp::log::assertion(kphp::coro::io_scheduler::get().start(awaiter_task)); + +// rpc_client_instance_st.response_awaiter_tasks.emplace(query_id, std::move(awaiter_task)); - rpc_client_instance_st.response_awaiter_tasks.emplace(query_id, std::move(awaiter_task)); + rpc_client_instance_st.rpc_requests_infos.emplace(query_id, уберите_меня_отсюда::rpc_request_info{rpc_d, collect_responses_extra_info}); return kphp::rpc::query_info{.id = query_id, .request_size = request_size, .timestamp = timestamp}; } diff --git a/runtime-light/stdlib/rpc/rpc-client-state.h b/runtime-light/stdlib/rpc/rpc-client-state.h index c4d86416b2..fcb7573e10 100644 --- a/runtime-light/stdlib/rpc/rpc-client-state.h +++ b/runtime-light/stdlib/rpc/rpc-client-state.h @@ -19,10 +19,20 @@ #include "runtime-light/stdlib/rpc/rpc-tl-defs.h" #include "runtime-light/stdlib/rpc/rpc-tl-query.h" +namespace уберите_меня_отсюда { + +struct rpc_request_info { + k2::descriptor rpc_d; + bool collect_responses_extra_info; +}; + +} + struct RpcClientInstanceState final : private vk::not_copyable { CurrentTlQuery current_client_query{}; int64_t current_query_id{kphp::rpc::VALID_QUERY_ID_RANGE_START}; + kphp::stl::unordered_map rpc_requests_infos; kphp::stl::unordered_map>, kphp::memory::script_allocator> response_awaiter_tasks; kphp::stl::unordered_map, kphp::memory::script_allocator> response_fetcher_instances; kphp::stl::unordered_map, kphp::memory::script_allocator> From bf28c06dd01519aee6d1cb1a2f660c4c0e0005e6 Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Wed, 20 May 2026 12:31:54 +0300 Subject: [PATCH 07/17] removed test trash --- cmake/utils.cmake | 22 +++---- common.tl | 124 ----------------------------------- common.tlo | Bin 9084 -> 0 bytes rpc-client-latency-bench.php | 97 --------------------------- 4 files changed, 11 insertions(+), 232 deletions(-) delete mode 100644 common.tl delete mode 100644 common.tlo delete mode 100644 rpc-client-latency-bench.php diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 62a8639b44..56c09db09a 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -91,17 +91,17 @@ function(update_git_submodule SUBMODULE_PATH) message(STATUS "Updating Git submodule ${SUBMODULE_PATH} ...") # Update submodules -# execute_process( -# COMMAND ${GIT_EXECUTABLE} submodule update --init ${ARGN} ${SUBMODULE_PATH} -# WORKING_DIRECTORY ${BASE_DIR} -# RESULT_VARIABLE return_code -# OUTPUT_VARIABLE stdout -# ERROR_VARIABLE stderr -# ) - -# if(NOT return_code EQUAL 0) -# message(FATAL_ERROR "Failed to update Git submodule ${SUBMODULE_PATH}: ${stdout} ${stderr}") -# endif() + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init ${ARGN} ${SUBMODULE_PATH} + WORKING_DIRECTORY ${BASE_DIR} + RESULT_VARIABLE return_code + OUTPUT_VARIABLE stdout + ERROR_VARIABLE stderr + ) + + if(NOT return_code EQUAL 0) + message(FATAL_ERROR "Failed to update Git submodule ${SUBMODULE_PATH}: ${stdout} ${stderr}") + endif() endfunction() function(detect_xcode_sdk_path SDK_PATH INCLUDE_DIRS) diff --git a/common.tl b/common.tl deleted file mode 100644 index 9cf3f1de25..0000000000 --- a/common.tl +++ /dev/null @@ -1,124 +0,0 @@ -///// -// -// Common Types -// -///// - -// Builtin types -int#a8509bda ? = Int; -long#22076cba ? = Long; -float#824dab22 ? = Float; // 4 bytes -- single precision -double#2210c154 ? = Double; // 8 bytes -- double precision -string#b5286e24 ? = String; - -// Boolean emulation -boolFalse#bc799737 = Bool; -boolTrue#997275b5 = Bool; - -// Boolean for diagonal queries -boolStat statTrue:int statFalse:int statUnknown:int = BoolStat; - -// Vector -vector#1cb5c415 {t:Type} # [t] = Vector t; -tuple#9770768a {t:Type} {n:#} [t] = Tuple t n; -vectorTotal {t:Type} total_count:int vector:%(Vector t) = VectorTotal t; - -// Dictionaries -dictionaryField {t:Type} key:string value:t = DictionaryField t; -dictionary#1f4c618f {t:Type} %(Vector %(DictionaryField t)) = Dictionary t; - -intKeyDictionaryField {t:Type} key:int value:t = IntKeyDictionaryField t; -intKeyDictionary#07bafc42 {t:Type} %(Vector %(intKeyDictionaryField t)) = IntKeyDictionary t; - -longKeyDictionaryField {t:Type} key:long value:t = LongKeyDictionaryField t; -longKeyDictionary#b424d8f1 {t:Type} %(Vector %(longKeyDictionaryField t)) = LongKeyDictionary t; - -// Maybe -resultFalse#27930a7b {t:Type} = Maybe t; -resultTrue#3f9c8ef8 {t:Type} result:t = Maybe t; - - -pair {X:Type} {Y:Type} a:X b:Y = Pair X Y; - -true = True; // this can be used as void type and serialized to empty array in PHP - -stat#9d56e6b2 %(Dictionary string) = Stat; - -rpcInvokeReqExtra#f3ef81a9 {flags:#} - return_binlog_pos:flags.0?%True - return_binlog_time:flags.1?%True - return_pid:flags.2?%True - return_request_sizes:flags.3?%True - return_failed_subqueries:flags.4?%True - return_query_stats:flags.6?%True - no_result:flags.7?%True // Currently for proxy only. Client goes to proxy, it clears this bit and sends query to engine. Client does not wait for answer. - // Bits 17, 22, 24 was used in before, but their support was dropped - wait_binlog_pos:flags.16?long // Perform query only after position in binlog is at least this - string_forward_keys:flags.18?%(Vector string) // For cluster that are split by string (like pmemcached in some modes) - first specified string is used to choose target, then it is deleted from vector - int_forward_keys:flags.19?%(Vector long) // First long is used to choose target. Then it is deleted from vector - string_forward:flags.20?string // Same as string_forward_keys, but it is not deleted - int_forward:flags.21?long // Same as int_forward_keys, but it is not deleted - custom_timeout_ms:flags.23?int // Custom timeout for query - supported_compression_version:flags.25?int // note, that client support compression, to possibly compress answers - random_delay:flags.26?double // starting query would be delayed by random number, not grater than given - return_view_number:flags.27?%True // Barsic related parameter: return view number in response - = RpcInvokeReqExtra flags; - -rpcReqResultExtra#c5011709 {flags:#} - binlog_pos:flags.0?long binlog_time:flags.1?long - engine_pid:flags.2?%net.Pid - request_size:flags.3?int response_size:flags.3?int - failed_subqueries:flags.4?int - compression_version:flags.5?int - stats:flags.6?%(Dictionary string) - epoch_number:flags.27?%long view_number:flags.27?%long - = RpcReqResultExtra flags; - - -// ReqResult -reqError#b527877d {X:Type} error_code:int error:string = ReqResult X; -reqResultHeader#8cc84ce1 {X:Type} flags:# extra:%(RpcReqResultExtra flags) result:X = ReqResult X; -//reqResultFalse {X:Type} = ReqResult X; -_ {X:Type} result:X = ReqResult X; - -rpcReqResult#63aeda4e {X:Type} query_id:long result:(ReqResult X) = RpcReqResult X; -rpcReqError#7ae432f5 {X:Type} query_id:long error_code:int error:string = RpcReqResult X; - -rpcPong#8430eaa7 ping_id:long = RpcPong; - -net.pid ip:int port_pid:int utime:int = net.Pid; - -left {X:Type} {Y:Type} value:X = Either X Y; -right {X:Type} {Y:Type} value:Y = Either X Y; - -memcache.not_found = memcache.Value; -memcache.strvalue value:string flags:int = memcache.Value; - ----functions--- - -@any rpcDestActor#7568aabd {X:Type} actor_id:long query:!X = X; -@any rpcDestActorFlags#f0a5acf7 {X:Type} actor_id:long flags:# extra:%(RpcInvokeReqExtra flags) query:!X = X; -@any rpcDestFlags#e352035e {X:Type} flags:# extra:%(RpcInvokeReqExtra flags) query:!X = X; - -@any rpcInvokeReq#2374df3d {X:Type} query_id:long query:!X = RpcReqResult X; - -//NEVER use this. Only for debugging engines. -@any @internal engine.setVerbosity verbosity:int = True; -@any @internal engine.setVerbosityType type:string verbosity:int = True; -@any @internal engine.sendSignal signal:int = True; -@any @internal engine.sleep time_ms:int = Bool; - -@any engine.nop = True; -@read engine.readNop = True; // same as nop, but assumed as read in proxy -@write engine.writeNop = True; // same as nop, but assumed as write in proxy -@any engine.stat = Stat; -@any engine.filteredStat stat_names:%(Vector string) = Stat; -@any engine.count = BoolStat; -@any engine.pid = net.Pid; -@any engine.version = String; - -@kphp kphp.pingRpcCluster#a677ee41 id:int = Int; - - - -@kphp memcache.get#d33b13ae key:string = memcache.Value; diff --git a/common.tlo b/common.tlo deleted file mode 100644 index ce69379adf2309440a343ca876ee30102152aa89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9084 zcmcJVe{38_702zwcm8oEj_o9F5=h!4P%0`5^$&h1gwP~SQ%EDrNu%};IIZv2_R8no zV{gx{gOH#^rHT-sf>clw_ybW|TICODgh(l(qOCw7fK(OQ2oV)f#g9UR08tern9uCK zz43VMy`z&(qyR^}N z&+kfa@7fatHPyXJ{jIfNU|v6W?*1Myha)Fa{hbzuM{r+o4sPw+=T#yv@SSi$<9aMS zxQWdtw-2;%`#rZ-Wy~z>k*C&fEA9)L6E#PZK#ap~++6_}1utge}ZI9l5 zPho#8zyNy;a9}PxIQH=B+x&=r8$D(K-P^hj2L4nst~sW9rFs7Sg&mI%g0YAQRz3w^+EU-RVF^AaA{=a)&m14Prf|En zFyXf1!p1y$>jwt9#vCt9xT4SY``9xtJ=r_%etg_*G;7NDs})AY*eZLhM?U@PKL*F^ zmD~KeV8&Gfx7;0tPV6U%3z5N3-}>{rk9RBlm~w->N53>MPyF%28~YW;P@l$!DFe25 zr1Kf{r`R0*-+B4ysji&$u)}}nnd0Fn^weyk&(sM|^uGVOzwYij5;m2abw9S64a|wT z`U8a{&AM6w=@|G~d<|ygwO4NJIg^yL~qr9rMz4ZgM0&J%9g( zKfU*Xg&*`zs64aZsWp@#)VBXKdh;8VnUmCXQoLX(= zRG#68uhHMB*Hjz{@FNaHo__gGAM+1){_ei;(^q&$E;iqt{`np;FZDS(8As$y4DaEo zb9#%XPAVNr*yHg9e=;v{powqL$#WKl_wd!Vg3ULn73f~Tt{}k%8NU6<#E+DN-eHY z_-@Vx&&W%|HeIiPwR*x4{pYy!}Dq6PY|cuYLaH?!H)$-VsF)`6*8i{l!=oHJSOl9S*wQ ztjF~x-?6hx^uNJ;-05g)i@0gKCR*=h?!<3!pcfz5YhRv41{`bQlb^l*Oi!rF+b~pB zSVsr-@uGOjv7^sLuv`hMaiz!JvSf{G3>ug2rNSM(^utfM@+=LmFCF~J=LQtlxOP9_ zI#o9mzU}(GFj;e^8nKVi1z*mjq6Z%r+_=`~9sSheVEKFD!N-4dY2ArK^zqzIEVX0X z&(^Ft-dS$>KaJxY|L4VVJG%8*CtP~g;XSc1nAj=4GwTf7cY~oDHABBV;rX>-s$370 zubCTZ_>(gRzN6v4p~W(hH|wS{;Id@Y+ShvOUNyzyvn=eo1gk=)={BNr!#n0GTB#iM zb(Y*UiQJ^))!b^i(VS57p|?mD+p=WECL|Wh6x@weUvU|kUOy;HS`weJQD==mFzp>W)d9DPrbu|qQ zb!$|fb3;B`HnIWYUt7LEbo{E)S#@g;hX&!eDZx|EXT|)S=gyb?=In&JD-xN_iHx~U zBu}y?Rriuy#5)>!Qr|_W^~_xa-_8BEyRJ_EWwmHjv*Sc`AVa@2#yoW4bfsU7nchdJ zPVO@#)!Dr##;+XXR+_ zV9fQD&yl85lh!pb@!HWY;wbBDf1#0G`qyO}$XrNKSV z9?{7du~hsoxdgk5asKb%yLn#Y*~aTT$tb>))SoL4(l zzNf~KxVCE?(e>MF|MQ|*3xX$?NWXbUB3$7@e^!ld*OuV$m%b;Ri|rM)AXwy+%?-V$UDZ9hV7f4_~Ao?sk{pKt;c?|;{x7h4i>#x zIqTW)&1^{avr#{&ceI;_-s7LTL*&>8WgbjBZ9n0ot;gSWYE5Eis7|_v+R#ZXBm0e> z`Avg~Y{T`dhrKD^Q9q~(XK=dKY9t-%dAT_>b1QocgrJzq4%iC!FP9%Pu`SEvOEs2dr4CAYnV)6Cj=q8B`IiL{7c23ILOaIxo;m(0X z=X~fzt_m#ir>WPRcl^w}-~6}vhlXeB)Admf5##mB-kLgSxgoU~y2T%>k_vyh`38?J z8ow_x_UYlB&-JIda>^yyOP-W*&3ojmb!+B88TZL=Pd7QY>{AE-oAob^Xzl1Qb75}n z?;w~Qnv^Vd*|k_fpKTbqYtfw@^JwlrAh7q}kA3sfpvh~0d@n^m+^8>!-K)aw3hul( zXX1DM?79SAmuv6GR*(N)=D{#k+sb-q$5z<~iAQ)b4|Z*4-`!npJ9b@+wqr;7{{j>b WIZMj#w7GNE#yNN;|6+IE8S(!~gxvc8 diff --git a/rpc-client-latency-bench.php b/rpc-client-latency-bench.php deleted file mode 100644 index 96604920c1..0000000000 --- a/rpc-client-latency-bench.php +++ /dev/null @@ -1,97 +0,0 @@ -isError()) { - critical_error("error response"); - } - } - -// echo "Wall time -> " . number_format(($end_time - $start_time) * 1000, 2) . ", requests time -> " . number_format(($requests_time_end - $requests_time_start) * 1000, 2) . -// ", responses time -> " . number_format(($responses_time_end - $responses_time_start) * 1000, 2) . "\n"; - - return $responses[0]; -} - -function fibonacciRecursive($n) { - if ($n <= 1) { - return $n; - } else { - return fibonacciRecursive($n - 1) + fibonacciRecursive($n - 2); - } -} - -$fibonacci_num = (int) $_ENV['FIBONACCI_NUM']; -// $fibonacci_num = 30; -// echo $fibonacci_num; - -// CPU WORK -$a = fibonacciRecursive($fibonacci_num); - -$sum_num = (int) $_ENV['SUM_NUM']; -$b = 0; -for ($i = 1; $i <= $sum_num; ++$i) { - $b += $i; -} - -$requests_num = (int) $_ENV['REQUESTS_NUM']; -// $requests_num = 0; -// echo $requests_num; - -// $key_len = (int) $_ENV['KEY_LEN']; - -// NET WORK -$query = rpc_server_fetch_request(); -$queries = array_fill(0, $requests_num, $query); - -if ($requests_num >= 0) { - $response = proxy_queries($queries); -} - -// $start = microtime(true); -// $queries = array_map(function ($i) { return $query; }, range(0, $requests_num)); -// $end = microtime(true); -// echo "It took " . number_format(($end - $start) * 1000, 2) . " to create an array\n"; - - -// rpc_server_store_response($response->getResult()); - -$responsee = VK\TL\memcache\Functions\memcache_get::createRpcServerResponse(new VK\TL\memcache\Types\memcache_not_found()); -rpc_server_store_response($responsee); - -// wait(fork(benchmark($queries)), 0.15); -// exit(0); From b7a3bc922c321ab14b94b94e8e66d9a573af7cbf Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Mon, 15 Jun 2026 13:23:50 +0300 Subject: [PATCH 08/17] error handling --- runtime-light/k2-platform/k2-api.h | 15 +- runtime-light/k2-platform/k2-header.h | 28 ++- runtime-light/stdlib/rpc/rpc-api.cpp | 205 ++++++-------------- runtime-light/stdlib/rpc/rpc-client-state.h | 4 +- runtime-light/stdlib/rpc/rpc-constants.h | 1 + 5 files changed, 95 insertions(+), 158 deletions(-) diff --git a/runtime-light/k2-platform/k2-api.h b/runtime-light/k2-platform/k2-api.h index 1a08d8ec82..6b378b8f88 100644 --- a/runtime-light/k2-platform/k2-api.h +++ b/runtime-light/k2-platform/k2-api.h @@ -219,12 +219,19 @@ inline std::expected rpc_send_request(std::string_view actor_ return {rpc_d}; } -inline size_t rpc_get_response_size(uint64_t rpc_d) noexcept { - return k2_rpc_get_response_size(rpc_d); +inline std::expected rpc_get_response_size(uint64_t rpc_d) noexcept { + size_t size{}; + if (auto error_code{k2_rpc_get_response_size(rpc_d, std::addressof(size))}; error_code != k2::errno_ok) { + return std::unexpected{error_code}; + } + return {size}; } -inline size_t rpc_fetch_response(uint64_t rpc_d, std::span buffer) noexcept { - return k2_rpc_fetch_response(rpc_d, buffer.data(), buffer.size()); +inline std::expected rpc_fetch_response(uint64_t rpc_d, std::span buffer) noexcept { + if (auto error_code{k2_rpc_fetch_response(rpc_d, buffer.data(), buffer.size())}; error_code != errno_ok) { + return std::unexpected{error_code}; + } + return {}; } inline void stream_status(k2::descriptor descriptor, StreamStatus* status) noexcept { diff --git a/runtime-light/k2-platform/k2-header.h b/runtime-light/k2-platform/k2-header.h index 38a818b0d3..57a8b8cde9 100644 --- a/runtime-light/k2-platform/k2-header.h +++ b/runtime-light/k2-platform/k2-header.h @@ -317,13 +317,35 @@ int32_t k2_unlink(const char* path, size_t path_len); int32_t k2_component_access(size_t name_len, const char* name); /** - * @return returns descriptor of rpc request, which should be later used to call `k2_rpc_fetch_response`. + * Try to send rpc request. If case of success write descriptor of rpc request to `rpc_d`, otherwise return `errno` != 0. + * which should be later used to call `k2_rpc_fetch_response`. + * + * @return return `0` on success. libc-like `errno` otherwise + * + * Possible `errno` values: + * `EAI_MEMORY` => max descriptors count achieved + * `EINVAL` => invalid `actor_name` or request, or connection pool is empty for this actor. */ int32_t k2_rpc_send_request(const char *actor_name, size_t actor_name_len, const void* request_ptr, size_t request_size, uint64_t *rpc_d); -size_t k2_rpc_get_response_size(uint64_t rpc_d); +/** + * Get response size for corresponding request of this `rpc_d`. Write 0 to `response_size` if response is not ready. + * Write response size value to `response_size` when response is ready. + * + * @return return `0` on success. libc-like `errno` otherwise + * + * `EINVAL` => invalid `rpc_d` descriptor, for example, it is unknown descriptor, or not rpc descriptor. + */ +int32_t k2_rpc_get_response_size(uint64_t rpc_d, size_t* response_size); -size_t k2_rpc_fetch_response(uint64_t rpc_d, void* buf, size_t buf_size); +/** + * Write response for corresponding request of this `rpc_d` to `buf`. Does nothing if response is not ready. + * + * @return return `0` on success. libc-like `errno` otherwise + * + * `EINVAL` => invalid `rpc_d` descriptor, for example, it is unknown descriptor, or not rpc descriptor. + */ +int32_t k2_rpc_fetch_response(uint64_t rpc_d, void* buf, size_t buf_size); /** * If the write or read status is `Blocked` - then the platform ensures that diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index 08f07b6785..5de888b7c8 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -159,35 +159,29 @@ kphp::rpc::query_info typed_rpc_tl_query_one_impl(std::string_view actor, const namespace { -static std::optional get_rpc_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) { - std::optional opt_response{std::nullopt}; - - size_t first_response_size{k2::rpc_get_response_size(rpc_d)}; - if (first_response_size == 0) { - // TODO ERROR - k2::exit(148); +static std::expected> get_rpc_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) { + std::expected first_response_size{k2::rpc_get_response_size(rpc_d)}; + if (!first_response_size) { + return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; } - string response{reinterpret_cast(k2::alloc(first_response_size)), static_cast(first_response_size)}; - size_t new_response_size = k2::rpc_fetch_response(rpc_d, {reinterpret_cast(response.buffer()), response.size()}); - if (new_response_size != first_response_size) { - // TODO ERROR - k2::exit(69); + string response{reinterpret_cast(k2::alloc(*first_response_size)), static_cast(*first_response_size)}; + std::expected new_response_size{k2::rpc_fetch_response(rpc_d, {reinterpret_cast(response.buffer()), response.size()})}; + if (!new_response_size) { + return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; } - opt_response = std::move(response); // update response extra info if needed if (collect_responses_extra_info) { auto& extra_info_map{RpcClientInstanceState::get().rpc_responses_extra_info}; if (const auto it_extra_info{extra_info_map.find(query_id)}; it_extra_info != extra_info_map.end()) [[likely]] { const auto timestamp{std::chrono::duration{std::chrono::system_clock::now().time_since_epoch()}.count()}; - const auto response_size{opt_response.transform([](const string& response) noexcept { return static_cast(response.size()); }).value_or(0)}; - it_extra_info->second.second = std::make_tuple(response_size, timestamp - std::get<1>(it_extra_info->second.second)); + it_extra_info->second.second = std::make_tuple(response.size(), timestamp - std::get<1>(it_extra_info->second.second)); it_extra_info->second.first = response_extra_info_status::ready; } else { kphp::log::warning("can't find extra info for RPC query {}", query_id); } } - return opt_response; + return {response}; } } // namespace @@ -202,22 +196,18 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no { const auto it_response_fetcher{rpc_client_instance_st.response_fetcher_instances.find(query_id)}; -// const auto it_fork_task{rpc_client_instance_st.response_awaiter_tasks.find(query_id)}; const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(query_id)}; - const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher/*, it_fork_task*/, it_rpc_request_info] noexcept { + const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_rpc_request_info] noexcept { if (it_response_fetcher != rpc_client_instance_st.response_fetcher_instances.end()) [[likely]] { rpc_client_instance_st.response_fetcher_instances.erase(it_response_fetcher); } -// if (it_fork_task != rpc_client_instance_st.response_awaiter_tasks.end()) [[likely]] { -// rpc_client_instance_st.response_awaiter_tasks.erase(it_fork_task); -// } if (it_rpc_request_info != rpc_client_instance_st.rpc_requests_infos.end()) [[likely]] { rpc_client_instance_st.rpc_requests_infos.erase(it_rpc_request_info); } }}; - if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end() /*|| it_fork_task == rpc_client_instance_st.response_awaiter_tasks.end()*/ || it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) - [[unlikely]] { + if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end() || + it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) [[unlikely]] { co_return TlRpcError::make_error(TL_ERROR_INTERNAL, string{"unexpectedly could not find query in pending queries"}); } rpc_query = std::move(it_response_fetcher->second); @@ -238,20 +228,29 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no kphp::log::assertion(opt_rpc_request_info.has_value()); auto rpc_request_info{*opt_rpc_request_info}; + k2::TimePoint now_instant{}; + k2::instant(std::addressof(now_instant)); + std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; + std::chrono::nanoseconds timeout{rpc_request_info.deadline - now_ns}; kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; - switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, std::chrono::milliseconds(1000))) { + switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, timeout)) { case kphp::coro::poll_status::event: { - opt_response = get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info); + std::expected> response_expected{get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; + if (!response_expected) { + // TODO std::move ?????????????????????? + std::pair error{response_expected.error()}; + co_return TlRpcError::make_error(error.first, error.second); + } + opt_response = *response_expected; break; } case kphp::coro::poll_status::closed: - k2::exit(70); + co_return TlRpcError::make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); case kphp::coro::poll_status::error: - k2::exit(71); + co_return TlRpcError::make_error(TL_ERROR_INTERNAL, string{"error fetching rpc response"}); case kphp::coro::poll_status::timeout: - k2::exit(79); + co_return TlRpcError::make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); } -// auto opt_response{co_await kphp::forks::id_managed(*std::exchange(opt_awaiter_task, std::nullopt))}; if (!opt_response) [[unlikely]] { co_return TlRpcError::make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); } @@ -279,24 +278,18 @@ kphp::coro::task> typed_rpc_tl_query_result_ { const auto it_response_fetcher{rpc_client_instance_st.response_fetcher_instances.find(query_id)}; -// const auto it_fork_task{rpc_client_instance_st.response_awaiter_tasks.find(query_id)}; const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(query_id)}; - const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher/*, it_fork_task*/, it_rpc_request_info] noexcept { + const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_rpc_request_info] noexcept { if (it_response_fetcher != rpc_client_instance_st.response_fetcher_instances.end()) [[likely]] { rpc_client_instance_st.response_fetcher_instances.erase(it_response_fetcher); } -// if (it_fork_task != rpc_client_instance_st.response_awaiter_tasks.end()) [[likely]] { -// rpc_client_instance_st.response_awaiter_tasks.erase(it_fork_task); -// } if (it_rpc_request_info != rpc_client_instance_st.rpc_requests_infos.end()) [[likely]] { rpc_client_instance_st.rpc_requests_infos.erase(it_rpc_request_info); } }}; -// kphp::log::warning("AAA"); - if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end()/* || it_fork_task == rpc_client_instance_st.response_awaiter_tasks.end()*/ || it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) - [[unlikely]] { -// kphp::log::warning("A"); + if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end() || + it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) [[unlikely]] { co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"unexpectedly could not find query in pending queries"}); } rpc_query = std::move(it_response_fetcher->second); @@ -304,15 +297,12 @@ kphp::coro::task> typed_rpc_tl_query_result_ } if (rpc_query.is_null()) [[unlikely]] { -// kphp::log::warning("B"); co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"can't use rpc_tl_query_result for non-TL query"}); } if (!rpc_query.get()->result_fetcher || rpc_query.get()->result_fetcher->empty()) [[unlikely]] { -// kphp::log::warning("C"); co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"rpc query has empty result fetcher"}); } if (!rpc_query.get()->result_fetcher->is_typed) [[unlikely]] { -// kphp::log::warning("D"); co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"can't get typed result from untyped TL query. Use consistent API for that"}); } @@ -320,33 +310,41 @@ kphp::coro::task> typed_rpc_tl_query_result_ kphp::log::assertion(opt_rpc_request_info.has_value()); auto rpc_request_info{*opt_rpc_request_info}; + k2::TimePoint now_instant{}; + // TODO call k2::instant once for all sending requests in batch + k2::instant(std::addressof(now_instant)); + std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; + std::chrono::nanoseconds timeout{rpc_request_info.deadline - now_ns}; kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; - switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, std::chrono::milliseconds(1000))) { + switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, timeout)) { case kphp::coro::poll_status::event: { - opt_response = get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info); + std::expected> response_expected{get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; + if (!response_expected) { + // TODO std::move ?????????????????????? + std::pair error{response_expected.error()}; + co_return error_factory.make_error(error.first, error.second); + } + opt_response = *response_expected; break; } case kphp::coro::poll_status::closed: - k2::exit(70); + co_return error_factory.make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); case kphp::coro::poll_status::error: - k2::exit(71); + co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"error fetching rpc response"}); case kphp::coro::poll_status::timeout: - k2::exit(79); + co_return error_factory.make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); } - // auto opt_response{co_await kphp::forks::id_managed(*std::exchange(opt_awaiter_task, std::nullopt))}; if (!opt_response) [[unlikely]] { -// kphp::log::warning("E"); co_return error_factory.make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); } - auto response{*std::move(opt_response)}; // don't check response's emptyness; will throw if it's empty, indicating a fetch error + auto response{*std::move(opt_response)}; // don't check response's emptiness; will throw if it's empty, indicating a fetch error f$rpc_clean(); RpcServerInstanceState::get().tl_fetcher = tl::fetcher{{reinterpret_cast(response.c_str()), response.size()}}; auto res{fetch_function_typed(rpc_query, error_factory)}; // THROWING // handle exceptions that could arise during fetch_function_typed if (auto err{error_factory.transform_exception_into_error_if_possible()}; !err.is_null()) [[unlikely]] { -// kphp::log::warning("F"); co_return std::move(err); } co_return std::move(res); @@ -359,38 +357,6 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional auto& rpc_server_instance_st{RpcServerInstanceState::get()}; const size_t request_size{rpc_server_instance_st.tl_storer.view().size_bytes()}; const auto timestamp{std::chrono::duration{std::chrono::system_clock::now().time_since_epoch()}.count()}; - /* - - auto expected_stream{kphp::component::stream::open(actor, k2::stream_kind::component)}; - if (!expected_stream) [[unlikely]] { - co_return kphp::rpc::query_info{ - .id = kphp::rpc::INVALID_QUERY_ID, .request_size = rpc_server_instance_st.tl_storer.view().size_bytes(), .timestamp = timestamp}; - } - - auto stream{*std::move(expected_stream)}; - { - auto tl_storer{std::exchange(rpc_server_instance_st.tl_storer, tl::storer{0})}; - const vk::final_action finalizer{[&tl_storer, &rpc_server_instance_st] noexcept { - if (tl_storer.capacity() > rpc_server_instance_st.tl_storer.capacity()) { - std::swap(tl_storer, rpc_server_instance_st.tl_storer); - } - }}; - - // prepare and send RPC request - // 'request_buf' will look like this: - // [ RpcExtraHeaders (optional) ] [ payload ] - if (const auto& [opt_new_extra_header, cur_extra_header_size]{kphp::rpc::regularize_extra_headers(tl_storer.view(), ignore_answer)}; opt_new_extra_header) - { std::span request_body{tl_storer.view().subspan(cur_extra_header_size)}; std::span new_header{reinterpret_cast(std::addressof(*opt_new_extra_header)), sizeof(std::remove_cvref_t)}; - - if (!co_await stream.write_all(new_header) || !co_await kphp::component::send_request(stream, request_body)) [[unlikely]] { - co_return kphp::rpc::query_info{.id = kphp::rpc::INVALID_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; - } - } else if (!co_await kphp::component::send_request(stream, tl_storer.view())) [[unlikely]] { - co_return kphp::rpc::query_info{.id = kphp::rpc::INVALID_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; - } - } - */ auto tl_storer{std::exchange(rpc_server_instance_st.tl_storer, tl::storer{0})}; const vk::final_action finalizer{[&tl_storer, &rpc_server_instance_st] noexcept { @@ -401,7 +367,7 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional // TODO make special (may be resource) type for rpc request; auto rpc_d_exp{k2::rpc_send_request(actor, tl_storer.view())}; if (!rpc_d_exp) { - k2::exit(123); + return kphp::rpc::query_info{.id = kphp::rpc::INTERNAL_ERROR, .request_size = request_size, .timestamp = timestamp}; } // TODO why is query_id incremented here but not before the request ??? @@ -414,68 +380,6 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional k2::descriptor rpc_d{*rpc_d_exp}; - // create a task to wait for RPC response. we need to do it even if 'ignore_answer' is 'true' to make sure - // that the stream will not be closed too early. otherwise, platform may even not send RPC request - /*static constexpr auto awaiter_coroutine{[](int64_t query_id, k2::descriptor rpc_d, std::chrono::milliseconds timeout, - bool collect_responses_extra_info) noexcept -> kphp::coro::shared_task> { - std::optional opt_response{std::nullopt}; - - kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; - switch (co_await m_scheduler.poll(rpc_d, kphp::coro::poll_op::read, timeout)) { - case kphp::coro::poll_status::event: { - size_t response_size{k2::rpc_get_response_size(rpc_d)}; - if (response_size == 0) { - // TODO ERROR - k2::exit(148); - } - string response{reinterpret_cast(k2::alloc(response_size)), static_cast(response_size)}; - size_t new_response_size = k2::rpc_fetch_response(rpc_d, {reinterpret_cast(response.buffer()), response.size()}); - if (new_response_size != response_size) { - // TODO ERROR - k2::exit(69); - } - opt_response = std::move(response); - break; - } - case kphp::coro::poll_status::closed: - k2::exit(70); - // TODO really nothing ? - // fallthrough - case kphp::coro::poll_status::error: - k2::exit(71); - // TODO ERROR ??? - // nothing - // fallthrough - case kphp::coro::poll_status::timeout: - k2::exit(79); - // nothing - // fallthrough - } - - *//* - std::optional opt_response{std::in_place}; - 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; - } - *//* - - // update response extra info if needed - if (collect_responses_extra_info) { - auto& extra_info_map{RpcClientInstanceState::get().rpc_responses_extra_info}; - if (const auto it_extra_info{extra_info_map.find(query_id)}; it_extra_info != extra_info_map.end()) [[likely]] { - const auto timestamp{std::chrono::duration{std::chrono::system_clock::now().time_since_epoch()}.count()}; - const auto response_size{opt_response.transform([](const string& response) noexcept { return static_cast(response.size()); }).value_or(0)}; - it_extra_info->second.second = std::make_tuple(response_size, timestamp - std::get<1>(it_extra_info->second.second)); - it_extra_info->second.first = response_extra_info_status::ready; - } else { - kphp::log::warning("can't find extra info for RPC query {}", query_id); - } - } - - co_return std::move(opt_response); - }};*/ - static constexpr auto ignore_answer_awaiter_coroutine{[](k2::descriptor rpc_d, std::chrono::milliseconds timeout) noexcept -> kphp::coro::shared_task<> { auto fetch_task{kphp::coro::io_scheduler::get().poll(rpc_d, kphp::coro::poll_op::read, timeout)}; std::ignore = co_await kphp::coro::io_scheduler::get().schedule(std::move(fetch_task), timeout); @@ -503,13 +407,14 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional return kphp::rpc::query_info{.id = kphp::rpc::IGNORED_ANSWER_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; } // start awaiter task - // TODO need to move rpc_d ? -// auto awaiter_task{awaiter_coroutine(query_id, std::move(rpc_d), timeout, collect_responses_extra_info)}; -// kphp::log::assertion(kphp::coro::io_scheduler::get().start(awaiter_task)); -// rpc_client_instance_st.response_awaiter_tasks.emplace(query_id, std::move(awaiter_task)); + k2::TimePoint now_instant{}; + k2::instant(std::addressof(now_instant)); + std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; + std::chrono::nanoseconds timeout_ns{duration_cast(timeout)}; + std::chrono::nanoseconds deadline{now_ns + timeout_ns}; - rpc_client_instance_st.rpc_requests_infos.emplace(query_id, уберите_меня_отсюда::rpc_request_info{rpc_d, collect_responses_extra_info}); + rpc_client_instance_st.rpc_requests_infos.emplace(query_id, уберите_меня_отсюда::rpc_request_info{rpc_d, deadline, collect_responses_extra_info}); return kphp::rpc::query_info{.id = query_id, .request_size = request_size, .timestamp = timestamp}; } diff --git a/runtime-light/stdlib/rpc/rpc-client-state.h b/runtime-light/stdlib/rpc/rpc-client-state.h index fcb7573e10..07160e38a5 100644 --- a/runtime-light/stdlib/rpc/rpc-client-state.h +++ b/runtime-light/stdlib/rpc/rpc-client-state.h @@ -19,14 +19,16 @@ #include "runtime-light/stdlib/rpc/rpc-tl-defs.h" #include "runtime-light/stdlib/rpc/rpc-tl-query.h" +// TODO куды ? namespace уберите_меня_отсюда { struct rpc_request_info { k2::descriptor rpc_d; + std::chrono::nanoseconds deadline; bool collect_responses_extra_info; }; -} +} // namespace уберите_меня_отсюда struct RpcClientInstanceState final : private vk::not_copyable { CurrentTlQuery current_client_query{}; diff --git a/runtime-light/stdlib/rpc/rpc-constants.h b/runtime-light/stdlib/rpc/rpc-constants.h index 2f004f12da..9f9c7a2034 100644 --- a/runtime-light/stdlib/rpc/rpc-constants.h +++ b/runtime-light/stdlib/rpc/rpc-constants.h @@ -11,6 +11,7 @@ namespace kphp::rpc { inline constexpr int64_t VALID_QUERY_ID_RANGE_START = 1; inline constexpr int64_t INVALID_QUERY_ID = 0; inline constexpr int64_t IGNORED_ANSWER_QUERY_ID = -1; +inline constexpr int64_t INTERNAL_ERROR = 2; enum class error : int32_t { // stream errors From 83f13174934e805c5d9d35e76e328c54a98dc52f Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Mon, 15 Jun 2026 14:56:46 +0300 Subject: [PATCH 09/17] rpc_queue_push --- runtime-light/stdlib/rpc/rpc-queue-functions.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/runtime-light/stdlib/rpc/rpc-queue-functions.h b/runtime-light/stdlib/rpc/rpc-queue-functions.h index d120f452fd..8a80292c06 100644 --- a/runtime-light/stdlib/rpc/rpc-queue-functions.h +++ b/runtime-light/stdlib/rpc/rpc-queue-functions.h @@ -22,16 +22,22 @@ namespace kphp::rpc { inline void rpc_queue_push(int64_t queue_id, int64_t request_id) noexcept { - static constexpr auto rpc_queue_wrapper_task{[](kphp::coro::shared_task<> awaiter_task, int64_t request_id) noexcept -> kphp::coro::task { - co_await std::move(awaiter_task).when_ready(); + static constexpr auto rpc_queue_wrapper_task{[](уберите_меня_отсюда::rpc_request_info request_info, int64_t request_id) noexcept -> kphp::coro::task { + k2::TimePoint now_instant{}; + // TODO call k2::instant once for all sending requests in batch + k2::instant(std::addressof(now_instant)); + std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; + std::chrono::nanoseconds timeout{request_info.deadline - now_ns}; + kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; + co_await m_scheduler.poll(request_info.rpc_d, kphp::coro::poll_op::read, timeout); co_return request_id; }}; auto& rpc_client_instance_st{RpcClientInstanceState::get()}; - const auto it_awaiter_task{rpc_client_instance_st.response_awaiter_tasks.find(request_id)}; - if (it_awaiter_task == rpc_client_instance_st.response_awaiter_tasks.end()) [[unlikely]] { - kphp::log::warning("could not find rpc query with id {} in pending queries", queue_id); + const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(request_id)}; + if (it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) [[unlikely]] { + kphp::log::warning("could not find rpc query with id {} in pending requests", queue_id); return; } @@ -43,7 +49,7 @@ inline void rpc_queue_push(int64_t queue_id, int64_t request_id) noexcept { } auto& await_set{(*opt_await_set).get()}; - await_set.push(rpc_queue_wrapper_task(static_cast>(it_awaiter_task->second), request_id)); + await_set.push(rpc_queue_wrapper_task(it_rpc_request_info->second, request_id)); } inline int64_t rpc_queue_create(std::span request_ids) noexcept { From c69a239a8f709ac6714fb4b0dba204109235edfa Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Mon, 15 Jun 2026 16:42:38 +0300 Subject: [PATCH 10/17] kphp::rpc::request_info --- runtime-light/stdlib/rpc/rpc-api.cpp | 10 +++++----- runtime-light/stdlib/rpc/rpc-client-state.h | 14 ++------------ .../stdlib/rpc/rpc-queue-functions.h | 3 ++- runtime-light/stdlib/rpc/rpc-request-info.h | 19 +++++++++++++++++++ 4 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 runtime-light/stdlib/rpc/rpc-request-info.h diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index 5de888b7c8..85082fd4df 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -34,6 +34,7 @@ #include "runtime-light/stdlib/rpc/rpc-constants.h" #include "runtime-light/stdlib/rpc/rpc-extra-headers.h" #include "runtime-light/stdlib/rpc/rpc-extra-info.h" +#include "runtime-light/stdlib/rpc/rpc-request-info.h" #include "runtime-light/stdlib/rpc/rpc-tl-error.h" #include "runtime-light/stdlib/rpc/rpc-tl-query.h" #include "runtime-light/streams/read-ext.h" @@ -192,9 +193,9 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no auto& rpc_client_instance_st{RpcClientInstanceState::get()}; class_instance rpc_query{}; - std::optional<уберите_меня_отсюда::rpc_request_info> opt_rpc_request_info{}; + std::optional opt_rpc_request_info{}; - { + { const auto it_response_fetcher{rpc_client_instance_st.response_fetcher_instances.find(query_id)}; const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(query_id)}; const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_rpc_request_info] noexcept { @@ -274,7 +275,7 @@ kphp::coro::task> typed_rpc_tl_query_result_ auto& rpc_client_instance_st{RpcClientInstanceState::get()}; class_instance rpc_query{}; - std::optional<уберите_меня_отсюда::rpc_request_info> opt_rpc_request_info{}; + std::optional opt_rpc_request_info{}; { const auto it_response_fetcher{rpc_client_instance_st.response_fetcher_instances.find(query_id)}; @@ -364,7 +365,6 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional std::swap(tl_storer, rpc_server_instance_st.tl_storer); } }}; - // TODO make special (may be resource) type for rpc request; auto rpc_d_exp{k2::rpc_send_request(actor, tl_storer.view())}; if (!rpc_d_exp) { return kphp::rpc::query_info{.id = kphp::rpc::INTERNAL_ERROR, .request_size = request_size, .timestamp = timestamp}; @@ -414,7 +414,7 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional std::chrono::nanoseconds timeout_ns{duration_cast(timeout)}; std::chrono::nanoseconds deadline{now_ns + timeout_ns}; - rpc_client_instance_st.rpc_requests_infos.emplace(query_id, уберите_меня_отсюда::rpc_request_info{rpc_d, deadline, collect_responses_extra_info}); + rpc_client_instance_st.rpc_requests_infos.emplace(query_id, kphp::rpc::request_info{rpc_d, deadline, collect_responses_extra_info}); return kphp::rpc::query_info{.id = query_id, .request_size = request_size, .timestamp = timestamp}; } diff --git a/runtime-light/stdlib/rpc/rpc-client-state.h b/runtime-light/stdlib/rpc/rpc-client-state.h index 07160e38a5..003fec2066 100644 --- a/runtime-light/stdlib/rpc/rpc-client-state.h +++ b/runtime-light/stdlib/rpc/rpc-client-state.h @@ -16,25 +16,15 @@ #include "runtime-light/coroutine/shared-task.h" #include "runtime-light/stdlib/rpc/rpc-constants.h" #include "runtime-light/stdlib/rpc/rpc-extra-info.h" +#include "runtime-light/stdlib/rpc/rpc-request-info.h" #include "runtime-light/stdlib/rpc/rpc-tl-defs.h" #include "runtime-light/stdlib/rpc/rpc-tl-query.h" -// TODO куды ? -namespace уберите_меня_отсюда { - -struct rpc_request_info { - k2::descriptor rpc_d; - std::chrono::nanoseconds deadline; - bool collect_responses_extra_info; -}; - -} // namespace уберите_меня_отсюда - struct RpcClientInstanceState final : private vk::not_copyable { CurrentTlQuery current_client_query{}; int64_t current_query_id{kphp::rpc::VALID_QUERY_ID_RANGE_START}; - kphp::stl::unordered_map rpc_requests_infos; + kphp::stl::unordered_map rpc_requests_infos; kphp::stl::unordered_map>, kphp::memory::script_allocator> response_awaiter_tasks; kphp::stl::unordered_map, kphp::memory::script_allocator> response_fetcher_instances; kphp::stl::unordered_map, kphp::memory::script_allocator> diff --git a/runtime-light/stdlib/rpc/rpc-queue-functions.h b/runtime-light/stdlib/rpc/rpc-queue-functions.h index 8a80292c06..019aebdada 100644 --- a/runtime-light/stdlib/rpc/rpc-queue-functions.h +++ b/runtime-light/stdlib/rpc/rpc-queue-functions.h @@ -18,11 +18,12 @@ #include "runtime-light/stdlib/fork/fork-functions.h" #include "runtime-light/stdlib/rpc/rpc-client-state.h" #include "runtime-light/stdlib/rpc/rpc-queue-state.h" +#include "runtime-light/stdlib/rpc/rpc-request-info.h" namespace kphp::rpc { inline void rpc_queue_push(int64_t queue_id, int64_t request_id) noexcept { - static constexpr auto rpc_queue_wrapper_task{[](уберите_меня_отсюда::rpc_request_info request_info, int64_t request_id) noexcept -> kphp::coro::task { + static constexpr auto rpc_queue_wrapper_task{[](kphp::rpc::request_info request_info, int64_t request_id) noexcept -> kphp::coro::task { k2::TimePoint now_instant{}; // TODO call k2::instant once for all sending requests in batch k2::instant(std::addressof(now_instant)); diff --git a/runtime-light/stdlib/rpc/rpc-request-info.h b/runtime-light/stdlib/rpc/rpc-request-info.h new file mode 100644 index 0000000000..cbb7190fc8 --- /dev/null +++ b/runtime-light/stdlib/rpc/rpc-request-info.h @@ -0,0 +1,19 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2026 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include + +#include "runtime-light/k2-platform/k2-api.h" + +namespace kphp::rpc { + +struct request_info { + k2::descriptor rpc_d; + std::chrono::nanoseconds deadline; + bool collect_responses_extra_info; +}; + +} // namespace kphp::rpc From 742da8d3f2b855904049fcb9bd2c2c696a00d03b Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Wed, 17 Jun 2026 18:37:00 +0300 Subject: [PATCH 11/17] fmt --- runtime-light/k2-platform/k2-header.h | 2 +- runtime-light/stdlib/rpc/rpc-api.cpp | 8 +++++--- runtime-light/stdlib/rpc/rpc-api.h | 22 ++++++++++------------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/runtime-light/k2-platform/k2-header.h b/runtime-light/k2-platform/k2-header.h index 57a8b8cde9..2acdb1ffeb 100644 --- a/runtime-light/k2-platform/k2-header.h +++ b/runtime-light/k2-platform/k2-header.h @@ -326,7 +326,7 @@ int32_t k2_component_access(size_t name_len, const char* name); * `EAI_MEMORY` => max descriptors count achieved * `EINVAL` => invalid `actor_name` or request, or connection pool is empty for this actor. */ -int32_t k2_rpc_send_request(const char *actor_name, size_t actor_name_len, const void* request_ptr, size_t request_size, uint64_t *rpc_d); +int32_t k2_rpc_send_request(const char* actor_name, size_t actor_name_len, const void* request_ptr, size_t request_size, uint64_t* rpc_d); /** * Get response size for corresponding request of this `rpc_d`. Write 0 to `response_size` if response is not ready. diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index 85082fd4df..4b77bbab65 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -195,7 +195,7 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no class_instance rpc_query{}; std::optional opt_rpc_request_info{}; - { + { const auto it_response_fetcher{rpc_client_instance_st.response_fetcher_instances.find(query_id)}; const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(query_id)}; const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_rpc_request_info] noexcept { @@ -236,7 +236,8 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, timeout)) { case kphp::coro::poll_status::event: { - std::expected> response_expected{get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; + std::expected> response_expected{ + get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; if (!response_expected) { // TODO std::move ?????????????????????? std::pair error{response_expected.error()}; @@ -319,7 +320,8 @@ kphp::coro::task> typed_rpc_tl_query_result_ kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, timeout)) { case kphp::coro::poll_status::event: { - std::expected> response_expected{get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; + std::expected> response_expected{ + get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; if (!response_expected) { // TODO std::move ?????????????????????? std::pair error{response_expected.error()}; diff --git a/runtime-light/stdlib/rpc/rpc-api.h b/runtime-light/stdlib/rpc/rpc-api.h index 405d7bdaa0..324130e975 100644 --- a/runtime-light/stdlib/rpc/rpc-api.h +++ b/runtime-light/stdlib/rpc/rpc-api.h @@ -40,8 +40,7 @@ struct query_info { double timestamp{0.0}; }; -kphp::rpc::query_info send_request(std::string_view actor, std::optional timeout, bool ignore_answer, - bool collect_responses_extra_info) noexcept; +kphp::rpc::query_info send_request(std::string_view actor, std::optional timeout, bool ignore_answer, bool collect_responses_extra_info) noexcept; inline kphp::coro::task> send_response(std::span response) noexcept { auto& rpc_server_instance_st{RpcServerInstanceState::get()}; @@ -59,13 +58,13 @@ inline kphp::coro::task> send_response(std::span opt_timeout, - bool collect_resp_extra_info, bool ignore_answer) noexcept; +kphp::rpc::query_info rpc_tl_query_one_impl(std::string_view actor, mixed tl_object, std::optional opt_timeout, bool collect_resp_extra_info, + bool ignore_answer) noexcept; kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) noexcept; kphp::rpc::query_info typed_rpc_tl_query_one_impl(std::string_view actor, const RpcRequest& rpc_request, std::optional opt_timeout, - bool collect_responses_extra_info, bool ignore_answer) noexcept; + bool collect_responses_extra_info, bool ignore_answer) noexcept; kphp::coro::task> typed_rpc_tl_query_result_one_impl(int64_t query_id, const RpcErrorFactory& error_factory) noexcept; @@ -288,8 +287,7 @@ inline int64_t f$rpc_tl_pending_queries_count() noexcept { // === client untyped ============================================================================= inline array f$rpc_send_requests(string actor, array tl_objects, Optional timeout, bool ignore_answer, - class_instance requests_extra_info, - bool need_responses_extra_info) noexcept { + class_instance requests_extra_info, bool need_responses_extra_info) noexcept { if (ignore_answer && need_responses_extra_info) [[unlikely]] { kphp::log::warning("both $ignore_answer and $need_responses_extra_info are 'true'. Metrics won't be collected"); } @@ -337,9 +335,9 @@ kphp::coro::task>> f$rpc_fetch_responses_synchronously(array< // === client typed =============================================================================== template rpc_function_type, std::same_as rpc_request_type = KphpRpcRequest> -array -f$rpc_send_typed_query_requests(string actor, array> query_functions, Optional timeout, bool ignore_answer, - class_instance requests_extra_info, bool need_responses_extra_info) noexcept { +array f$rpc_send_typed_query_requests(string actor, array> query_functions, Optional timeout, + bool ignore_answer, class_instance requests_extra_info, + bool need_responses_extra_info) noexcept { if (ignore_answer && need_responses_extra_info) [[unlikely]] { kphp::log::warning("both $ignore_answer and $need_responses_extra_info are 'true'. Metrics won't be collected"); } @@ -350,8 +348,8 @@ f$rpc_send_typed_query_requests(string actor, array{timeout.val()} : std::optional{}}; for (const auto& it : std::as_const(query_functions)) { - const auto query_info{kphp::rpc::detail::typed_rpc_tl_query_one_impl( - {actor.c_str(), actor.size()}, rpc_request_type{it.get_value()}, opt_timeout, collect_resp_extra_info, ignore_answer)}; + const auto query_info{kphp::rpc::detail::typed_rpc_tl_query_one_impl({actor.c_str(), actor.size()}, rpc_request_type{it.get_value()}, opt_timeout, + collect_resp_extra_info, ignore_answer)}; query_ids.set_value(it.get_key(), query_info.id); req_extra_info_arr.set_value(it.get_key(), kphp::rpc::request_extra_info{query_info.request_size}); } From 2ad3c3eb860bc55046d0588e0bc2df3f916552ac Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Thu, 18 Jun 2026 22:53:42 +0300 Subject: [PATCH 12/17] added RpcKind + better docs --- builtin-functions/kphp-light/stdlib/rpc.txt | 1 - runtime-light/k2-platform/k2-api.h | 2 +- runtime-light/k2-platform/k2-header.h | 12 +++++++++--- runtime-light/stdlib/rpc/rpc-api.cpp | 12 ++++++------ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/builtin-functions/kphp-light/stdlib/rpc.txt b/builtin-functions/kphp-light/stdlib/rpc.txt index e82e507e12..c7ccf4a7a0 100644 --- a/builtin-functions/kphp-light/stdlib/rpc.txt +++ b/builtin-functions/kphp-light/stdlib/rpc.txt @@ -40,7 +40,6 @@ final class KphpRpcRequestsExtraInfo { public function get (); } -/** @kphp-extern-func-info */ function rpc_send_requests($actor ::: string, $arr ::: array, $timeout ::: ?float, diff --git a/runtime-light/k2-platform/k2-api.h b/runtime-light/k2-platform/k2-api.h index 6b378b8f88..2f6233646d 100644 --- a/runtime-light/k2-platform/k2-api.h +++ b/runtime-light/k2-platform/k2-api.h @@ -212,7 +212,7 @@ inline int32_t component_access(std::string_view component_name) noexcept { inline std::expected rpc_send_request(std::string_view actor_name, std::span request_buffer) noexcept { uint64_t rpc_d{}; - if (auto error_code{k2_rpc_send_request(actor_name.data(), actor_name.size(), request_buffer.data(), request_buffer.size(), std::addressof(rpc_d))}; + if (auto error_code{k2_rpc_send(actor_name.data(), actor_name.size(), request_buffer.data(), request_buffer.size(), RpcKind::TL_RPC, std::addressof(rpc_d))}; error_code != k2::errno_ok) { return std::unexpected{error_code}; } diff --git a/runtime-light/k2-platform/k2-header.h b/runtime-light/k2-platform/k2-header.h index 2acdb1ffeb..6c0ea96d60 100644 --- a/runtime-light/k2-platform/k2-header.h +++ b/runtime-light/k2-platform/k2-header.h @@ -97,6 +97,10 @@ enum UpdateStatus { NewDescriptor = 2, }; +enum RpcKind { + TL_RPC = 0, +}; + struct ImageInfo { // Base const char* image_name; @@ -317,7 +321,7 @@ int32_t k2_unlink(const char* path, size_t path_len); int32_t k2_component_access(size_t name_len, const char* name); /** - * Try to send rpc request. If case of success write descriptor of rpc request to `rpc_d`, otherwise return `errno` != 0. + * Try to send rpc request. In case of success write descriptor of rpc request to `rpc_d`, otherwise return `errno` != 0, * which should be later used to call `k2_rpc_fetch_response`. * * @return return `0` on success. libc-like `errno` otherwise @@ -326,14 +330,15 @@ int32_t k2_component_access(size_t name_len, const char* name); * `EAI_MEMORY` => max descriptors count achieved * `EINVAL` => invalid `actor_name` or request, or connection pool is empty for this actor. */ -int32_t k2_rpc_send_request(const char* actor_name, size_t actor_name_len, const void* request_ptr, size_t request_size, uint64_t* rpc_d); +int32_t k2_rpc_send(const char* actor_name, size_t actor_name_len, const void* request_ptr, size_t request_size, enum RpcKind rpc_kind, uint64_t* rpc_d); /** * Get response size for corresponding request of this `rpc_d`. Write 0 to `response_size` if response is not ready. - * Write response size value to `response_size` when response is ready. + * Write response size value to `response_size` if response is ready. * * @return return `0` on success. libc-like `errno` otherwise * + * Possible `errno` values: * `EINVAL` => invalid `rpc_d` descriptor, for example, it is unknown descriptor, or not rpc descriptor. */ int32_t k2_rpc_get_response_size(uint64_t rpc_d, size_t* response_size); @@ -343,6 +348,7 @@ int32_t k2_rpc_get_response_size(uint64_t rpc_d, size_t* response_size); * * @return return `0` on success. libc-like `errno` otherwise * + * Possible `errno` values: * `EINVAL` => invalid `rpc_d` descriptor, for example, it is unknown descriptor, or not rpc descriptor. */ int32_t k2_rpc_fetch_response(uint64_t rpc_d, void* buf, size_t buf_size); diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index 4b77bbab65..3f84d2f678 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -160,7 +160,7 @@ kphp::rpc::query_info typed_rpc_tl_query_one_impl(std::string_view actor, const namespace { -static std::expected> get_rpc_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) { +std::expected> get_rpc_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) { std::expected first_response_size{k2::rpc_get_response_size(rpc_d)}; if (!first_response_size) { return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; @@ -236,11 +236,11 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, timeout)) { case kphp::coro::poll_status::event: { - std::expected> response_expected{ + std::expected> response_expected{ get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; if (!response_expected) { // TODO std::move ?????????????????????? - std::pair error{response_expected.error()}; + std::pair error{response_expected.error()}; co_return TlRpcError::make_error(error.first, error.second); } opt_response = *response_expected; @@ -320,11 +320,11 @@ kphp::coro::task> typed_rpc_tl_query_result_ kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, timeout)) { case kphp::coro::poll_status::event: { - std::expected> response_expected{ + std::expected> response_expected{ get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; if (!response_expected) { // TODO std::move ?????????????????????? - std::pair error{response_expected.error()}; + std::pair error{response_expected.error()}; co_return error_factory.make_error(error.first, error.second); } opt_response = *response_expected; @@ -389,7 +389,7 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional // normalize timeout using namespace std::chrono_literals; - static constexpr auto DEFAULT_TIMEOUT{/*TODO RETURN 300ms BEFORE MERGE*/ 3000ms}; + static constexpr auto DEFAULT_TIMEOUT{300ms}; static constexpr auto MIN_TIMEOUT{1ms}; static constexpr auto MAX_TIMEOUT{std::chrono::duration_cast(24h)}; static_assert(MIN_TIMEOUT <= MAX_TIMEOUT, "calling std::clamp will lead to undefined behavior"); From ed49edb44c626a15cd48c9fa30f484711b118a4d Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Fri, 19 Jun 2026 15:31:13 +0300 Subject: [PATCH 13/17] query_handle --- runtime-light/stdlib/rpc/rpc-api.cpp | 142 ++++-------------- runtime-light/stdlib/rpc/rpc-client-state.h | 2 +- runtime-light/stdlib/rpc/rpc-query-handle.cpp | 50 ++++++ .../stdlib/rpc/rpc-queue-functions.h | 17 +-- runtime-light/stdlib/rpc/rpc-request-info.h | 86 ++++++++++- runtime-light/stdlib/stdlib.cmake | 1 + runtime-light/stdlib/time/util.h | 40 +++++ 7 files changed, 211 insertions(+), 127 deletions(-) create mode 100644 runtime-light/stdlib/rpc/rpc-query-handle.cpp create mode 100644 runtime-light/stdlib/time/util.h diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index 3f84d2f678..0505b32b57 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -37,6 +37,7 @@ #include "runtime-light/stdlib/rpc/rpc-request-info.h" #include "runtime-light/stdlib/rpc/rpc-tl-error.h" #include "runtime-light/stdlib/rpc/rpc-tl-query.h" +#include "runtime-light/stdlib/time/util.h" #include "runtime-light/streams/read-ext.h" #include "runtime-light/streams/stream.h" #include "runtime-light/tl/tl-core.h" @@ -158,34 +159,6 @@ kphp::rpc::query_info typed_rpc_tl_query_one_impl(std::string_view actor, const return query_info; } -namespace { - -std::expected> get_rpc_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) { - std::expected first_response_size{k2::rpc_get_response_size(rpc_d)}; - if (!first_response_size) { - return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; - } - string response{reinterpret_cast(k2::alloc(*first_response_size)), static_cast(*first_response_size)}; - std::expected new_response_size{k2::rpc_fetch_response(rpc_d, {reinterpret_cast(response.buffer()), response.size()})}; - if (!new_response_size) { - return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; - } - // update response extra info if needed - if (collect_responses_extra_info) { - auto& extra_info_map{RpcClientInstanceState::get().rpc_responses_extra_info}; - if (const auto it_extra_info{extra_info_map.find(query_id)}; it_extra_info != extra_info_map.end()) [[likely]] { - const auto timestamp{std::chrono::duration{std::chrono::system_clock::now().time_since_epoch()}.count()}; - it_extra_info->second.second = std::make_tuple(response.size(), timestamp - std::get<1>(it_extra_info->second.second)); - it_extra_info->second.first = response_extra_info_status::ready; - } else { - kphp::log::warning("can't find extra info for RPC query {}", query_id); - } - } - - return {response}; -} -} // namespace - kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) noexcept { if (query_id < kphp::rpc::VALID_QUERY_ID_RANGE_START) [[unlikely]] { co_return TlRpcError::make_error(TL_ERROR_WRONG_QUERY_ID, string{"wrong query_id"}); @@ -193,26 +166,26 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no auto& rpc_client_instance_st{RpcClientInstanceState::get()}; class_instance rpc_query{}; - std::optional opt_rpc_request_info{}; + std::optional opt_rpc_request_handle{}; { const auto it_response_fetcher{rpc_client_instance_st.response_fetcher_instances.find(query_id)}; - const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(query_id)}; - const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_rpc_request_info] noexcept { + const auto it_rpc_request_handle{rpc_client_instance_st.rpc_query_handles.find(query_id)}; + const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_rpc_request_handle] noexcept { if (it_response_fetcher != rpc_client_instance_st.response_fetcher_instances.end()) [[likely]] { rpc_client_instance_st.response_fetcher_instances.erase(it_response_fetcher); } - if (it_rpc_request_info != rpc_client_instance_st.rpc_requests_infos.end()) [[likely]] { - rpc_client_instance_st.rpc_requests_infos.erase(it_rpc_request_info); + if (it_rpc_request_handle != rpc_client_instance_st.rpc_query_handles.end()) [[likely]] { + rpc_client_instance_st.rpc_query_handles.erase(it_rpc_request_handle); } }}; if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end() || - it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) [[unlikely]] { + it_rpc_request_handle == rpc_client_instance_st.rpc_query_handles.end()) [[unlikely]] { co_return TlRpcError::make_error(TL_ERROR_INTERNAL, string{"unexpectedly could not find query in pending queries"}); } rpc_query = std::move(it_response_fetcher->second); - opt_rpc_request_info.emplace(std::move(it_rpc_request_info->second)); + opt_rpc_request_handle.emplace(std::move(it_rpc_request_handle->second)); } if (rpc_query.is_null()) [[unlikely]] { @@ -225,39 +198,15 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no co_return TlRpcError::make_error(TL_ERROR_INTERNAL, string{"can't get untyped result from typed TL query. Use consistent API for that"}); } - std::optional opt_response{std::nullopt}; - - kphp::log::assertion(opt_rpc_request_info.has_value()); - auto rpc_request_info{*opt_rpc_request_info}; - k2::TimePoint now_instant{}; - k2::instant(std::addressof(now_instant)); - std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; - std::chrono::nanoseconds timeout{rpc_request_info.deadline - now_ns}; - kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; - switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, timeout)) { - case kphp::coro::poll_status::event: { - std::expected> response_expected{ - get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; - if (!response_expected) { - // TODO std::move ?????????????????????? - std::pair error{response_expected.error()}; - co_return TlRpcError::make_error(error.first, error.second); - } - opt_response = *response_expected; - break; - } - case kphp::coro::poll_status::closed: - co_return TlRpcError::make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); - case kphp::coro::poll_status::error: - co_return TlRpcError::make_error(TL_ERROR_INTERNAL, string{"error fetching rpc response"}); - case kphp::coro::poll_status::timeout: - co_return TlRpcError::make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); - } - if (!opt_response) [[unlikely]] { - co_return TlRpcError::make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); + kphp::log::assertion(opt_rpc_request_handle.has_value()); + auto rpc_request_handle{std::move(*opt_rpc_request_handle)}; + auto response_expected = co_await rpc_request_handle.get_response(); + if (!response_expected) [[unlikely]] { + std::pair error{response_expected.error()}; + co_return TlRpcError::make_error(error.first, error.second); } - auto response{*std::move(opt_response)}; // don't check response's emptyness; will throw if it's empty, indicating a fetch error + auto response{*std::move(response_expected)}; // don't check response's emptyness; will throw if it's empty, indicating a fetch error f$rpc_clean(); RpcServerInstanceState::get().tl_fetcher = tl::fetcher{{reinterpret_cast(response.c_str()), response.size()}}; @@ -276,26 +225,26 @@ kphp::coro::task> typed_rpc_tl_query_result_ auto& rpc_client_instance_st{RpcClientInstanceState::get()}; class_instance rpc_query{}; - std::optional opt_rpc_request_info{}; + std::optional opt_rpc_request_handle{}; { const auto it_response_fetcher{rpc_client_instance_st.response_fetcher_instances.find(query_id)}; - const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(query_id)}; - const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_rpc_request_info] noexcept { + const auto it_rpc_request_handle{rpc_client_instance_st.rpc_query_handles.find(query_id)}; + const vk::final_action finalizer{[&rpc_client_instance_st, it_response_fetcher, it_rpc_request_handle] noexcept { if (it_response_fetcher != rpc_client_instance_st.response_fetcher_instances.end()) [[likely]] { rpc_client_instance_st.response_fetcher_instances.erase(it_response_fetcher); } - if (it_rpc_request_info != rpc_client_instance_st.rpc_requests_infos.end()) [[likely]] { - rpc_client_instance_st.rpc_requests_infos.erase(it_rpc_request_info); + if (it_rpc_request_handle != rpc_client_instance_st.rpc_query_handles.end()) [[likely]] { + rpc_client_instance_st.rpc_query_handles.erase(it_rpc_request_handle); } }}; if (it_response_fetcher == rpc_client_instance_st.response_fetcher_instances.end() || - it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) [[unlikely]] { + it_rpc_request_handle == rpc_client_instance_st.rpc_query_handles.end()) [[unlikely]] { co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"unexpectedly could not find query in pending queries"}); } rpc_query = std::move(it_response_fetcher->second); - opt_rpc_request_info.emplace(std::move(it_rpc_request_info->second)); + opt_rpc_request_handle.emplace(std::move(it_rpc_request_handle->second)); } if (rpc_query.is_null()) [[unlikely]] { @@ -310,38 +259,15 @@ kphp::coro::task> typed_rpc_tl_query_result_ std::optional opt_response{std::nullopt}; - kphp::log::assertion(opt_rpc_request_info.has_value()); - auto rpc_request_info{*opt_rpc_request_info}; - k2::TimePoint now_instant{}; - // TODO call k2::instant once for all sending requests in batch - k2::instant(std::addressof(now_instant)); - std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; - std::chrono::nanoseconds timeout{rpc_request_info.deadline - now_ns}; - kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; - switch (co_await m_scheduler.poll(rpc_request_info.rpc_d, kphp::coro::poll_op::read, timeout)) { - case kphp::coro::poll_status::event: { - std::expected> response_expected{ - get_rpc_response(query_id, rpc_request_info.rpc_d, rpc_request_info.collect_responses_extra_info)}; - if (!response_expected) { - // TODO std::move ?????????????????????? - std::pair error{response_expected.error()}; - co_return error_factory.make_error(error.first, error.second); - } - opt_response = *response_expected; - break; - } - case kphp::coro::poll_status::closed: - co_return error_factory.make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); - case kphp::coro::poll_status::error: - co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"error fetching rpc response"}); - case kphp::coro::poll_status::timeout: - co_return error_factory.make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); - } - if (!opt_response) [[unlikely]] { - co_return error_factory.make_error(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"}); + kphp::log::assertion(opt_rpc_request_handle.has_value()); + auto rpc_request_handle{std::move(*opt_rpc_request_handle)}; + auto response_expected = co_await rpc_request_handle.get_response(); + if (!response_expected) [[unlikely]] { + std::pair error{response_expected.error()}; + co_return error_factory.make_error(error.first, error.second); } - auto response{*std::move(opt_response)}; // don't check response's emptiness; will throw if it's empty, indicating a fetch error + auto response{*std::move(response_expected)}; // don't check response's emptiness; will throw if it's empty, indicating a fetch error f$rpc_clean(); RpcServerInstanceState::get().tl_fetcher = tl::fetcher{{reinterpret_cast(response.c_str()), response.size()}}; @@ -401,7 +327,6 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional MIN_TIMEOUT, MAX_TIMEOUT)}; if (ignore_answer) { // start ignore answer awaiter task - // TODO need to move rpc_d ? auto ignore_answer_awaiter_task{ignore_answer_awaiter_coroutine(std::move(rpc_d), timeout)}; kphp::log::assertion(kphp::coro::io_scheduler::get().start(ignore_answer_awaiter_task)); @@ -410,13 +335,8 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional } // start awaiter task - k2::TimePoint now_instant{}; - k2::instant(std::addressof(now_instant)); - std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; - std::chrono::nanoseconds timeout_ns{duration_cast(timeout)}; - std::chrono::nanoseconds deadline{now_ns + timeout_ns}; - - rpc_client_instance_st.rpc_requests_infos.emplace(query_id, kphp::rpc::request_info{rpc_d, deadline, collect_responses_extra_info}); + std::chrono::nanoseconds deadline{timeout_to_deadline(timeout)}; + rpc_client_instance_st.rpc_query_handles.emplace(query_id, kphp::rpc::query_handle{std::move(rpc_d), query_id, deadline, collect_responses_extra_info}); return kphp::rpc::query_info{.id = query_id, .request_size = request_size, .timestamp = timestamp}; } diff --git a/runtime-light/stdlib/rpc/rpc-client-state.h b/runtime-light/stdlib/rpc/rpc-client-state.h index 003fec2066..f8046dd8cf 100644 --- a/runtime-light/stdlib/rpc/rpc-client-state.h +++ b/runtime-light/stdlib/rpc/rpc-client-state.h @@ -24,7 +24,7 @@ struct RpcClientInstanceState final : private vk::not_copyable { CurrentTlQuery current_client_query{}; int64_t current_query_id{kphp::rpc::VALID_QUERY_ID_RANGE_START}; - kphp::stl::unordered_map rpc_requests_infos; + kphp::stl::unordered_map rpc_query_handles; kphp::stl::unordered_map>, kphp::memory::script_allocator> response_awaiter_tasks; kphp::stl::unordered_map, kphp::memory::script_allocator> response_fetcher_instances; kphp::stl::unordered_map, kphp::memory::script_allocator> diff --git a/runtime-light/stdlib/rpc/rpc-query-handle.cpp b/runtime-light/stdlib/rpc/rpc-query-handle.cpp new file mode 100644 index 0000000000..12ff099438 --- /dev/null +++ b/runtime-light/stdlib/rpc/rpc-query-handle.cpp @@ -0,0 +1,50 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2026 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#include +#include +#include +#include +#include +#include + +#include "common/rpc-error-codes.h" +#include "runtime-common/core/runtime-core.h" +#include "runtime-light/k2-platform/k2-api.h" +#include "runtime-light/stdlib/diagnostics/logs.h" +#include "runtime-light/stdlib/rpc/rpc-client-state.h" + +namespace kphp::rpc { + +namespace impl { + +std::expected> get_ready_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) noexcept { + std::expected first_response_size{k2::rpc_get_response_size(rpc_d)}; + if (!first_response_size) { + return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; + } + string response{reinterpret_cast(k2::alloc(*first_response_size)), static_cast(*first_response_size)}; + std::expected new_response_size{k2::rpc_fetch_response(rpc_d, {reinterpret_cast(response.buffer()), response.size()})}; + if (!new_response_size) { + return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; + } + + // update response extra info if needed + if (collect_responses_extra_info) { + auto& rpc_client_instance_st{RpcClientInstanceState::get()}; + auto& extra_info_map{rpc_client_instance_st.rpc_responses_extra_info}; + if (const auto it_extra_info{extra_info_map.find(query_id)}; it_extra_info != extra_info_map.end()) [[likely]] { + const auto timestamp{std::chrono::duration{std::chrono::system_clock::now().time_since_epoch()}.count()}; + it_extra_info->second.second = std::make_tuple(response.size(), timestamp - std::get<1>(it_extra_info->second.second)); + it_extra_info->second.first = response_extra_info_status::ready; + } else { + kphp::log::warning("can't find extra info for RPC query {}", query_id); + } + } + + return {response}; +} + +} // namespace impl +} // namespace kphp::rpc diff --git a/runtime-light/stdlib/rpc/rpc-queue-functions.h b/runtime-light/stdlib/rpc/rpc-queue-functions.h index 019aebdada..55a6c7dba7 100644 --- a/runtime-light/stdlib/rpc/rpc-queue-functions.h +++ b/runtime-light/stdlib/rpc/rpc-queue-functions.h @@ -19,25 +19,20 @@ #include "runtime-light/stdlib/rpc/rpc-client-state.h" #include "runtime-light/stdlib/rpc/rpc-queue-state.h" #include "runtime-light/stdlib/rpc/rpc-request-info.h" +#include "runtime-light/stdlib/time/util.h" namespace kphp::rpc { inline void rpc_queue_push(int64_t queue_id, int64_t request_id) noexcept { - static constexpr auto rpc_queue_wrapper_task{[](kphp::rpc::request_info request_info, int64_t request_id) noexcept -> kphp::coro::task { - k2::TimePoint now_instant{}; - // TODO call k2::instant once for all sending requests in batch - k2::instant(std::addressof(now_instant)); - std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; - std::chrono::nanoseconds timeout{request_info.deadline - now_ns}; - kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; - co_await m_scheduler.poll(request_info.rpc_d, kphp::coro::poll_op::read, timeout); + static constexpr auto rpc_queue_wrapper_task{[](kphp::rpc::query_handle query_handle, int64_t request_id) noexcept -> kphp::coro::task { + std::ignore = co_await query_handle.get_response(); co_return request_id; }}; auto& rpc_client_instance_st{RpcClientInstanceState::get()}; - const auto it_rpc_request_info{rpc_client_instance_st.rpc_requests_infos.find(request_id)}; - if (it_rpc_request_info == rpc_client_instance_st.rpc_requests_infos.end()) [[unlikely]] { + const auto it_rpc_request_info{rpc_client_instance_st.rpc_query_handles.find(request_id)}; + if (it_rpc_request_info == rpc_client_instance_st.rpc_query_handles.end()) [[unlikely]] { kphp::log::warning("could not find rpc query with id {} in pending requests", queue_id); return; } @@ -50,7 +45,7 @@ inline void rpc_queue_push(int64_t queue_id, int64_t request_id) noexcept { } auto& await_set{(*opt_await_set).get()}; - await_set.push(rpc_queue_wrapper_task(it_rpc_request_info->second, request_id)); + await_set.push(rpc_queue_wrapper_task(std::move(it_rpc_request_info->second), request_id)); } inline int64_t rpc_queue_create(std::span request_ids) noexcept { diff --git a/runtime-light/stdlib/rpc/rpc-request-info.h b/runtime-light/stdlib/rpc/rpc-request-info.h index cbb7190fc8..2b5f5c0de2 100644 --- a/runtime-light/stdlib/rpc/rpc-request-info.h +++ b/runtime-light/stdlib/rpc/rpc-request-info.h @@ -5,15 +5,93 @@ #pragma once #include +#include +#include +#include +#include +#include +#include +#include "common/rpc-error-codes.h" +#include "rpc-client-state.h" +#include "runtime-common/core/runtime-core.h" +#include "runtime-light/coroutine/task.h" +#include "runtime-light/coroutine/io-scheduler.h" #include "runtime-light/k2-platform/k2-api.h" +#include "runtime-light/stdlib/diagnostics/logs.h" +#include "runtime-light/stdlib/rpc/rpc-client-state.h" +#include "runtime-light/stdlib/time/util.h" namespace kphp::rpc { -struct request_info { - k2::descriptor rpc_d; - std::chrono::nanoseconds deadline; - bool collect_responses_extra_info; +namespace impl { + +std::expected> get_ready_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) noexcept; + +} // namespace impl + +// TODO is it appropriate naming? May be query_handle? +class query_handle { + k2::descriptor rpc_d{k2::INVALID_PLATFORM_DESCRIPTOR}; + int64_t query_id; + std::chrono::nanoseconds deadline{}; + bool collect_responses_extra_info{false}; + +public: + query_handle() = delete; + + query_handle(k2::descriptor _rpc_d, int64_t query_id, std::chrono::nanoseconds _deadline, bool _collect_responses_extra_info) noexcept + : rpc_d{_rpc_d}, + query_id{query_id}, + deadline{_deadline}, + collect_responses_extra_info{_collect_responses_extra_info} {} + + query_handle(query_handle&& other) noexcept + : rpc_d{std::exchange(other.rpc_d, k2::INVALID_PLATFORM_DESCRIPTOR)}, + query_id{other.query_id}, + deadline{std::exchange(other.deadline, {})}, + collect_responses_extra_info{other.collect_responses_extra_info} {} + + query_handle& operator=(query_handle&& other) noexcept { + if (this != std::addressof(other)) { + close(); + rpc_d = std::exchange(other.rpc_d, k2::INVALID_PLATFORM_DESCRIPTOR); + query_id = other.query_id; + deadline = std::exchange(other.deadline, {}); + collect_responses_extra_info = other.collect_responses_extra_info; + } + return *this; + } + + query_handle(const query_handle& other) = delete; + + query_handle& operator=(const query_handle& other) = delete; + + ~query_handle() noexcept { + close(); + } + + void close() noexcept { + if (rpc_d != k2::INVALID_PLATFORM_DESCRIPTOR) { + k2::free_descriptor(std::exchange(rpc_d, k2::INVALID_PLATFORM_DESCRIPTOR)); + } + } + + kphp::coro::task>> get_response() noexcept { + kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; + std::chrono::nanoseconds timeout{deadline_to_timeout(deadline)}; + + switch (co_await m_scheduler.poll(rpc_d, kphp::coro::poll_op::read, timeout)) { + case kphp::coro::poll_status::event: + co_return impl::get_ready_response(query_id, rpc_d, collect_responses_extra_info); + case kphp::coro::poll_status::closed: + co_return std::unexpected{std::make_pair(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"})}; + case kphp::coro::poll_status::error: + co_return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; + case kphp::coro::poll_status::timeout: + co_return std::unexpected{std::make_pair(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"})}; + } + } }; } // namespace kphp::rpc diff --git a/runtime-light/stdlib/stdlib.cmake b/runtime-light/stdlib/stdlib.cmake index 92f67cd6b4..5ce1470ba5 100644 --- a/runtime-light/stdlib/stdlib.cmake +++ b/runtime-light/stdlib/stdlib.cmake @@ -29,6 +29,7 @@ prepend( rpc/rpc-client-state.cpp rpc/rpc-extra-headers.cpp rpc/rpc-extra-info.cpp + rpc/rpc-query-handle.cpp rpc/rpc-queue-state.cpp rpc/rpc-tl-builtins.cpp rpc/rpc-tl-error.cpp diff --git a/runtime-light/stdlib/time/util.h b/runtime-light/stdlib/time/util.h new file mode 100644 index 0000000000..8ea55d136d --- /dev/null +++ b/runtime-light/stdlib/time/util.h @@ -0,0 +1,40 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2026 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include + +#include "runtime-light/k2-platform/k2-api.h" +#include "runtime-light/metaprogramming/concepts.h" + +/** + * Calculate time remaining to the deadline. + */ +template +inline duration_type deadline_to_timeout(duration_type deadline) noexcept { + k2::TimePoint now_instant{}; + k2::instant(std::addressof(now_instant)); + + std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; + std::chrono::nanoseconds deadline_ns{duration_cast(deadline)}; + std::chrono::nanoseconds timeout_ns{deadline_ns - now_ns}; + + return duration_cast(timeout_ns); +} + +/** + * Converts timeout to time point, when timeout will elapse - deadline. + */ +template +inline duration_type timeout_to_deadline(duration_type timeout) { + k2::TimePoint now_instant{}; + k2::instant(std::addressof(now_instant)); + + std::chrono::nanoseconds now_ns{now_instant.time_point_ns}; + std::chrono::nanoseconds timeout_ns{duration_cast(timeout)}; + std::chrono::nanoseconds deadline{now_ns + timeout_ns}; + + return duration_cast(deadline); +} From 8ed260f2e320c92a26b7bf4a5ffa7e6db5bafaf6 Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Fri, 19 Jun 2026 16:08:44 +0300 Subject: [PATCH 14/17] send_and_get_handle --- runtime-light/stdlib/rpc/rpc-api.cpp | 29 +++++++------------ runtime-light/stdlib/rpc/rpc-query-handle.cpp | 22 ++++++++++++++ runtime-light/stdlib/rpc/rpc-request-info.h | 12 ++++++-- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index 0505b32b57..ff0316cbc5 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -293,24 +293,11 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional std::swap(tl_storer, rpc_server_instance_st.tl_storer); } }}; - auto rpc_d_exp{k2::rpc_send_request(actor, tl_storer.view())}; - if (!rpc_d_exp) { - return kphp::rpc::query_info{.id = kphp::rpc::INTERNAL_ERROR, .request_size = request_size, .timestamp = timestamp}; - } - // TODO why is query_id incremented here but not before the request ??? const auto query_id{rpc_client_instance_st.current_query_id++}; - // create response extra info - if (collect_responses_extra_info) { - rpc_client_instance_st.rpc_responses_extra_info.emplace(query_id, std::make_pair(response_extra_info_status::not_ready, response_extra_info{0, timestamp})); - } - - k2::descriptor rpc_d{*rpc_d_exp}; - - static constexpr auto ignore_answer_awaiter_coroutine{[](k2::descriptor rpc_d, std::chrono::milliseconds timeout) noexcept -> kphp::coro::shared_task<> { - auto fetch_task{kphp::coro::io_scheduler::get().poll(rpc_d, kphp::coro::poll_op::read, timeout)}; - std::ignore = co_await kphp::coro::io_scheduler::get().schedule(std::move(fetch_task), timeout); + static constexpr auto ignore_answer_awaiter_coroutine{[](query_handle handle, std::chrono::milliseconds timeout) noexcept -> kphp::coro::shared_task<> { + std::ignore = co_await handle.get_response(); }}; // normalize timeout @@ -325,9 +312,16 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional }) .value_or(DEFAULT_TIMEOUT), MIN_TIMEOUT, MAX_TIMEOUT)}; + + auto query_handle_expected{kphp::rpc::send_and_get_handle(actor, collect_responses_extra_info, timeout, query_id, tl_storer.view())}; + if (!query_handle_expected) { + return kphp::rpc::query_info{.id = kphp::rpc::INTERNAL_ERROR, .request_size = request_size, .timestamp = timestamp}; + } + auto query_handle{std::move(*query_handle_expected)}; + if (ignore_answer) { // start ignore answer awaiter task - auto ignore_answer_awaiter_task{ignore_answer_awaiter_coroutine(std::move(rpc_d), timeout)}; + auto ignore_answer_awaiter_task{ignore_answer_awaiter_coroutine(std::move(query_handle), timeout)}; kphp::log::assertion(kphp::coro::io_scheduler::get().start(ignore_answer_awaiter_task)); rpc_client_instance_st.ignore_answer_request_awaiter_tasks.push(std::move(ignore_answer_awaiter_task)); @@ -335,8 +329,7 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional } // start awaiter task - std::chrono::nanoseconds deadline{timeout_to_deadline(timeout)}; - rpc_client_instance_st.rpc_query_handles.emplace(query_id, kphp::rpc::query_handle{std::move(rpc_d), query_id, deadline, collect_responses_extra_info}); + rpc_client_instance_st.rpc_query_handles.emplace(query_id, std::move(query_handle)); return kphp::rpc::query_info{.id = query_id, .request_size = request_size, .timestamp = timestamp}; } diff --git a/runtime-light/stdlib/rpc/rpc-query-handle.cpp b/runtime-light/stdlib/rpc/rpc-query-handle.cpp index 12ff099438..73115472cb 100644 --- a/runtime-light/stdlib/rpc/rpc-query-handle.cpp +++ b/runtime-light/stdlib/rpc/rpc-query-handle.cpp @@ -46,5 +46,27 @@ std::expected> get_ready_response(int64_t que return {response}; } +// TODO naming ?? +std::expected send_and_get_handle(std::string_view actor, bool collect_responses_extra_info, std::chrono::milliseconds timeout, + int64_t query_id, std::span request_buffer) noexcept { + auto& rpc_client_instance_st{RpcClientInstanceState::get()}; + + auto rpc_d_exp{k2::rpc_send_request(actor, request_buffer)}; + if (!rpc_d_exp) { + return std::unexpected{rpc_d_exp.error()}; + } + k2::descriptor rpc_d{*rpc_d_exp}; + + + // create response extra info + if (collect_responses_extra_info) { + rpc_client_instance_st.rpc_responses_extra_info.emplace(query_id, std::make_pair(response_extra_info_status::not_ready, response_extra_info{0, timestamp})); + } + + std::chrono::nanoseconds deadline{timeout_to_deadline(timeout)}; + + return {query_handle{std::move(rpc_d), query_id, deadline, collect_responses_extra_info}}; +} + } // namespace impl } // namespace kphp::rpc diff --git a/runtime-light/stdlib/rpc/rpc-request-info.h b/runtime-light/stdlib/rpc/rpc-request-info.h index 2b5f5c0de2..e9ef4bd13d 100644 --- a/runtime-light/stdlib/rpc/rpc-request-info.h +++ b/runtime-light/stdlib/rpc/rpc-request-info.h @@ -15,8 +15,8 @@ #include "common/rpc-error-codes.h" #include "rpc-client-state.h" #include "runtime-common/core/runtime-core.h" -#include "runtime-light/coroutine/task.h" #include "runtime-light/coroutine/io-scheduler.h" +#include "runtime-light/coroutine/task.h" #include "runtime-light/k2-platform/k2-api.h" #include "runtime-light/stdlib/diagnostics/logs.h" #include "runtime-light/stdlib/rpc/rpc-client-state.h" @@ -78,6 +78,10 @@ class query_handle { } kphp::coro::task>> get_response() noexcept { + if (rpc_d == k2::INVALID_PLATFORM_DESCRIPTOR) { + co_return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"fetching rpc response from empty handle"})}; + } + kphp::coro::io_scheduler& m_scheduler{kphp::coro::io_scheduler::get()}; std::chrono::nanoseconds timeout{deadline_to_timeout(deadline)}; @@ -85,13 +89,15 @@ class query_handle { case kphp::coro::poll_status::event: co_return impl::get_ready_response(query_id, rpc_d, collect_responses_extra_info); case kphp::coro::poll_status::closed: + case kphp::coro::poll_status::timeout: co_return std::unexpected{std::make_pair(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"})}; case kphp::coro::poll_status::error: co_return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; - case kphp::coro::poll_status::timeout: - co_return std::unexpected{std::make_pair(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"})}; } } }; +std::expected send_and_get_handle(std::string_view actor, bool collect_responses_extra_info, std::chrono::milliseconds timeout, + int64_t query_id, std::span request_buffer) noexcept; + } // namespace kphp::rpc From 51e14589ada0f94524127b38d934686a9eb2f4c5 Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Fri, 19 Jun 2026 20:24:17 +0300 Subject: [PATCH 15/17] refactored query_handle a bit --- runtime-light/stdlib/rpc/rpc-query-handle.cpp | 52 +++++++++---------- runtime-light/stdlib/rpc/rpc-request-info.h | 5 +- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/runtime-light/stdlib/rpc/rpc-query-handle.cpp b/runtime-light/stdlib/rpc/rpc-query-handle.cpp index 73115472cb..c66b3d9ee0 100644 --- a/runtime-light/stdlib/rpc/rpc-query-handle.cpp +++ b/runtime-light/stdlib/rpc/rpc-query-handle.cpp @@ -19,14 +19,37 @@ namespace kphp::rpc { namespace impl { -std::expected> get_ready_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) noexcept { +// TODO naming ?? +std::expected send_and_get_handle(std::string_view actor, bool collect_responses_extra_info, std::chrono::milliseconds timeout, + int64_t query_id, std::span request_buffer) noexcept { + auto& rpc_client_instance_st{RpcClientInstanceState::get()}; + + auto rpc_d_exp{k2::rpc_send_request(actor, request_buffer)}; + if (!rpc_d_exp) { + return std::unexpected{rpc_d_exp.error()}; + } + k2::descriptor rpc_d{*rpc_d_exp}; + + // create response extra info + if (collect_responses_extra_info) { + rpc_client_instance_st.rpc_responses_extra_info.emplace(query_id, std::make_pair(response_extra_info_status::not_ready, response_extra_info{0, timestamp})); + } + + std::chrono::nanoseconds deadline{timeout_to_deadline(timeout)}; + + return {query_handle{std::move(rpc_d), query_id, deadline, collect_responses_extra_info}}; +} + +} // namespace impl + +std::expected> query_handle::get_ready_response() noexcept { std::expected first_response_size{k2::rpc_get_response_size(rpc_d)}; if (!first_response_size) { return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; } string response{reinterpret_cast(k2::alloc(*first_response_size)), static_cast(*first_response_size)}; - std::expected new_response_size{k2::rpc_fetch_response(rpc_d, {reinterpret_cast(response.buffer()), response.size()})}; - if (!new_response_size) { + std::expected response_fetch_result{k2::rpc_fetch_response(rpc_d, {reinterpret_cast(response.buffer()), response.size()})}; + if (!response_fetch_result) { return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; } @@ -46,27 +69,4 @@ std::expected> get_ready_response(int64_t que return {response}; } -// TODO naming ?? -std::expected send_and_get_handle(std::string_view actor, bool collect_responses_extra_info, std::chrono::milliseconds timeout, - int64_t query_id, std::span request_buffer) noexcept { - auto& rpc_client_instance_st{RpcClientInstanceState::get()}; - - auto rpc_d_exp{k2::rpc_send_request(actor, request_buffer)}; - if (!rpc_d_exp) { - return std::unexpected{rpc_d_exp.error()}; - } - k2::descriptor rpc_d{*rpc_d_exp}; - - - // create response extra info - if (collect_responses_extra_info) { - rpc_client_instance_st.rpc_responses_extra_info.emplace(query_id, std::make_pair(response_extra_info_status::not_ready, response_extra_info{0, timestamp})); - } - - std::chrono::nanoseconds deadline{timeout_to_deadline(timeout)}; - - return {query_handle{std::move(rpc_d), query_id, deadline, collect_responses_extra_info}}; -} - -} // namespace impl } // namespace kphp::rpc diff --git a/runtime-light/stdlib/rpc/rpc-request-info.h b/runtime-light/stdlib/rpc/rpc-request-info.h index e9ef4bd13d..95d63164f4 100644 --- a/runtime-light/stdlib/rpc/rpc-request-info.h +++ b/runtime-light/stdlib/rpc/rpc-request-info.h @@ -87,7 +87,7 @@ class query_handle { switch (co_await m_scheduler.poll(rpc_d, kphp::coro::poll_op::read, timeout)) { case kphp::coro::poll_status::event: - co_return impl::get_ready_response(query_id, rpc_d, collect_responses_extra_info); + co_return get_ready_response(); case kphp::coro::poll_status::closed: case kphp::coro::poll_status::timeout: co_return std::unexpected{std::make_pair(TL_ERROR_QUERY_TIMEOUT, string{"rpc response timeout"})}; @@ -95,6 +95,9 @@ class query_handle { co_return std::unexpected{std::make_pair(TL_ERROR_INTERNAL, string{"error fetching rpc response"})}; } } + +private: + std::expected> get_ready_response() noexcept; }; std::expected send_and_get_handle(std::string_view actor, bool collect_responses_extra_info, std::chrono::milliseconds timeout, From b55db67157395c9aaa87fd76fbf706c6713e643f Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Sun, 21 Jun 2026 20:52:03 +0300 Subject: [PATCH 16/17] refactorrr --- runtime-light/stdlib/rpc/rpc-api.cpp | 26 +++++++------------ runtime-light/stdlib/rpc/rpc-client-state.h | 2 +- runtime-light/stdlib/rpc/rpc-query-handle.cpp | 6 +---- ...{rpc-request-info.h => rpc-query-handle.h} | 22 ++++++---------- .../stdlib/rpc/rpc-queue-functions.h | 2 +- runtime-light/stdlib/time/util.h | 1 + 6 files changed, 22 insertions(+), 37 deletions(-) rename runtime-light/stdlib/rpc/{rpc-request-info.h => rpc-query-handle.h} (82%) diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index ff0316cbc5..ccf5faeb95 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -34,7 +34,7 @@ #include "runtime-light/stdlib/rpc/rpc-constants.h" #include "runtime-light/stdlib/rpc/rpc-extra-headers.h" #include "runtime-light/stdlib/rpc/rpc-extra-info.h" -#include "runtime-light/stdlib/rpc/rpc-request-info.h" +#include "runtime-light/stdlib/rpc/rpc-query-handle.h" #include "runtime-light/stdlib/rpc/rpc-tl-error.h" #include "runtime-light/stdlib/rpc/rpc-tl-query.h" #include "runtime-light/stdlib/time/util.h" @@ -199,11 +199,10 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no } kphp::log::assertion(opt_rpc_request_handle.has_value()); - auto rpc_request_handle{std::move(*opt_rpc_request_handle)}; - auto response_expected = co_await rpc_request_handle.get_response(); + auto response_expected{co_await opt_rpc_request_handle->get_response()}; if (!response_expected) [[unlikely]] { - std::pair error{response_expected.error()}; - co_return TlRpcError::make_error(error.first, error.second); + std::pair error{std::move(response_expected.error())}; + co_return TlRpcError::make_error(error.first, std::move(error.second)); } auto response{*std::move(response_expected)}; // don't check response's emptyness; will throw if it's empty, indicating a fetch error @@ -257,14 +256,11 @@ kphp::coro::task> typed_rpc_tl_query_result_ co_return error_factory.make_error(TL_ERROR_INTERNAL, string{"can't get typed result from untyped TL query. Use consistent API for that"}); } - std::optional opt_response{std::nullopt}; - kphp::log::assertion(opt_rpc_request_handle.has_value()); - auto rpc_request_handle{std::move(*opt_rpc_request_handle)}; - auto response_expected = co_await rpc_request_handle.get_response(); + auto response_expected{co_await opt_rpc_request_handle->get_response()}; if (!response_expected) [[unlikely]] { - std::pair error{response_expected.error()}; - co_return error_factory.make_error(error.first, error.second); + std::pair error{std::move(response_expected.error())}; + co_return error_factory.make_error(error.first, std::move(error.second)); } auto response{*std::move(response_expected)}; // don't check response's emptiness; will throw if it's empty, indicating a fetch error @@ -296,9 +292,8 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional const auto query_id{rpc_client_instance_st.current_query_id++}; - static constexpr auto ignore_answer_awaiter_coroutine{[](query_handle handle, std::chrono::milliseconds timeout) noexcept -> kphp::coro::shared_task<> { - std::ignore = co_await handle.get_response(); - }}; + static constexpr auto ignore_answer_awaiter_coroutine{ + [](query_handle handle, std::chrono::milliseconds timeout) noexcept -> kphp::coro::shared_task<> { std::ignore = co_await handle.get_response(); }}; // normalize timeout using namespace std::chrono_literals; @@ -313,7 +308,7 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional .value_or(DEFAULT_TIMEOUT), MIN_TIMEOUT, MAX_TIMEOUT)}; - auto query_handle_expected{kphp::rpc::send_and_get_handle(actor, collect_responses_extra_info, timeout, query_id, tl_storer.view())}; + auto query_handle_expected{kphp::rpc::send_and_get_handle(actor, collect_responses_extra_info, timeout, timestamp, query_id, tl_storer.view())}; if (!query_handle_expected) { return kphp::rpc::query_info{.id = kphp::rpc::INTERNAL_ERROR, .request_size = request_size, .timestamp = timestamp}; } @@ -327,7 +322,6 @@ kphp::rpc::query_info send_request(std::string_view actor, std::optional rpc_client_instance_st.ignore_answer_request_awaiter_tasks.push(std::move(ignore_answer_awaiter_task)); return kphp::rpc::query_info{.id = kphp::rpc::IGNORED_ANSWER_QUERY_ID, .request_size = request_size, .timestamp = timestamp}; } - // start awaiter task rpc_client_instance_st.rpc_query_handles.emplace(query_id, std::move(query_handle)); return kphp::rpc::query_info{.id = query_id, .request_size = request_size, .timestamp = timestamp}; diff --git a/runtime-light/stdlib/rpc/rpc-client-state.h b/runtime-light/stdlib/rpc/rpc-client-state.h index f8046dd8cf..a53d3f1fca 100644 --- a/runtime-light/stdlib/rpc/rpc-client-state.h +++ b/runtime-light/stdlib/rpc/rpc-client-state.h @@ -16,7 +16,7 @@ #include "runtime-light/coroutine/shared-task.h" #include "runtime-light/stdlib/rpc/rpc-constants.h" #include "runtime-light/stdlib/rpc/rpc-extra-info.h" -#include "runtime-light/stdlib/rpc/rpc-request-info.h" +#include "runtime-light/stdlib/rpc/rpc-query-handle.h" #include "runtime-light/stdlib/rpc/rpc-tl-defs.h" #include "runtime-light/stdlib/rpc/rpc-tl-query.h" diff --git a/runtime-light/stdlib/rpc/rpc-query-handle.cpp b/runtime-light/stdlib/rpc/rpc-query-handle.cpp index c66b3d9ee0..c6016887f5 100644 --- a/runtime-light/stdlib/rpc/rpc-query-handle.cpp +++ b/runtime-light/stdlib/rpc/rpc-query-handle.cpp @@ -17,11 +17,9 @@ namespace kphp::rpc { -namespace impl { - // TODO naming ?? std::expected send_and_get_handle(std::string_view actor, bool collect_responses_extra_info, std::chrono::milliseconds timeout, - int64_t query_id, std::span request_buffer) noexcept { + double timestamp, int64_t query_id, std::span request_buffer) noexcept { auto& rpc_client_instance_st{RpcClientInstanceState::get()}; auto rpc_d_exp{k2::rpc_send_request(actor, request_buffer)}; @@ -40,8 +38,6 @@ std::expected send_and_get_handle(std::string_view actor, return {query_handle{std::move(rpc_d), query_id, deadline, collect_responses_extra_info}}; } -} // namespace impl - std::expected> query_handle::get_ready_response() noexcept { std::expected first_response_size{k2::rpc_get_response_size(rpc_d)}; if (!first_response_size) { diff --git a/runtime-light/stdlib/rpc/rpc-request-info.h b/runtime-light/stdlib/rpc/rpc-query-handle.h similarity index 82% rename from runtime-light/stdlib/rpc/rpc-request-info.h rename to runtime-light/stdlib/rpc/rpc-query-handle.h index 95d63164f4..6409d211a3 100644 --- a/runtime-light/stdlib/rpc/rpc-request-info.h +++ b/runtime-light/stdlib/rpc/rpc-query-handle.h @@ -24,12 +24,6 @@ namespace kphp::rpc { -namespace impl { - -std::expected> get_ready_response(int64_t query_id, k2::descriptor rpc_d, bool collect_responses_extra_info) noexcept; - -} // namespace impl - // TODO is it appropriate naming? May be query_handle? class query_handle { k2::descriptor rpc_d{k2::INVALID_PLATFORM_DESCRIPTOR}; @@ -40,24 +34,24 @@ class query_handle { public: query_handle() = delete; - query_handle(k2::descriptor _rpc_d, int64_t query_id, std::chrono::nanoseconds _deadline, bool _collect_responses_extra_info) noexcept + query_handle(k2::descriptor _rpc_d, int64_t _query_id, std::chrono::nanoseconds _deadline, bool _collect_responses_extra_info) noexcept : rpc_d{_rpc_d}, - query_id{query_id}, + query_id{_query_id}, deadline{_deadline}, collect_responses_extra_info{_collect_responses_extra_info} {} query_handle(query_handle&& other) noexcept : rpc_d{std::exchange(other.rpc_d, k2::INVALID_PLATFORM_DESCRIPTOR)}, query_id{other.query_id}, - deadline{std::exchange(other.deadline, {})}, + deadline{other.deadline}, collect_responses_extra_info{other.collect_responses_extra_info} {} query_handle& operator=(query_handle&& other) noexcept { if (this != std::addressof(other)) { - close(); + drop(); rpc_d = std::exchange(other.rpc_d, k2::INVALID_PLATFORM_DESCRIPTOR); query_id = other.query_id; - deadline = std::exchange(other.deadline, {}); + deadline = other.deadline; collect_responses_extra_info = other.collect_responses_extra_info; } return *this; @@ -68,10 +62,10 @@ class query_handle { query_handle& operator=(const query_handle& other) = delete; ~query_handle() noexcept { - close(); + drop(); } - void close() noexcept { + void drop() noexcept { if (rpc_d != k2::INVALID_PLATFORM_DESCRIPTOR) { k2::free_descriptor(std::exchange(rpc_d, k2::INVALID_PLATFORM_DESCRIPTOR)); } @@ -101,6 +95,6 @@ class query_handle { }; std::expected send_and_get_handle(std::string_view actor, bool collect_responses_extra_info, std::chrono::milliseconds timeout, - int64_t query_id, std::span request_buffer) noexcept; + double timestamp, int64_t query_id, std::span request_buffer) noexcept; } // namespace kphp::rpc diff --git a/runtime-light/stdlib/rpc/rpc-queue-functions.h b/runtime-light/stdlib/rpc/rpc-queue-functions.h index 55a6c7dba7..624cd1d596 100644 --- a/runtime-light/stdlib/rpc/rpc-queue-functions.h +++ b/runtime-light/stdlib/rpc/rpc-queue-functions.h @@ -17,8 +17,8 @@ #include "runtime-light/stdlib/diagnostics/logs.h" #include "runtime-light/stdlib/fork/fork-functions.h" #include "runtime-light/stdlib/rpc/rpc-client-state.h" +#include "runtime-light/stdlib/rpc/rpc-query-handle.h" #include "runtime-light/stdlib/rpc/rpc-queue-state.h" -#include "runtime-light/stdlib/rpc/rpc-request-info.h" #include "runtime-light/stdlib/time/util.h" namespace kphp::rpc { diff --git a/runtime-light/stdlib/time/util.h b/runtime-light/stdlib/time/util.h index 8ea55d136d..4749b958b5 100644 --- a/runtime-light/stdlib/time/util.h +++ b/runtime-light/stdlib/time/util.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "runtime-light/k2-platform/k2-api.h" #include "runtime-light/metaprogramming/concepts.h" From e77636ccef35064acda408e3655e91165e5b35d3 Mon Sep 17 00:00:00 2001 From: Nikita Siniachenko Date: Sun, 21 Jun 2026 21:44:30 +0300 Subject: [PATCH 17/17] k2_rpc_get_response_size() and k2_rpc_fetch_response() now return `EAGAIN` errno if response is not ready --- runtime-light/k2-platform/k2-header.h | 7 +++++-- runtime-light/stdlib/rpc/rpc-api.cpp | 2 +- runtime-light/stdlib/rpc/rpc-query-handle.cpp | 3 +-- runtime-light/stdlib/rpc/rpc-query-handle.h | 1 - runtime-light/stdlib/rpc/rpc-queue-functions.h | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/runtime-light/k2-platform/k2-header.h b/runtime-light/k2-platform/k2-header.h index 6c0ea96d60..b43b38bbc8 100644 --- a/runtime-light/k2-platform/k2-header.h +++ b/runtime-light/k2-platform/k2-header.h @@ -333,23 +333,26 @@ int32_t k2_component_access(size_t name_len, const char* name); int32_t k2_rpc_send(const char* actor_name, size_t actor_name_len, const void* request_ptr, size_t request_size, enum RpcKind rpc_kind, uint64_t* rpc_d); /** - * Get response size for corresponding request of this `rpc_d`. Write 0 to `response_size` if response is not ready. + * Get response size for corresponding request of this `rpc_d`. Write 0 to `response_size` and return `EAGAIN` if response is not ready. * Write response size value to `response_size` if response is ready. * * @return return `0` on success. libc-like `errno` otherwise * * Possible `errno` values: * `EINVAL` => invalid `rpc_d` descriptor, for example, it is unknown descriptor, or not rpc descriptor. + * `EAGAIN` => response is not ready yet. */ int32_t k2_rpc_get_response_size(uint64_t rpc_d, size_t* response_size); /** - * Write response for corresponding request of this `rpc_d` to `buf`. Does nothing if response is not ready. + * Write response for corresponding request of this `rpc_d` to `buf`. Return `EAGAIN` if response is not ready. + * If `buf_size` < response size, then write first `buf_size` bytes of response to `buf`. * * @return return `0` on success. libc-like `errno` otherwise * * Possible `errno` values: * `EINVAL` => invalid `rpc_d` descriptor, for example, it is unknown descriptor, or not rpc descriptor. + * `EAGAIN` => response is not ready yet. */ int32_t k2_rpc_fetch_response(uint64_t rpc_d, void* buf, size_t buf_size); diff --git a/runtime-light/stdlib/rpc/rpc-api.cpp b/runtime-light/stdlib/rpc/rpc-api.cpp index ccf5faeb95..d9fc6430d3 100644 --- a/runtime-light/stdlib/rpc/rpc-api.cpp +++ b/runtime-light/stdlib/rpc/rpc-api.cpp @@ -205,7 +205,7 @@ kphp::coro::task> rpc_tl_query_result_one_impl(int64_t query_id) no co_return TlRpcError::make_error(error.first, std::move(error.second)); } - auto response{*std::move(response_expected)}; // don't check response's emptyness; will throw if it's empty, indicating a fetch error + auto response{*std::move(response_expected)}; // don't check response's emptiness; will throw if it's empty, indicating a fetch error f$rpc_clean(); RpcServerInstanceState::get().tl_fetcher = tl::fetcher{{reinterpret_cast(response.c_str()), response.size()}}; diff --git a/runtime-light/stdlib/rpc/rpc-query-handle.cpp b/runtime-light/stdlib/rpc/rpc-query-handle.cpp index c6016887f5..6d898ad9e3 100644 --- a/runtime-light/stdlib/rpc/rpc-query-handle.cpp +++ b/runtime-light/stdlib/rpc/rpc-query-handle.cpp @@ -17,7 +17,6 @@ namespace kphp::rpc { -// TODO naming ?? std::expected send_and_get_handle(std::string_view actor, bool collect_responses_extra_info, std::chrono::milliseconds timeout, double timestamp, int64_t query_id, std::span request_buffer) noexcept { auto& rpc_client_instance_st{RpcClientInstanceState::get()}; @@ -35,7 +34,7 @@ std::expected send_and_get_handle(std::string_view actor, std::chrono::nanoseconds deadline{timeout_to_deadline(timeout)}; - return {query_handle{std::move(rpc_d), query_id, deadline, collect_responses_extra_info}}; + return {query_handle{rpc_d, query_id, deadline, collect_responses_extra_info}}; } std::expected> query_handle::get_ready_response() noexcept { diff --git a/runtime-light/stdlib/rpc/rpc-query-handle.h b/runtime-light/stdlib/rpc/rpc-query-handle.h index 6409d211a3..a74806a404 100644 --- a/runtime-light/stdlib/rpc/rpc-query-handle.h +++ b/runtime-light/stdlib/rpc/rpc-query-handle.h @@ -24,7 +24,6 @@ namespace kphp::rpc { -// TODO is it appropriate naming? May be query_handle? class query_handle { k2::descriptor rpc_d{k2::INVALID_PLATFORM_DESCRIPTOR}; int64_t query_id; diff --git a/runtime-light/stdlib/rpc/rpc-queue-functions.h b/runtime-light/stdlib/rpc/rpc-queue-functions.h index 624cd1d596..0c894d4813 100644 --- a/runtime-light/stdlib/rpc/rpc-queue-functions.h +++ b/runtime-light/stdlib/rpc/rpc-queue-functions.h @@ -33,7 +33,7 @@ inline void rpc_queue_push(int64_t queue_id, int64_t request_id) noexcept { const auto it_rpc_request_info{rpc_client_instance_st.rpc_query_handles.find(request_id)}; if (it_rpc_request_info == rpc_client_instance_st.rpc_query_handles.end()) [[unlikely]] { - kphp::log::warning("could not find rpc query with id {} in pending requests", queue_id); + kphp::log::warning("could not find rpc query with id {} in pending queries", queue_id); return; }