Author: mreutegg
Date: Thu Dec 13 10:41:35 2018
New Revision: 1848837
URL: http://svn.apache.org/viewvc?rev=1848837&view=rev
Log:
OAK-7959: MongoDocumentStore causes scan of entire nodes collection on startup
Modified:
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/mongo/MongoUtils.java
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoUtilsTest.java
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=1848837&r1=1848836&r2=1848837&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
Thu Dec 13 10:41:35 2018
@@ -274,13 +274,13 @@ public class MongoDocumentStore implemen
useClientSession = !builder.isClientSessionDisabled()
&&
Boolean.parseBoolean(System.getProperty("oak.mongo.clientSession", "true"));
- // counting the number of documents in the nodes collection and
- // checking existing indexes is performed against the MongoDB primary
- // this ensure the information is up-to-date and accurate
- long initialDocsCount = getNodesCount();
+ // reading documents in the nodes collection and checking
+ // existing indexes is performed against the MongoDB primary
+ // this ensures the information is up-to-date and accurate
+ boolean emptyNodesCollection = execute(session ->
MongoUtils.isCollectionEmpty(nodes, session));
// compound index on _modified and _id
- if (initialDocsCount == 0) {
+ if (emptyNodesCollection) {
// this is an empty store, create a compound index
// on _modified and _id (OAK-3071)
createIndex(nodes, new String[]{NodeDocument.MODIFIED_IN_SECS,
Document.ID},
@@ -300,7 +300,7 @@ public class MongoDocumentStore implemen
// index on _deleted for fast lookup of potentially garbage
// depending on the MongoDB version, create a partial index
- if (initialDocsCount == 0) {
+ if (emptyNodesCollection) {
if (mongoStatus.isVersion(3, 2)) {
createPartialIndex(nodes, new String[]{DELETED_ONCE,
MODIFIED_IN_SECS},
new boolean[]{true, true}, "{" + DELETED_ONCE +
":true}");
@@ -320,7 +320,7 @@ public class MongoDocumentStore implemen
}
// compound index on _sdType and _sdMaxRevTime
- if (initialDocsCount == 0) {
+ if (emptyNodesCollection) {
// this is an empty store, create compound index
// on _sdType and _sdMaxRevTime (OAK-6129)
createIndex(nodes, new String[]{SD_TYPE, SD_MAX_REV_TIME_IN_SECS},
@@ -1853,25 +1853,6 @@ public class MongoDocumentStore implemen
});
}
- /**
- * Returns the number of documents in the {@link #nodes} collection. The
read
- * always happens on the MongoDB primary.
- *
- * @return the number of documents in the {@link #nodes} collection.
- */
- private long getNodesCount() {
- return execute(session -> {
- MongoCollection<?> c =
nodes.withReadPreference(ReadPreference.primary());
- long count;
- if (session != null) {
- count = c.countDocuments(session);
- } else {
- count = c.countDocuments();
- }
- return count;
- });
- }
-
private boolean withClientSession() {
return status.isClientSessionSupported() && useClientSession;
}
Modified:
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoUtils.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoUtils.java?rev=1848837&r1=1848836&r2=1848837&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoUtils.java
(original)
+++
jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoUtils.java
Thu Dec 13 10:41:35 2018
@@ -25,12 +25,17 @@ import com.mongodb.MongoException;
import com.mongodb.MongoNotPrimaryException;
import com.mongodb.MongoSocketException;
import com.mongodb.MongoWriteConcernException;
+import com.mongodb.ReadPreference;
import com.mongodb.WriteConcernException;
+import com.mongodb.client.ClientSession;
+import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.IndexOptions;
import org.apache.jackrabbit.oak.plugins.document.DocumentStoreException.Type;
import org.bson.Document;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
@@ -140,6 +145,26 @@ class MongoUtils {
}
/**
+ * Returns {@code true} if the given collection is empty, {@code false}
+ * otherwise. The check always happens on the MongoDB primary.
+ *
+ * @param collection the collection to check.
+ * @param session an optional client session.
+ * @return {@code true} if empty, {@code false} otherwise.
+ */
+ static boolean isCollectionEmpty(@NotNull MongoCollection<?> collection,
+ @Nullable ClientSession session) {
+ MongoCollection<?> c =
collection.withReadPreference(ReadPreference.primary());
+ FindIterable<BasicDBObject> result;
+ if (session != null) {
+ result = c.find(session, BasicDBObject.class);
+ } else {
+ result = c.find(BasicDBObject.class);
+ }
+ return result.limit(1).first() == null;
+ }
+
+ /**
* Returns the {@code DocumentStoreException} {@link Type} for the given
* throwable.
*
Modified:
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoUtilsTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoUtilsTest.java?rev=1848837&r1=1848836&r2=1848837&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoUtilsTest.java
(original)
+++
jackrabbit/oak/trunk/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoUtilsTest.java
Thu Dec 13 10:41:35 2018
@@ -20,14 +20,17 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import com.mongodb.BasicDBObject;
import com.mongodb.DuplicateKeyException;
import com.mongodb.MongoCommandException;
import com.mongodb.MongoException;
import com.mongodb.MongoSocketException;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcernException;
+import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoCollection;
+import org.apache.jackrabbit.oak.plugins.document.Collection;
import org.apache.jackrabbit.oak.plugins.document.MongoConnectionFactory;
import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
import org.bson.BsonDocument;
@@ -155,6 +158,48 @@ public class MongoUtilsTest {
assertEquals(TRANSIENT, getDocumentStoreExceptionTypeFor(new
MongoSocketException("message", new ServerAddress())));
}
+ @Test
+ public void isCollectionEmpty() {
+ MongoConnection c = connectionFactory.getConnection();
+ assertNotNull(c);
+ c.getDatabase().drop();
+
+ String collectionName = Collection.NODES.toString();
+ MongoStatus status = new MongoStatus(c.getMongoClient(),
c.getDBName());
+
+ // consider empty when collection doesn't exist
+ MongoCollection<BasicDBObject> nodes = c.getDatabase()
+ .getCollection(collectionName, BasicDBObject.class);
+ assertTrue(MongoUtils.isCollectionEmpty(nodes, null));
+ if (status.isClientSessionSupported()) {
+ try (ClientSession s = c.getMongoClient().startSession()) {
+ assertTrue(MongoUtils.isCollectionEmpty(nodes, s));
+ }
+ }
+
+ // insert a document
+ nodes.insertOne(new BasicDBObject("p", "v"));
+
+ // check again
+ assertFalse(MongoUtils.isCollectionEmpty(nodes, null));
+ if (status.isClientSessionSupported()) {
+ try (ClientSession s = c.getMongoClient().startSession()) {
+ assertFalse(MongoUtils.isCollectionEmpty(nodes, s));
+ }
+ }
+
+ // remove any document
+ nodes.deleteMany(new BasicDBObject());
+
+ // must be empty again
+ assertTrue(MongoUtils.isCollectionEmpty(nodes, null));
+ if (status.isClientSessionSupported()) {
+ try (ClientSession s = c.getMongoClient().startSession()) {
+ assertTrue(MongoUtils.isCollectionEmpty(nodes, s));
+ }
+ }
+ }
+
private static MongoCommandException newMongoCommandException(int code) {
return new MongoCommandException(response(code), new ServerAddress());
}