Skip to content

Commit 082597a

Browse files
committed
Unit test http retries with exponential backoff
1 parent cec3b98 commit 082597a

File tree

3 files changed

+72
-5
lines changed

3 files changed

+72
-5
lines changed

ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ namespace client
3939
{
4040
namespace curl
4141
{
42-
const std::chrono::milliseconds default_http_conn_timeout(5000); // ms
43-
const std::string http_status_regexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*";
44-
const std::string http_header_regexp = "(.*)\\: (.*)\\n*";
42+
const std::chrono::milliseconds kDefaultHttpConnTimeout(5000); // ms
43+
const std::string kHttpStatusRegexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*";
44+
const std::string kHttpHeaderRegexp = "(.*)\\: (.*)\\n*";
4545

4646
class HttpClient;
4747
class Session;
@@ -161,7 +161,7 @@ class HttpOperation
161161
opentelemetry::ext::http::client::Compression::kNone,
162162
// Default connectivity and response size options
163163
bool is_raw_response = false,
164-
std::chrono::milliseconds http_conn_timeout = default_http_conn_timeout,
164+
std::chrono::milliseconds http_conn_timeout = kDefaultHttpConnTimeout,
165165
bool reuse_connection = false,
166166
bool is_log_enabled = false,
167167
const opentelemetry::ext::http::client::RetryPolicy &retry_policy = {});

ext/src/http/client/curl/http_operation_curl.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1305,7 +1305,7 @@ Headers HttpOperation::GetResponseHeaders()
13051305
// switching to string comparison. Need to debug and revert back.
13061306

13071307
/*std::smatch match;
1308-
std::regex http_headers_regex(http_header_regexp);
1308+
std::regex http_headers_regex(kHttpHeaderRegexp);
13091309
if (std::regex_search(header, match, http_headers_regex))
13101310
result.insert(std::pair<nostd::string_view, nostd::string_view>(
13111311
static_cast<nostd::string_view>(match[1]), static_cast<nostd::string_view>(match[2])));

ext/test/http/curl_http_test.cc

+67
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
#include <curl/curlver.h>
5+
#include <gmock/gmock.h>
56
#include <gtest/gtest.h>
67
#include <string.h>
78
#include <atomic>
@@ -108,6 +109,17 @@ class FinishInCallbackHandler : public CustomEventHandler
108109
std::shared_ptr<http_client::Session> session_;
109110
};
110111

112+
class RetryEventHandler : public CustomEventHandler
113+
{
114+
void OnResponse(http_client::Response &response) noexcept override
115+
{
116+
ASSERT_EQ(429, response.GetStatusCode());
117+
ASSERT_EQ(response.GetBody().size(), 0);
118+
is_called_.store(true, std::memory_order_release);
119+
got_response_.store(true, std::memory_order_release);
120+
}
121+
};
122+
111123
class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRequestCallback
112124
{
113125
protected:
@@ -139,6 +151,7 @@ class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRe
139151
server_.addHandler("/simple/", *this);
140152
server_.addHandler("/get/", *this);
141153
server_.addHandler("/post/", *this);
154+
server_.addHandler("/retry/", *this);
142155
server_.start();
143156
is_running_ = true;
144157
}
@@ -170,6 +183,13 @@ class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRe
170183
response.body = "{'k1':'v1', 'k2':'v2', 'k3':'v3'}";
171184
response_status = 200;
172185
}
186+
else if (request.uri == "/retry/")
187+
{
188+
std::unique_lock<std::mutex> lk1(mtx_requests);
189+
received_requests_.push_back(request);
190+
response.headers["Content-Type"] = "text/plain";
191+
response_status = 429;
192+
}
173193

174194
cv_got_events.notify_one();
175195

@@ -330,6 +350,52 @@ TEST_F(BasicCurlHttpTests, CurlHttpOperations)
330350
delete handler;
331351
}
332352

353+
TEST_F(BasicCurlHttpTests, ExponentialBackoffRetry)
354+
{
355+
using ::testing::AllOf;
356+
using ::testing::Gt;
357+
using ::testing::Lt;
358+
359+
RetryEventHandler handler;
360+
http_client::HttpSslOptions no_ssl;
361+
http_client::Body body;
362+
http_client::Headers headers;
363+
http_client::Compression compression = http_client::Compression::kNone;
364+
http_client::RetryPolicy retry_policy = {4, 1.0f, 5.0f, 2.0f};
365+
366+
curl::HttpOperation operation(http_client::Method::Post, "http://127.0.0.1:19000/retry/", no_ssl,
367+
&handler, headers, body, compression, false,
368+
curl::kDefaultHttpConnTimeout, false, false, retry_policy);
369+
370+
auto first_attempt_time = std::chrono::system_clock::now();
371+
ASSERT_EQ(CURLE_OK, operation.Send());
372+
ASSERT_TRUE(operation.IsRetryable());
373+
ASSERT_THAT(
374+
operation.NextRetryTime().time_since_epoch().count(),
375+
AllOf(Gt((first_attempt_time + std::chrono::milliseconds{750}).time_since_epoch().count()),
376+
Lt((first_attempt_time + std::chrono::milliseconds{1250}).time_since_epoch().count())));
377+
378+
auto second_attempt_time = std::chrono::system_clock::now();
379+
ASSERT_EQ(CURLE_OK, operation.Send());
380+
ASSERT_TRUE(operation.IsRetryable());
381+
ASSERT_THAT(
382+
operation.NextRetryTime().time_since_epoch().count(),
383+
AllOf(
384+
Gt((second_attempt_time + std::chrono::milliseconds{1550}).time_since_epoch().count()),
385+
Lt((second_attempt_time + std::chrono::milliseconds{2450}).time_since_epoch().count())));
386+
387+
auto third_attempt_time = std::chrono::system_clock::now();
388+
ASSERT_EQ(CURLE_OK, operation.Send());
389+
ASSERT_TRUE(operation.IsRetryable());
390+
ASSERT_THAT(
391+
operation.NextRetryTime().time_since_epoch().count(),
392+
AllOf(Gt((third_attempt_time + std::chrono::milliseconds{3150}).time_since_epoch().count()),
393+
Lt((third_attempt_time + std::chrono::milliseconds{4850}).time_since_epoch().count())));
394+
395+
ASSERT_EQ(CURLE_OK, operation.Send());
396+
ASSERT_FALSE(operation.IsRetryable());
397+
}
398+
333399
TEST_F(BasicCurlHttpTests, SendGetRequestSync)
334400
{
335401
received_requests_.clear();
@@ -559,6 +625,7 @@ TEST_F(BasicCurlHttpTests, ElegantQuitQuick)
559625
ASSERT_TRUE(handler->is_called_);
560626
ASSERT_TRUE(handler->got_response_);
561627
}
628+
562629
TEST_F(BasicCurlHttpTests, BackgroundThreadWaitMore)
563630
{
564631
{

0 commit comments

Comments
 (0)