parent
2c8e557bb4
commit
36b0c6f6f0
12 changed files with 143 additions and 17 deletions
|
@ -8,6 +8,8 @@ fetch_extra_delay=2
|
||||||
api_key=my_key_here
|
api_key=my_key_here
|
||||||
store_raw_json=true
|
store_raw_json=true
|
||||||
json_store_mode=xz
|
json_store_mode=xz
|
||||||
|
max_connection_retries=3
|
||||||
|
error_retry_timeout=360
|
||||||
|
|
||||||
[timing]
|
[timing]
|
||||||
items=604800
|
items=604800
|
||||||
|
|
|
@ -74,6 +74,14 @@ namespace {
|
||||||
constexpr const char g_creators_time[] = "creators";
|
constexpr const char g_creators_time[] = "creators";
|
||||||
constexpr const char g_creators_time_def[] = "600";
|
constexpr const char g_creators_time_def[] = "600";
|
||||||
|
|
||||||
|
constexpr const char g_max_conn_retries_sect[] = "options";
|
||||||
|
constexpr const char g_max_conn_retries[] = "max_connection_retries";
|
||||||
|
constexpr const char g_max_conn_retries_def[] = "0";
|
||||||
|
|
||||||
|
constexpr const char g_err_retry_timeout_sect[] = "options";
|
||||||
|
constexpr const char g_err_retry_timeout[] = "error_retry_timeout";
|
||||||
|
constexpr const char g_err_retry_timeout_def[] = "600";
|
||||||
|
|
||||||
bool equal (std::string_view a, std::string_view b) {
|
bool equal (std::string_view a, std::string_view b) {
|
||||||
return a.size() == b.size() and std::equal(
|
return a.size() == b.size() and std::equal(
|
||||||
a.begin(), a.end(),
|
a.begin(), a.end(),
|
||||||
|
@ -235,4 +243,14 @@ oro::SourceFormat AppConfig::json_store_mode() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t AppConfig::error_retry_timeout() const {
|
||||||
|
std::string_view val = value_ifp(m_ini, g_err_retry_timeout_sect, g_err_retry_timeout, g_err_retry_timeout_def, false);
|
||||||
|
return std::max(dhandy::int_conv<std::size_t>(val), g_min_update_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int AppConfig::max_connection_retries() const {
|
||||||
|
std::string_view val = value_ifp(m_ini, g_max_conn_retries_sect, g_max_conn_retries, g_max_conn_retries_def, false);
|
||||||
|
return dhandy::int_conv<unsigned int>(val);
|
||||||
|
}
|
||||||
|
|
||||||
} //namespace duck
|
} //namespace duck
|
||||||
|
|
|
@ -42,6 +42,9 @@ public:
|
||||||
std::size_t creators_timeout() const;
|
std::size_t creators_timeout() const;
|
||||||
oro::SourceFormat json_store_mode() const;
|
oro::SourceFormat json_store_mode() const;
|
||||||
|
|
||||||
|
std::size_t error_retry_timeout() const;
|
||||||
|
unsigned int max_connection_retries() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
kamokan::IniFile m_ini;
|
kamokan::IniFile m_ini;
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,6 +55,24 @@ namespace {
|
||||||
private:
|
private:
|
||||||
eve::Eventia* m_eventia;
|
eve::Eventia* m_eventia;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TimerSettingsHelper : TimerSettings {
|
||||||
|
TimerSettingsHelper (
|
||||||
|
double ed,
|
||||||
|
double et,
|
||||||
|
oro::SourceFormat sf,
|
||||||
|
unsigned int er
|
||||||
|
) :
|
||||||
|
TimerSettings {0.0, ed, et, sf, er}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerSettings operator() (double mwait) const {
|
||||||
|
TimerSettings ret = *static_cast<const TimerSettings*>(this);
|
||||||
|
ret.min_wait = mwait;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
} //unnamed namespace
|
} //unnamed namespace
|
||||||
|
|
||||||
void test(oro::Api* api, oro::OriginsDB* db, const AppConfig& app_conf) {
|
void test(oro::Api* api, oro::OriginsDB* db, const AppConfig& app_conf) {
|
||||||
|
@ -62,27 +80,28 @@ void test(oro::Api* api, oro::OriginsDB* db, const AppConfig& app_conf) {
|
||||||
typedef TimerOroApi<oro::DBOperation::Items> TimerItems;
|
typedef TimerOroApi<oro::DBOperation::Items> TimerItems;
|
||||||
typedef TimerOroApi<oro::DBOperation::Icons> TimerIcons;
|
typedef TimerOroApi<oro::DBOperation::Icons> TimerIcons;
|
||||||
typedef TimerOroApi<oro::DBOperation::Creators> TimerCreators;
|
typedef TimerOroApi<oro::DBOperation::Creators> TimerCreators;
|
||||||
typedef TimerSettings TSet;
|
|
||||||
|
|
||||||
std::cout << "Running with " << app_conf.worker_threads() << " worker threads\n";
|
std::cout << "Running with " << app_conf.worker_threads() << " worker threads\n";
|
||||||
eve::Eventia worker;
|
eve::Eventia worker;
|
||||||
EventiaThreadPool pool(&worker, app_conf.worker_threads());
|
EventiaThreadPool pool(&worker, app_conf.worker_threads());
|
||||||
|
|
||||||
const double ed = static_cast<double>(app_conf.fetch_extra_delay());
|
|
||||||
|
|
||||||
auto sig_int = worker.make_event<SignalInt>(&worker);
|
auto sig_int = worker.make_event<SignalInt>(&worker);
|
||||||
const oro::SourceFormat sf =
|
|
||||||
(app_conf.store_raw_json() ? app_conf.json_store_mode() : oro::SourceFormat::None);
|
|
||||||
|
|
||||||
const double w1 = static_cast<double>(app_conf.items_timeout());
|
const double w1 = static_cast<double>(app_conf.items_timeout());
|
||||||
const double w2 = static_cast<double>(app_conf.icons_timeout());
|
const double w2 = static_cast<double>(app_conf.icons_timeout());
|
||||||
const double w3 = static_cast<double>(app_conf.shops_timeout());
|
const double w3 = static_cast<double>(app_conf.shops_timeout());
|
||||||
const double w4 = static_cast<double>(app_conf.creators_timeout());
|
const double w4 = static_cast<double>(app_conf.creators_timeout());
|
||||||
|
|
||||||
auto timer_items = worker.make_event<TimerItems>(TSet{w1, ed, sf}, &pool, api, db);
|
TimerSettingsHelper tset{
|
||||||
auto timer_icons = worker.make_event<TimerIcons>(TSet{w2, ed, sf}, &pool, api, db);
|
static_cast<double>(app_conf.fetch_extra_delay()),
|
||||||
auto timer_shops = worker.make_event<TimerShops>(TSet{w3, ed, sf}, &pool, api, db);
|
static_cast<double>(app_conf.error_retry_timeout()),
|
||||||
auto timer_creat = worker.make_event<TimerCreators>(TSet{w4, ed, sf}, &pool, api, db);
|
(app_conf.store_raw_json() ? app_conf.json_store_mode() : oro::SourceFormat::None),
|
||||||
|
app_conf.max_connection_retries()
|
||||||
|
};
|
||||||
|
auto timer_items = worker.make_event<TimerItems>(tset(w1), &pool, api, db);
|
||||||
|
auto timer_icons = worker.make_event<TimerIcons>(tset(w2), &pool, api, db);
|
||||||
|
auto timer_shops = worker.make_event<TimerShops>(tset(w3), &pool, api, db);
|
||||||
|
auto timer_creat = worker.make_event<TimerCreators>(tset(w4), &pool, api, db);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pool.submit(worker.event_functor());
|
pool.submit(worker.event_functor());
|
||||||
|
|
|
@ -24,6 +24,14 @@
|
||||||
|
|
||||||
namespace nap {
|
namespace nap {
|
||||||
|
|
||||||
|
enum class ConnectionResult : unsigned int {
|
||||||
|
Ok,
|
||||||
|
CouldntResolveProxy,
|
||||||
|
CouldntResolveHost,
|
||||||
|
CouldntConnect,
|
||||||
|
TimedOut
|
||||||
|
};
|
||||||
|
|
||||||
struct HttpResponse {
|
struct HttpResponse {
|
||||||
std::unique_ptr<char[]> raw;
|
std::unique_ptr<char[]> raw;
|
||||||
std::vector<std::pair<std::string_view, std::string_view>> header_list;
|
std::vector<std::pair<std::string_view, std::string_view>> header_list;
|
||||||
|
@ -31,7 +39,8 @@ struct HttpResponse {
|
||||||
std::string_view body;
|
std::string_view body;
|
||||||
std::string_view http_ver;
|
std::string_view http_ver;
|
||||||
std::string_view code_desc;
|
std::string_view code_desc;
|
||||||
unsigned int code;
|
unsigned int code{0};
|
||||||
|
ConnectionResult conn_result{ConnectionResult::Ok};
|
||||||
};
|
};
|
||||||
|
|
||||||
} //namespace nap
|
} //namespace nap
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "private/mime_split.hpp"
|
#include "private/mime_split.hpp"
|
||||||
#include "duckhandy/int_conv.hpp"
|
#include "duckhandy/int_conv.hpp"
|
||||||
#include "api_nap_exception.hpp"
|
#include "api_nap_exception.hpp"
|
||||||
|
#include <curl_exception.h>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
@ -269,7 +270,13 @@ ApiOutput<Creators> ApiNap::fame_list(bool with_raw) {
|
||||||
|
|
||||||
template <typename T, typename F>
|
template <typename T, typename F>
|
||||||
ApiOutput<T> ApiNap::fetch_and_parse (const char* endpoint, F&& data_fill, bool with_raw) {
|
ApiOutput<T> ApiNap::fetch_and_parse (const char* endpoint, F&& data_fill, bool with_raw) {
|
||||||
auto resp = m_qrest.fetch(m_prefix + endpoint);
|
nap::HttpResponse resp;
|
||||||
|
try {
|
||||||
|
resp = m_qrest.fetch(m_prefix + endpoint);
|
||||||
|
}
|
||||||
|
catch (const curl::curl_easy_exception& err) {
|
||||||
|
throw ConnectionError(err.what(), err.get_code());
|
||||||
|
}
|
||||||
|
|
||||||
if (200 != resp.code)
|
if (200 != resp.code)
|
||||||
throw ServerError(resp);
|
throw ServerError(resp);
|
||||||
|
|
|
@ -64,4 +64,14 @@ std::string_view ContentTypeError::content_type() const noexcept {
|
||||||
return {m_content_type.data()};
|
return {m_content_type.data()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConnectionError::ConnectionError (const std::string& reason, unsigned int code) :
|
||||||
|
std::runtime_error("Connection error: " + reason),
|
||||||
|
m_curl_code(code)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ConnectionError::curl_code() const {
|
||||||
|
return m_curl_code;
|
||||||
|
}
|
||||||
|
|
||||||
} //namespace oro
|
} //namespace oro
|
||||||
|
|
|
@ -38,11 +38,21 @@ private:
|
||||||
|
|
||||||
class ContentTypeError : public std::runtime_error {
|
class ContentTypeError : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
explicit ContentTypeError (std::string_view type, std::string_view subtype);
|
ContentTypeError (std::string_view type, std::string_view subtype);
|
||||||
std::string_view content_type() const noexcept;
|
std::string_view content_type() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<char, 32> m_content_type;
|
std::array<char, 32> m_content_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ConnectionError : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
ConnectionError (const std::string& reason, unsigned int code);
|
||||||
|
|
||||||
|
unsigned int curl_code() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int m_curl_code;
|
||||||
|
};
|
||||||
|
|
||||||
} //namespace oro
|
} //namespace oro
|
||||||
|
|
|
@ -76,10 +76,12 @@ TimerBase::TimerBase (
|
||||||
eve::Timer(initial_timer(*db, type, 2.0), ctx),
|
eve::Timer(initial_timer(*db, type, 2.0), ctx),
|
||||||
m_extra_delay(settings.extra_delay),
|
m_extra_delay(settings.extra_delay),
|
||||||
m_min_wait(settings.min_wait),
|
m_min_wait(settings.min_wait),
|
||||||
|
m_err_timeout(settings.err_timeout),
|
||||||
m_pool(pool),
|
m_pool(pool),
|
||||||
m_oro_api(oro_api),
|
m_oro_api(oro_api),
|
||||||
m_db(db),
|
m_db(db),
|
||||||
m_source_store_mode(settings.src_store_mode)
|
m_source_store_mode(settings.src_store_mode),
|
||||||
|
m_err_retries(settings.err_retries)
|
||||||
{
|
{
|
||||||
assert(m_pool);
|
assert(m_pool);
|
||||||
assert(m_oro_api);
|
assert(m_oro_api);
|
||||||
|
@ -133,4 +135,12 @@ void TimerBase::update_db (const oro::Creators& creators, const oro::Header& hea
|
||||||
db().update(creators, calc_next_update(header, m_min_wait, m_extra_delay), source);
|
db().update(creators, calc_next_update(header, m_min_wait, m_extra_delay), source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double TimerBase::error_timeout() const noexcept {
|
||||||
|
return m_err_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int TimerBase::error_retries() const noexcept {
|
||||||
|
return m_err_retries;
|
||||||
|
}
|
||||||
|
|
||||||
} //namespace duck
|
} //namespace duck
|
||||||
|
|
|
@ -37,15 +37,25 @@ namespace duck {
|
||||||
class EventiaThreadPool;
|
class EventiaThreadPool;
|
||||||
|
|
||||||
struct TimerSettings {
|
struct TimerSettings {
|
||||||
TimerSettings (double min_wait, double extra_delay, oro::SourceFormat store_mode) :
|
TimerSettings (
|
||||||
|
double min_wait,
|
||||||
|
double extra_delay,
|
||||||
|
double err_timeout,
|
||||||
|
oro::SourceFormat store_mode,
|
||||||
|
unsigned int err_retries
|
||||||
|
) :
|
||||||
min_wait(min_wait),
|
min_wait(min_wait),
|
||||||
extra_delay(extra_delay),
|
extra_delay(extra_delay),
|
||||||
src_store_mode(store_mode)
|
err_timeout(err_timeout),
|
||||||
|
src_store_mode(store_mode),
|
||||||
|
err_retries(err_retries)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
double min_wait;
|
double min_wait;
|
||||||
double extra_delay;
|
double extra_delay;
|
||||||
|
double err_timeout;
|
||||||
oro::SourceFormat src_store_mode;
|
oro::SourceFormat src_store_mode;
|
||||||
|
unsigned int err_retries;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TimerBase : public eve::Timer {
|
class TimerBase : public eve::Timer {
|
||||||
|
@ -73,15 +83,21 @@ protected:
|
||||||
oro::OriginsDB& db();
|
oro::OriginsDB& db();
|
||||||
void reset_db_access_time (oro::DBOperation op);
|
void reset_db_access_time (oro::DBOperation op);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
double error_timeout() const noexcept;
|
||||||
|
unsigned int error_retries() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void fetch_data(oro::SourceFormat store_mode) = 0;
|
virtual void fetch_data(oro::SourceFormat store_mode) = 0;
|
||||||
|
|
||||||
double m_extra_delay;
|
double m_extra_delay;
|
||||||
double m_min_wait;
|
double m_min_wait;
|
||||||
|
double m_err_timeout;
|
||||||
EventiaThreadPool* m_pool;
|
EventiaThreadPool* m_pool;
|
||||||
oro::Api* m_oro_api;
|
oro::Api* m_oro_api;
|
||||||
oro::OriginsDB* m_db;
|
oro::OriginsDB* m_db;
|
||||||
oro::SourceFormat m_source_store_mode;
|
oro::SourceFormat m_source_store_mode;
|
||||||
|
unsigned int m_err_retries;
|
||||||
};
|
};
|
||||||
|
|
||||||
} //namespace duck
|
} //namespace duck
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
# include <restc-cpp/error.h>
|
# include <restc-cpp/error.h>
|
||||||
#elif defined(OROTOOL_WITH_NAP)
|
#elif defined(OROTOOL_WITH_NAP)
|
||||||
# include "oro/api_nap_exception.hpp"
|
# include "oro/api_nap_exception.hpp"
|
||||||
|
# include <curl/curl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace duck {
|
namespace duck {
|
||||||
|
@ -62,7 +63,8 @@ inline TimerOroApi<Op>::TimerOroApi (
|
||||||
oro::Api* oro_api,
|
oro::Api* oro_api,
|
||||||
oro::OriginsDB* db
|
oro::OriginsDB* db
|
||||||
) :
|
) :
|
||||||
TimerBase(ctx, Op, settings, pool, oro_api, db)
|
TimerBase(ctx, Op, settings, pool, oro_api, db),
|
||||||
|
m_retries_left(TimerBase::error_retries())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,9 +113,27 @@ inline void TimerOroApi<Op>::fetch_data (oro::SourceFormat store_mode) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (const oro::ConnectionError& err) {
|
||||||
|
switch (err.curl_code()) {
|
||||||
|
case CURLE_OPERATION_TIMEDOUT:
|
||||||
|
case CURLE_COULDNT_RESOLVE_PROXY:
|
||||||
|
case CURLE_COULDNT_RESOLVE_HOST:
|
||||||
|
case CURLE_COULDNT_CONNECT:
|
||||||
|
if (m_retries_left) {
|
||||||
|
--m_retries_left;
|
||||||
|
this->set_timer(error_timeout());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
m_retries_left = error_retries();
|
||||||
if (429 == status_code) {
|
if (429 == status_code) {
|
||||||
oro::Header head;
|
oro::Header head;
|
||||||
head.date.ts = std::chrono::time_point_cast<oro::timestamp_t::duration>(std::chrono::system_clock::now());
|
head.date.ts = std::chrono::time_point_cast<oro::timestamp_t::duration>(std::chrono::system_clock::now());
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void fetch_data (oro::SourceFormat store_mode) override;
|
virtual void fetch_data (oro::SourceFormat store_mode) override;
|
||||||
|
|
||||||
|
unsigned int m_retries_left;
|
||||||
};
|
};
|
||||||
|
|
||||||
} //namespace duck
|
} //namespace duck
|
||||||
|
|
Loading…
Reference in a new issue