Skip to content

Commit 061064c

Browse files
committed
Add timeout and retry on rpc loader.
1 parent c36d44f commit 061064c

3 files changed

Lines changed: 87 additions & 16 deletions

File tree

source/loaders/rpc_loader/source/rpc_loader_impl.cpp

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,12 @@ typedef struct loader_impl_rpc_handle_type
6969

7070
typedef struct loader_impl_rpc_function_type
7171
{
72-
loader_impl_rpc_function_type(loader_impl_rpc rpc_impl, const std::string &url, bool is_async, const std::string &func_name) :
73-
rpc_impl(rpc_impl), url(url + (is_async ? "await/" : "call/") + func_name) {}
72+
loader_impl_rpc_function_type(loader_impl_rpc rpc_impl, const std::string &url, bool is_async, const std::string &func_name, const int &timeout) :
73+
rpc_impl(rpc_impl), url(url + (is_async ? "await/" : "call/") + func_name), timeout(timeout) {}
7474

7575
loader_impl_rpc rpc_impl;
7676
std::string url;
77+
int timeout;
7778

7879
} * loader_impl_rpc_function;
7980

@@ -95,7 +96,7 @@ struct rpc_async_context
9596
};
9697

9798
static size_t rpc_loader_impl_write_data(void *buffer, size_t size, size_t nmemb, void *userp);
98-
static int rpc_loader_impl_discover_value(loader_impl_rpc rpc_impl, const std::string &url, value v, context ctx);
99+
static int rpc_loader_impl_discover_value(loader_impl_rpc rpc_impl, const std::string &url, value v, context ctx, const int &timeout);
99100
static int rpc_loader_impl_initialize_types(loader_impl impl, loader_impl_rpc rpc_impl);
100101

101102
size_t rpc_loader_impl_write_data(void *buffer, size_t size, size_t nmemb, void *userp)
@@ -203,6 +204,7 @@ function_return function_rpc_interface_invoke(function func, function_impl impl,
203204
curl_easy_setopt(easy, CURLOPT_POSTFIELDS, buffer);
204205
curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, body_request_size - 1);
205206
curl_easy_setopt(easy, CURLOPT_WRITEDATA, static_cast<loader_impl_rpc_write_data>(&write_data));
207+
curl_easy_setopt(easy, CURLOPT_TIMEOUT_MS, rpc_function->timeout);
206208

207209
CURLcode res;
208210

@@ -423,6 +425,7 @@ function_return function_rpc_interface_await(function func, function_impl impl,
423425
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, rpc_loader_impl_write_data);
424426
curl_easy_setopt(easy, CURLOPT_WRITEDATA, static_cast<loader_impl_rpc_write_data>(&async_ctx->write_data));
425427
curl_easy_setopt(easy, CURLOPT_PRIVATE, async_ctx);
428+
curl_easy_setopt(easy, CURLOPT_TIMEOUT_MS, rpc_function->timeout);
426429

427430
/* COPYPOSTFIELDS copies data internally, safe to free buffer after */
428431
curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE, (long)(body_request_size - 1));
@@ -744,7 +747,7 @@ int rpc_loader_impl_clear(loader_impl impl, loader_handle handle)
744747
return 0;
745748
}
746749

747-
int rpc_loader_impl_discover_value(loader_impl_rpc rpc_impl, const std::string &url, void *v, context ctx)
750+
int rpc_loader_impl_discover_value(loader_impl_rpc rpc_impl, const std::string &url, void *v, context ctx, const int &timeout)
748751
{
749752
metacall::map_typed<std::string, metacall::array> inspect(v);
750753

@@ -763,7 +766,7 @@ int rpc_loader_impl_discover_value(loader_impl_rpc rpc_impl, const std::string &
763766
const auto &signature_map = func_map["signature"].as<metacall::map_typed<std::string, metacall::value>>();
764767
const auto &args = signature_map["args"].as<metacall::array>();
765768

766-
loader_impl_rpc_function rpc_func = new loader_impl_rpc_function_type(rpc_impl, url, is_async, func_name);
769+
loader_impl_rpc_function rpc_func = new loader_impl_rpc_function_type(rpc_impl, url, is_async, func_name, timeout);
767770

768771
function f = function_create(func_name.c_str(), args.size(), rpc_func, &function_rpc_singleton);
769772
signature s = function_signature(f);
@@ -810,26 +813,33 @@ int rpc_loader_impl_discover(loader_impl impl, loader_handle handle, context ctx
810813

811814
for (const auto &config : rpc_handle->configs)
812815
{
813-
auto urls = config["urls"].as<metacall::array>();
814-
// TODO:
815-
// auto timeout = config["timeout"].as<int>();
816-
// auto retry = config["retry"].as<int>();
816+
const auto urls = config["urls"].as<metacall::array>();
817+
const auto timeout = config["timeout"].as<int>();
818+
const auto retry = config["retry"].as<int>();
817819

818820
for (const auto &url : urls)
819821
{
820822
loader_impl_rpc_write_data_type write_data;
821823
const auto &url_str = url.as<std::string>();
822824
std::string base_url = url_str.back() != '/' ? url_str + '/' : url_str;
823825
std::string inspect_url = base_url + "inspect";
826+
int retry_count = 0;
824827
CURLcode res;
825828

826-
curl_easy_setopt(rpc_impl->discover_curl, CURLOPT_URL, inspect_url.c_str());
827-
curl_easy_setopt(rpc_impl->discover_curl, CURLOPT_WRITEDATA, static_cast<loader_impl_rpc_write_data>(&write_data));
829+
do
830+
{
831+
curl_easy_setopt(rpc_impl->discover_curl, CURLOPT_URL, inspect_url.c_str());
832+
curl_easy_setopt(rpc_impl->discover_curl, CURLOPT_WRITEDATA, static_cast<loader_impl_rpc_write_data>(&write_data));
833+
curl_easy_setopt(rpc_impl->discover_curl, CURLOPT_TIMEOUT_MS, timeout);
834+
835+
log_write("metacall", metacall::detail::LOG_LEVEL_DEBUG, "Trying to connect to %s retry %d/%d with timeout %d", inspect_url.c_str(), retry_count + 1, retry, timeout);
836+
837+
/* Skip curl_multi_perform use-of-uninitialized-value from heap allocation of curl_mvaprintf in uninstrumented libcurl */
838+
memory_sanitizer_uninstrumented({
839+
res = curl_easy_perform(rpc_impl->discover_curl);
840+
});
828841

829-
/* Skip curl_multi_perform use-of-uninitialized-value from heap allocation of curl_mvaprintf in uninstrumented libcurl */
830-
memory_sanitizer_uninstrumented({
831-
res = curl_easy_perform(rpc_impl->discover_curl);
832-
});
842+
} while (retry_count++ < retry && res != CURLE_OK);
833843

834844
if (res != CURLE_OK)
835845
{
@@ -849,7 +859,7 @@ int rpc_loader_impl_discover(loader_impl impl, loader_handle handle, context ctx
849859
}
850860

851861
/* Discover the functions from the inspect value */
852-
if (rpc_loader_impl_discover_value(rpc_impl, base_url, inspect_value, ctx) != 0)
862+
if (rpc_loader_impl_discover_value(rpc_impl, base_url, inspect_value, ctx, timeout) != 0)
853863
{
854864
log_write("metacall", metacall::detail::LOG_LEVEL_ERROR, "Invalid inspect value discover from API endpoint %s", url_str.c_str());
855865
return 1;

source/tests/metacall_rpc_test/source/metacall_rpc_test.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,5 +536,55 @@ TEST_F(metacall_rpc_test, EmptyShutdown)
536536
#endif
537537

538538
/* Should return cleanly with no crash or hang */
539+
metacall_destroy();
540+
}
541+
542+
TEST_F(metacall_rpc_test, TimeoutFail)
543+
{
544+
ASSERT_EQ((int)0, (int)metacall_initialize());
545+
546+
#if defined(OPTION_BUILD_LOADERS_RPC)
547+
{
548+
static const char buffer[] =
549+
"{\n"
550+
" \"urls\": [\n"
551+
" \"http://localhost:6094/timeoutfail/example/v1\"\n"
552+
" ],\n"
553+
" \"timeout\": 500,\n"
554+
" \"retry\": 5\n"
555+
"}\n";
556+
557+
void *memory_handle = NULL;
558+
559+
ASSERT_NE((int)0, (int)metacall_load_from_memory("rpc", buffer, sizeof(buffer), &memory_handle));
560+
ASSERT_EQ(memory_handle, nullptr);
561+
}
562+
#endif
563+
564+
metacall_destroy();
565+
}
566+
567+
TEST_F(metacall_rpc_test, TimeoutFailSome)
568+
{
569+
ASSERT_EQ((int)0, (int)metacall_initialize());
570+
571+
#if defined(OPTION_BUILD_LOADERS_RPC)
572+
{
573+
static const char buffer[] =
574+
"{\n"
575+
" \"urls\": [\n"
576+
" \"http://localhost:6094/timeoutfailsome/example/v1\"\n"
577+
" ],\n"
578+
" \"timeout\": 2000,\n"
579+
" \"retry\": 10\n"
580+
"}\n";
581+
582+
void *memory_handle = NULL;
583+
584+
ASSERT_EQ((int)0, (int)metacall_load_from_memory("rpc", buffer, sizeof(buffer), &memory_handle));
585+
ASSERT_NE(memory_handle, nullptr);
586+
}
587+
#endif
588+
539589
metacall_destroy();
540590
}

source/tests/metacall_rpc_test/source/server.js

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)