refs #601 Introduce "pinned" values, which are loaded synchronously by the CDataCache constructor.

This commit is contained in:
Mathew Sutcliffe
2016-03-10 20:44:23 +00:00
parent c6a038aaa8
commit 259f8d7a68
2 changed files with 64 additions and 5 deletions

View File

@@ -67,6 +67,7 @@ namespace BlackMisc
if (! QFile::exists(m_revisionFileName)) { QFile(m_revisionFileName).open(QFile::WriteOnly); } if (! QFile::exists(m_revisionFileName)) { QFile(m_revisionFileName).open(QFile::WriteOnly); }
m_watcher.addPath(m_revisionFileName); m_watcher.addPath(m_revisionFileName);
m_serializer.start(); m_serializer.start();
m_serializer.loadFromStore({}, false, true); // load pinned values
loadFromStoreAsync(); loadFromStoreAsync();
} }
@@ -113,6 +114,11 @@ namespace BlackMisc
QTimer::singleShot(0, &m_serializer, [this, key, timestamp] { m_revision.overrideTimestamp(key, timestamp); }); 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) QString lockFileError(const QLockFile &lock)
{ {
switch (lock.error()) switch (lock.error())
@@ -226,9 +232,9 @@ namespace BlackMisc
applyDeferredChanges(); // apply changes which we grabbed at the last minute above 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()) if (lock && m_cache->m_revision.isPendingRead())
{ {
CValueCachePacket newValues; CValueCachePacket newValues;
@@ -263,7 +269,7 @@ namespace BlackMisc
}); });
} }
CDataCacheRevision::LockGuard CDataCacheRevision::beginUpdate(const QMap<QString, qint64> &timestamps, bool updateUuid) CDataCacheRevision::LockGuard CDataCacheRevision::beginUpdate(const QMap<QString, qint64> &timestamps, bool updateUuid, bool pinsOnly)
{ {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
@@ -317,6 +323,15 @@ namespace BlackMisc
if (m_pendingWrite) { return guard; } if (m_pendingWrite) { return guard; }
return {}; 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) else if (revisionFile.size() > 0)
{ {
@@ -357,6 +372,7 @@ namespace BlackMisc
json.insert("uuid", m_uuid.toString()); json.insert("uuid", m_uuid.toString());
json.insert("timestamps", toJson(timestamps)); json.insert("timestamps", toJson(timestamps));
json.insert("ttl", toJson(m_timesToLive)); json.insert("ttl", toJson(m_timesToLive));
json.insert("pins", toJson(m_pinnedValues));
revisionFile.write(QJsonDocument(json).toJson()); revisionFile.write(QJsonDocument(json).toJson());
} }
@@ -465,6 +481,13 @@ namespace BlackMisc
m_lockFile.unlock(); m_lockFile.unlock();
} }
void CDataCacheRevision::pinValue(const QString &key)
{
Q_ASSERT(! m_updateInProgress);
m_pinnedValues.insert(key);
}
QJsonObject CDataCacheRevision::toJson(const QMap<QString, qint64> &timestamps) QJsonObject CDataCacheRevision::toJson(const QMap<QString, qint64> &timestamps)
{ {
QJsonObject result; QJsonObject result;
@@ -484,6 +507,26 @@ namespace BlackMisc
} }
return result; return result;
} }
QJsonArray CDataCacheRevision::toJson(const QSet<QString> &pins)
{
QJsonArray result;
for (auto it = pins.begin(); it != pins.end(); ++it)
{
result.push_back(*it);
}
return result;
}
QSet<QString> CDataCacheRevision::fromJson(const QJsonArray &pins)
{
QSet<QString> result;
for (auto it = pins.begin(); it != pins.end(); ++it)
{
result.insert(it->toString());
}
return result;
}
} }
//! \endcond //! \endcond

View File

@@ -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). //! 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 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. //! \param updateUuid Whether to prepare for an actual update, or just interrograte whether one is needed.
LockGuard beginUpdate(const QMap<QString, qint64> &timestamps, bool updateUuid = true); //! \param pinsOnly Only load pinned values.
LockGuard beginUpdate(const QMap<QString, qint64> &timestamps, bool updateUuid = true, bool pinsOnly = false);
//! During update, writes a new revision file with new timestamps. //! During update, writes a new revision file with new timestamps.
void writeNewRevision(const QMap<QString, qint64> &timestamps); void writeNewRevision(const QMap<QString, qint64> &timestamps);
@@ -110,6 +111,9 @@ namespace BlackMisc
//! Causes the new timestamp to be written to the revision file. //! Causes the new timestamp to be written to the revision file.
void overrideTimestamp(const QString &key, qint64 timestamp); 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: private:
mutable QMutex m_mutex { QMutex::Recursive }; mutable QMutex m_mutex { QMutex::Recursive };
bool m_updateInProgress = false; bool m_updateInProgress = false;
@@ -120,10 +124,13 @@ namespace BlackMisc
QUuid m_uuid; QUuid m_uuid;
QMap<QString, qint64> m_timestamps; QMap<QString, qint64> m_timestamps;
QMap<QString, qint64> m_timesToLive; QMap<QString, qint64> m_timesToLive;
QSet<QString> m_pinnedValues;
std::vector<std::promise<void>> m_promises; std::vector<std::promise<void>> m_promises;
static QJsonObject toJson(const QMap<QString, qint64> &timestamps); static QJsonObject toJson(const QMap<QString, qint64> &timestamps);
static QMap<QString, qint64> fromJson(const QJsonObject &timestamps); static QMap<QString, qint64> fromJson(const QJsonObject &timestamps);
static QJsonArray toJson(const QSet<QString> &pins);
static QSet<QString> 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. //! 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 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 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. //! \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: signals:
//! Signal back to the cache when values have been loaded. //! Signal back to the cache when values have been loaded.
@@ -194,6 +202,9 @@ namespace BlackMisc
//! Method used for implementing timestamp renewal. //! Method used for implementing timestamp renewal.
void renewTimestamp(const QString &key, qint64 timestamp); void renewTimestamp(const QString &key, qint64 timestamp);
//! Method used for implementing pinning values.
void pinValue(const QString &key);
private: private:
CDataCache(); CDataCache();
@@ -231,6 +242,7 @@ namespace BlackMisc
CData::CCached(CDataCache::instance(), Trait::key(), Trait::isValid, Trait::defaultValue(), owner, slot) 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::timeToLive() >= 0) { CDataCache::instance()->setTimeToLive(Trait::key(), Trait::timeToLive()); }
if (Trait::isPinned()) { CDataCache::instance()->pinValue(Trait::key()); }
} }
//! Reset the data to its default value. //! Reset the data to its default value.
@@ -282,6 +294,10 @@ namespace BlackMisc
//! Default is -1 which means value never becomes stale. //! Default is -1 which means value never becomes stale.
static int timeToLive() { return -1; } 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. //! Deleted default constructor.
CDataTrait() = delete; CDataTrait() = delete;