You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2018/09/25 19:26:09 UTC
svn commit: r1841976 - in /jackrabbit/oak/trunk/oak-store-document/src:
main/java/org/apache/jackrabbit/oak/plugins/document/
main/java/org/apache/jackrabbit/oak/plugins/document/memory/
main/java/org/apache/jackrabbit/oak/plugins/document/mongo/ main/...
Author: mreutegg
Date: Tue Sep 25 19:26:09 2018
New Revision: 1841976
URL: http://svn.apache.org/viewvc?rev=1841976&view=rev
Log:
OAK-7745: Clarify update semantics on deleted nodes
Modified:
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentStore.java
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/RecoveryLock.java
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/memory/MemoryDocumentStore.java
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BulkCreateOrUpdateTest.java
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java
Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentStore.java?rev=1841976&r1=1841975&r2=1841976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentStore.java Tue Sep 25 19:26:09 2018
@@ -257,7 +257,9 @@ public interface DocumentStore {
/**
* Atomically checks if the document exists and updates it, otherwise the
- * document is created (aka upsert). The returned document is immutable.
+ * document is created (aka "upsert"), unless the update operation requires
+ * the document to be present (see {@link UpdateOp#isNew()}). The returned
+ * document is immutable.
* <p>
* If this method fails with a {@code DocumentStoreException}, then the
* document may or may not have been created or updated. It is the
@@ -267,13 +269,20 @@ public interface DocumentStore {
* cache. That is, an implementation could simply evict documents with the
* given keys from the cache.
*
- * @param <T> the document type
- * @param collection the collection
- * @param update the update operation (where {@link Condition}s are not allowed)
- * @return the old document or <code>null</code> if it didn't exist before.
- * @throws IllegalArgumentException when the {@linkplain UpdateOp} is conditional
- * @throws DocumentStoreException if the operation failed. E.g. because of
- * an I/O error.
+ * @param <T>
+ * the document type
+ * @param collection
+ * the collection
+ * @param update
+ * the update operation (where {@link Condition}s are not
+ * allowed)
+ * @return the old document or {@code null} if it either didn't exist
+ * before, or the {@linkplain UpdateOp} required the document to be
+ * present but {@link UpdateOp#isNew()} was {@code false}.
+ * @throws IllegalArgumentException
+ * when the {@linkplain UpdateOp} is conditional
+ * @throws DocumentStoreException
+ * if the operation failed. E.g. because of an I/O error.
*/
@Nullable
<T extends Document> T createOrUpdate(Collection<T> collection,
Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/RecoveryLock.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/RecoveryLock.java?rev=1841976&r1=1841975&r2=1841976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/RecoveryLock.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/RecoveryLock.java Tue Sep 25 19:26:09 2018
@@ -108,7 +108,7 @@ class RecoveryLock {
}
ClusterNodeInfoDocument old = store.findAndUpdate(CLUSTER_NODES, update);
if (old == null) {
- throw new RuntimeException("ClusterNodeInfo document for " + clusterId + " missing.");
+ throw new DocumentStoreException("ClusterNodeInfo document for " + clusterId + " does not exist.");
}
LOG.info("Released recovery lock for cluster id {} (recovery successful: {})",
clusterId, success);
Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/memory/MemoryDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/memory/MemoryDocumentStore.java?rev=1841976&r1=1841975&r2=1841976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/memory/MemoryDocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/memory/MemoryDocumentStore.java Tue Sep 25 19:26:09 2018
@@ -327,7 +327,7 @@ public class MemoryDocumentStore impleme
T doc = collection.newDocument(this);
if (oldDoc == null) {
if (!update.isNew()) {
- throw new DocumentStoreException("Document does not exist: " + update.getId());
+ return null;
}
} else {
oldDoc.deepCopy(doc);
Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java?rev=1841976&r1=1841975&r2=1841976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java Tue Sep 25 19:26:09 2018
@@ -140,6 +140,17 @@ public class MongoDocumentStore implemen
public static final int IN_CLAUSE_BATCH_SIZE = 500;
+ /**
+ * A conflicting ID assignment on insert. Used by
+ * {@link #sendBulkUpdate(Collection, java.util.Collection, Map)} for
+ * {@code UpdateOp} with {@code isNew == false}. An upsert with this
+ * operation will always fail when there is no existing document.
+ */
+ private static final Map CONFLICT_ON_INSERT = new BasicDBObject(
+ "$setOnInsert",
+ new BasicDBObject(Document.ID, "a").append(Document.ID, "b")
+ ).toMap();
+
private MongoCollection<BasicDBObject> nodes;
private final MongoCollection<BasicDBObject> clusterNodes;
private final MongoCollection<BasicDBObject> settings;
@@ -891,7 +902,7 @@ public class MongoDocumentStore implemen
MongoCollection<BasicDBObject> dbCollection = getDBCollection(collection);
// make sure we don't modify the original updateOp
updateOp = updateOp.copy();
- Bson update = createUpdate(updateOp, false);
+ Bson update = createUpdate(updateOp, !upsert);
Lock lock = null;
if (collection == Collection.NODES) {
@@ -959,7 +970,7 @@ public class MongoDocumentStore implemen
}
});
- if (oldNode == null){
+ if (oldNode == null && upsert) {
newEntry = true;
}
@@ -982,6 +993,9 @@ public class MongoDocumentStore implemen
} else {
// updateOp without conditions and not an upsert
// this means the document does not exist
+ if (collection == Collection.NODES) {
+ nodesCache.invalidate(updateOp.getId());
+ }
}
return oldDoc;
} catch (Exception e) {
@@ -1001,7 +1015,7 @@ public class MongoDocumentStore implemen
throws DocumentStoreException {
log("createOrUpdate", update);
UpdateUtils.assertUnconditional(update);
- T doc = findAndModify(collection, update, true, false);
+ T doc = findAndModify(collection, update, update.isNew(), false);
log("createOrUpdate returns ", doc);
return doc;
}
@@ -1220,14 +1234,18 @@ public class MongoDocumentStore implemen
for (UpdateOp updateOp : updateOps) {
String id = updateOp.getId();
Bson query = createQueryForUpdate(id, updateOp.getConditions());
+ // fail on insert when isNew == false
+ boolean failInsert = !updateOp.isNew();
T oldDoc = oldDocs.get(id);
if (oldDoc == null || oldDoc == NodeDocument.NULL) {
query = Filters.and(query, Filters.exists(Document.MOD_COUNT, false));
- writes.add(new UpdateOneModel<>(query, createUpdate(updateOp, true), new UpdateOptions().upsert(true)));
} else {
query = Filters.and(query, Filters.eq(Document.MOD_COUNT, oldDoc.getModCount()));
- writes.add(new UpdateOneModel<>(query, createUpdate(updateOp, false), new UpdateOptions().upsert(true)));
}
+ writes.add(new UpdateOneModel<>(query,
+ createUpdate(updateOp, failInsert),
+ new UpdateOptions().upsert(true))
+ );
bulkIds[i++] = id;
}
@@ -1631,11 +1649,11 @@ public class MongoDocumentStore implemen
* Creates a MongoDB update object from the given UpdateOp.
*
* @param updateOp the update op.
- * @param includeId whether to include the SET id operation
+ * @param failOnInsert whether to create an update that will fail on insert.
* @return the DBObject.
*/
- @NotNull
- private static BasicDBObject createUpdate(UpdateOp updateOp, boolean includeId) {
+ private static BasicDBObject createUpdate(UpdateOp updateOp,
+ boolean failOnInsert) {
BasicDBObject setUpdates = new BasicDBObject();
BasicDBObject maxUpdates = new BasicDBObject();
BasicDBObject incUpdates = new BasicDBObject();
@@ -1643,9 +1661,6 @@ public class MongoDocumentStore implemen
// always increment modCount
updateOp.increment(Document.MOD_COUNT, 1);
- if (includeId) {
- setUpdates.append(Document.ID, updateOp.getId());
- }
// other updates
for (Entry<Key, Operation> entry : updateOp.getChanges().entrySet()) {
@@ -1687,6 +1702,10 @@ public class MongoDocumentStore implemen
update.append("$unset", unsetUpdates);
}
+ if (failOnInsert) {
+ update.putAll(CONFLICT_ON_INSERT);
+ }
+
return update;
}
Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java?rev=1841976&r1=1841975&r2=1841976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java Tue Sep 25 19:26:09 2018
@@ -359,7 +359,7 @@ public class RDBDocumentStore implements
@Override
public <T extends Document> T createOrUpdate(Collection<T> collection, UpdateOp update) {
UpdateUtils.assertUnconditional(update);
- return internalCreateOrUpdate(collection, update, true, false);
+ return internalCreateOrUpdate(collection, update, update.isNew(), false, RETRIES);
}
@Override
@@ -521,11 +521,15 @@ public class RDBDocumentStore implements
for (UpdateOp update : updates) {
String id = update.getId();
T modifiedDoc = collection.newDocument(this);
- if (oldDocs.containsKey(id)) {
- oldDocs.get(id).deepCopy(modifiedDoc);
+ T oldDoc = oldDocs.get(id);
+ if (oldDoc != null) {
+ oldDoc.deepCopy(modifiedDoc);
}
UpdateUtils.applyChanges(modifiedDoc, update);
- docsToUpdate.add(modifiedDoc);
+ if (oldDoc != null || update.isNew()) {
+ // only create if updateOp allows it
+ docsToUpdate.add(modifiedDoc);
+ }
keysToUpdate.add(id);
}
@@ -567,7 +571,7 @@ public class RDBDocumentStore implements
@Override
public <T extends Document> T findAndUpdate(Collection<T> collection, UpdateOp update) {
- return internalCreateOrUpdate(collection, update, false, true);
+ return internalCreateOrUpdate(collection, update, false, true, RETRIES);
}
@Override
@@ -1538,14 +1542,12 @@ public class RDBDocumentStore implements
@Nullable
private <T extends Document> T internalCreateOrUpdate(Collection<T> collection, UpdateOp update, boolean allowCreate,
- boolean checkConditions) {
+ boolean checkConditions, int retries) {
T oldDoc = readDocumentCached(collection, update.getId(), Integer.MAX_VALUE);
if (oldDoc == null) {
- if (!allowCreate) {
+ if (!allowCreate || !update.isNew()) {
return null;
- } else if (!update.isNew()) {
- throw new DocumentStoreException("Document does not exist: " + update.getId());
}
T doc = collection.newDocument(this);
if (checkConditions && !checkConditions(doc, update.getConditions())) {
@@ -1574,15 +1576,19 @@ public class RDBDocumentStore implements
LOG.error("insert failed, but document " + update.getId() + " is not present, aborting", ex);
throw (ex);
}
- return internalUpdate(collection, update, oldDoc, checkConditions, RETRIES);
+ return internalUpdate(collection, update, oldDoc, checkConditions, retries);
}
} else {
- T result = internalUpdate(collection, update, oldDoc, checkConditions, RETRIES);
+ T result = internalUpdate(collection, update, oldDoc, checkConditions, retries);
if (allowCreate && result == null) {
- // TODO OAK-2655 need to implement some kind of retry
- LOG.error("update of " + update.getId() + " failed, race condition?");
- throw new DocumentStoreException("update of " + update.getId() + " failed, race condition?", null,
- DocumentStoreException.Type.TRANSIENT);
+ if (retries > 0) {
+ result = internalCreateOrUpdate(collection, update, allowCreate, checkConditions, retries - 1);
+ }
+ else {
+ LOG.error("update of " + update.getId() + " failed, race condition?");
+ throw new DocumentStoreException("update of " + update.getId() + " failed, race condition?", null,
+ DocumentStoreException.Type.TRANSIENT);
+ }
}
return result;
}
@@ -1615,6 +1621,9 @@ public class RDBDocumentStore implements
if (lastmodcount == newmodcount) {
// cached copy did not change so it probably was
// updated by a different instance, get a fresh one
+ if (collection == Collection.NODES) {
+ nodesCache.invalidate(update.getId());
+ }
oldDoc = readDocumentUncached(collection, update.getId(), null);
}
}
Modified: jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java?rev=1841976&r1=1841975&r2=1841976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java Tue Sep 25 19:26:09 2018
@@ -111,11 +111,11 @@ public class BasicDocumentStoreTest exte
assertNull(nd.get(NodeDocument.HAS_BINARY_FLAG));
assertFalse(nd.wasDeletedOnce());
assertFalse(nd.hasBinary());
-
+
up = new UpdateOp(id, false);
up.set(NodeDocument.DELETED_ONCE, true);
super.ds.findAndUpdate(Collection.NODES, up);
-
+
super.ds.invalidateCache();
nd = super.ds.find(Collection.NODES, id, 0);
assertEquals(true, nd.get(NodeDocument.DELETED_ONCE));
@@ -127,20 +127,20 @@ public class BasicDocumentStoreTest exte
up.set(NodeDocument.DELETED_ONCE, false);
up.set(NodeDocument.HAS_BINARY_FLAG, NodeDocument.HAS_BINARY_VAL);
super.ds.findAndUpdate(Collection.NODES, up);
-
+
super.ds.invalidateCache();
nd = super.ds.find(Collection.NODES, id, 0);
assertEquals(false, nd.get(NodeDocument.DELETED_ONCE));
assertEquals(NodeDocument.HAS_BINARY_VAL, nd.get(NodeDocument.HAS_BINARY_FLAG));
assertFalse(nd.wasDeletedOnce());
assertTrue(nd.hasBinary());
-
+
// remove
up = new UpdateOp(id, false);
up.remove(NodeDocument.DELETED_ONCE);
up.remove(NodeDocument.HAS_BINARY_FLAG);
super.ds.findAndUpdate(Collection.NODES, up);
-
+
super.ds.invalidateCache();
nd = super.ds.find(Collection.NODES, id, 0);
assertNull(nd.get(NodeDocument.DELETED_ONCE));
@@ -162,11 +162,7 @@ public class BasicDocumentStoreTest exte
public void testCreateOrUpdate() {
String id = this.getClass().getName() + ".testCreateOrUpdate";
- // remove if present
- NodeDocument nd = super.ds.find(Collection.NODES, id);
- if (nd != null) {
- super.ds.remove(Collection.NODES, id);
- }
+ super.ds.remove(Collection.NODES, id);
// create
UpdateOp up = new UpdateOp(id, true);
@@ -179,6 +175,21 @@ public class BasicDocumentStoreTest exte
}
@Test
+ public void testCreateOrUpdateIsNewFalse() {
+ String id = this.getClass().getName() + ".testCreateOrUpdateIsNewFalse";
+
+ super.ds.remove(Collection.NODES, id);
+
+ // create with isNew==false
+ UpdateOp up = new UpdateOp(id, false);
+ assertNull(super.ds.createOrUpdate(Collection.NODES, up));
+
+ // has the document been created?
+ NodeDocument nd = super.ds.find(Collection.NODES, id);
+ assertNull(nd);
+ }
+
+ @Test
public void testCreateOrUpdateWithoutIdInUpdateOp() {
String id = this.getClass().getName() + ".testCreateOrUpdateWithoutIdInUpdateOp";
Modified: jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BulkCreateOrUpdateTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BulkCreateOrUpdateTest.java?rev=1841976&r1=1841975&r2=1841976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BulkCreateOrUpdateTest.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/BulkCreateOrUpdateTest.java Tue Sep 25 19:26:09 2018
@@ -350,4 +350,68 @@ public class BulkCreateOrUpdateTest exte
assertEquals("The value is not correct", 100l, newDoc.get("prop_" + i));
}
}
+
+ @Test
+ public void testBulkCreateOrUpdateIsNewFalse() {
+ bulkCreateOrUpdateIsNewFalse(2);
+ }
+
+ @Test
+ public void testBulkCreateOrUpdateIsNewFalseMany() {
+ bulkCreateOrUpdateIsNewFalse(10);
+ }
+
+ private void bulkCreateOrUpdateIsNewFalse(int numUpdates) {
+ String id1 = this.getClass().getName() + ".testBulkCreateOrUpdateIsNewFalse";
+ List<String> ids = new ArrayList<>();
+ for (int i = 1; i < numUpdates; i++) {
+ ids.add(id1 + "b" + i);
+ }
+
+ removeMe.add(id1);
+ removeMe.addAll(ids);
+
+ // remove id1
+ super.ds.remove(Collection.NODES, id1);
+ List<NodeDocument> initial = new ArrayList<>();
+ // insert other ids
+ for (String id : ids) {
+ UpdateOp up = new UpdateOp(id, true);
+ assertNull(super.ds.createOrUpdate(Collection.NODES, up));
+ NodeDocument doc = super.ds.find(Collection.NODES, id);
+ assertNotNull(doc);
+ initial.add(doc);
+ }
+
+ // bulk update
+ List<UpdateOp> ops = new ArrayList<>();
+ ops.add(new UpdateOp(id1, false));
+ for (String id : ids) {
+ ops.add(new UpdateOp(id, false));
+ }
+
+ List<NodeDocument> result = super.ds.createOrUpdate(Collection.NODES, ops);
+ assertEquals(numUpdates, result.size());
+
+ // id1 result should be reported as null and not be created
+ assertNull(result.get(0));
+ assertNull(ds.find(Collection.NODES, id1));
+
+ // for other ids result should be reported with previous doc
+ for (int i = 1; i < numUpdates; i++) {
+ NodeDocument prev = result.get(i);
+ assertNotNull(prev);
+ String id = ids.get(i - 1);
+ assertEquals(id, prev.getId());
+ if (prev.getModCount() != null) {
+ assertEquals(initial.get(i - 1).getModCount(), prev.getModCount());
+ }
+
+ NodeDocument updated = super.ds.find(Collection.NODES, id);
+ assertNotNull(updated);
+ if (prev.getModCount() != null) {
+ assertNotEquals(updated.getModCount(), prev.getModCount());
+ }
+ }
+ }
}
Modified: jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java?rev=1841976&r1=1841975&r2=1841976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CommitTest.java Tue Sep 25 19:26:09 2018
@@ -124,7 +124,7 @@ public class CommitTest {
try {
c.apply();
fail("commit must fail");
- } catch (DocumentStoreException e) {
+ } catch (ConflictException e) {
// expected
assertTrue("Unexpected exception message: " + e.getMessage(),
e.getMessage().contains("does not exist"));
Modified: jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java?rev=1841976&r1=1841975&r2=1841976&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java Tue Sep 25 19:26:09 2018
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.plugin
import static java.util.Collections.synchronizedList;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
@@ -28,12 +29,12 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-
import org.apache.jackrabbit.oak.plugins.document.util.Utils;
import org.junit.Test;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
public class MultiDocumentStoreTest extends AbstractMultiDocumentStoreTest {
public MultiDocumentStoreTest(DocumentStoreFixture dsf) {
@@ -225,4 +226,114 @@ public class MultiDocumentStoreTest exte
assertEquals(2L, foo.get("_ds1"));
assertEquals(1L, foo.get("_ds2"));
}
+
+ @Test
+ public void testUpdateOrCreateDeletedDocument() {
+
+ String id = Utils.getIdFromPath("/foo");
+ removeMe.add(id);
+
+ UpdateOp op1 = new UpdateOp(id, true);
+ assertNull(ds1.createOrUpdate(Collection.NODES, op1));
+
+ NodeDocument d = ds1.find(Collection.NODES, id);
+ assertNotNull(d);
+ Long mc = d.getModCount();
+
+ ds2.remove(Collection.NODES, id);
+
+ UpdateOp op2 = new UpdateOp(id, true);
+ NodeDocument prev = ds1.createOrUpdate(Collection.NODES, op2);
+
+ assertNull(prev);
+
+ // make sure created
+ NodeDocument created = ds1.find(Collection.NODES, id);
+ assertNotNull(created);
+ assertEquals(mc, created.getModCount());
+ }
+
+ @Test
+ public void testUpdateNoCreateDeletedDocument() {
+ String id = Utils.getIdFromPath("/foo");
+ removeMe.add(id);
+
+ UpdateOp op1 = new UpdateOp(id, true);
+ assertNull(ds1.createOrUpdate(Collection.NODES, op1));
+
+ NodeDocument d = ds1.find(Collection.NODES, id);
+ assertNotNull(d);
+
+ ds2.remove(Collection.NODES, id);
+
+ UpdateOp op2 = new UpdateOp(id, false);
+ NodeDocument prev = ds1.createOrUpdate(Collection.NODES, op2);
+
+ assertNull(prev);
+
+ // make sure not created
+ assertNull(ds1.find(Collection.NODES, id));
+ }
+
+ @Test
+ public void batchUpdateNoCreateDeletedDocument() {
+ batchUpdateNoCreateDeletedDocument(2);
+ }
+
+ @Test
+ public void batchUpdateNoCreateDeletedDocumentMany() {
+ batchUpdateNoCreateDeletedDocument(10);
+ }
+
+ private void batchUpdateNoCreateDeletedDocument(int numUpdates) {
+ String id1 = this.getClass().getName() + ".batchUpdateNoCreateDeletedDocument";
+ List<String> ids = new ArrayList<>();
+ for (int i = 1; i < numUpdates; i++) {
+ ids.add(id1 + "b" + i);
+ }
+
+ removeMe.add(id1);
+ removeMe.addAll(ids);
+
+ List<String> allIds = new ArrayList<>(ids);
+ allIds.add(id1);
+ // create docs
+ for (String id : allIds) {
+ UpdateOp up = new UpdateOp(id, true);
+ assertNull(ds1.createOrUpdate(Collection.NODES, up));
+ NodeDocument doc = ds1.find(Collection.NODES, id);
+ assertNotNull(doc);
+ }
+
+ // remove id1 on ds2
+ ds2.remove(Collection.NODES, id1);
+
+ // bulk update
+ List<UpdateOp> ops = new ArrayList<>();
+ ops.add(new UpdateOp(id1, false));
+ for (String id : ids) {
+ ops.add(new UpdateOp(id, false));
+ }
+
+ List<NodeDocument> result = ds1.createOrUpdate(Collection.NODES, ops);
+ assertEquals(numUpdates, result.size());
+
+ // id1 result should be reported as null and not be created
+ assertNull(result.get(0));
+ assertNull(ds1.find(Collection.NODES, id1));
+
+ // for other ids result should be reported with previous doc
+ for (int i = 1; i < numUpdates; i++) {
+ NodeDocument prev = result.get(i);
+ assertNotNull(prev);
+ String id = ids.get(i - 1);
+ assertEquals(id, prev.getId());
+
+ NodeDocument updated = ds1.find(Collection.NODES, id);
+ assertNotNull(updated);
+ if (prev.getModCount() != null) {
+ assertNotEquals(updated.getModCount(), prev.getModCount());
+ }
+ }
+ }
}