diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java index ff6ca44..47393f6 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java @@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.plugins.document; import static com.google.common.base.Preconditions.checkArgument; import static org.apache.jackrabbit.oak.commons.PathUtils.concat; +import static org.apache.jackrabbit.oak.plugins.document.util.MongoConnection.readConcernLevel; import java.io.InputStream; import java.net.UnknownHostException; @@ -45,6 +46,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.util.concurrent.MoreExecutors; import com.mongodb.DB; +import com.mongodb.ReadConcernLevel; import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.cache.CacheLIRS; @@ -79,6 +81,7 @@ import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore; import org.apache.jackrabbit.oak.plugins.document.rdb.RDBOptions; import org.apache.jackrabbit.oak.plugins.document.rdb.RDBVersionGCSupport; import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection; +import org.apache.jackrabbit.oak.plugins.document.mongo.MongoStatus; import org.apache.jackrabbit.oak.plugins.document.util.RevisionsKey; import org.apache.jackrabbit.oak.plugins.document.util.StringValue; import org.apache.jackrabbit.oak.spi.blob.AbstractBlobStore; @@ -544,6 +547,7 @@ public class DocumentMK { private DocumentNodeStore nodeStore; private DocumentStore documentStore; private String mongoUri; + private MongoStatus mongoStatus; private DiffCache diffCache; private BlobStore blobStore; private int clusterId = Integer.getInteger("oak.documentMK.clusterId", 0); @@ -606,10 +610,14 @@ public class DocumentMK { this.mongoUri = uri; DB db = new MongoConnection(uri).getDB(name); + MongoStatus status = new MongoStatus(db); if (!MongoConnection.hasWriteConcern(uri)) { db.setWriteConcern(MongoConnection.getDefaultWriteConcern(db)); } - setMongoDB(db, blobCacheSizeMB); + if (status.isMajorityReadConcernSupported() && status.isMajorityReadConcernEnabled() && !MongoConnection.hasReadConcern(uri)) { + db.setReadConcern(MongoConnection.getDefaultReadConcern(db)); + } + setMongoDB(db, status, blobCacheSizeMB); return this; } @@ -621,10 +629,27 @@ public class DocumentMK { */ public Builder setMongoDB(@Nonnull DB db, int blobCacheSizeMB) { + return setMongoDB(db, new MongoStatus(db), blobCacheSizeMB); + } + + private Builder setMongoDB(@Nonnull DB db, + MongoStatus status, + int blobCacheSizeMB) { if (!MongoConnection.hasSufficientWriteConcern(db)) { LOG.warn("Insufficient write concern: " + db.getWriteConcern() + " At least " + MongoConnection.getDefaultWriteConcern(db) + " is recommended."); } + if (status.isMajorityReadConcernSupported() && !status.isMajorityReadConcernEnabled()) { + LOG.warn("The read concern should be enabled on mongod using --enableMajorityReadConcern"); + } else if (status.isMajorityReadConcernSupported() && !MongoConnection.hasSufficientReadConcern(db)) { + ReadConcernLevel currentLevel = readConcernLevel(db.getReadConcern()); + ReadConcernLevel recommendedLevel = readConcernLevel(MongoConnection.getDefaultReadConcern(db)); + if (currentLevel == null) { + LOG.warn("Read concern hasn't been set. At least " + recommendedLevel + " is recommended."); + } else { + LOG.warn("Insufficient read concern: " + currentLevel + ". At least " + recommendedLevel + " is recommended."); + } + } if (this.documentStore == null) { this.documentStore = new MongoDocumentStore(db, this); } @@ -662,6 +687,16 @@ public class DocumentMK { } /** + * Returns the status of the Mongo server configured in the {@link #setMongoDB(String, String, int)} method. + * + * @return the status or null if the {@link #setMongoDB(String, String, int)} method hasn't + * been called. + */ + public MongoStatus getMongoStatus() { + return mongoStatus; + } + + /** * Sets a {@link DataSource} to use for the RDB document and blob * stores. * diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java index 3310811..3c183a0 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java @@ -34,8 +34,6 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; @@ -43,7 +41,6 @@ import javax.annotation.Nullable; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; @@ -135,11 +132,6 @@ public class MongoDocumentStore implements DocumentStore, RevisionListener { public static final int IN_CLAUSE_BATCH_SIZE = 500; - private static final ImmutableSet SERVER_DETAIL_FIELD_NAMES - = ImmutableSet.builder() - .add("host", "process", "connections", "repl", "storageEngine", "mem") - .build(); - private final DBCollection nodes; private final DBCollection clusterNodes; private final DBCollection settings; @@ -233,11 +225,14 @@ public class MongoDocumentStore implements DocumentStore, RevisionListener { private boolean hasModifiedIdCompoundIndex = true; public MongoDocumentStore(DB db, DocumentMK.Builder builder) { - CommandResult serverStatus = db.command("serverStatus"); - String version = checkVersion(db, serverStatus); + MongoStatus mongoStatus = builder.getMongoStatus(); + if (mongoStatus == null) { + mongoStatus = new MongoStatus(db); + } + mongoStatus.checkVersion(); metadata = ImmutableMap.builder() .put("type", "mongo") - .put("version", version) + .put("version", mongoStatus.getVersion()) .build(); this.db = db; @@ -295,47 +290,9 @@ public class MongoDocumentStore implements DocumentStore, RevisionListener { LOG.info("Connected to MongoDB {} with maxReplicationLagMillis {}, " + "maxDeltaForModTimeIdxSecs {}, disableIndexHint {}, " + "{}, serverStatus {}", - version, maxReplicationLagMillis, maxDeltaForModTimeIdxSecs, + mongoStatus.getVersion(), maxReplicationLagMillis, maxDeltaForModTimeIdxSecs, disableIndexHint, db.getWriteConcern(), - serverDetails(serverStatus)); - } - - @Nonnull - private static String checkVersion(DB db, CommandResult serverStatus) { - String version = serverStatus.getString("version"); - if (version == null) { - // OAK-4841: serverStatus was probably unauthorized, - // use buildInfo command to get version - version = db.command("buildInfo").getString("version"); - } - Matcher m = Pattern.compile("^(\\d+)\\.(\\d+)\\..*").matcher(version); - if (!m.matches()) { - throw new IllegalArgumentException("Malformed MongoDB version: " + version); - } - int major = Integer.parseInt(m.group(1)); - int minor = Integer.parseInt(m.group(2)); - if (major > 2) { - return version; - } - if (minor < 6) { - String msg = "MongoDB version 2.6.0 or higher required. " + - "Currently connected to a MongoDB with version: " + version; - throw new RuntimeException(msg); - } - - return version; - } - - @Nonnull - private static String serverDetails(CommandResult serverStatus) { - Map details = Maps.newHashMap(); - for (String key : SERVER_DETAIL_FIELD_NAMES) { - Object value = serverStatus.get(key); - if (value != null) { - details.put(key, value); - } - } - return details.toString(); + mongoStatus.getServerDetails()); } @Override diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoStatus.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoStatus.java new file mode 100644 index 0000000..6e683d9 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoStatus.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.plugins.document.mongo; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.MongoQueryException; +import com.mongodb.ReadConcern; +import com.mongodb.client.model.DBCollectionFindOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MongoStatus { + + private static final Logger LOG = LoggerFactory.getLogger(MongoStatus.class); + + private static final ImmutableSet SERVER_DETAIL_FIELD_NAMES + = ImmutableSet.builder() + .add("host", "process", "connections", "repl", "storageEngine", "mem") + .build(); + + private final DB db; + + private BasicDBObject serverStatus; + + private BasicDBObject buildInfo; + + private String version; + + private Boolean majorityReadConcernSupported; + + private Boolean majorityReadConcernEnabled; + + public MongoStatus(@Nonnull DB db) { + this.db = db; + } + + public void checkVersion() { + if (!isVersion(2, 6)) { + String msg = "MongoDB version 2.6.0 or higher required. " + + "Currently connected to a MongoDB with version: " + version; + throw new RuntimeException(msg); + } + } + + /** + * Check if the majority read concern is supported by this storage engine. + * The fact that read concern is supported doesn't it can be used - it also + * has to be enabled. + * + * @return true if the majority read concern is supported + */ + public boolean isMajorityReadConcernSupported() { + if (majorityReadConcernSupported == null) { + BasicDBObject stat = getServerStatus(); + if (stat.isEmpty()) { + LOG.debug("User doesn't have privileges to get server status; falling back to the isMajorityReadConcernEnabled()"); + return isMajorityReadConcernEnabled(); + } else { + if (stat.containsField("storageEngine")) { + BasicDBObject storageEngine = (BasicDBObject) stat.get("storageEngine"); + majorityReadConcernSupported = storageEngine.getBoolean("supportsCommittedReads"); + } else { + majorityReadConcernSupported = false; + } + } + } + return majorityReadConcernSupported; + } + + /** + * Check if the majority read concern is enabled and can be used for queries. + * + * @return true if the majority read concern is enabled + */ + public boolean isMajorityReadConcernEnabled() { + if (majorityReadConcernEnabled == null) { + // Mongo API doesn't seem to provide an option to check whether the + // majority read concern has been enabled, so we have to try to use + // it and optionally catch the exception. + DBCollection emptyCollection = db.getCollection("emptyCollection-" + System.currentTimeMillis()); + DBCursor cursor = emptyCollection.find(new BasicDBObject(), new DBCollectionFindOptions().readConcern(ReadConcern.MAJORITY)); + try { + cursor.hasNext(); + majorityReadConcernEnabled = true; + } catch (MongoQueryException e) { + majorityReadConcernEnabled = false; + } finally { + cursor.close(); + } + } + return majorityReadConcernEnabled; + } + + @Nonnull + public String getServerDetails() { + Map details = Maps.newHashMap(); + for (String key : SERVER_DETAIL_FIELD_NAMES) { + Object value = getServerStatus().get(key); + if (value != null) { + details.put(key, value); + } + } + return details.toString(); + } + + @Nonnull + public String getVersion() { + if (version == null) { + String v = getServerStatus().getString("version"); + if (v == null) { + // OAK-4841: serverStatus was probably unauthorized, + // use buildInfo command to get version + v = getBuildInfo().getString("version"); + } + version = v; + } + return version; + } + + private boolean isVersion(int requiredMajor, int requiredMinor) { + String v = getVersion(); + Matcher m = Pattern.compile("^(\\d+)\\.(\\d+)\\..*").matcher(v); + if (!m.matches()) { + throw new IllegalArgumentException("Malformed MongoDB version: " + v); + } + int major = Integer.parseInt(m.group(1)); + int minor = Integer.parseInt(m.group(2)); + + if (major > requiredMajor) { + return true; + } else if (major == requiredMajor) { + return minor >= requiredMinor; + } else { + return false; + } + } + + private BasicDBObject getServerStatus() { + if (serverStatus == null) { + serverStatus = db.command("serverStatus"); + } + return serverStatus; + } + + private BasicDBObject getBuildInfo() { + if (buildInfo == null) { + buildInfo = db.command("buildInfo"); + } + return buildInfo; + } + + // for testing purposes + void setVersion(String version) { + this.version = version; + } + + void setServerStatus(BasicDBObject serverStatus) { + this.majorityReadConcernSupported = null; + this.serverStatus = serverStatus; + } +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MongoConnection.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MongoConnection.java index fd811f4..34db007 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MongoConnection.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/MongoConnection.java @@ -17,15 +17,19 @@ package org.apache.jackrabbit.oak.plugins.document.util; import java.net.UnknownHostException; +import java.util.Set; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import com.google.common.base.Objects; +import com.google.common.collect.ImmutableSet; import com.mongodb.DB; import com.mongodb.MongoClient; import com.mongodb.MongoClientOptions; import com.mongodb.MongoClientURI; +import com.mongodb.ReadConcern; +import com.mongodb.ReadConcernLevel; import com.mongodb.WriteConcern; import static com.google.common.base.Preconditions.checkNotNull; @@ -37,6 +41,7 @@ public class MongoConnection { private static final int DEFAULT_MAX_WAIT_TIME = (int) TimeUnit.MINUTES.toMillis(1); private static final WriteConcern WC_UNKNOWN = new WriteConcern("unknown"); + private static final Set REPLICA_RC = ImmutableSet.of(ReadConcernLevel.MAJORITY, ReadConcernLevel.LINEARIZABLE); private final MongoClientURI mongoURI; private final MongoClient mongo; @@ -133,6 +138,18 @@ public class MongoConnection { } /** + * Returns {@code true} if the given {@code uri} has a read concern set. + * @param uri the URI to check. + * @return {@code true} if the URI has a read concern set, {@code false} + * otherwise. + */ + public static boolean hasReadConcern(@Nonnull String uri) { + ReadConcern rc = new MongoClientURI(checkNotNull(uri)) + .getOptions().getReadConcern(); + return readConcernLevel(rc) != null; + } + + /** * Returns the default write concern depending on MongoDB deployment. *
    *
  • {@link WriteConcern#MAJORITY}: for a MongoDB replica set
  • @@ -153,6 +170,36 @@ public class MongoConnection { } /** + * Returns the default read concern depending on MongoDB deployment. + *
      + *
    • {@link ReadConcern#MAJORITY}: for a MongoDB replica set with w=majority
    • + *
    • {@link ReadConcern#LOCAL}: for other cases
    • + *
    + * + * @param db the connection to MongoDB. + * @return the default write concern to use for Oak. + */ + public static ReadConcern getDefaultReadConcern(@Nonnull DB db) { + ReadConcern r; + if (checkNotNull(db).getMongo().getReplicaSetStatus() != null && isMajorityWriteConcern(db)) { + r = ReadConcern.MAJORITY; + } else { + r = ReadConcern.LOCAL; + } + return r; + } + + /** + * Returns true if the majority write concern is used for the given DB. + * + * @param db the connection to MongoDB. + * @return true if the majority write concern has been configured; false otherwise + */ + public static boolean isMajorityWriteConcern(@Nonnull DB db) { + return "majority".equals(db.getWriteConcern().getWObject()); + } + + /** * Returns {@code true} if the default write concern on the {@code db} is * sufficient for Oak. On a replica set Oak expects at least w=2. For * a single MongoDB node deployment w=1 is sufficient. @@ -181,4 +228,29 @@ public class MongoConnection { return w >= 1; } } + + /** + * Returns {@code true} if the default read concern on the {@code db} is + * sufficient for Oak. On a replica set Oak expects majority or linear. For + * a single MongoDB node deployment local is sufficient. + * + * @param db the database. + * @return whether the read concern is sufficient. + */ + public static boolean hasSufficientReadConcern(@Nonnull DB db) { + ReadConcernLevel r = readConcernLevel(checkNotNull(db).getReadConcern()); + if (db.getMongo().getReplicaSetStatus() == null) { + return true; + } else { + return REPLICA_RC.contains(r); + } + } + + public static ReadConcernLevel readConcernLevel(ReadConcern readConcern) { + if (readConcern.isServerDefault()) { + return null; + } else { + return ReadConcernLevel.fromString(readConcern.asDocument().getString("level").getValue()); + } + } } \ No newline at end of file diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoConnectionTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoConnectionTest.java index 3f1fd97..c836705 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoConnectionTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoConnectionTest.java @@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.plugins.document.mongo; import com.mongodb.DB; import com.mongodb.Mongo; +import com.mongodb.ReadConcern; import com.mongodb.ReplicaSetStatus; import com.mongodb.WriteConcern; @@ -39,6 +40,12 @@ public class MongoConnectionTest { } @Test + public void hasReadConcern() throws Exception { + assertFalse(MongoConnection.hasReadConcern("mongodb://localhost:27017/foo")); + assertTrue(MongoConnection.hasReadConcern("mongodb://localhost:27017/foo?readconcernlevel=majority")); + } + + @Test public void sufficientWriteConcern() throws Exception { sufficientWriteConcernReplicaSet(WriteConcern.ACKNOWLEDGED, false); sufficientWriteConcernReplicaSet(WriteConcern.JOURNALED, false); @@ -65,6 +72,17 @@ public class MongoConnectionTest { sufficientWriteConcernSingleNode(WriteConcern.UNACKNOWLEDGED, false); } + @Test + public void sufficientReadConcern() throws Exception { + sufficientReadConcernReplicaSet(ReadConcern.DEFAULT, false); + sufficientReadConcernReplicaSet(ReadConcern.LOCAL, false); + sufficientReadConcernReplicaSet(ReadConcern.MAJORITY, true); + + sufficientReadConcernSingleNode(ReadConcern.DEFAULT, true); + sufficientReadConcernSingleNode(ReadConcern.LOCAL, true); + sufficientReadConcernSingleNode(ReadConcern.MAJORITY, true); + } + private void sufficientWriteConcernReplicaSet(WriteConcern w, boolean sufficient) { sufficientWriteConcern(w, true, sufficient); @@ -78,6 +96,29 @@ public class MongoConnectionTest { private void sufficientWriteConcern(WriteConcern w, boolean replicaSet, boolean sufficient) { + DB db = mockDB(ReadConcern.DEFAULT, w, replicaSet); + assertEquals(sufficient, MongoConnection.hasSufficientWriteConcern(db)); + } + + private void sufficientReadConcernReplicaSet(ReadConcern r, + boolean sufficient) { + sufficientReadConcern(r, true, sufficient); + } + + private void sufficientReadConcernSingleNode(ReadConcern r, + boolean sufficient) { + sufficientReadConcern(r, false, sufficient); + } + private void sufficientReadConcern(ReadConcern r, + boolean replicaSet, + boolean sufficient) { + DB db = mockDB(r, replicaSet ? WriteConcern.MAJORITY : WriteConcern.W1, replicaSet); + assertEquals(sufficient, MongoConnection.hasSufficientReadConcern(db)); + } + + private DB mockDB(ReadConcern r, + WriteConcern w, + boolean replicaSet) { ReplicaSetStatus status; if (replicaSet) { status = mock(ReplicaSetStatus.class); @@ -88,7 +129,8 @@ public class MongoConnectionTest { Mongo mongo = mock(Mongo.class); when(db.getMongo()).thenReturn(mongo); when(db.getWriteConcern()).thenReturn(w); + when(db.getReadConcern()).thenReturn(r); when(mongo.getReplicaSetStatus()).thenReturn(status); - assertEquals(sufficient, MongoConnection.hasSufficientWriteConcern(db)); + return db; } } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoStatusTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoStatusTest.java new file mode 100644 index 0000000..7ea80e6 --- /dev/null +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoStatusTest.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.plugins.document.mongo; + +import com.mongodb.BasicDBObject; +import org.apache.jackrabbit.oak.plugins.document.MongoConnectionFactory; +import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; + +import static org.apache.jackrabbit.oak.plugins.document.MongoUtils.isAvailable; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +public class MongoStatusTest { + + @Rule + public MongoConnectionFactory connectionFactory = new MongoConnectionFactory(); + + private MongoStatus status; + + @BeforeClass + public static void mongoAvailable() { + assumeTrue(isAvailable()); + } + + @Before + public void createStatus() { + MongoConnection c = connectionFactory.getConnection(); + status = new MongoStatus(c.getDB()); + } + + @Test + public void testDetails() { + String details = status.getServerDetails(); + assertNotNull(details); + assertFalse(details.isEmpty()); + assertTrue(details.startsWith("{")); + assertTrue(details.endsWith("}")); + assertTrue(details.contains("host=")); + } + + @Test + public void testReadConcern() { + BasicDBObject mockServerStatus = new BasicDBObject(); + BasicDBObject storageEngine = new BasicDBObject(); + status.setServerStatus(mockServerStatus); + + assertFalse(status.isMajorityReadConcernSupported()); + + mockServerStatus.put("storageEngine", storageEngine); + status.setServerStatus(mockServerStatus); + assertFalse(status.isMajorityReadConcernSupported()); + + storageEngine.put("supportsCommittedReads", false); + status.setServerStatus(mockServerStatus); + assertFalse(status.isMajorityReadConcernSupported()); + + storageEngine.put("supportsCommittedReads", true); + status.setServerStatus(mockServerStatus); + assertTrue(status.isMajorityReadConcernSupported()); + } + + @Test + public void testGetVersion() { + assertTrue(status.getVersion().matches("^\\d+\\.\\d+\\.\\d+$")); + } + + @Test + public void testCheckVersionValid() { + for (String v : new String[] { "2.6.0", "2.7.0", "3.0.0"}) { + status.setVersion(v); + status.checkVersion(); + } + } + + @Test + public void testCheckVersionInvalid() { + for (String v : new String[] { "1.0.0", "2.0.0", "2.5.0"}) { + status.setVersion(v); + try { + status.checkVersion(); + fail("Version " + v + " shouldn't be allowed"); + } catch (Exception e) { + } + } + } +} diff --git a/oak-parent/pom.xml b/oak-parent/pom.xml index 0b8971a..a1d99e6 100644 --- a/oak-parent/pom.xml +++ b/oak-parent/pom.xml @@ -49,7 +49,7 @@ MongoMKDB2 SegmentMK 4.7.1 - 3.2.2 + 3.4.0