From bef19a5240fedada067ad7d8b851fd47f7b66ced Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Sat, 6 Feb 2016 18:43:35 +0000 Subject: [PATCH] refs #581, #592 Allow to renew a stale value by updating the timestamp on disk without altering the value. --- src/blackmisc/datacache.cpp | 40 +++++++++++++++++++++++++++++++++++++ src/blackmisc/datacache.h | 9 +++++++++ 2 files changed, 49 insertions(+) diff --git a/src/blackmisc/datacache.cpp b/src/blackmisc/datacache.cpp index c4a705b9f..2adbd3f7a 100644 --- a/src/blackmisc/datacache.cpp +++ b/src/blackmisc/datacache.cpp @@ -105,6 +105,11 @@ namespace BlackMisc QTimer::singleShot(0, &m_serializer, [this, key, ttl] { m_revision.setTimeToLive(key, ttl); }); } + void CDataCache::renewTimestamp(const QString &key, qint64 timestamp) + { + QTimer::singleShot(0, &m_serializer, [this, key, timestamp] { m_revision.overrideTimestamp(key, timestamp); }); + } + QString lockFileError(const QLockFile &lock) { switch (lock.error()) @@ -385,6 +390,41 @@ namespace BlackMisc m_timesToLive.insert(key, ttl); } + void CDataCacheRevision::overrideTimestamp(const QString &key, qint64 timestamp) + { + Q_ASSERT(! m_updateInProgress); + Q_ASSERT(! m_lockFile.isLocked()); + + if (! m_lockFile.lock()) + { + CLogMessage(this).error("Failed to lock %1: %2") << m_basename << lockFileError(m_lockFile); + m_lockFile.unlock(); + return; + } + + CAtomicFile revisionFile(m_basename + "/.rev"); + if (revisionFile.exists()) + { + if (! revisionFile.open(QFile::ReadWrite | QFile::Text)) + { + CLogMessage(this).error("Failed to open %1: %2") << revisionFile.fileName() << revisionFile.errorString(); + m_lockFile.unlock(); + return; + } + + auto json = QJsonDocument::fromJson(revisionFile.readAll()).object(); + auto timestamps = json.value("timestamps").toObject(); + timestamps.insert(key, timestamp); + json.insert("timestamps", timestamps); + + if (! (revisionFile.seek(0) && revisionFile.resize(0) && revisionFile.write(QJsonDocument(json).toJson()) && revisionFile.checkedClose())) + { + CLogMessage(this).error("Failed to write to %1: %2") << revisionFile.fileName() << revisionFile.errorString(); + } + } + m_lockFile.unlock(); + } + QJsonObject CDataCacheRevision::toJson(const QMap ×tamps) { QJsonObject result; diff --git a/src/blackmisc/datacache.h b/src/blackmisc/datacache.h index 94f1ecf19..9c1304fd2 100644 --- a/src/blackmisc/datacache.h +++ b/src/blackmisc/datacache.h @@ -76,6 +76,9 @@ namespace BlackMisc //! Set TTL value that will be written to the revision file. void setTimeToLive(const QString &key, int ttl); + //! Causes the new timestamp to be written to the revision file. + void overrideTimestamp(const QString &key, qint64 timestamp); + private: mutable QMutex m_mutex { QMutex::Recursive }; bool m_updateInProgress = false; @@ -157,6 +160,9 @@ namespace BlackMisc //! Method used for implementing TTL. void setTimeToLive(const QString &key, int ttl); + //! Method used for implementing timestamp renewal. + void renewTimestamp(const QString &key, qint64 timestamp); + private: CDataCache(); @@ -204,6 +210,9 @@ namespace BlackMisc //! True if the current timestamp is older than the TTL (time to live). bool isStale() const { return this->getTimestamp() + Trait::timeToLive() > QDateTime::currentMSecsSinceEpoch(); } + //! Don't change the value, but write a new timestamp, to extend the life of the value. + void renewTimestamp(qint64 timestamp) { return CDataCache::instance()->renewTimestamp(this->getKey(), timestamp); } + //! Return a future providing the value. If the value is still loading, the future will wait for it. //! If the value is not present, the variant is null. Bypasses async get and inhibits notification slot. std::future syncLoad() { return CDataCache::instance()->syncLoad(m_owner, this->getKey()); }