diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-06-26 10:24:58 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-06-26 10:24:58 +0200 |
commit | da4b74e593f1b1262e83824cc499bd855f1b4e3e (patch) | |
tree | 95b56aa073a85022c4218375e1c788894f01c2b6 /common/storage/entitystore.cpp | |
parent | 121c3bc96a273790414ae114082053cb649fc49a (diff) | |
download | sink-da4b74e593f1b1262e83824cc499bd855f1b4e3e.tar.gz sink-da4b74e593f1b1262e83824cc499bd855f1b4e3e.zip |
Avoid overwriting local changes with remote modifications.
The case we ran into is the following:
* Fetching the full payload and marking all messages of a thread as read
happens simultaneously.
* The local modification to mark as read gets immediately overwritten
when the full payload arrives.
* Eventually the modification gets replayed to the server though (and
the reversal isn't because coming from the source), so on next sync the
situation fixes itself.
To be able to improve this we try to protect local modifications in that
properties that have been modified since baseRevision (which currently
isn't, but should be equal to the last to the server replayed revision)
are not overwritten. This conflict resolution strategy thus always
prefers local modifications. baseRevision is currently set to the
current maximum revision of the store at the time when the resource
creates the modification.
Diffstat (limited to 'common/storage/entitystore.cpp')
-rw-r--r-- | common/storage/entitystore.cpp | 32 |
1 files changed, 26 insertions, 6 deletions
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index c5b5ffc..dd6bbf0 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp | |||
@@ -247,22 +247,26 @@ bool EntityStore::add(const QByteArray &type, ApplicationDomainType entity, bool | |||
247 | return true; | 247 | return true; |
248 | } | 248 | } |
249 | 249 | ||
250 | ApplicationDomain::ApplicationDomainType EntityStore::applyDiff(const QByteArray &type, const ApplicationDomainType ¤t, const ApplicationDomainType &diff, const QByteArrayList &deletions) const | 250 | ApplicationDomain::ApplicationDomainType EntityStore::applyDiff(const QByteArray &type, const ApplicationDomainType ¤t, const ApplicationDomainType &diff, const QByteArrayList &deletions, const QSet<QByteArray> &excludeProperties) const |
251 | { | 251 | { |
252 | SinkTraceCtx(d->logCtx) << "Applying diff: " << current.availableProperties() << "Deletions: " << deletions << "Changeset: " << diff.changedProperties(); | 252 | SinkTraceCtx(d->logCtx) << "Applying diff: " << current.availableProperties() << "Deletions: " << deletions << "Changeset: " << diff.changedProperties() << "Excluded: " << excludeProperties; |
253 | auto newEntity = *ApplicationDomainType::getInMemoryRepresentation<ApplicationDomainType>(current, current.availableProperties()); | 253 | auto newEntity = *ApplicationDomainType::getInMemoryRepresentation<ApplicationDomainType>(current, current.availableProperties()); |
254 | 254 | ||
255 | // Apply diff | 255 | // Apply diff |
256 | for (const auto &property : diff.changedProperties()) { | 256 | for (const auto &property : diff.changedProperties()) { |
257 | const auto value = diff.getProperty(property); | 257 | if (!excludeProperties.contains(property)) { |
258 | if (value.isValid()) { | 258 | const auto value = diff.getProperty(property); |
259 | newEntity.setProperty(property, value); | 259 | if (value.isValid()) { |
260 | newEntity.setProperty(property, value); | ||
261 | } | ||
260 | } | 262 | } |
261 | } | 263 | } |
262 | 264 | ||
263 | // Remove deletions | 265 | // Remove deletions |
264 | for (const auto &property : deletions) { | 266 | for (const auto &property : deletions) { |
265 | newEntity.setProperty(property, QVariant()); | 267 | if (!excludeProperties.contains(property)) { |
268 | newEntity.setProperty(property, QVariant()); | ||
269 | } | ||
266 | } | 270 | } |
267 | return newEntity; | 271 | return newEntity; |
268 | } | 272 | } |
@@ -639,6 +643,22 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid) | |||
639 | return true; | 643 | return true; |
640 | } | 644 | } |
641 | 645 | ||
646 | void EntityStore::readRevisions(const QByteArray &type, const QByteArray &uid, qint64 startingRevision, const std::function<void(const QByteArray &uid, qint64 revision, const EntityBuffer &entity)> callback) | ||
647 | { | ||
648 | Q_ASSERT(d); | ||
649 | Q_ASSERT(!uid.isEmpty()); | ||
650 | DataStore::mainDatabase(d->transaction, type) | ||
651 | .scan(uid, | ||
652 | [&](const QByteArray &key, const QByteArray &value) -> bool { | ||
653 | const auto revision = DataStore::revisionFromKey(key); | ||
654 | if (revision >= startingRevision) { | ||
655 | callback(DataStore::uidFromKey(key), revision, Sink::EntityBuffer(value.data(), value.size())); | ||
656 | } | ||
657 | return true; | ||
658 | }, | ||
659 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error while reading: " << error.message; }, true); | ||
660 | |||
661 | } | ||
642 | 662 | ||
643 | qint64 EntityStore::maxRevision() | 663 | qint64 EntityStore::maxRevision() |
644 | { | 664 | { |