diff options
author | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2022-10-19 14:45:13 +0300 |
---|---|---|
committer | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2022-10-21 15:21:24 +0300 |
commit | 2dbb8c19ea19b9cef28e29899e50b788eded6f4d (patch) | |
tree | 410463f11d6cb3d56557a703c4cd55b3fd22602d | |
parent | f5dff60486e4d6ef355e1420226f1d059b0e441d (diff) |
Add possibility to synchronize metadata cache manually
The contents of the cache subdirectories and manifest.json may mismatch
if the installer process is killed after the metadata fetch, because the
manifest file is normally synchronized only when the cache object gets
destroyed. While this is a recoverable error, it creates unnecessary
overhead of discarding broken items and downloading them again.
Add support for synchronizing the cache by the caller and do so each
time the cache is updated by Metadatajob.
Task-number: QTIFW-2817
Change-Id: Ia4cd3de44d57e8d732d11dc26968aa87323ada0f
Reviewed-by: Katja Marttila <katja.marttila@qt.io>
-rw-r--r-- | src/libs/installer/genericdatacache.cpp | 22 | ||||
-rw-r--r-- | src/libs/installer/genericdatacache.h | 1 | ||||
-rw-r--r-- | src/libs/installer/metadatajob.cpp | 20 | ||||
-rw-r--r-- | tests/auto/installer/metadatacache/tst_metadatacache.cpp | 26 |
4 files changed, 65 insertions, 4 deletions
diff --git a/src/libs/installer/genericdatacache.cpp b/src/libs/installer/genericdatacache.cpp index 7f21a5365..1b33976c8 100644 --- a/src/libs/installer/genericdatacache.cpp +++ b/src/libs/installer/genericdatacache.cpp @@ -112,6 +112,11 @@ CacheableItem::~CacheableItem() methods declared in the \l{CacheableItem} interface. The GenericDataCache\<T\> class can still be explicitly specialized to use the derived type as a template argument, to allow retrieving items as the derived type without casting. + + Each cache has a manifest file in its root directory, which lists the version + and wrapped type of the cache, and all its items. The file is updated automatically + when the cache object is destructed, or it can be updated periodically by + calling \l{sync()}. */ /*! @@ -273,6 +278,23 @@ bool GenericDataCache<T>::clear() } /*! + \fn template <typename T> QInstaller::GenericDataCache<T>::sync() + + Synchronizes the contents of the cache to its manifest file. Returns \c true + if the manifest file was updates successfully, \c false otherwise. +*/ +template<typename T> +bool GenericDataCache<T>::sync() +{ + if (m_invalidated) { + setErrorString(QCoreApplication::translate("GenericDataCache", + "Cannot synchronize invalidated cache.")); + return false; + } + return toDisk(); +} + +/*! \fn template <typename T> QInstaller::GenericDataCache<T>::isValid() const Returns \c true if the cache is valid, \c false otherwise. A cache is considered diff --git a/src/libs/installer/genericdatacache.h b/src/libs/installer/genericdatacache.h index 7b909cc7a..5042ed9a4 100644 --- a/src/libs/installer/genericdatacache.h +++ b/src/libs/installer/genericdatacache.h @@ -72,6 +72,7 @@ public: bool initialize(); bool clear(); + bool sync(); bool isValid() const; QString errorString() const; diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp index a15209a31..659648e1a 100644 --- a/src/libs/installer/metadatajob.cpp +++ b/src/libs/installer/metadatajob.cpp @@ -346,12 +346,11 @@ bool MetadataJob::updateCache() // Register items from current run to cache QStringList registeredKeys; + bool success = true; for (auto *meta : qAsConst(m_fetchedMetadata)) { if (!m_metaFromCache.registerItem(meta, true)) { - emitFinishedWithError(QInstaller::CacheError, m_metaFromCache.errorString() - + u' ' - + tr("Clearing the cache directory and restarting the application may solve this.")); - return false; + success = false; + break; } meta->setPersistentRepositoryPath(meta->repository().url()); registeredKeys.append(m_fetchedMetadata.key(meta)); @@ -360,11 +359,24 @@ bool MetadataJob::updateCache() for (auto &key : qAsConst(registeredKeys)) m_fetchedMetadata.remove(key); + // Bail out if there was error while registering items + if (!success) { + emitFinishedWithError(QInstaller::CacheError, m_metaFromCache.errorString() + u' ' + + tr("Clearing the cache directory and restarting the application may solve this.")); + m_metaFromCache.sync(); + return false; + } + // ...and clean up obsolete cached items const QList<Metadata *> obsolete = m_metaFromCache.obsoleteItems(); for (auto *meta : obsolete) m_metaFromCache.removeItem(meta->checksum()); + if (!m_metaFromCache.sync()) { + emitFinishedWithError(QInstaller::CacheError, m_metaFromCache.errorString() + u' ' + + tr("Clearing the cache directory and restarting the application may solve this.")); + return false; + } return true; } diff --git a/tests/auto/installer/metadatacache/tst_metadatacache.cpp b/tests/auto/installer/metadatacache/tst_metadatacache.cpp index 46e4d1dfe..f3ab1b98e 100644 --- a/tests/auto/installer/metadatacache/tst_metadatacache.cpp +++ b/tests/auto/installer/metadatacache/tst_metadatacache.cpp @@ -86,6 +86,24 @@ private: } } + QStringList itemsFromManifest(const QString &manifestPath) + { + QFile manifestFile(manifestPath); + if (!manifestFile.open(QIODevice::ReadOnly)) + return QStringList(); + + const QByteArray manifestData = manifestFile.readAll(); + const QJsonDocument manifestJsonDoc(QJsonDocument::fromJson(manifestData)); + const QJsonObject docJsonObject = manifestJsonDoc.object(); + const QJsonArray itemsJsonArray = docJsonObject.value(QLatin1String("items")).toArray(); + + QStringList items; + for (const auto &itemJsonValue : itemsJsonArray) + items << itemJsonValue.toString(); + + return items; + } + QByteArray checksumFromUpdateFile(const QString &directory) { QFile updateFile(directory + QDir::separator() + QLatin1String("Updates.xml")); @@ -130,6 +148,9 @@ private slots: metadata = cache.itemByChecksum(m_newMetadataItemChecksum); QVERIFY(metadata); QVERIFY(metadata->isValid()); + QVERIFY(!QFileInfo::exists(m_cachePath + "/manifest.json")); + QVERIFY(cache.sync()); + QVERIFY(itemsFromManifest(m_cachePath + "/manifest.json").contains(QLatin1String(m_newMetadataItemChecksum))); QVERIFY(cache.clear()); QVERIFY(!QFileInfo::exists(m_cachePath)); @@ -141,11 +162,16 @@ private slots: GenericDataCache<Metadata> cache(m_cachePath, "Metadata", "1.0.0"); Metadata *metadata = new Metadata(":/data/local-temp-repository/"); + QVERIFY(itemsFromManifest(m_cachePath + "/manifest.json").contains(QLatin1String(m_oldMetadataItemChecksum))); QVERIFY(cache.registerItem(metadata)); metadata = cache.itemByChecksum(m_newMetadataItemChecksum); QVERIFY(metadata); QVERIFY(metadata->isValid()); + QVERIFY(cache.sync()); + const QStringList manifestItems = itemsFromManifest(m_cachePath + "/manifest.json"); + QVERIFY(manifestItems.contains(QLatin1String(m_oldMetadataItemChecksum))); + QVERIFY(manifestItems.contains(QLatin1String(m_newMetadataItemChecksum))); QVERIFY(cache.clear()); QVERIFY(!QFileInfo::exists(m_cachePath)); |