summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2018-06-25 10:57:46 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2018-06-25 11:08:29 +0200
commit121c3bc96a273790414ae114082053cb649fc49a (patch)
tree262f994aaa0f7008aa9acf7589fc99a4447740f7
parented17c92c9f3be95e9b280f2abc67f1c0ba48e8c4 (diff)
downloadsink-121c3bc96a273790414ae114082053cb649fc49a.tar.gz
sink-121c3bc96a273790414ae114082053cb649fc49a.zip
Fixed the lost update scenario
If we get a fetchMore right between when the revision was updated and the incrementalQuery actually running, we ended up loosing the update because the result provider ended up with a too recent revision after the additional initial query.
-rw-r--r--common/queryrunner.cpp6
-rw-r--r--common/queryrunner.h15
-rw-r--r--tests/querytest.cpp49
3 files changed, 68 insertions, 2 deletions
diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp
index 0977940..9ac3517 100644
--- a/common/queryrunner.cpp
+++ b/common/queryrunner.cpp
@@ -152,7 +152,11 @@ void QueryRunner<DomainType>::fetch(const Sink::Query &query, const QByteArray &
152 if (query.liveQuery()) { 152 if (query.liveQuery()) {
153 mResourceAccess->sendRevisionReplayedCommand(result.newRevision); 153 mResourceAccess->sendRevisionReplayedCommand(result.newRevision);
154 } 154 }
155 mResultProvider->setRevision(result.newRevision); 155 //Initial queries do not fetch updates, so avoid updating the revision when fetching more content.
156 //Otherwise we end up breaking incremental updates.
157 if (!mResultProvider->revision()) {
158 mResultProvider->setRevision(result.newRevision);
159 }
156 mResultProvider->initialResultSetComplete(result.replayedAll); 160 mResultProvider->initialResultSetComplete(result.replayedAll);
157 if (mRequestFetchMore) { 161 if (mRequestFetchMore) {
158 mRequestFetchMore = false; 162 mRequestFetchMore = false;
diff --git a/common/queryrunner.h b/common/queryrunner.h
index e449570..52b5e2d 100644
--- a/common/queryrunner.h
+++ b/common/queryrunner.h
@@ -35,6 +35,16 @@ class QueryRunnerBase : public QObject
35public: 35public:
36 typedef std::function<void(Sink::ApplicationDomain::ApplicationDomainType &domainObject)> ResultTransformation; 36 typedef std::function<void(Sink::ApplicationDomain::ApplicationDomainType &domainObject)> ResultTransformation;
37 37
38 /// Disable query updates on revision change. Used for testing only.
39 void ignoreRevisionChanges(bool ignore) {
40 mIgnoreRevisionChanges = ignore;
41 }
42
43 /// Manually triger a revision change. Used for testing only.
44 void triggerRevisionChange() {
45 revisionChanged();
46 }
47
38protected: 48protected:
39 typedef std::function<KAsync::Job<void>()> QueryFunction; 49 typedef std::function<KAsync::Job<void>()> QueryFunction;
40 50
@@ -52,7 +62,9 @@ protected slots:
52 */ 62 */
53 void revisionChanged() 63 void revisionChanged()
54 { 64 {
55 run().exec(); 65 if (!mIgnoreRevisionChanges) {
66 run().exec();
67 }
56 } 68 }
57 69
58private: 70private:
@@ -65,6 +77,7 @@ private:
65 } 77 }
66 78
67 QueryFunction queryFunction; 79 QueryFunction queryFunction;
80 bool mIgnoreRevisionChanges{false};
68}; 81};
69 82
70/** 83/**
diff --git a/tests/querytest.cpp b/tests/querytest.cpp
index b52ba96..7685086 100644
--- a/tests/querytest.cpp
+++ b/tests/querytest.cpp
@@ -1337,6 +1337,55 @@ private slots:
1337 } 1337 }
1338 1338
1339 /* 1339 /*
1340 * This test excercises the scenario where a fetchMore is triggered after
1341 * the revision is already updated in storage, but the incremental query was not run yet.
1342 * This resulted in lost modification updates.
1343 */
1344 void testQueryRunnerDontMissUpdatesWithFetchMore()
1345 {
1346 // Setup
1347 auto folder1 = Folder::createEntity<Folder>("sink.dummy.instance1");
1348 folder1.setName("name1");
1349 VERIFYEXEC(Sink::Store::create<Folder>(folder1));
1350 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1"));
1351
1352 Query query;
1353 query.setFlags(Query::LiveQuery);
1354
1355 Sink::ResourceContext resourceContext{"sink.dummy.instance1", "sink.dummy", Sink::AdaptorFactoryRegistry::instance().getFactories("sink.dummy")};
1356 Sink::Log::Context logCtx;
1357 auto runner = new QueryRunner<Folder>(query, resourceContext, ApplicationDomain::getTypeName<Folder>(), logCtx);
1358
1359 auto emitter = runner->emitter();
1360 QList<Folder::Ptr> added;
1361 emitter->onAdded([&](Folder::Ptr folder) {
1362 added << folder;
1363 });
1364 QList<Folder::Ptr> modified;
1365 emitter->onModified([&](Folder::Ptr folder) {
1366 modified << folder;
1367 });
1368
1369 emitter->fetch();
1370 QTRY_COMPARE(added.size(), 1);
1371 QCOMPARE(modified.size(), 0);
1372
1373 runner->ignoreRevisionChanges(true);
1374
1375 folder1.setName("name2");
1376 VERIFYEXEC(Sink::Store::modify<Folder>(folder1));
1377 VERIFYEXEC(Sink::ResourceControl::flushMessageQueue("sink.dummy.instance1"));
1378
1379 emitter->fetch();
1380
1381 runner->ignoreRevisionChanges(false);
1382 runner->triggerRevisionChange();
1383
1384 QTRY_COMPARE(added.size(), 1);
1385 QTRY_COMPARE(modified.size(), 1);
1386 }
1387
1388 /*
1340 * This test is here to ensure we don't crash if we call removeFromDisk with a running query. 1389 * This test is here to ensure we don't crash if we call removeFromDisk with a running query.
1341 */ 1390 */
1342 void testRemoveFromDiskWithRunningQuery() 1391 void testRemoveFromDiskWithRunningQuery()