stefan-egli commented on code in PR #1261:
URL: https://github.com/apache/jackrabbit-oak/pull/1261#discussion_r1464610212


##########
oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreSweepTest.java:
##########
@@ -70,12 +81,266 @@ public void before() throws Exception {
         ns = createDocumentNodeStore(0);
     }
 
+    @After
+    public void after() throws Exception {
+        if (mf != null) {
+            mf.dispose();
+            mf = null;
+        }
+    }
+
     @AfterClass
     public static void resetClock() {
         Revision.resetClockToDefault();
         ClusterNodeInfo.resetClockToDefault();
     }
 
+    interface UpdateCallback {
+        /**
+         * @return true to continue going via this UpdateHandler, false to 
stop using
+         *         this UpdateHandler
+         */
+        boolean handleUpdate(UpdateOp op);
+    }
+
+    /** limited purpose MongoFixture that allows to pause after a specific 
update */
+    MongoFixture pausableMongoDocumentStore(final String targetId,
+            final UpdateCallback updateHandler) {
+        return new MongoFixture() {
+            @Override
+            public DocumentStore createDocumentStore(Builder builder) {
+                try {
+                    MongoConnection connection = MongoUtils.getConnection();
+                    CountingMongoDatabase db = new CountingMongoDatabase(
+                            connection.getDatabase());
+                    return new MongoDocumentStore(connection.getMongoClient(), 
db,
+                            builder) {
+                        volatile boolean done;
+
+                        @Override
+                        public <T extends Document> T findAndUpdate(
+                                Collection<T> collection, UpdateOp update) {
+                            try {
+                                return super.findAndUpdate(collection, update);
+                            } finally {
+                                updateHandler(targetId, updateHandler, update);
+                            }
+                        }
+
+                        private void updateHandler(final String targetId,
+                                final UpdateCallback updateHandler, UpdateOp 
update) {
+                            if (done) {
+                                return;
+                            }
+                            if (update.getId().equals(targetId)) {
+                                if (!updateHandler.handleUpdate(update)) {
+                                    done = true;
+                                    return;
+                                }
+                            }
+                        }
+
+                        @Override
+                        public <T extends Document> T createOrUpdate(
+                                Collection<T> collection, UpdateOp update) {
+                            try {
+                                return super.createOrUpdate(collection, 
update);
+                            } finally {
+                                updateHandler(targetId, updateHandler, update);
+                            }
+                        }
+
+                        @Override
+                        public <T extends Document> List<T> createOrUpdate(
+                                Collection<T> collection, List<UpdateOp> 
updateOps) {
+                            try {
+                                return super.createOrUpdate(collection, 
updateOps);
+                            } finally {
+                                for (UpdateOp update : updateOps) {
+                                    updateHandler(targetId, updateHandler, 
update);
+                                }
+                            }
+                        }
+                    };
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+    }
+
+    /**
+     * Test to show-case a race-condition between a collision and
+     * MongoDocumentStore's nodesCache: when a document is read into
+     * the nodesCache shortly before a collision is rolled back,
+     * it runs risk of later making uncommitted changes visible.
+     * <p/>
+     * The test case works as follows:
+     * <ul>
+     * <li>consider clusterId 2 and 4 being active in a cluster</li>
+     * <li>a target path /parent/foo (and sibling /parent/bar) having 
+     * formerly been created</li>
+     * <li>clusterId 2: now wants to delete /parent/foo and /parent/bar, and 
starts
+     * doing so versus mongo by first setting _deleted:true on those two nodes
+     * (using revision r123456789a-0-2)</li>
+     * <li>before clusterId 2 continues, clusterId 4 comes with a conflicting 
update
+     * on /parent/bar (using revision r123456789b-0-4). This update notices
+     * the changes from 2 and leaves a corresponding collision marker (on 0:/
+     * with _collisions.r123456789a-0-2=r123456789b-0-4)</li>
+     * <li>beforre clusterId 4 proceeds, it happens to force a read from
+     * 2:/parent/foo from mongo - this is achieved as a result of
+     * another /parent/foo</li>
+     * <li>the result of the above is clusterId 4 having a state of 
2:/parent/foo
+     * in its MongoDocumentStore nodesCache that contains uncommitted 
information.
+     * In this test case, that uncommitted information is a deletion. But it 
could
+     * be anything else really.</li>
+     * <li>then things continue on both clusterId 2 and 4 (from the previous
+     * test-pause)</li>
+     * <li>then clusterId 2 does another, unrelated change on /parent/bar,
+     * thereby resulting in a newer lastRev (on root and /parent) than the 
collision.
+     * Also, it results in a sweepRev that is newer than the collision</li>
+     * <li>when later, clusterId 4 reads /parent/foo, it still finds the
+     * cached 2:/parent/foo document with the uncommitted data - plus it
+     * now has a readRevision/lastRevision that is newer than that - plus
+     * it will resolve that uncommitted data's revision (r123456789a-0-2)
+     * as commitvalue="c", since it is older than the sweepRevision</li>
+     * <li>and thus, clusterId 4 managed to read uncommitted, rolled back data
+     * from an earlier collision</li>
+     * </ul>
+     */
+    @Test
+    @Ignore(value = "OAK-10595")
+    public void cachingUncommittedBeforeCollisionRollback() throws Exception {
+        // two nodes part of the game:
+        // 1 : the main one that starts to do a subtree deletion
+        // 2 : a peer one that gets in between the above and causes a collision
+        // as a result 1 manages to read 2's rolled back (uncommitted) state as

Review Comment:
   yes you're right, it's the other way round. (it's also a bit confusing that 
this says 1 and 2 while everywhere else 2 and 4 are used.. I'll adjust this as 
well)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@jackrabbit.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to