mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-22 06:45:37 +08:00
Always read bootstrap.json from local file
This simplifies the bootstrap phase and synchronization.
This commit is contained in:
@@ -61,81 +61,19 @@ namespace BlackCore
|
||||
CStatusMessageList CSetupReader::asyncLoad()
|
||||
{
|
||||
CStatusMessageList msgs;
|
||||
if (!m_localSetupFileValue.isEmpty())
|
||||
if (m_localSetupFileValue.isEmpty())
|
||||
{
|
||||
// TODO Check if file exists or is broken
|
||||
msgs = this->readLocalBootstrapFile(CSwiftDirectories::shareDirectory() + "/shared/bootstrap/bootstrap.json");
|
||||
}
|
||||
else
|
||||
{
|
||||
msgs = this->readLocalBootstrapFile(m_localSetupFileValue);
|
||||
}
|
||||
msgs.push_back(this->manageSetupAvailability(false, msgs.isSuccess()));
|
||||
return msgs;
|
||||
}
|
||||
|
||||
m_setup.synchronize(); // make sure it is loaded
|
||||
const CGlobalSetup cachedSetup = m_setup.get();
|
||||
const bool cacheAvailable = cachedSetup.wasLoaded();
|
||||
msgs.push_back(cacheAvailable ?
|
||||
CStatusMessage(this, CStatusMessage::SeverityInfo, u"Cached setup synchronized and contains data") :
|
||||
CStatusMessage(this, CStatusMessage::SeverityInfo, u"Cached setup synchronized, but no data in cache"));
|
||||
|
||||
if (m_bootstrapMode == CacheOnly)
|
||||
{
|
||||
msgs.push_back(cacheAvailable ?
|
||||
CStatusMessage(this, CStatusMessage::SeverityInfo, u"Cache only setup, using it as it is") :
|
||||
CStatusMessage(this, CStatusMessage::SeverityError, u"Cache only setup, but cache is empty"));
|
||||
msgs.push_back(this->manageSetupAvailability(false, false)); // treat cache as local read
|
||||
return msgs;
|
||||
}
|
||||
|
||||
m_bootstrapUrls.clear(); // clean up previous values
|
||||
|
||||
// web URL
|
||||
if (!m_bootstrapUrlFileValue.isEmpty())
|
||||
{
|
||||
// start with the one from cmd args
|
||||
m_bootstrapUrls.push_front(CUrl(m_bootstrapUrlFileValue));
|
||||
}
|
||||
|
||||
// if ever loaded add those URLs
|
||||
if (cacheAvailable)
|
||||
{
|
||||
if (m_bootstrapMode != Explicit)
|
||||
{
|
||||
// also use previously cached URLs
|
||||
const CUrlList bootstrapCacheUrls(cachedSetup.getSwiftBootstrapFileUrls());
|
||||
m_bootstrapUrls.push_back(bootstrapCacheUrls);
|
||||
msgs.push_back(bootstrapCacheUrls.isEmpty() ?
|
||||
CStatusMessage(this, CStatusMessage::SeverityWarning, u"No bootstrap URLs in cache") :
|
||||
CStatusMessage(this, CStatusMessage::SeverityInfo, u"Adding " % QString::number(bootstrapCacheUrls.size()) % u" bootstrap URLs from cache"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityInfo, u"Empty cache, will not add URLs from cache"));
|
||||
}
|
||||
|
||||
m_bootstrapUrls.removeDuplicates(); // clean up
|
||||
if (m_bootstrapUrls.isEmpty())
|
||||
{
|
||||
// after all still empty
|
||||
msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityInfo, u"Your log files are here: " % CSwiftDirectories::logDirectory()));
|
||||
msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityError, u"No bootstrap URLs, cannot load setup"));
|
||||
}
|
||||
else
|
||||
{
|
||||
CStatusMessageList readMsgs = this->triggerReadSetup(); // async loading
|
||||
if (cacheAvailable && readMsgs.isFailure())
|
||||
{
|
||||
// error, but cache is available, we can continue
|
||||
readMsgs.clampSeverity(CStatusMessage::SeverityWarning);
|
||||
msgs.push_back(CStatusMessage(this, CStatusMessage::SeverityWarning, u"Loading setup failed, but cache is available, will continue"));
|
||||
msgs.push_back(readMsgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
msgs.push_back(readMsgs);
|
||||
}
|
||||
}
|
||||
return msgs;
|
||||
}
|
||||
|
||||
bool CSetupReader::parseCmdLineArguments()
|
||||
{
|
||||
// copy vars at beginning to simplify a threadsafe version in the future
|
||||
@@ -207,79 +145,6 @@ namespace BlackCore
|
||||
this->manageSetupAvailability(false, false); // fake a failed web read
|
||||
}
|
||||
|
||||
void CSetupReader::readSetup()
|
||||
{
|
||||
const CStatusMessageList msgs(this->triggerReadSetup());
|
||||
if (!msgs.isSuccess())
|
||||
{
|
||||
CLogMessage::preformatted(msgs);
|
||||
}
|
||||
}
|
||||
|
||||
CStatusMessageList CSetupReader::triggerReadSetup()
|
||||
{
|
||||
if (!sApp || m_shutdown) { return CStatusMessage(this, CStatusMessage::SeverityError, u"shutdown"); }
|
||||
if (m_bootstrapUrls.isEmpty())
|
||||
{
|
||||
const CStatusMessage m(this, CStatusMessage::SeverityError, u"No bootstrap URLs");
|
||||
CStatusMessageList msgs(m);
|
||||
this->setLastSetupReadErrorMessages(msgs);
|
||||
return msgs;
|
||||
}
|
||||
|
||||
// same web load within 5000ms
|
||||
const CGlobalSetup setup = m_setup.get();
|
||||
if (m_setup.lastUpdatedAge() < 5000 && setup.wasLoadedFromWeb()) // really loaded from web
|
||||
{
|
||||
const CStatusMessage m(this, CStatusMessage::SeverityInfo, u"Update info just updated, skip read");
|
||||
CStatusMessageList msgs(m);
|
||||
this->setLastSetupReadErrorMessages(msgs);
|
||||
return msgs;
|
||||
}
|
||||
|
||||
// trigger two reads, the file size is small
|
||||
CStatusMessage m1, m2;
|
||||
const CUrlList randomUrls = m_bootstrapUrls.randomElements(2);
|
||||
CUrl url = randomUrls.front();
|
||||
if (!url.isEmpty())
|
||||
{
|
||||
m1 = CStatusMessage(this, CStatusMessage::SeverityInfo, u"Start reading bootstrap 1st URL: " % url.toQString());
|
||||
sApp->getFromNetwork(url.toNetworkRequest(), { this, &CSetupReader::parseBootstrapFile }, { this, &CSetupReader::networkReplyProgress });
|
||||
}
|
||||
else
|
||||
{
|
||||
m1 = CStatusMessage(this, CStatusMessage::SeverityError, u"First bootstrap URL is empty");
|
||||
}
|
||||
|
||||
emit this->setupLoadingMessages(m1);
|
||||
|
||||
url = randomUrls.back();
|
||||
if (!url.isEmpty())
|
||||
{
|
||||
m2 = CStatusMessage(this, CStatusMessage::SeverityInfo, u"Will also trigger deferred bootstrap reading 2nd URL: " % url.toQString());
|
||||
QPointer<CSetupReader> myself(this);
|
||||
QTimer::singleShot(2000, this, [ = ]
|
||||
{
|
||||
if (!myself || !sApp || sApp->isShuttingDown()) { return; }
|
||||
if (!m_lastSuccessfulSetupUrl.isEmpty())
|
||||
{
|
||||
// already read
|
||||
CLogMessage(this).info(u"Cancel second bootstrap read ('%1'), as there was a 1st read: '%2'") << url.toQString() << m_lastSuccessfulSetupUrl;
|
||||
return;
|
||||
}
|
||||
sApp->getFromNetwork(url.toNetworkRequest(), { this, &CSetupReader::parseBootstrapFile }, { this, &CSetupReader::networkReplyProgress });
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m2 = CStatusMessage(this, CStatusMessage::SeverityWarning, u"2nd bootstrap URL is empty");
|
||||
}
|
||||
|
||||
CStatusMessageList msgs({m1, m2});
|
||||
this->setLastSetupReadErrorMessages(msgs);
|
||||
return msgs;
|
||||
}
|
||||
|
||||
CSetupReader::BootstrapMode CSetupReader::stringToEnum(const QString &s)
|
||||
{
|
||||
const QString bsm(s.toLower().trimmed());
|
||||
@@ -333,104 +198,6 @@ namespace BlackCore
|
||||
}
|
||||
}
|
||||
|
||||
void CSetupReader::parseBootstrapFile(QNetworkReply *nwReplyPtr)
|
||||
{
|
||||
// wrap pointer, make sure any exit cleans up reply
|
||||
// required to use delete later as object is created in a different thread
|
||||
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
|
||||
if (m_shutdown) { return; }
|
||||
|
||||
const QUrl url(nwReply->url());
|
||||
const QString urlString(url.toString());
|
||||
|
||||
if (nwReply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
qint64 lastModified = CNetworkUtils::lastModifiedMsSinceEpoch(nwReply.data());
|
||||
QString setupJson(nwReplyPtr->readAll());
|
||||
nwReplyPtr->close();
|
||||
if (setupJson.isEmpty())
|
||||
{
|
||||
const CStatusMessage m = CLogMessage(this).info(u"No bootstrap setup file at '%1'") << urlString;
|
||||
emit this->setupLoadingMessages(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
const CGlobalSetup currentSetup = m_setup.get();
|
||||
CGlobalSetup loadedSetup;
|
||||
loadedSetup.convertFromJson(setupJson, false);
|
||||
loadedSetup.markAsLoadedFromFile(false);
|
||||
|
||||
// make sure the file is not broken
|
||||
const bool loadedFileBroken = (loadedSetup.getMappingMinimumVersionString() == CBuildConfig::getVersionString()) || loadedSetup.getMappingMinimumVersionString().isEmpty();
|
||||
loadedSetup.markAsLoadedFromWeb(!loadedFileBroken);
|
||||
BLACK_VERIFY_X(!loadedFileBroken, Q_FUNC_INFO, "Appears that the bootstrap file is not really parsed");
|
||||
if (loadedFileBroken)
|
||||
{
|
||||
CLogMessage(this).warning(u"Setup file downloaded from '%1' seems to be broken! Ignoring it.") << urlString;
|
||||
return;
|
||||
}
|
||||
|
||||
const CUrl sharedUrl(loadedSetup.getCorrespondingSharedUrl(url));
|
||||
if (!sharedUrl.isEmpty()) { emit this->successfullyReadSharedUrl(sharedUrl); }
|
||||
|
||||
if (lastModified > 0 && lastModified > loadedSetup.getMSecsSinceEpoch()) { loadedSetup.setMSecsSinceEpoch(lastModified); }
|
||||
bool sameVersionLoaded = (loadedSetup == currentSetup);
|
||||
if (sameVersionLoaded)
|
||||
{
|
||||
CLogMessage(this).info(u"Same setup version loaded from '%1' as already in data cache '%2'") << urlString << m_setup.getFilename();
|
||||
CLogMessage::preformatted(this->manageSetupAvailability(true));
|
||||
return; // success
|
||||
}
|
||||
|
||||
// in the past I used to do a timestamp comparison here and skipped further setting
|
||||
// with changed files from a different URL this was wrongly assuming outdated loaded files and was removed
|
||||
const CStatusMessage m = m_setup.set(loadedSetup, loadedSetup.getMSecsSinceEpoch());
|
||||
CLogMessage::preformatted(m);
|
||||
if (m.isSeverityInfoOrLess())
|
||||
{
|
||||
// no issue with cache
|
||||
const CStatusMessage m2 = CLogMessage(this).info(u"Loaded setup from '%1'") << urlString;
|
||||
emit this->setupLoadingMessages(m2);
|
||||
CLogMessage(this).info(u"Setup: Updated data cache in '%1'") << m_setup.getFilename();
|
||||
{
|
||||
QWriteLocker l(&m_lockSetup);
|
||||
m_lastSuccessfulSetupUrl = urlString;
|
||||
}
|
||||
}
|
||||
CLogMessage::preformatted(this->manageSetupAvailability(true));
|
||||
return; // success
|
||||
}
|
||||
catch (const CJsonException &ex)
|
||||
{
|
||||
// we downloaded an unparsable JSON file.
|
||||
// as we control those files something is wrong
|
||||
const QString errorMsg = QStringLiteral("Setup file loaded from '%1' cannot be parsed").arg(urlString);
|
||||
const CStatusMessage msg = CStatusMessage::fromJsonException(ex, this, errorMsg);
|
||||
CLogMessage::preformatted(msg);
|
||||
emit this->setupLoadingMessages(msg);
|
||||
|
||||
// in dev. I get notified, in productive code I try next URL by falling thru
|
||||
BLACK_VERIFY_X(false, Q_FUNC_INFO, errorMsg.toLocal8Bit().constData());
|
||||
m_bootstrapReadErrors++;
|
||||
}
|
||||
} // json empty
|
||||
} // no error
|
||||
else
|
||||
{
|
||||
// network error, log as warning as we will read again if possible
|
||||
// however, store as error because this will be a possible root cause if nothing else is
|
||||
nwReply->abort();
|
||||
m_bootstrapReadErrors++;
|
||||
if (m_bootstrapReadErrors > 1)
|
||||
{
|
||||
// getting here means at least 2 URLs failes
|
||||
this->manageSetupAvailability(false); // this updates also the cache availability so we can contine with cache only
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QStringList &CSetupReader::getLogCategories()
|
||||
{
|
||||
static const QStringList cats({ "swift.setupreader", CLogCategories::webservice(), CLogCategories::startup()});
|
||||
|
||||
@@ -161,21 +161,9 @@ namespace BlackCore
|
||||
BlackMisc::CStatusMessageList m_setupReadErrorMsgs; //!< last parsing error messages
|
||||
BlackMisc::CData<Data::TGlobalSetup> m_setup { this }; //!< data cache setup
|
||||
|
||||
//! Setup has been read (aka bootstrap file)
|
||||
void parseBootstrapFile(QNetworkReply *nwReplyPtr);
|
||||
|
||||
//! Update info has been read
|
||||
void parseUpdateInfoFile(QNetworkReply *nwReplyPtr);
|
||||
|
||||
//! Do reading
|
||||
void readSetup();
|
||||
|
||||
//! Read by local individual file and update cache from that
|
||||
BlackMisc::CStatusMessageList readLocalBootstrapFile(const QString &fileName);
|
||||
|
||||
//! Trigger reading
|
||||
BlackMisc::CStatusMessageList triggerReadSetup();
|
||||
|
||||
//! Emit the availability signal and state and trigger follow up actions
|
||||
//! \threadsafe
|
||||
BlackMisc::CStatusMessageList manageSetupAvailability(bool webRead, bool localRead = false);
|
||||
|
||||
Reference in New Issue
Block a user