allow to specify how many times we can try to connect to the database at startup

add config options:
db_startup_connection_max_attempts - default 0 (infinite)
db_startup_connection_attempt_delay - delay in seconds between attempts (default 5)

BREAKING CHANGE: WINIX_PLUGIN_INIT plugin message requires to set result status,
you have to set the result status to true (env.res) if your
plugin was initialized correctly, otherwise winix will not start
This commit is contained in:
Tomasz Sowa 2022-04-29 06:17:16 +02:00
parent c6c50a5d23
commit da2dec447b
18 changed files with 154 additions and 41 deletions

View File

@ -178,7 +178,7 @@ Log & App::GetMainLog()
void App::InitPlugins()
void App::LoadPlugins()
{
plugin.LoadPlugins(config.plugins_dir, config.plugin_file);
}
@ -350,8 +350,26 @@ bool App::TryToMakeDatabaseMigration()
}
bool App::InitializePlugins()
{
PluginRes plugin_res = plugin.Call((Session*)0, WINIX_PLUGIN_INIT);
if( plugin_res.res_false > 0 )
{
log << log1 << "App: " << plugin_res.res_false << " plugin(s) cannot initialize itself, exiting" << logend;
return false;
}
return true;
}
bool App::Init()
{
// load plugins before loading sessions - session_manager.LoadSessions()
// because some of the plugins can init its own sessions dates
LoadPlugins();
if( !config.db_conn_string.empty() )
postgresql_connector.set_conn_param(config.db_conn_string);
else
@ -359,7 +377,11 @@ bool App::Init()
postgresql_connector.set_logger(log);
postgresql_connector.set_log_queries(config.log_db_query);
postgresql_connector.wait_for_connection();
if( !postgresql_connector.wait_for_connection(config.db_startup_connection_max_attempts, config.db_startup_connection_attempt_delay) )
{
return false;
}
model_connector.set_flat_connector(json_connector);
model_connector.set_db_connector(postgresql_connector);
@ -378,6 +400,7 @@ bool App::Init()
model_connector.set_winix_time_zones(&system.time_zones);
model_connector.set_winix_pattern_cacher(&TemplatesFunctions::pattern_cacher);
// CHECKME this will call WINIX_MAKE_DATABASE_MIGRATION, but WINIX_PLUGIN_INIT was not called yet, it is correct?
if( !TryToMakeDatabaseMigration() )
return false;
@ -387,13 +410,17 @@ bool App::Init()
else
db_conn.SetConnParam(config.db_host, config.db_hostaddr, config.db_port, config.db_database, config.db_user, config.db_pass);
db_conn.WaitForConnection();
if( !db_conn.WaitForConnection(config.db_startup_connection_max_attempts, config.db_startup_connection_attempt_delay) )
return false;
db.LogQueries(config.log_db_query);
cur.request->Clear();
compress.set_dependency(&winix_base);
compress.Init();
system.Init();
if( !system.Init() )
return false;
functions.Init();
templates.Init(); // init templates after functions are created
@ -414,9 +441,10 @@ bool App::Init()
cookie_parser.set_dependency(&winix_model);
plugin.Call((Session*)0, WINIX_PLUGIN_INIT);
if( !AddSystemThreads() )
return false;
return true;
return InitializePlugins();
}
@ -2788,6 +2816,15 @@ int sig;
}
bool App::AddSystemThreads()
{
bool ok = true;
ok = ok && system.thread_manager.Add(&session_manager, L"session_manager");
return ok;
}
void App::StartThreads()
{
@ -2797,7 +2834,7 @@ void App::StartThreads()
// special thread only for signals
pthread_create(&signal_thread, 0, SpecialThreadForSignals, this);
system.thread_manager.Add(&session_manager, L"session_manager");
// start all threads from thread manager
system.thread_manager.StartAll();
}

View File

@ -74,7 +74,8 @@ public:
bool DropPrivileges();
void InitLoggers();
Log & GetMainLog();
void InitPlugins();
void LoadPlugins();
bool InitializePlugins();
bool Init();
void Start();
void Close();
@ -188,6 +189,8 @@ private:
// file logger, one object for all Log objects
FileLog file_log;
bool AddSystemThreads();
bool TranslateFCGInames(char * sock, char * sock_user, char * sock_group);
bool InitFCGIChmodChownSocket(char * sock, char * sock_user, char * sock_group);
bool DropPrivileges(char * user, char * group);

View File

@ -187,9 +187,12 @@ void Config::AssignValues()
db_database = Text(L"db_database");
db_user = Text(L"db_user");
db_pass = Text(L"db_pass");
db_startup_connection_max_attempts = Size(L"db_startup_connection_max_attempts", 0);
db_startup_connection_attempt_delay = Size(L"db_startup_connection_attempt_delay", 5);
db_make_migration_if_needed = Bool(L"db_make_migration_if_needed", true);
db_stop_if_migration_fails = Bool(L"db_stop_if_migration_fails", true);
item_url_empty = Text(L"item_url_empty");
url_proto = Text(L"url_proto", L"http://");

View File

@ -242,12 +242,21 @@ public:
std::wstring db_user;
std::wstring db_pass;
// specify how many times we can try to connect to the database at startup
// default 0 (infinite)
size_t db_startup_connection_max_attempts;
// delay between each connection attempt at startup
// default 5 (seconds)
size_t db_startup_connection_attempt_delay;
// make database migration if needed
//
bool db_make_migration_if_needed;
// if a migration fails then stop winix
bool db_stop_if_migration_fails;
// the name of the cookie which has the session identifier
std::wstring http_session_id_name;

View File

@ -144,6 +144,8 @@ namespace Winix
// winix is initialized,
// now you can initialize your plugin
// session pointer is null
// you have to set the result status to true (env.res) if your
// plugin was initialized correctly, otherwise winix will not start
#define WINIX_PLUGIN_INIT 30080
// winix is ready to remove a plugin

View File

@ -124,7 +124,7 @@ void System::ReadTimeZones()
}
void System::Init()
bool System::Init()
{
//thread_manager.SetSynchro(synchro);
thread_manager.Init();
@ -157,21 +157,24 @@ void System::Init()
notify.SetUsers(&users);
notify.SetDirs(&dirs);
notify.SetThreadManager(&thread_manager);
notify.Init();
if( !notify.Init() )
return false;
image.SetDb(db);
image.SetConfig(config);
image.SetSystem(this);
thread_manager.Add(&image, L"image");
if( !thread_manager.Add(&image, L"image") )
return false;
// SetSynchro will be called by ThreadManager itself
// job.ReadFromFile();
thread_manager.Add(&job, L"job");
if( !thread_manager.Add(&job, L"job") )
return false;
ReadTimeZones();
return true;
}

View File

@ -120,7 +120,7 @@ public:
void set_dependency(WinixModelDeprecated * winix_model);
void Init();
bool Init();
void AddParams(const ParamTab & param_tab, std::wstring & str, bool clear_str = true);

View File

@ -70,7 +70,7 @@ sigset_t set;
void ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name)
bool ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name)
{
thread_tab.emplace_back();
ThreadItem & item = thread_tab.back();
@ -96,7 +96,14 @@ void ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name)
data.postgresql_connector.set_logger(item.object->get_logger());
data.postgresql_connector.set_log_queries(config->log_db_query);
data.postgresql_connector.wait_for_connection();
if( !data.postgresql_connector.wait_for_connection(config->db_startup_connection_max_attempts, config->db_startup_connection_attempt_delay) )
{
Log * plog = item.object->get_logger();
(*plog) << logsave;
return false;
}
data.model_connector.set_db_connector(data.postgresql_connector);
data.model_connector.set_flat_connector(data.json_connector);
data.model_connector.set_logger(item.object->get_logger());
@ -125,23 +132,26 @@ void ThreadManager::Add(BaseThread * pbase, const wchar_t * thread_name)
log << log4 << "TM: added a thread to the queue, number: " << (thread_tab.size()-1)
<< ", name: " << thread_name << logend;
}
return true;
}
void ThreadManager::Add(BaseThread & pbase, const wchar_t * thread_name)
bool ThreadManager::Add(BaseThread & pbase, const wchar_t * thread_name)
{
Add(&pbase, thread_name);
return Add(&pbase, thread_name);
}
void ThreadManager::Add(BaseThread * pbase, const std::wstring & thread_name)
bool ThreadManager::Add(BaseThread * pbase, const std::wstring & thread_name)
{
Add(pbase, thread_name.c_str());
return Add(pbase, thread_name.c_str());
}
void ThreadManager::Add(BaseThread & pbase, const std::wstring & thread_name)
bool ThreadManager::Add(BaseThread & pbase, const std::wstring & thread_name)
{
Add(&pbase, thread_name.c_str());
return Add(&pbase, thread_name.c_str());
}

View File

@ -59,10 +59,10 @@ public:
// adding a new thread to the queue
// the thread will be running only if we call StartAll() before
// otherwise the thread will be waiting for StartAll()
void Add(BaseThread * pbase, const wchar_t * thread_name);
void Add(BaseThread & pbase, const wchar_t * thread_name);
void Add(BaseThread * pbase, const std::wstring & thread_name);
void Add(BaseThread & pbase, const std::wstring & thread_name);
bool Add(BaseThread * pbase, const wchar_t * thread_name);
bool Add(BaseThread & pbase, const wchar_t * thread_name);
bool Add(BaseThread * pbase, const std::wstring & thread_name);
bool Add(BaseThread & pbase, const std::wstring & thread_name);
// starting all threads
void StartAll();

View File

@ -130,26 +130,63 @@ void DbConn::Connect()
}
void DbConn::LogNoConnection(size_t attempts)
{
log << log2 << "Db: connection to the database cannot be established";
log << ", (" << attempts << " attempt(s))" << logend;
log << logsave;
}
void DbConn::LogConnectionSocket()
{
log << log2 << "Db: connection to the database works fine" << logend;
log << log3 << "Db: connection socket: " << PQsocket(pg_conn) << logend;
log << logsave;
}
void DbConn::WaitForConnection()
bool DbConn::WaitForConnection(size_t attempts_max, size_t attempt_delay)
{
size_t attempts = 0;
bool attempts_exceeded = false;
if( attempt_delay == 0 )
attempt_delay = 1;
if( attempt_delay > 120 )
attempt_delay = 120;
if( !pg_conn || PQstatus(pg_conn) != CONNECTION_OK )
{
log << log3 << "Db: waiting for the db to be ready...." << logend << logsave;
while( !AssertConnection(false, false) )
sleep(5);
while( !attempts_exceeded && !AssertConnection(false, false) )
{
if( attempts_max != 0 )
{
attempts += 1;
attempts_exceeded = (attempts >= attempts_max);
}
if( !attempts_exceeded )
{
sleep(attempt_delay);
}
}
}
if( attempts_exceeded )
{
LogNoConnection(attempts);
}
else
{
LogConnectionSocket();
}
return !attempts_exceeded;
}

View File

@ -57,7 +57,7 @@ public:
void SetConnParam(const std::wstring & host, const std::wstring & hostaddr, const std::wstring & port,
const std::wstring & database, const std::wstring & user, const std::wstring & pass);
void Connect();
void WaitForConnection();
bool WaitForConnection(size_t attempts_max, size_t attempt_delay);
void Close();
bool AssertConnection(bool put_log = true, bool throw_if_no_connection = true);
void SetDbParameters();
@ -65,6 +65,7 @@ public:
private:
void LogNoConnection(size_t attempts);
void LogConnectionSocket();
PGconn * pg_conn;

View File

@ -353,10 +353,6 @@ using Winix::app;
app.LogUserGroups();
Winix::SavePidFile(log);
// load plugins before loading sessions - session_manager.LoadSessions()
// because some of the plugins can init its own sessions dates
app.InitPlugins();
// app.Init() starts other threads as well (they will be waiting on the lock)
if( !app.Init() )
{

View File

@ -85,14 +85,16 @@ void Notify::SetThreadManager(ThreadManager * pmanager)
void Notify::Init()
bool Notify::Init()
{
//notify_thread.SetConfig(config);
notify_thread.SetUsers(users);
notify_thread.SetNotifyPool(&notify_pool);
notify_thread.SetPatterns(&patterns);
//notify_thread.SetFileLog(file_log);
thread_manager->Add(&notify_thread, L"notifications");
if( !thread_manager->Add(&notify_thread, L"notifications") )
return false;
patterns.SetDirectories(config->txt_templates_dir, config->txt_templates_dir_default);
patterns.SetLocale(&TemplatesFunctions::locale);
@ -103,6 +105,8 @@ void Notify::Init()
notify_template_reset_password = AddTemplate(L"notify_reset_password.txt");
plugin->Call((Session*)0, WINIX_NOTIFY_ADD_TEMPLATE);
return true;
}

View File

@ -70,7 +70,7 @@ public:
void SetThreadManager(ThreadManager * pmanager);
//void SetFileLog(FileLog * file_log);
void Init();
bool Init();
size_t AddTemplate(const std::wstring & file_name);
void ReadTemplates();

View File

@ -87,7 +87,7 @@ void InitPlugin(PluginInfo & info)
{
export_thread.SetBaseUrl(info.config->base_url);
info.system->thread_manager.Add(&export_thread, L"export");
info.res = info.system->thread_manager.Add(&export_thread, L"export");
export_info.ReadExportDirs();
}

View File

@ -156,6 +156,7 @@ void ProcessRequest(PluginInfo & info)
void InitPlugin(PluginInfo & info)
{
ConfigReload(info);
info.res = true;
}

View File

@ -176,6 +176,12 @@ void Rescan(PluginInfo & info)
}
}
void PluginInit(PluginInfo & info)
{
Rescan(info);
info.res = true;
}
} // namespace
@ -202,7 +208,7 @@ using namespace Thread;
info.plugin->Assign(WINIX_PL_THREAD_PREPARE_THREAD, PrepareThread);
// temporarily
info.plugin->Assign(WINIX_PLUGIN_INIT, Rescan);
info.plugin->Assign(WINIX_PLUGIN_INIT, PluginInit);
tdb.SetConn(info.db->GetConn());
tdb.LogQueries(info.config->log_db_query);

View File

@ -130,6 +130,7 @@ void RemoveTicket(PluginInfo & info)
void InitTicket(PluginInfo & info)
{
ticket_info.progress_prefix = info.config->Text(L"ticket_form_progress_prefix", L"progress");
info.res = true;
}