refactor: Remove CNetworkWatchdog

The watchdog was used in a few places as a shortcut to skip reading
data. Further, it was used in some places in the UI to display
connectivity. But it also introduced quite some complexity. In some
cases it can be fragile: network accessibilty cannot be looked up on all
platforms/hardware constellations. The connectivity could change
between the last watchdog call and the real call. Hence all readers must
still handle the case where the connection fails.
To simplify swift and further reduce the dependency onto the project
infrastructure (pings etc.), this removes the watchdog.
This also removes the QNetworkConfigurationManager, which is deprecated
and not available with Qt6.
This commit is contained in:
Lars Toenning
2024-03-12 09:27:02 +01:00
parent d31084db3a
commit b4cbed107b
32 changed files with 18 additions and 1073 deletions

View File

@@ -244,11 +244,6 @@ namespace BlackCore::Db
this->threadAssertCheck();
if (!this->doWorkCheck()) { return; }
entity &= CEntityFlags::AirportEntity;
if (!this->isInternetAccessible())
{
emit this->dataRead(entity, CEntityFlags::ReadSkipped, 0, {});
return;
}
if (entity.testFlag(CEntityFlags::AirportEntity))
{

View File

@@ -213,7 +213,6 @@ namespace BlackCore::Db
// ps_read is implemented in the derived classes
if (entities == CEntityFlags::NoEntity) { return; }
if (!this->isInternetAccessible(QStringLiteral("No network/internet access, will not read %1").arg(CEntityFlags::flagToString(entities)))) { return; }
//! https://dev.swift-project.org/T490
QPointer<CDatabaseReader> myself(this);
@@ -381,8 +380,6 @@ namespace BlackCore::Db
int CDatabaseReader::requestHeadersOfSharedFiles(CEntityFlags::Entity entities)
{
if (!this->isInternetAccessible(QStringLiteral("No network/internet access, will not read shared file headers for %1").arg(CEntityFlags::flagToString(entities)))) { return false; }
CEntityFlags::Entity allEntities = entities & CEntityFlags::AllDbEntitiesNoInfoObjects;
CEntityFlags::Entity currentEntity = CEntityFlags::iterateDbEntities(allEntities);
const CUrl urlSharedDbdata = CDatabaseReader::getSharedDbdataDirectoryUrl();

View File

@@ -171,12 +171,6 @@ namespace BlackCore::Db
if (!this->doWorkCheck()) { return; }
entities &= CEntityFlags::AllIcaoCountriesCategory;
if (!this->isInternetAccessible())
{
emit this->dataRead(entities, CEntityFlags::ReadSkipped, 0, {});
return;
}
CEntityFlags::Entity entitiesTriggered = CEntityFlags::NoEntity;
CUrl url;
if (entities.testFlag(CEntityFlags::AircraftIcaoEntity))

View File

@@ -182,11 +182,6 @@ namespace BlackCore::Db
this->threadAssertCheck();
if (!this->doWorkCheck()) { return; }
entities &= CEntityFlags::DistributorLiveryModel;
if (!this->isInternetAccessible())
{
emit this->dataRead(entities, CEntityFlags::ReadSkipped, 0, {});
return;
}
CEntityFlags::Entity triggeredRead = CEntityFlags::NoEntity;
CUrl url;

View File

@@ -1,356 +0,0 @@
// SPDX-FileCopyrightText: Copyright (C) 2017 swift Project Community / Contributors
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
#include "blackcore/db/networkwatchdog.h"
#include "blackcore/application.h"
#include "blackmisc/logmessage.h"
#include "blackmisc/network/networkutils.h"
#include <QNetworkReply>
#include <QDateTime>
#include <QPointer>
using namespace BlackMisc;
using namespace BlackMisc::Network;
using namespace BlackCore::Data;
namespace BlackCore::Db
{
const QStringList &CNetworkWatchdog::getLogCategories()
{
static const QStringList cats = CContinuousWorker::getLogCategories() + QStringList {
CLogCategories::swiftDbWebservice(), CLogCategories::webservice(), CLogCategories::network()
};
return cats;
}
CNetworkWatchdog::CNetworkWatchdog(bool networkAccessible, QObject *owner) : CContinuousWorker(owner, "swift DB watchdog")
{
Q_ASSERT_X(owner, Q_FUNC_INFO, "Need owner (normally sApp)");
m_networkAccessible = networkAccessible;
m_internetAccessible = networkAccessible;
m_dbAccessible = networkAccessible && m_checkDbAccessibility;
m_updateTimer.setInterval(10 * 1000);
connect(&m_updateTimer, &QTimer::timeout, this, &CNetworkWatchdog::doWork);
}
void CNetworkWatchdog::setDbAccessibility(bool accessible)
{
m_dbAccessible = accessible;
m_internetAccessible = m_internetAccessible && this->isNetworkkAccessibleOrCheckDisabled();
// restart timer
QPointer<CNetworkWatchdog> myself(this);
QTimer::singleShot(0, &m_updateTimer, [=] {
if (!myself) { return; }
m_updateTimer.start();
});
}
bool CNetworkWatchdog::hasWorkingSharedUrl() const
{
if (!this->isNetworkkAccessibleOrCheckDisabled()) { return false; }
return !this->getWorkingSharedUrl().isEmpty();
}
CUrl CNetworkWatchdog::getWorkingSharedUrl() const
{
if (!this->isNetworkkAccessibleOrCheckDisabled()) { return CUrl(); }
QReadLocker l(&m_lockUrl);
return m_workingSharedUrl;
}
int CNetworkWatchdog::triggerCheck()
{
if (!this->doWorkCheck()) { return false; } // senseless
if (m_checkInProgress) { return -1; }
const int n = this->getCheckCount();
const QPointer<CNetworkWatchdog> myself(this);
QTimer::singleShot(0, this, [=] {
if (!myself) { return; }
this->doWork();
});
return n; // triggered
}
QString CNetworkWatchdog::getLastPingDbUrl() const
{
QReadLocker l(&m_lockUrl);
return m_lastPingUrl;
}
QString CNetworkWatchdog::getCheckInfo() const
{
static const QString info("Network accessibility check: %1 | Internet accessible: %2 (good: %3 / bad: %4), swift DB accessible: %5 (good: %6 / bad: %7) DB last ping URL: '%8' canConnect: %9ms");
const QString pUrl(this->getLastPingDbUrl());
static const QString cct = QString::number(CanConnectTimeMs);
return info.arg(boolToEnabledDisabled(!this->isNetworkAccessibilityCheckDisabled()), boolToYesNo(this->isInternetAccessible())).arg(m_totalGoodCountInternet).arg(m_totalBadCountInternet).arg(boolToYesNo(this->isSwiftDbAccessible())).arg(m_totalGoodCountDb).arg(m_totalBadCountDb).arg(pUrl, cct); // cct has to be string, otherwise the % in the URL will be replaced
}
void CNetworkWatchdog::setWorkingSharedUrl(const CUrl &workingUrl)
{
QWriteLocker l(&m_lockUrl);
m_workingSharedUrl = workingUrl;
}
void CNetworkWatchdog::doWork()
{
if (!this->doWorkCheck()) { return; }
if (m_checkInProgress) { return; }
m_checkInProgress = true;
// lazy init
if (!this->hasWorkingSharedUrl())
{
this->initWorkingSharedUrlFromSetup();
}
// checks
do
{
const bool wasDbAvailable = m_dbAccessible;
const bool wasInternetAvailable = m_internetAccessible;
const bool networkAccessible = this->isNetworkkAccessibleOrCheckDisabled();
const CUrl testUrl(CNetworkWatchdog::dbTestUrl());
bool canConnectDb = m_checkDbAccessibility && networkAccessible;
if (canConnectDb)
{
// running here in background worker check twice
canConnectDb = CNetworkUtils::canConnect(testUrl, CanConnectTimeMs);
if (!canConnectDb)
{
canConnectDb = CNetworkUtils::canConnect(testUrl, CanConnectTimeMs);
}
}
if (m_checkDbAccessibility && m_doDetailedCheck && canConnectDb)
{
const qint64 pingIntervalSecs = sApp->getGlobalSetup().getDbClientPingIntervalSecs();
if (QDateTime::currentSecsSinceEpoch() >= pingIntervalSecs)
{
m_nextPingSecsSinceEpoch = QDateTime::currentSecsSinceEpoch() + pingIntervalSecs;
this->pingDbClientService(CGlobalSetup::PingStarted);
canConnectDb = m_lastClientPingSuccess;
}
}
bool canConnectInternet = canConnectDb;
bool checkInternetAccess = !canConnectDb;
m_dbAccessible = canConnectDb;
if (canConnectDb)
{
// DB available means internet available
m_internetAccessible = canConnectDb;
}
// check shared URL
if (!this->doWorkCheck()) { break; }
if (m_checkSharedUrl && networkAccessible)
{
if (CNetworkUtils::canConnect(this->getWorkingSharedUrl()))
{
canConnectInternet = true;
checkInternetAccess = false;
}
else
{
const CUrl sharedUrl = this->getWorkingSharedUrl();
if (!sharedUrl.isEmpty())
{
canConnectInternet = true;
checkInternetAccess = false;
this->setWorkingSharedUrl(sharedUrl);
}
}
}
// check internet access
if (!this->doWorkCheck()) { break; }
if (checkInternetAccess)
{
QString message;
static const QString testHost1("www.google.com"); // what else?
canConnectInternet = CNetworkUtils::canConnect(testHost1, 443, message, CanConnectTimeMs); // running in background worker
if (!canConnectInternet)
{
static const QString testHost2("www.microsoft.com"); // secondary test
canConnectInternet = CNetworkUtils::canConnect(testHost2, 80, message, CanConnectTimeMs); // running in background worker
}
if (canConnectInternet) { m_totalGoodCountInternet++; }
else { m_totalBadCountInternet++; }
}
m_internetAccessible = networkAccessible && canConnectInternet;
// signals
this->triggerChangedSignals(wasDbAvailable, wasInternetAvailable);
}
while (false);
m_updateTimer.start(); // restart
m_totalCheckCount++;
m_checkInProgress = false;
}
bool CNetworkWatchdog::doWorkCheck() const
{
if (!sApp || sApp->isShuttingDown()) { return false; }
if (!this->isEnabled()) { return false; }
return true;
}
void CNetworkWatchdog::setNetworkAccessibility(QNetworkAccessManager::NetworkAccessibility accessibility)
{
// avoid unnecessary signals
const int accessiblityInt = static_cast<int>(accessibility);
if (m_networkAccessibility == accessiblityInt) { return; }
if (m_disableNetworkCheck) { return; } // ignore with disabled check
// shift to thread
if (!CThreadUtils::isInThisThread(this))
{
QPointer<CNetworkWatchdog> myself(this);
QTimer::singleShot(0, this, [=] {
if (!sApp || sApp->isShuttingDown() || !myself) { return; }
this->setNetworkAccessibility(accessibility);
});
return;
}
// set values
m_networkAccessibility = accessiblityInt;
const bool db = m_dbAccessible;
const bool internet = m_internetAccessible;
// Intentionally rating unknown as "accessible"
if (accessibility == QNetworkAccessManager::NotAccessible)
{
m_networkAccessible = false;
m_dbAccessible = false;
m_internetAccessible = false;
this->triggerChangedSignals(db, internet);
}
else
{
m_networkAccessible = true;
const QPointer<CNetworkWatchdog> myself(this);
QTimer::singleShot(0, this, [=] {
if (!myself) { return; }
this->doWork();
});
}
emit this->changedNetworkAccessible(accessibility);
}
void CNetworkWatchdog::networkConfigurationsUpdateCompleted()
{
// void
}
void CNetworkWatchdog::setOnline(bool online)
{
m_online = online;
}
void CNetworkWatchdog::gracefulShutdown()
{
this->pingDbClientService(CGlobalSetup::PingCompleteShutdown);
this->quit();
}
void CNetworkWatchdog::pingDbClientService(CGlobalSetup::PingType type, bool force)
{
if (!sApp || sApp->isShuttingDown()) { return; }
if (!force && !this->isSwiftDbAccessible()) { return; }
const CGlobalSetup gs = sApp->getGlobalSetup();
const CUrl pingUrl = gs.getDbClientPingServiceUrl(type);
sApp->getFromNetwork(pingUrl, { this, &CNetworkWatchdog::replyPingClientService });
}
bool CNetworkWatchdog::disableNetworkAccessibilityCheck(bool disable)
{
if (disable == m_disableNetworkCheck) { return false; }
m_disableNetworkCheck = disable;
return true;
}
void CNetworkWatchdog::replyPingClientService(QNetworkReply *nwReply)
{
// init and clean up
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nw(nwReply); // delete reply
const bool ok = (nw->error() == QNetworkReply::NoError);
const QString errorString = nw->errorString();
const QString url = nw->url().toString();
nw->close();
if (!sApp || sApp->isShuttingDown()) { return; }
Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Wrong thread");
m_lastClientPingSuccess = ok;
{
QWriteLocker l(&m_lockUrl);
m_lastPingUrl = url;
}
if (ok)
{
// be a little less verbose
if ((m_totalGoodCountDb % 5 == 0) || m_consecutivePingBadCount > 0)
{
CLogMessage(this).info(u"Watchdog pinged '%1'") << url;
}
m_totalGoodCountDb++;
m_consecutivePingBadCount = 0;
}
else
{
m_totalBadCountDb++;
m_consecutivePingBadCount++;
if (m_logOwnMessages)
{
CStatusMessage(this).warning(u"Watchdog ping failed, error: '%1', total good/bad DB counts: %2/%3") << errorString << m_totalGoodCountDb << m_totalBadCountDb;
}
}
this->setDbAccessibility(ok);
}
void CNetworkWatchdog::triggerChangedSignals(bool oldDbAccessible, bool oldInternetAccessible)
{
if (!this->doWorkCheck()) { return; }
if (oldDbAccessible != m_dbAccessible)
{
const CUrl testUrl(this->dbTestUrl());
emit this->changedSwiftDbAccessibility(m_dbAccessible, testUrl);
}
if (oldInternetAccessible != m_internetAccessible)
{
emit this->changedInternetAccessibility(m_internetAccessible);
}
}
void CNetworkWatchdog::initWorkingSharedUrlFromSetup()
{
const CUrl workingUrl(CNetworkWatchdog::workingSharedUrlFromSetup()); // takes long
this->setWorkingSharedUrl(workingUrl);
}
CUrl CNetworkWatchdog::dbTestUrl()
{
// requires global setup to be read
if (!sApp || sApp->isShuttingDown()) { return CUrl(); }
const CUrl testUrl(sApp->getGlobalSetup().getDbHomePageUrl());
return testUrl;
}
CUrl CNetworkWatchdog::workingSharedUrlFromSetup()
{
const CUrlList urls(sApp->getGlobalSetup().getSwiftSharedUrls());
CFailoverUrlList failoverUrls(urls);
return failoverUrls.getRandomWorkingUrl(2, CanConnectTimeMs); // uses CNetworkUtils::canConnect
}
} // ns

View File

@@ -1,192 +0,0 @@
// SPDX-FileCopyrightText: Copyright (C) 2017 swift Project Community / Contributors
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
//! \file
#ifndef BLACKCORE_DB_NETWORKWATCHDOG_H
#define BLACKCORE_DB_NETWORKWATCHDOG_H
#include "blackcore/data/globalsetup.h"
#include "blackcore/blackcoreexport.h"
#include "blackmisc/network/url.h"
#include "blackmisc/worker.h"
#include "blackmisc/logcategories.h"
#include <atomic>
#include <QReadWriteLock>
#include <QNetworkAccessManager>
namespace BlackCore::Db
{
//! Monitoring the swift DB, internet access, shared URLs
class BLACKCORE_EXPORT CNetworkWatchdog : public BlackMisc::CContinuousWorker
{
Q_OBJECT
public:
//! Log categories
static const QStringList &getLogCategories();
//! Ctor
explicit CNetworkWatchdog(bool networkAccessible, QObject *owner);
//! Network status changed, use this function to inform the watchdog
//! \threadsafe
void setNetworkAccessibility(QNetworkAccessManager::NetworkAccessibility accessibility);
//! Configuration updates completed as reported by QNetworkConfigurationManager::updateCompleted
void networkConfigurationsUpdateCompleted();
//! Set online as reported by QNetworkConfigurationManager::onlineStateChanged
//! \threadsafe
void setOnline(bool online);
//! DB available?
//! \threadsafe
bool isSwiftDbAccessible() const { return m_dbAccessible; }
//! Set DB as avialable (from external)
//! \remark if data was read from DB, this can save another check
//! \threadsafe
void setDbAccessibility(bool accessible);
//! DB is accessible
//! \threadsafe
void setDbIsAccessible() { this->setDbAccessibility(true); }
//! DB is NOT accessible
//! \threadsafe
void setDbIsNotAccessible() { this->setDbAccessibility(false); }
//! Check the DB availability, can disable the check
//! \threadsafe
void setCheckDbAccessibility(bool check) { m_checkDbAccessibility = check; }
//! Check the shared URL, can disable the check
//! \threadsafe
void setCheckSharedUrl(bool check) { m_checkSharedUrl = check; }
//! Do a detailed check via HTTP
//! \threadsafe
void setDoDetailedCheck(bool check) { m_doDetailedCheck = check; }
//! Internet available?
//! \threadsafe
bool isInternetAccessible() const { return m_internetAccessible; }
//! Accesible or check disabled?
bool isNetworkkAccessibleOrCheckDisabled() const { return m_networkAccessible || m_disableNetworkCheck; }
//! Has working shared URL?
//! \threadsafe
bool hasWorkingSharedUrl() const;
//! A working shared URL
//! \threadsafe
BlackMisc::Network::CUrl getWorkingSharedUrl() const;
//! Log.own status messages
//! \threadsafe
void setLogOwnMessages(bool log) { m_logOwnMessages = log; }
//! Run a check
int triggerCheck();
//! Number of completed checks
//! \threadsafe
int getCheckCount() const { return m_totalCheckCount; }
//! Last URL used for ping /DB ping service)
QString getLastPingDbUrl() const;
//! Number of completed checks
//! \threadsafe
QString getCheckInfo() const;
//! Set working URL from external
//! \threadsafe
void setWorkingSharedUrl(const BlackMisc::Network::CUrl &workingUrl);
//! Graceful shutdown
void gracefulShutdown();
//! Ping the DB server, fire and forget (no feedback etc)
void pingDbClientService(Data::CGlobalSetup::PingType type = Data::CGlobalSetup::PingUnspecific, bool force = false);
//! Disable the network check
//! \remark if disabled network reports always accessible
//! \threadsafe
bool disableNetworkAccessibilityCheck(bool disable);
//! Has network check been disabled?
//! \threadsafe
bool isNetworkAccessibilityCheckDisabled() const { return m_disableNetworkCheck; }
//! Network check enabled?
//! \threadsafe
bool isNetworkAccessibilityCheckEnabled() const { return !this->isNetworkAccessibilityCheckDisabled(); }
//! The URL being tested
//! \remark depends on BlackCore::Application::getGlobalSetup()
//! \private primarily accessible for unit tests
static BlackMisc::Network::CUrl dbTestUrl();
signals:
//! DB was available, but not longer is and vice versa
void changedSwiftDbAccessibility(bool available, const BlackMisc::Network::CUrl &url);
//! Internet was available, but not longer is and vice versa
void changedInternetAccessibility(bool available);
//! Cleaned version of QNetworkAccessManager::networkAccessibleChanged
//! \remark does only fire if the accessibility changed
void changedNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible);
private:
static constexpr int CanConnectTimeMs = 5000;
//! Do work, i.e. check connectivity
void doWork();
//! Do check
bool doWorkCheck() const;
//! Trigger the changed signals and avoid unneccessary signals
void triggerChangedSignals(bool oldDbAccessible, bool oldInternetAccessible);
//! Init a working shared URL
void initWorkingSharedUrlFromSetup();
//! Received reply of client service ping
void replyPingClientService(QNetworkReply *nwReply);
//! Obtain working DB data file location URL
//! \remark depends on BlackCore::Application::getGlobalSetup()
static BlackMisc::Network::CUrl workingSharedUrlFromSetup();
std::atomic_bool m_logOwnMessages { true };
std::atomic_bool m_doDetailedCheck { true };
std::atomic_bool m_networkAccessible { true };
std::atomic_bool m_disableNetworkCheck { false }; //!< if this is true, network accessible always reports true/accessible
std::atomic_bool m_online { true };
std::atomic_bool m_internetAccessible { true };
std::atomic_bool m_dbAccessible { true };
std::atomic_bool m_lastClientPingSuccess { true }; //!< ping swift DB client service, real HTTP response
std::atomic_bool m_checkDbAccessibility { true };
std::atomic_bool m_checkSharedUrl { true };
std::atomic_bool m_checkInProgress { false }; //!< a check is currently in progress
std::atomic<qint64> m_nextPingSecsSinceEpoch { 0 }; //!< time at which next ping will be sent
std::atomic_int m_networkAccessibility { QNetworkAccessManager::Accessible }; //!< last state
std::atomic_int m_totalCheckCount { 0 }; //!< counting number of checks
std::atomic_int m_totalBadCountDb { 0 }; //!< Total number of DB failing counts (only real responses when tried)
std::atomic_int m_totalBadCountInternet { 0 }; //!< Total number of Internet failing count (only when network is accessible)
std::atomic_int m_totalGoodCountDb { 0 };
std::atomic_int m_totalGoodCountInternet { 0 };
std::atomic_int m_consecutivePingBadCount { 0 }; //!< Bad count of ping until a godd state is received
QString m_lastPingUrl;
BlackMisc::Network::CUrl m_workingSharedUrl;
mutable QReadWriteLock m_lockUrl;
};
} // ns
#endif // guard