Skip to content

Commit 0c8a1a0

Browse files
authored
Revert "Merges requests in sync CallApi (#1166)" (#1180)
This reverts commit 0d9351d. Changes have to be reworked. Now they cause a sleep in network thread on retry and has some issues due to merge Relates-To: OLPEDGE-2417 Signed-off-by: Iuliia Moroz <[email protected]>
1 parent 77d5762 commit 0c8a1a0

File tree

13 files changed

+367
-240
lines changed

13 files changed

+367
-240
lines changed

olp-cpp-sdk-core/include/olp/core/client/HttpResponse.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ class CORE_API HttpResponse {
135135
// solution as a second step.
136136
response.str(other.response.str());
137137
}
138-
network_statistics_ = other.GetNetworkStatistics();
139138
}
140139

141140
/**
@@ -151,7 +150,6 @@ class CORE_API HttpResponse {
151150
status = other.status;
152151
response << other.response.rdbuf();
153152
headers = other.headers;
154-
network_statistics_ = other.GetNetworkStatistics();
155153
}
156154

157155
return *this;

olp-cpp-sdk-core/src/client/OlpClient.cpp

Lines changed: 170 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2019-2021 HERE Europe B.V.
2+
* Copyright (C) 2019-2020 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,7 +49,6 @@ struct RequestSettings {
4949
std::chrono::milliseconds accumulated_wait_time{0};
5050
std::chrono::milliseconds current_backdown_period{0};
5151
const std::chrono::milliseconds max_wait_time{0};
52-
olp::client::NetworkStatistics accumulated_statistics;
5352
};
5453
} // namespace
5554

@@ -91,6 +90,10 @@ HttpResponse ToHttpResponse(const http::SendOutcome& outcome) {
9190
http::ErrorCodeToString(outcome.GetErrorCode())};
9291
}
9392

93+
bool StatusSuccess(int status) {
94+
return status >= 0 && status < http::HttpStatusCode::BAD_REQUEST;
95+
}
96+
9497
bool CaseInsensitiveCompare(const std::string& str1, const std::string& str2) {
9598
return (str1.size() == str2.size()) &&
9699
std::equal(str1.begin(), str1.end(), str2.begin(),
@@ -134,13 +137,15 @@ void ExecuteSingleRequest(const std::shared_ptr<http::Network>& network,
134137
request, response_body,
135138
[=](const http::NetworkResponse& response) {
136139
auto status = response.GetStatus();
137-
138-
HttpResponse http_response{status, std::move(*response_body),
139-
std::move(*headers)};
140-
http_response.SetNetworkStatistics(
141-
{response.GetBytesUploaded(), response.GetBytesDownloaded()});
142-
143-
callback(response.GetRequestId(), std::move(http_response));
140+
if (!StatusSuccess(status)) {
141+
response_body->str(
142+
response.GetError().empty()
143+
? "Error occurred, please check HTTP status code"
144+
: response.GetError());
145+
}
146+
147+
callback(response.GetRequestId(),
148+
{status, std::move(*response_body), std::move(*headers)});
144149
},
145150
[=](std::string key, std::string value) {
146151
headers->emplace_back(std::move(key), std::move(value));
@@ -153,7 +158,6 @@ void ExecuteSingleRequest(const std::shared_ptr<http::Network>& network,
153158
}
154159

155160
id = send_outcome.GetRequestId();
156-
157161
return CancellationToken([=] { network->Cancel(id); });
158162
};
159163

@@ -177,7 +181,6 @@ NetworkCallbackType GetRetryCallback(
177181
const NetworkRequestPtr& request) {
178182
return [=](const http::RequestId request_id, HttpResponse response) mutable {
179183
++settings->current_try;
180-
settings->accumulated_statistics += response.GetNetworkStatistics();
181184

182185
if (CheckRetryCondition(*settings, retry_settings, response)) {
183186
// Response is either successull or retries count/time expired
@@ -187,11 +190,9 @@ NetworkCallbackType GetRetryCallback(
187190
"Wrong response received, pending_request=%" PRIu64
188191
", request_id=%" PRIu64,
189192
pending_request->GetRequestId(), request_id);
193+
return;
190194
}
191195

192-
// Set accumulated statistics for all retries
193-
response.SetNetworkStatistics(settings->accumulated_statistics);
194-
195196
if (merge) {
196197
const auto& url = request->GetUrl();
197198
pending_requests->OnRequestCompleted(request_id, url,
@@ -250,9 +251,79 @@ http::NetworkRequest::HttpVerb GetHttpVerb(const std::string& verb) {
250251
return http::NetworkRequest::HttpVerb::GET;
251252
}
252253

254+
HttpResponse SendRequest(const http::NetworkRequest& request,
255+
const olp::client::OlpClientSettings& settings,
256+
const olp::client::RetrySettings& retry_settings,
257+
client::CancellationContext context) {
258+
struct ResponseData {
259+
Condition condition;
260+
http::NetworkResponse response{kCancelledErrorResponse};
261+
http::Headers headers;
262+
};
263+
264+
auto response_data = std::make_shared<ResponseData>();
265+
auto response_body = std::make_shared<std::stringstream>();
266+
http::SendOutcome outcome{http::ErrorCode::CANCELLED_ERROR};
267+
const auto timeout = std::chrono::seconds(retry_settings.timeout);
268+
269+
context.ExecuteOrCancelled(
270+
[&]() {
271+
outcome = settings.network_request_handler->Send(
272+
request, response_body,
273+
[response_data](http::NetworkResponse response) {
274+
response_data->response = std::move(response);
275+
response_data->condition.Notify();
276+
},
277+
[response_data](std::string key, std::string value) {
278+
response_data->headers.emplace_back(std::move(key),
279+
std::move(value));
280+
});
281+
282+
if (!outcome.IsSuccessful()) {
283+
OLP_SDK_LOG_WARNING_F(kLogTag,
284+
"SendRequest: sending request failed, url=%s",
285+
request.GetUrl().c_str());
286+
return CancellationToken();
287+
}
288+
289+
auto request_handler = settings.network_request_handler;
290+
auto request_id = outcome.GetRequestId();
291+
292+
return CancellationToken([=]() {
293+
request_handler->Cancel(request_id);
294+
response_data->condition.Notify();
295+
});
296+
},
297+
[&]() { response_data->condition.Notify(); });
298+
299+
if (!outcome.IsSuccessful()) {
300+
return ToHttpResponse(outcome);
301+
}
302+
303+
if (!response_data->condition.Wait(timeout)) {
304+
OLP_SDK_LOG_WARNING_F(kLogTag, "Request %" PRIu64 " timed out!",
305+
outcome.GetRequestId());
306+
context.CancelOperation();
307+
return ToHttpResponse(kTimeoutErrorResponse);
308+
}
309+
310+
if (context.IsCancelled()) {
311+
return ToHttpResponse(kCancelledErrorResponse);
312+
}
313+
314+
HttpResponse response{response_data->response.GetStatus(),
315+
std::move(*response_body),
316+
std::move(response_data->headers)};
317+
318+
response.SetNetworkStatistics(GetStatistics(response_data->response));
319+
320+
return response;
321+
}
322+
253323
bool IsPending(const PendingUrlRequestPtr& request) {
254324
// A request is pending when it already triggered a Network call
255-
return request && request->IsPending();
325+
return request &&
326+
request->GetRequestId() != PendingUrlRequest::kInvalidRequestId;
256327
}
257328

258329
} // namespace
@@ -466,49 +537,103 @@ HttpResponse OlpClient::OlpClientImpl::CallApi(
466537
std::string path, std::string method,
467538
OlpClient::ParametersType query_params,
468539
OlpClient::ParametersType header_params,
469-
OlpClient::ParametersType forms_params,
540+
OlpClient::ParametersType /*forms_params*/,
470541
OlpClient::RequestBodyType post_body, std::string content_type,
471542
CancellationContext context) const {
472543
if (!settings_.network_request_handler) {
473544
return HttpResponse(static_cast<int>(olp::http::ErrorCode::OFFLINE_ERROR),
474545
"Network request handler is empty.");
475546
}
476547

477-
struct ResponseData {
478-
Condition condition;
479-
HttpResponse response = ToHttpResponse(kCancelledErrorResponse);
480-
};
548+
const auto& retry_settings = settings_.retry_settings;
549+
auto network_settings =
550+
http::NetworkSettings()
551+
.WithTransferTimeout(retry_settings.timeout)
552+
.WithConnectionTimeout(retry_settings.timeout)
553+
.WithProxySettings(
554+
settings_.proxy_settings.value_or(http::NetworkProxySettings()));
481555

482-
auto response_data = std::make_shared<ResponseData>();
556+
auto network_request = http::NetworkRequest(
557+
utils::Url::Construct(GetBaseUrl(), path, query_params));
483558

484-
auto callback = [=](HttpResponse http_response) {
485-
response_data->response = std::move(http_response);
486-
response_data->condition.Notify();
487-
};
559+
network_request.WithVerb(GetHttpVerb(method))
560+
.WithBody(std::move(post_body))
561+
.WithSettings(std::move(network_settings));
488562

489-
context.ExecuteOrCancelled(
490-
[&]() -> client::CancellationToken {
491-
auto token = CallApi(path, method, query_params, header_params,
492-
forms_params, post_body, content_type, callback);
493-
return CancellationToken([=] {
494-
token.Cancel();
495-
response_data->condition.Notify();
496-
});
497-
},
498-
[=]() { response_data->condition.Notify(); });
563+
for (const auto& header : default_headers_) {
564+
network_request.WithHeader(header.first, header.second);
565+
}
499566

500-
// We have to wait settings_.retry_settings.timeout for the initial request.
501-
// And double this time for retries.
502-
const auto timeout =
503-
std::chrono::seconds(settings_.retry_settings.timeout *
504-
(settings_.retry_settings.max_attempts ? 2 : 1));
505-
if (!response_data->condition.Wait(timeout)) {
506-
context.CancelOperation();
507-
return ToHttpResponse(kTimeoutErrorResponse);
567+
std::string custom_user_agent;
568+
for (const auto& header : header_params) {
569+
// Merge all User-Agent headers into one header.
570+
// This is required for (at least) iOS network implementation,
571+
// which uses headers dictionary without any duplicates.
572+
// User agents entries are usually separated by a whitespace, e.g.
573+
// Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Firefox/47.0
574+
if (CaseInsensitiveCompare(header.first, http::kUserAgentHeader)) {
575+
custom_user_agent += header.second + std::string(" ");
576+
} else {
577+
network_request.WithHeader(header.first, header.second);
578+
}
508579
}
509580

510-
return response_data->response;
511-
} // namespace client
581+
custom_user_agent += http::kOlpSdkUserAgent;
582+
network_request.WithHeader(http::kUserAgentHeader, custom_user_agent);
583+
584+
if (!content_type.empty()) {
585+
network_request.WithHeader(http::kContentTypeHeader, content_type);
586+
}
587+
588+
auto backdown_period =
589+
std::chrono::milliseconds(retry_settings.initial_backdown_period);
590+
591+
AddBearer(query_params.empty(), network_request);
592+
593+
auto response =
594+
SendRequest(network_request, settings_, retry_settings, context);
595+
596+
NetworkStatistics accumulated_statistics = response.GetNetworkStatistics();
597+
598+
// Make sure that we don't wait longer than `timeout` in retry settings
599+
auto accumulated_wait_time = backdown_period;
600+
const auto max_wait_time =
601+
std::chrono::duration_cast<std::chrono::milliseconds>(
602+
std::chrono::seconds(retry_settings.timeout));
603+
for (int i = 1; i <= retry_settings.max_attempts && !context.IsCancelled() &&
604+
accumulated_wait_time < max_wait_time;
605+
i++) {
606+
if (StatusSuccess(response.status)) {
607+
return response;
608+
}
609+
610+
if (!retry_settings.retry_condition(response)) {
611+
return response;
612+
}
613+
614+
// do the periodical sleep and check for cancellation status in between.
615+
auto duration_to_sleep =
616+
std::min(backdown_period, max_wait_time - accumulated_wait_time);
617+
accumulated_wait_time += duration_to_sleep;
618+
619+
while (duration_to_sleep.count() > 0 && !context.IsCancelled()) {
620+
const auto sleep_ms =
621+
std::min(std::chrono::milliseconds(1000), duration_to_sleep);
622+
std::this_thread::sleep_for(sleep_ms);
623+
duration_to_sleep -= sleep_ms;
624+
}
625+
626+
backdown_period = CalculateNextWaitTime(retry_settings, i);
627+
response = SendRequest(network_request, settings_, retry_settings, context);
628+
629+
// In case we retry, accumulate the stats
630+
accumulated_statistics += response.GetNetworkStatistics();
631+
}
632+
633+
response.SetNetworkStatistics(accumulated_statistics);
634+
635+
return response;
636+
}
512637

513638
OlpClient::OlpClient() : impl_(std::make_shared<OlpClientImpl>()){};
514639
OlpClient::OlpClient(const OlpClientSettings& settings, std::string base_url)

olp-cpp-sdk-core/src/client/PendingUrlRequests.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2020-2021 HERE Europe B.V.
2+
* Copyright (C) 2020 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -90,13 +90,6 @@ class PendingUrlRequest {
9090
/// Will be called when the Network response arrives.
9191
void OnRequestCompleted(HttpResponse response);
9292

93-
/// Returns request's status. It is 'true' for merged requests or for requests
94-
/// that are waiting for the Network callback
95-
bool IsPending() const {
96-
std::lock_guard<std::mutex> lock(mutex_);
97-
return http_request_id_ != kInvalidRequestId || callbacks_.size() > 1u;
98-
}
99-
10093
private:
10194
/// Keeping the class thread safe.
10295
mutable std::mutex mutex_;

olp-cpp-sdk-core/tests/client/ApiLookupClientImplTest.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2020-2021 HERE Europe B.V.
2+
* Copyright (C) 2020 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -316,18 +316,17 @@ TEST_F(ApiLookupClientImplTest, LookupApi) {
316316
{
317317
SCOPED_TRACE("Network request cancelled by user");
318318
client::CancellationContext context;
319-
std::thread cancel_job;
320319
EXPECT_CALL(*network_, Send(IsGetRequest(lookup_url), _, _, _, _))
321320
.Times(1)
322-
.WillOnce([&](olp::http::NetworkRequest /*request*/,
321+
.WillOnce([=, &context](
322+
olp::http::NetworkRequest /*request*/,
323323
olp::http::Network::Payload /*payload*/,
324324
olp::http::Network::Callback /*callback*/,
325325
olp::http::Network::HeaderCallback /*header_callback*/,
326326
olp::http::Network::DataCallback /*data_callback*/)
327327
-> olp::http::SendOutcome {
328328
// spawn a 'user' response of cancelling
329-
cancel_job =
330-
std::thread([=, &context]() { context.CancelOperation(); });
329+
std::thread([&context]() { context.CancelOperation(); }).detach();
331330

332331
// note no network response thread spawns
333332

@@ -339,7 +338,6 @@ TEST_F(ApiLookupClientImplTest, LookupApi) {
339338
client::ApiLookupClientImpl client(catalog_hrn, settings_);
340339
auto response = client.LookupApi(service_name, service_version,
341340
client::FetchOptions::OnlineOnly, context);
342-
cancel_job.join();
343341

344342
EXPECT_FALSE(response.IsSuccessful());
345343
EXPECT_EQ(response.GetError().GetErrorCode(), client::ErrorCode::Cancelled);

olp-cpp-sdk-dataservice-read/src/repositories/DataRepository.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <olp/core/logging/Log.h>
2929
#include "CatalogRepository.h"
3030
#include "DataCacheRepository.h"
31+
#include "NamedMutex.h"
3132
#include "PartitionsCacheRepository.h"
3233
#include "PartitionsRepository.h"
3334
#include "generated/api/BlobApi.h"
@@ -131,6 +132,14 @@ BlobApi::DataResponse DataRepository::GetBlobData(
131132
return {{client::ErrorCode::PreconditionFailed, "Data handle is missing"}};
132133
}
133134

135+
NamedMutex mutex(catalog_.ToString() + layer + *data_handle);
136+
std::unique_lock<NamedMutex> lock(mutex, std::defer_lock);
137+
138+
// If we are not planning to go online or access the cache, do not lock.
139+
if (fetch_option != CacheOnly && fetch_option != OnlineOnly) {
140+
lock.lock();
141+
}
142+
134143
repository::DataCacheRepository repository(
135144
catalog_, settings_.cache, settings_.default_cache_expiration);
136145

0 commit comments

Comments
 (0)