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
|
||||
store_raw_json=true
|
||||
json_store_mode=xz
|
||||
max_connection_retries=3
|
||||
error_retry_timeout=360
|
||||
|
||||
[timing]
|
||||
items=604800
|
||||
|
|
|
@ -74,6 +74,14 @@ namespace {
|
|||
constexpr const char g_creators_time[] = "creators";
|
||||
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) {
|
||||
return a.size() == b.size() and std::equal(
|
||||
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
|
||||
|
|
|
@ -42,6 +42,9 @@ public:
|
|||
std::size_t creators_timeout() const;
|
||||
oro::SourceFormat json_store_mode() const;
|
||||
|
||||
std::size_t error_retry_timeout() const;
|
||||
unsigned int max_connection_retries() const;
|
||||
|
||||
private:
|
||||
kamokan::IniFile m_ini;
|
||||
};
|
||||
|
|
|
@ -55,6 +55,24 @@ namespace {
|
|||
private:
|
||||
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
|
||||
|
||||
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::Icons> TimerIcons;
|
||||
typedef TimerOroApi<oro::DBOperation::Creators> TimerCreators;
|
||||
typedef TimerSettings TSet;
|
||||
|
||||
std::cout << "Running with " << app_conf.worker_threads() << " worker threads\n";
|
||||
eve::Eventia worker;
|
||||
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);
|
||||
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 w2 = static_cast<double>(app_conf.icons_timeout());
|
||||
const double w3 = static_cast<double>(app_conf.shops_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);
|
||||
auto timer_icons = worker.make_event<TimerIcons>(TSet{w2, ed, sf}, &pool, api, db);
|
||||
auto timer_shops = worker.make_event<TimerShops>(TSet{w3, ed, sf}, &pool, api, db);
|
||||
auto timer_creat = worker.make_event<TimerCreators>(TSet{w4, ed, sf}, &pool, api, db);
|
||||
TimerSettingsHelper tset{
|
||||
static_cast<double>(app_conf.fetch_extra_delay()),
|
||||
static_cast<double>(app_conf.error_retry_timeout()),
|
||||
(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 {
|
||||
pool.submit(worker.event_functor());
|
||||
|
|
|
@ -24,6 +24,14 @@
|
|||
|
||||
namespace nap {
|
||||
|
||||
enum class ConnectionResult : unsigned int {
|
||||
Ok,
|
||||
CouldntResolveProxy,
|
||||
CouldntResolveHost,
|
||||
CouldntConnect,
|
||||
TimedOut
|
||||
};
|
||||
|
||||
struct HttpResponse {
|
||||
std::unique_ptr<char[]> raw;
|
||||
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 http_ver;
|
||||
std::string_view code_desc;
|
||||
unsigned int code;
|
||||
unsigned int code{0};
|
||||
ConnectionResult conn_result{ConnectionResult::Ok};
|
||||
};
|
||||
|
||||
} //namespace nap
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "private/mime_split.hpp"
|
||||
#include "duckhandy/int_conv.hpp"
|
||||
#include "api_nap_exception.hpp"
|
||||
#include <curl_exception.h>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
@ -269,7 +270,13 @@ ApiOutput<Creators> ApiNap::fame_list(bool with_raw) {
|
|||
|
||||
template <typename T, typename F>
|
||||
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)
|
||||
throw ServerError(resp);
|
||||
|
|
|
@ -64,4 +64,14 @@ std::string_view ContentTypeError::content_type() const noexcept {
|
|||
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
|
||||
|
|
|
@ -38,11 +38,21 @@ private:
|
|||
|
||||
class ContentTypeError : public std::runtime_error {
|
||||
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;
|
||||
|
||||
private:
|
||||
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
|
||||
|
|
|
@ -76,10 +76,12 @@ TimerBase::TimerBase (
|
|||
eve::Timer(initial_timer(*db, type, 2.0), ctx),
|
||||
m_extra_delay(settings.extra_delay),
|
||||
m_min_wait(settings.min_wait),
|
||||
m_err_timeout(settings.err_timeout),
|
||||
m_pool(pool),
|
||||
m_oro_api(oro_api),
|
||||
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_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);
|
||||
}
|
||||
|
||||
double TimerBase::error_timeout() const noexcept {
|
||||
return m_err_timeout;
|
||||
}
|
||||
|
||||
unsigned int TimerBase::error_retries() const noexcept {
|
||||
return m_err_retries;
|
||||
}
|
||||
|
||||
} //namespace duck
|
||||
|
|
|
@ -37,15 +37,25 @@ namespace duck {
|
|||
class EventiaThreadPool;
|
||||
|
||||
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),
|
||||
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 extra_delay;
|
||||
double err_timeout;
|
||||
oro::SourceFormat src_store_mode;
|
||||
unsigned int err_retries;
|
||||
};
|
||||
|
||||
class TimerBase : public eve::Timer {
|
||||
|
@ -73,15 +83,21 @@ protected:
|
|||
oro::OriginsDB& db();
|
||||
void reset_db_access_time (oro::DBOperation op);
|
||||
|
||||
protected:
|
||||
double error_timeout() const noexcept;
|
||||
unsigned int error_retries() const noexcept;
|
||||
|
||||
private:
|
||||
virtual void fetch_data(oro::SourceFormat store_mode) = 0;
|
||||
|
||||
double m_extra_delay;
|
||||
double m_min_wait;
|
||||
double m_err_timeout;
|
||||
EventiaThreadPool* m_pool;
|
||||
oro::Api* m_oro_api;
|
||||
oro::OriginsDB* m_db;
|
||||
oro::SourceFormat m_source_store_mode;
|
||||
unsigned int m_err_retries;
|
||||
};
|
||||
|
||||
} //namespace duck
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
# include <restc-cpp/error.h>
|
||||
#elif defined(OROTOOL_WITH_NAP)
|
||||
# include "oro/api_nap_exception.hpp"
|
||||
# include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
namespace duck {
|
||||
|
@ -62,7 +63,8 @@ inline TimerOroApi<Op>::TimerOroApi (
|
|||
oro::Api* oro_api,
|
||||
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;
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
|
||||
m_retries_left = error_retries();
|
||||
if (429 == status_code) {
|
||||
oro::Header head;
|
||||
head.date.ts = std::chrono::time_point_cast<oro::timestamp_t::duration>(std::chrono::system_clock::now());
|
||||
|
|
|
@ -39,6 +39,8 @@ public:
|
|||
|
||||
private:
|
||||
virtual void fetch_data (oro::SourceFormat store_mode) override;
|
||||
|
||||
unsigned int m_retries_left;
|
||||
};
|
||||
|
||||
} //namespace duck
|
||||
|
|
Loading…
Reference in a new issue