From 259f8d7a684eaddf587aa4d20e5d63e6126965c3 Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Thu, 10 Mar 2016 20:44:23 +0000 Subject: [PATCH] refs #601 Introduce "pinned" values, which are loaded synchronously by the CDataCache constructor. --- src/blackmisc/datacache.cpp | 49 ++++++++++++++++++++++++++++++++++--- src/blackmisc/datacache.h | 20 +++++++++++++-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/blackmisc/datacache.cpp b/src/blackmisc/datacache.cpp index 7bf99e513..b2f5e5c4f 100644 --- a/src/blackmisc/datacache.cpp +++ b/src/blackmisc/datacache.cpp @@ -67,6 +67,7 @@ namespace BlackMisc if (! QFile::exists(m_revisionFileName)) { QFile(m_revisionFileName).open(QFile::WriteOnly); } m_watcher.addPath(m_revisionFileName); m_serializer.start(); + m_serializer.loadFromStore({}, false, true); // load pinned values loadFromStoreAsync(); } @@ -113,6 +114,11 @@ namespace BlackMisc QTimer::singleShot(0, &m_serializer, [this, key, timestamp] { m_revision.overrideTimestamp(key, timestamp); }); } + void CDataCache::pinValue(const QString &key) + { + QTimer::singleShot(0, &m_serializer, [this, key] { m_revision.pinValue(key); }); + } + QString lockFileError(const QLockFile &lock) { switch (lock.error()) @@ -226,9 +232,9 @@ namespace BlackMisc applyDeferredChanges(); // apply changes which we grabbed at the last minute above } - CDataCacheRevision::LockGuard CDataCacheSerializer::loadFromStore(const BlackMisc::CValueCachePacket &baseline, bool defer) + CDataCacheRevision::LockGuard CDataCacheSerializer::loadFromStore(const BlackMisc::CValueCachePacket &baseline, bool defer, bool pinsOnly) { - auto lock = m_cache->m_revision.beginUpdate(baseline.toTimestampMap()); + auto lock = m_cache->m_revision.beginUpdate(baseline.toTimestampMap(), ! pinsOnly, pinsOnly); if (lock && m_cache->m_revision.isPendingRead()) { CValueCachePacket newValues; @@ -263,7 +269,7 @@ namespace BlackMisc }); } - CDataCacheRevision::LockGuard CDataCacheRevision::beginUpdate(const QMap ×tamps, bool updateUuid) + CDataCacheRevision::LockGuard CDataCacheRevision::beginUpdate(const QMap ×tamps, bool updateUuid, bool pinsOnly) { QMutexLocker lock(&m_mutex); @@ -317,6 +323,15 @@ namespace BlackMisc if (m_pendingWrite) { return guard; } return {}; } + + if (pinsOnly) + { + auto pins = fromJson(json.value("pins").toArray()); + for (const auto &key : m_timestamps.keys()) + { + if (! pins.contains(key)) { m_timestamps.remove(key); } + } + } } else if (revisionFile.size() > 0) { @@ -357,6 +372,7 @@ namespace BlackMisc json.insert("uuid", m_uuid.toString()); json.insert("timestamps", toJson(timestamps)); json.insert("ttl", toJson(m_timesToLive)); + json.insert("pins", toJson(m_pinnedValues)); revisionFile.write(QJsonDocument(json).toJson()); } @@ -465,6 +481,13 @@ namespace BlackMisc m_lockFile.unlock(); } + void CDataCacheRevision::pinValue(const QString &key) + { + Q_ASSERT(! m_updateInProgress); + + m_pinnedValues.insert(key); + } + QJsonObject CDataCacheRevision::toJson(const QMap ×tamps) { QJsonObject result; @@ -484,6 +507,26 @@ namespace BlackMisc } return result; } + + QJsonArray CDataCacheRevision::toJson(const QSet &pins) + { + QJsonArray result; + for (auto it = pins.begin(); it != pins.end(); ++it) + { + result.push_back(*it); + } + return result; + } + + QSet CDataCacheRevision::fromJson(const QJsonArray &pins) + { + QSet result; + for (auto it = pins.begin(); it != pins.end(); ++it) + { + result.insert(it->toString()); + } + return result; + } } //! \endcond diff --git a/src/blackmisc/datacache.h b/src/blackmisc/datacache.h index a37dea840..e1ae3f408 100644 --- a/src/blackmisc/datacache.h +++ b/src/blackmisc/datacache.h @@ -78,7 +78,8 @@ namespace BlackMisc //! Return value can be converted to bool, false means update is not started (error, or already up-to-date). //! \param timestamps Current in-memory timestamps, to be compared with the on-disk ones. //! \param updateUuid Whether to prepare for an actual update, or just interrograte whether one is needed. - LockGuard beginUpdate(const QMap ×tamps, bool updateUuid = true); + //! \param pinsOnly Only load pinned values. + LockGuard beginUpdate(const QMap ×tamps, bool updateUuid = true, bool pinsOnly = false); //! During update, writes a new revision file with new timestamps. void writeNewRevision(const QMap ×tamps); @@ -110,6 +111,9 @@ namespace BlackMisc //! Causes the new timestamp to be written to the revision file. void overrideTimestamp(const QString &key, qint64 timestamp); + //! Set the flag which will cause the value to be pre-loaded. + void pinValue(const QString &key); + private: mutable QMutex m_mutex { QMutex::Recursive }; bool m_updateInProgress = false; @@ -120,10 +124,13 @@ namespace BlackMisc QUuid m_uuid; QMap m_timestamps; QMap m_timesToLive; + QSet m_pinnedValues; std::vector> m_promises; static QJsonObject toJson(const QMap ×tamps); static QMap fromJson(const QJsonObject ×tamps); + static QJsonArray toJson(const QSet &pins); + static QSet fromJson(const QJsonArray &pins); }; /*! @@ -145,8 +152,9 @@ namespace BlackMisc //! Also called by saveToStore, to ensure that remote changes to unrelated values are not lost. //! \param baseline A snapshot of the currently loaded values, taken when the load is queued. //! \param defer Whether to defer applying the changes. Used when called by saveToStore. + //! \param pinsOnly Only load pinned values. //! \return Usually ignored, but can be held in order to retain the revision file lock. - CDataCacheRevision::LockGuard loadFromStore(const BlackMisc::CValueCachePacket &baseline, bool defer = false); + CDataCacheRevision::LockGuard loadFromStore(const BlackMisc::CValueCachePacket &baseline, bool defer = false, bool pinsOnly = false); signals: //! Signal back to the cache when values have been loaded. @@ -194,6 +202,9 @@ namespace BlackMisc //! Method used for implementing timestamp renewal. void renewTimestamp(const QString &key, qint64 timestamp); + //! Method used for implementing pinning values. + void pinValue(const QString &key); + private: CDataCache(); @@ -231,6 +242,7 @@ namespace BlackMisc CData::CCached(CDataCache::instance(), Trait::key(), Trait::isValid, Trait::defaultValue(), owner, slot) { if (Trait::timeToLive() >= 0) { CDataCache::instance()->setTimeToLive(Trait::key(), Trait::timeToLive()); } + if (Trait::isPinned()) { CDataCache::instance()->pinValue(Trait::key()); } } //! Reset the data to its default value. @@ -282,6 +294,10 @@ namespace BlackMisc //! Default is -1 which means value never becomes stale. static int timeToLive() { return -1; } + //! If true, then value will be synchronously loaded when CDataCache is constructed. + //! Good for small, important values; bad for large ones. + static bool isPinned() { return false; } + //! Deleted default constructor. CDataTrait() = delete;