1919
2020#include " ApiLookupClientImpl.h"
2121
22+ #include < olp/core/client/HRN.h>
2223#include < olp/core/logging/Log.h>
2324#include " client/api/PlatformApi.h"
2425#include " client/api/ResourcesApi.h"
@@ -29,6 +30,8 @@ namespace client {
2930
3031namespace {
3132constexpr auto kLogTag = " ApiLookupClientImpl" ;
33+ constexpr time_t kLookupApiDefaultExpiryTime = 3600 ;
34+ constexpr time_t kLookupApiShortExpiryTime = 300 ;
3235
3336std::string FindApi (const Apis& apis, const std::string& service,
3437 const std::string& version) {
@@ -42,6 +45,14 @@ std::string FindApi(const Apis& apis, const std::string& service,
4245 return it->GetBaseUrl ();
4346}
4447
48+ OlpClient CreateClient (const std::string& base_url,
49+ const OlpClientSettings& settings) {
50+ OlpClient client;
51+ client.SetBaseUrl (base_url);
52+ client.SetSettings (settings);
53+ return client;
54+ }
55+
4556olp::client::OlpClient GetStaticUrl (
4657 const olp::client::HRN& catalog,
4758 const olp::client::OlpClientSettings& settings) {
@@ -50,25 +61,37 @@ olp::client::OlpClient GetStaticUrl(
5061 auto url = provider (catalog);
5162 if (!url.empty ()) {
5263 url += " /catalogs/" + catalog.ToCatalogHRNString ();
53- OlpClient result_client;
54- result_client.SetBaseUrl (url);
55- result_client.SetSettings (settings);
56- return result_client;
64+ return CreateClient (url, settings);
5765 }
5866 }
5967
6068 return {};
6169}
6270
71+ ApiLookupClient::LookupApiResponse NotFoundInCacheError () {
72+ return ApiError (client::ErrorCode::NotFound,
73+ " CacheOnly: resource not found in cache" );
74+ }
75+
76+ ApiLookupClient::LookupApiResponse ServiceNotAvailable () {
77+ return ApiError (client::ErrorCode::ServiceUnavailable,
78+ " Service/Version not available for given HRN" );
79+ }
80+
81+ std::string ClientCacheKey (const std::string& service,
82+ const std::string& service_version) {
83+ return service + service_version;
84+ }
6385} // namespace
6486
6587ApiLookupClientImpl::ApiLookupClientImpl (const HRN& catalog,
6688 const OlpClientSettings& settings)
67- : catalog_(catalog), settings_(settings) {
89+ : catalog_(catalog),
90+ catalog_string_ (catalog_.ToString()),
91+ settings_(settings) {
6892 auto provider = settings_.api_lookup_settings .lookup_endpoint_provider ;
6993 const auto & base_url = provider (catalog_.GetPartition ());
70- lookup_client_.SetBaseUrl (base_url);
71- lookup_client_.SetSettings (settings_);
94+ lookup_client_ = CreateClient (base_url, settings_);
7295}
7396
7497ApiLookupClient::LookupApiResponse ApiLookupClientImpl::LookupApi (
@@ -79,68 +102,54 @@ ApiLookupClient::LookupApiResponse ApiLookupClientImpl::LookupApi(
79102 return result_client;
80103 }
81104
82- repository::ApiCacheRepository repository (catalog_, settings_.cache );
83- const auto hrn = catalog_.ToCatalogHRNString ();
84-
85105 if (options != OnlineOnly && options != CacheWithUpdate) {
86- auto url = repository.Get (service, service_version);
87- if (url) {
88- OLP_SDK_LOG_DEBUG_F (kLogTag , " LookupApi(%s/%s) found in cache, hrn='%s'" ,
89- service.c_str (), service_version.c_str (),
90- hrn.c_str ());
91- result_client.SetBaseUrl (*url);
92- result_client.SetSettings (settings_);
93- return result_client;
106+ auto client = GetCachedClient (service, service_version);
107+ if (client) {
108+ return *client;
94109 } else if (options == CacheOnly) {
95- return {{client::ErrorCode::NotFound,
96- " CacheOnly: resource not found in cache" }};
110+ return NotFoundInCacheError ();
97111 }
98112 }
99113
100- OLP_SDK_LOG_DEBUG_F (kLogTag ,
101- " LookupApi(%s/%s) cache miss, requesting, hrn='%s'" ,
102- service.c_str (), service_version.c_str (), hrn.c_str ());
103-
104114 PlatformApi::ApisResponse api_response;
105115 if (service == " config" ) {
106116 api_response = PlatformApi::GetApis (lookup_client_, context);
107117 } else {
108- api_response = ResourcesApi::GetApis (lookup_client_, hrn, context);
118+ api_response =
119+ ResourcesApi::GetApis (lookup_client_, catalog_string_, context);
109120 }
110121
111122 if (!api_response.IsSuccessful ()) {
112- OLP_SDK_LOG_WARNING_F (kLogTag ,
113- " LookupApi(%s/%s) unsuccessful, hrn='%s', error='%s'" ,
114- service.c_str (), service_version.c_str (), hrn .c_str (),
115- api_response.GetError ().GetMessage ().c_str ());
123+ OLP_SDK_LOG_WARNING_F (
124+ kLogTag , " LookupApi(%s/%s) unsuccessful, hrn='%s', error='%s'" ,
125+ service.c_str (), service_version.c_str (), catalog_string_ .c_str (),
126+ api_response.GetError ().GetMessage ().c_str ());
116127 return api_response.GetError ();
117128 }
118129
119130 const auto & api_result = api_response.GetResult ();
120131 if (options != OnlineOnly && options != CacheWithUpdate) {
121- for (const auto & service_api : api_result.first ) {
122- repository.Put (service_api.GetApi (), service_api.GetVersion (),
123- service_api.GetBaseUrl (), api_result.second );
124- }
132+ PutToDiskCache (api_result);
125133 }
126134
127135 auto url = FindApi (api_result.first , service, service_version);
128136 if (url.empty ()) {
129137 OLP_SDK_LOG_WARNING_F (
130138 kLogTag , " LookupApi(%s/%s) service not found, hrn='%s'" ,
131- service.c_str (), service_version.c_str (), hrn .c_str ());
139+ service.c_str (), service_version.c_str (), catalog_string_ .c_str ());
132140
133- return {{client::ErrorCode::ServiceUnavailable,
134- " Service/Version not available for given HRN" }};
141+ return ServiceNotAvailable ();
135142 }
136143
137- OLP_SDK_LOG_DEBUG_F (
138- kLogTag , " LookupApi(%s/%s) found, hrn='%s', service_url='%s'" ,
139- service.c_str (), service_version.c_str (), hrn.c_str (), url.c_str ());
144+ OLP_SDK_LOG_DEBUG_F (kLogTag ,
145+ " LookupApi(%s/%s) found, hrn='%s', service_url='%s'" ,
146+ service.c_str (), service_version.c_str (),
147+ catalog_string_.c_str (), url.c_str ());
140148
141- result_client.SetBaseUrl (url);
142- result_client.SetSettings (settings_);
143- return result_client;
149+ const auto expiration = api_result.second ;
150+
151+ return CreateAndCacheClient (url, ClientCacheKey (service, service_version),
152+ expiration);
144153}
145154
146155CancellationToken ApiLookupClientImpl::LookupApi (
@@ -152,76 +161,124 @@ CancellationToken ApiLookupClientImpl::LookupApi(
152161 return CancellationToken ();
153162 }
154163
155- repository::ApiCacheRepository repository (catalog_, settings_.cache );
156- const auto hrn = catalog_.ToCatalogHRNString ();
157-
158164 if (options != OnlineOnly && options != CacheWithUpdate) {
159- auto url = repository.Get (service, service_version);
160- if (url) {
161- OLP_SDK_LOG_DEBUG_F (kLogTag , " LookupApi(%s/%s) found in cache, hrn='%s'" ,
162- service.c_str (), service_version.c_str (),
163- hrn.c_str ());
164- result_client.SetBaseUrl (*url);
165- result_client.SetSettings (settings_);
166- callback (result_client);
165+ auto client = GetCachedClient (service, service_version);
166+ if (client) {
167+ callback (*client);
167168 return CancellationToken ();
168169 } else if (options == CacheOnly) {
169- ApiLookupClient::LookupApiResponse response{
170- {client::ErrorCode::NotFound,
171- " CacheOnly: resource not found in cache" }};
172- callback (response);
170+ callback (NotFoundInCacheError ());
173171 return CancellationToken ();
174172 }
175173 }
176174
177- OLP_SDK_LOG_DEBUG_F (kLogTag ,
178- " LookupApi(%s/%s) cache miss, requesting, hrn='%s'" ,
179- service.c_str (), service_version.c_str (), hrn.c_str ());
180-
181175 PlatformApi::ApisCallback lookup_callback =
182176 [=](PlatformApi::ApisResponse response) mutable -> void {
183177 if (!response.IsSuccessful ()) {
184178 OLP_SDK_LOG_WARNING_F (
185179 kLogTag , " LookupApi(%s/%s) unsuccessful, hrn='%s', error='%s'" ,
186- service.c_str (), service_version.c_str (), hrn .c_str (),
180+ service.c_str (), service_version.c_str (), catalog_string_ .c_str (),
187181 response.GetError ().GetMessage ().c_str ());
188182 callback (response.GetError ());
189183 return ;
190184 }
191185
192186 const auto & api_result = response.GetResult ();
193187 if (options != OnlineOnly && options != CacheWithUpdate) {
194- for (const auto & service_api : api_result.first ) {
195- repository.Put (service_api.GetApi (), service_api.GetVersion (),
196- service_api.GetBaseUrl (), api_result.second );
197- }
188+ PutToDiskCache (api_result);
198189 }
199190
200191 const auto url = FindApi (api_result.first , service, service_version);
201192 if (url.empty ()) {
202193 OLP_SDK_LOG_WARNING_F (
203194 kLogTag , " LookupApi(%s/%s) service not found, hrn='%s'" ,
204- service.c_str (), service_version.c_str (), hrn .c_str ());
195+ service.c_str (), service_version.c_str (), catalog_string_ .c_str ());
205196
206- callback ({{client::ErrorCode::ServiceUnavailable,
207- " Service/Version not available for given HRN" }});
197+ callback (ServiceNotAvailable ());
208198 return ;
209199 }
210200
211- OLP_SDK_LOG_DEBUG_F (
212- kLogTag , " LookupApi(%s/%s) found, hrn='%s', service_url='%s'" ,
213- service.c_str (), service_version.c_str (), hrn.c_str (), url.c_str ());
201+ OLP_SDK_LOG_DEBUG_F (kLogTag ,
202+ " LookupApi(%s/%s) found, hrn='%s', service_url='%s'" ,
203+ service.c_str (), service_version.c_str (),
204+ catalog_string_.c_str (), url.c_str ());
214205
215- OlpClient result_client;
216- result_client.SetBaseUrl (url);
217- result_client.SetSettings (settings_);
218- callback (result_client);
206+ callback (CreateAndCacheClient (url, ClientCacheKey (service, service_version),
207+ api_result.second ));
219208 };
220209
221210 if (service == " config" ) {
222211 return PlatformApi::GetApis (lookup_client_, lookup_callback);
223212 }
224- return ResourcesApi::GetApis (lookup_client_, hrn, lookup_callback);
213+ return ResourcesApi::GetApis (lookup_client_, catalog_string_,
214+ lookup_callback);
215+ }
216+
217+ OlpClient ApiLookupClientImpl::CreateAndCacheClient (
218+ const std::string& base_url, const std::string& cache_key,
219+ boost::optional<time_t > expiration) {
220+ std::lock_guard<std::mutex> lock (cached_clients_mutex_);
221+ ClientWithExpiration& client_with_expiration = cached_clients_[cache_key];
222+
223+ const auto current_base_url = client_with_expiration.client .GetBaseUrl ();
224+ if (current_base_url.empty ()) {
225+ client_with_expiration.client .SetSettings (settings_);
226+ }
227+ if (current_base_url != base_url) {
228+ client_with_expiration.client .SetBaseUrl (base_url);
229+ }
230+
231+ client_with_expiration.expire_at =
232+ std::chrono::steady_clock::now () +
233+ std::chrono::seconds (expiration.value_or (kLookupApiDefaultExpiryTime ));
234+ return client_with_expiration.client ;
235+ }
236+
237+ boost::optional<OlpClient> ApiLookupClientImpl::GetCachedClient (
238+ const std::string& service, const std::string& service_version) {
239+ const std::string key = ClientCacheKey (service, service_version);
240+
241+ {
242+ std::lock_guard<std::mutex> lock (cached_clients_mutex_);
243+ const auto client_it = cached_clients_.find (key);
244+ if (client_it != cached_clients_.end ()) {
245+ const ClientWithExpiration& client_with_expirtation = client_it->second ;
246+ if (client_with_expirtation.expire_at >
247+ std::chrono::steady_clock::now ()) {
248+ OLP_SDK_LOG_DEBUG_F (
249+ kLogTag , " LookupApi(%s/%s) found in client cache, hrn='%s'" ,
250+ service.c_str (), service_version.c_str (), catalog_string_.c_str ());
251+ return client_with_expirtation.client ;
252+ }
253+ }
254+ }
255+
256+ repository::ApiCacheRepository cache_repository_ (catalog_, settings_.cache );
257+ const auto base_url = cache_repository_.Get (service, service_version);
258+ if (base_url) {
259+ OLP_SDK_LOG_DEBUG_F (
260+ kLogTag , " LookupApi(%s/%s) found in disk cache, hrn='%s'" ,
261+ service.c_str (), service_version.c_str (), catalog_string_.c_str ());
262+ } else {
263+ OLP_SDK_LOG_DEBUG_F (
264+ kLogTag , " LookupApi(%s/%s) cache miss in disk cache, hrn='%s'" ,
265+ service.c_str (), service_version.c_str (), catalog_string_.c_str ());
266+ return boost::none;
267+ }
268+
269+ // When the service url is retrieved from disk cache we assume it is valid for
270+ // five minutes, after five minutes we repeat. This is because we cannot
271+ // retrieve the exact expiration from cache so to not use a possibly expired
272+ // URL for 1h we use it for 5min and check again.
273+ return CreateAndCacheClient (*base_url, key, kLookupApiShortExpiryTime );
274+ }
275+
276+ void ApiLookupClientImpl::PutToDiskCache (const ApisResult& available_services) {
277+ repository::ApiCacheRepository cache_repository_ (catalog_, settings_.cache );
278+ for (const auto & service_api : available_services.first ) {
279+ cache_repository_.Put (service_api.GetApi (), service_api.GetVersion (),
280+ service_api.GetBaseUrl (), available_services.second );
281+ }
225282}
226283
227284} // namespace client
0 commit comments