getServersWithAcceptableLatencyDifference(final
if (!cur.isOk()) {
continue;
}
- if (cur.getAveragePingTimeNanos() - TimeUnit.NANOSECONDS.convert(acceptableLatencyDifference, timeUnit) <= bestPingTime) {
+ if (cur.getAveragePingTimeNanos() - acceptableLatencyDifferenceNanos <= bestPingTime) {
goodSecondaries.add(cur);
}
}
diff --git a/src/main/com/mongodb/MessageSettings.java b/src/main/com/mongodb/MessageSettings.java
index 95c6c67284b..b40061f991d 100644
--- a/src/main/com/mongodb/MessageSettings.java
+++ b/src/main/com/mongodb/MessageSettings.java
@@ -22,9 +22,11 @@
final class MessageSettings {
private static final int DEFAULT_MAX_DOCUMENT_SIZE = 0x1000000; // 16MB
private static final int DEFAULT_MAX_MESSAGE_SIZE = 0x2000000; // 32MB
+ private static final int DEFAULT_MAX_WRITE_BATCH_SIZE = 1000;
private final int maxDocumentSize;
private final int maxMessageSize;
+ private final int maxWriteBatchSize;
public static Builder builder() {
return new Builder();
@@ -33,6 +35,7 @@ public static Builder builder() {
static final class Builder {
private int maxDocumentSize = DEFAULT_MAX_DOCUMENT_SIZE;
private int maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE;
+ private int maxWriteBatchSize = DEFAULT_MAX_WRITE_BATCH_SIZE;
public MessageSettings build() {
return new MessageSettings(this);
@@ -48,6 +51,12 @@ public Builder maxMessageSize(final int maxMessageSize) {
this.maxMessageSize = maxMessageSize;
return this;
}
+
+ public Builder maxWriteBatchSize(final int maxWriteBatchSize) {
+ this.maxWriteBatchSize = maxWriteBatchSize;
+ return this;
+ }
+
// CHECKSTYLE:ON
}
@@ -59,8 +68,13 @@ public int getMaxMessageSize() {
return maxMessageSize;
}
+ public int getMaxWriteBatchSize() {
+ return maxWriteBatchSize;
+ }
+
MessageSettings(final Builder builder) {
this.maxDocumentSize = builder.maxDocumentSize;
this.maxMessageSize = builder.maxMessageSize;
+ this.maxWriteBatchSize = builder.maxWriteBatchSize;
}
}
\ No newline at end of file
diff --git a/src/main/com/mongodb/Mongo.java b/src/main/com/mongodb/Mongo.java
index a0526c52483..165f960314a 100644
--- a/src/main/com/mongodb/Mongo.java
+++ b/src/main/com/mongodb/Mongo.java
@@ -91,7 +91,7 @@ public class Mongo {
@Deprecated
public static final int MINOR_VERSION = 12;
- private static final String FULL_VERSION = "2.12.0-SNAPSHOT";
+ private static final String FULL_VERSION = "2.12.0";
static int cleanerIntervalMS;
diff --git a/src/main/com/mongodb/MongoClientOptions.java b/src/main/com/mongodb/MongoClientOptions.java
index 5c9de009e02..38a9c56a34b 100644
--- a/src/main/com/mongodb/MongoClientOptions.java
+++ b/src/main/com/mongodb/MongoClientOptions.java
@@ -64,6 +64,7 @@ public static class Builder {
private int heartbeatSocketTimeout = Integer.parseInt(System.getProperty("com.mongodb.updaterSocketTimeoutMS", "20000"));
private int heartbeatThreadCount;
private int acceptableLatencyDifference = Integer.parseInt(System.getProperty("com.mongodb.slaveAcceptableLatencyMS", "15"));
+ private String requiredReplicaSetName;
/**
* Sets the heartbeat frequency.
@@ -453,6 +454,20 @@ public Builder alwaysUseMBeans(final boolean alwaysUseMBeans) {
return this;
}
+
+ /**
+ * Sets the required replica set name for the cluster.
+ *
+ * @param requiredReplicaSetName the required replica set name for the replica set.
+ * @return this
+ * @see MongoClientOptions#getRequiredReplicaSetName()
+ * @since 2.12
+ */
+ public Builder requiredReplicaSetName(final String requiredReplicaSetName) {
+ this.requiredReplicaSetName = requiredReplicaSetName;
+ return this;
+ }
+
/**
* Sets defaults to be what they are in {@code MongoOptions}.
*
@@ -473,7 +488,7 @@ public Builder legacyDefaults() {
public MongoClientOptions build() {
return new MongoClientOptions(this);
}
- }
+ }
/**
* Create a new Builder instance. This is a convenience method, equivalent to {@code new MongoClientOptions.Builder()}.
@@ -801,6 +816,22 @@ public int getAcceptableLatencyDifference() {
return acceptableLatencyDifference;
}
+ /**
+ * Gets the required replica set name. With this option set, the MongoClient instance will
+ * 1. Connect in replica set mode, and discover all members of the set based on the given servers
+ *
+ * 2. Make sure that the set name reported by all members matches the required set name.
+ *
+ * 3. Refuse to service any requests if any member of the seed list is not part of a replica set with the required name.
+ *
+ *
+ * @return the required replica set name
+ * @since 2.12
+ */
+ public String getRequiredReplicaSetName() {
+ return requiredReplicaSetName;
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) {
@@ -887,6 +918,10 @@ public boolean equals(final Object o) {
if (!writeConcern.equals(that.writeConcern)) {
return false;
}
+ if (requiredReplicaSetName != null ? !requiredReplicaSetName.equals(that.requiredReplicaSetName)
+ : that.requiredReplicaSetName != null) {
+ return false;
+ }
return true;
}
@@ -918,6 +953,7 @@ public int hashCode() {
result = 31 * result + heartbeatSocketTimeout;
result = 31 * result + heartbeatThreadCount;
result = 31 * result + acceptableLatencyDifference;
+ result = 31 * result + (requiredReplicaSetName != null ? requiredReplicaSetName.hashCode() : 0);
return result;
}
@@ -946,6 +982,7 @@ public String toString() {
+ ", heartbeatSocketTimeout=" + heartbeatSocketTimeout
+ ", heartbeatThreadCount=" + heartbeatThreadCount
+ ", acceptableLatencyDifference=" + acceptableLatencyDifference
+ + ", requiredReplicaSetName=" + requiredReplicaSetName
+ '}';
}
@@ -975,6 +1012,7 @@ private MongoClientOptions(final Builder builder) {
heartbeatSocketTimeout = builder.heartbeatSocketTimeout;
heartbeatThreadCount = builder.heartbeatThreadCount;
acceptableLatencyDifference = builder.acceptableLatencyDifference;
+ requiredReplicaSetName = builder.requiredReplicaSetName;
}
@@ -1003,4 +1041,5 @@ private MongoClientOptions(final Builder builder) {
private final int heartbeatSocketTimeout;
private final int heartbeatThreadCount;
private final int acceptableLatencyDifference;
+ private final String requiredReplicaSetName;
}
diff --git a/src/main/com/mongodb/MongoClientURI.java b/src/main/com/mongodb/MongoClientURI.java
index e41ae45a20d..19a5795a0f6 100644
--- a/src/main/com/mongodb/MongoClientURI.java
+++ b/src/main/com/mongodb/MongoClientURI.java
@@ -66,10 +66,13 @@
* {@code ssl=true|false}: Whether to connect using SSL.
* {@code connectTimeoutMS=ms}: How long a connection can take to be opened before timing out.
* {@code socketTimeoutMS=ms}: How long a send or receive on a socket can take before timing out.
+ * {@code maxIdleTimeMS=ms}: Maximum idle time of a pooled connection. A connection that exceeds this limit will be closed
+ * {@code maxLifeTimeMS=ms}: Maximum life time of a pooled connection. A connection that exceeds this limit will be closed
*
* Connection pool configuration:
*
* - {@code maxPoolSize=n}: The maximum number of connections in the connection pool.
+ * - {@code minPoolSize=n}: The minimum number of connections in the connection pool.
* - {@code waitQueueMultiple=n} : this multiplier, multiplied with the maxPoolSize setting, gives the maximum number of
* threads that may be waiting for a connection to become available from the pool. All further threads will get an
* exception right away.
@@ -262,14 +265,18 @@ public MongoClientURI(String uri, MongoClientOptions.Builder builder) {
static Set allKeys = new HashSet();
static {
+ generalOptionsKeys.add("minpoolsize");
generalOptionsKeys.add("maxpoolsize");
generalOptionsKeys.add("waitqueuemultiple");
generalOptionsKeys.add("waitqueuetimeoutms");
generalOptionsKeys.add("connecttimeoutms");
+ generalOptionsKeys.add("maxidletimems");
+ generalOptionsKeys.add("maxlifetimems");
generalOptionsKeys.add("sockettimeoutms");
generalOptionsKeys.add("sockettimeoutms");
generalOptionsKeys.add("autoconnectretry");
generalOptionsKeys.add("ssl");
+ generalOptionsKeys.add("replicaset");
readPreferenceKeys.add("slaveok");
readPreferenceKeys.add("readpreference");
@@ -308,6 +315,12 @@ private MongoClientOptions createOptions(Map> optionsMap, M
if (key.equals("maxpoolsize")) {
builder.connectionsPerHost(Integer.parseInt(value));
+ } else if (key.equals("minpoolsize")) {
+ builder.minConnectionsPerHost(Integer.parseInt(value));
+ } else if (key.equals("maxidletimems")) {
+ builder.maxConnectionIdleTime(Integer.parseInt(value));
+ } else if (key.equals("maxlifetimems")) {
+ builder.maxConnectionLifeTime(Integer.parseInt(value));
} else if (key.equals("waitqueuemultiple")) {
builder.threadsAllowedToBlockForConnectionMultiplier(Integer.parseInt(value));
} else if (key.equals("waitqueuetimeoutms")) {
@@ -318,6 +331,8 @@ private MongoClientOptions createOptions(Map> optionsMap, M
builder.socketTimeout(Integer.parseInt(value));
} else if (key.equals("autoconnectretry")) {
builder.autoConnectRetry(_parseBoolean(value));
+ } else if (key.equals("replicaset")) {
+ builder.requiredReplicaSetName(value);
} else if (key.equals("ssl")) {
if (_parseBoolean(value)) {
builder.socketFactory(SSLSocketFactory.getDefault());
diff --git a/src/main/com/mongodb/MongoCursorNotFoundException.java b/src/main/com/mongodb/MongoCursorNotFoundException.java
index a28c9aafb6c..d2b78281887 100644
--- a/src/main/com/mongodb/MongoCursorNotFoundException.java
+++ b/src/main/com/mongodb/MongoCursorNotFoundException.java
@@ -18,6 +18,8 @@
/**
* Subclass of {@link MongoException} representing a cursor-not-found exception.
+ *
+ * @since 2.12
*/
public class MongoCursorNotFoundException extends MongoException {
diff --git a/src/main/com/mongodb/MongoOptions.java b/src/main/com/mongodb/MongoOptions.java
index 1e287fbbf7b..4be62d71d6b 100644
--- a/src/main/com/mongodb/MongoOptions.java
+++ b/src/main/com/mongodb/MongoOptions.java
@@ -68,6 +68,7 @@ public MongoOptions(final MongoClientOptions options) {
heartbeatReadTimeoutMS = options.getHeartbeatSocketTimeout();
heartbeatThreadCount = options.getHeartbeatThreadCount();
acceptableLatencyDifferenceMS = options.getAcceptableLatencyDifference();
+ requiredReplicaSetName = options.getRequiredReplicaSetName();
}
public void reset(){
@@ -102,6 +103,7 @@ public void reset(){
heartbeatReadTimeoutMS = Integer.parseInt(System.getProperty("com.mongodb.updaterSocketTimeoutMS", "20000"));
heartbeatThreadCount = 0;
acceptableLatencyDifferenceMS = Integer.parseInt(System.getProperty("com.mongodb.slaveAcceptableLatencyMS", "15"));
+ requiredReplicaSetName = null;
}
public MongoOptions copy() {
@@ -137,6 +139,7 @@ public MongoOptions copy() {
m.heartbeatReadTimeoutMS = heartbeatReadTimeoutMS;
m.heartbeatThreadCount = heartbeatThreadCount;
m.acceptableLatencyDifferenceMS = acceptableLatencyDifferenceMS;
+ m.requiredReplicaSetName = requiredReplicaSetName;
return m;
}
@@ -250,6 +253,10 @@ public boolean equals(final Object o) {
if (writeConcern != null ? !writeConcern.equals(options.writeConcern) : options.writeConcern != null) {
return false;
}
+ if (requiredReplicaSetName != null ? !requiredReplicaSetName.equals(options.requiredReplicaSetName)
+ : options.requiredReplicaSetName != null) {
+ return false;
+ }
return true;
}
@@ -284,6 +291,7 @@ public int hashCode() {
result = 31 * result + heartbeatReadTimeoutMS;
result = 31 * result + acceptableLatencyDifferenceMS;
result = 31 * result + heartbeatThreadCount;
+ result = 31 * result + (requiredReplicaSetName != null ? requiredReplicaSetName.hashCode() : 0);
return result;
}
@@ -469,6 +477,8 @@ public int hashCode() {
int acceptableLatencyDifferenceMS;
int heartbeatThreadCount;
+ String requiredReplicaSetName;
+
/**
* @return The description for MongoClient
instances created with these options
*/
@@ -808,6 +818,16 @@ public void setAlwaysUseMBeans(final boolean alwaysUseMBeans) {
this.alwaysUseMBeans = alwaysUseMBeans;
}
+ /**
+ * Gets the required replica set name that this client should be connecting to.
+ *
+ * @return the required replica set name, or null if none is required
+ * @since 2.12
+ */
+ public String getRequiredReplicaSetName() {
+ return requiredReplicaSetName;
+ }
+
@Override
public String toString() {
return "MongoOptions{" +
@@ -833,6 +853,7 @@ public String toString() {
", cursorFinalizerEnabled=" + cursorFinalizerEnabled +
", writeConcern=" + writeConcern +
", alwaysUseMBeans=" + alwaysUseMBeans +
+ ", requiredReplicaSetName=" + requiredReplicaSetName +
'}';
}
}
diff --git a/src/main/com/mongodb/MongoSocketException.java b/src/main/com/mongodb/MongoSocketException.java
index 74a6e7a7371..601b9af65f7 100644
--- a/src/main/com/mongodb/MongoSocketException.java
+++ b/src/main/com/mongodb/MongoSocketException.java
@@ -19,7 +19,9 @@
import java.io.IOException;
/**
- * Subclass of {@link MongoException} representing a network-related exception
+ * Subclass of {@link MongoException} representing a network-related exception.
+ *
+ * @since 2.12
*/
public class MongoSocketException extends MongoException {
diff --git a/src/main/com/mongodb/StickyHAShardedClusterServerSelector.java b/src/main/com/mongodb/MongosHAServerSelector.java
similarity index 83%
rename from src/main/com/mongodb/StickyHAShardedClusterServerSelector.java
rename to src/main/com/mongodb/MongosHAServerSelector.java
index b107d830845..af4f46b5c1c 100644
--- a/src/main/com/mongodb/StickyHAShardedClusterServerSelector.java
+++ b/src/main/com/mongodb/MongosHAServerSelector.java
@@ -22,8 +22,8 @@
import java.util.List;
import java.util.Set;
-class StickyHAShardedClusterServerSelector implements ServerSelector {
- private ServerAddress stickyMongos;
+class MongosHAServerSelector implements ServerSelector {
+ private ServerAddress stickTo;
private Set consideredServers = new HashSet();
@Override
@@ -36,9 +36,9 @@ public List choose(final ClusterDescription clusterDescriptio
Set okServers = getOkServers(clusterDescription);
synchronized (this) {
- if (!consideredServers.containsAll(okServers) || !okServers.contains(stickyMongos)) {
- if (stickyMongos != null && !okServers.contains(stickyMongos)) {
- stickyMongos = null;
+ if (!consideredServers.containsAll(okServers) || !okServers.contains(stickTo)) {
+ if (stickTo != null && !okServers.contains(stickTo)) {
+ stickTo = null;
consideredServers.clear();
}
ServerDescription fastestServer = null;
@@ -48,22 +48,22 @@ public List choose(final ClusterDescription clusterDescriptio
}
}
if (fastestServer != null) {
- stickyMongos = fastestServer.getAddress();
+ stickTo = fastestServer.getAddress();
consideredServers.addAll(okServers);
}
}
- if (stickyMongos == null) {
+ if (stickTo == null) {
return Collections.emptyList();
} else {
- return Arrays.asList(clusterDescription.getByServerAddress(stickyMongos));
+ return Arrays.asList(clusterDescription.getByServerAddress(stickTo));
}
}
}
@Override
public String toString() {
- return "StickyHAShardedClusterServerSelector{"
- + "stickyMongos=" + stickyMongos
+ return "MongosHAServerSelector{"
+ + (stickTo == null ? "" : "stickTo=" + stickTo)
+ '}';
}
diff --git a/src/main/com/mongodb/NoOpServerSelector.java b/src/main/com/mongodb/NoOpServerSelector.java
deleted file mode 100644
index 4d5f9fa3f60..00000000000
--- a/src/main/com/mongodb/NoOpServerSelector.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2008-2014 MongoDB, Inc.
- *
- * Licensed 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 com.mongodb;
-
-import java.util.List;
-
-class NoOpServerSelector implements ServerSelector {
- @Override
- public List choose(final ClusterDescription clusterDescription) {
- return clusterDescription.getAny();
- }
-
- @Override
- public String toString() {
- return "NoOpServerSelector{}";
- }
-}
diff --git a/src/main/com/mongodb/ParallelScanOptions.java b/src/main/com/mongodb/ParallelScanOptions.java
index 49270375140..e91874c920c 100644
--- a/src/main/com/mongodb/ParallelScanOptions.java
+++ b/src/main/com/mongodb/ParallelScanOptions.java
@@ -35,12 +35,16 @@ public static class Builder {
/**
* Set the requested number of cursors to iterate in parallel.
+ *
+ * Note: this is the maximum number of cursors the server will return, it may return fewer cursors.
+ *
*
- * @param numCursors the number of cursors requested, which must be >= 1
+ * @param numCursors the number of cursors requested, which must be >= 1 and <= 10000
* @return this
*/
public Builder numCursors(final int numCursors) {
isTrue("numCursors >= 1", numCursors >= 1);
+ isTrue("numCursors <= 10000", numCursors <= 10000);
this.numCursors = numCursors;
return this;
diff --git a/src/main/com/mongodb/ServerVersion.java b/src/main/com/mongodb/ServerVersion.java
index 7f778883cbc..f2774e4237c 100644
--- a/src/main/com/mongodb/ServerVersion.java
+++ b/src/main/com/mongodb/ServerVersion.java
@@ -17,10 +17,10 @@
package com.mongodb;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import static java.util.Arrays.asList;
import static org.bson.util.Assertions.isTrue;
import static org.bson.util.Assertions.notNull;
@@ -34,7 +34,7 @@ class ServerVersion implements Comparable {
* Creates a server version which will compare as less than all other valid versions
*/
public ServerVersion() {
- this.versionList = Collections.unmodifiableList(Arrays.asList(0, 0, 0));
+ this.versionList = Collections.unmodifiableList(asList(0, 0, 0));
}
/**
@@ -48,6 +48,16 @@ public ServerVersion(final List versionList) {
this.versionList = Collections.unmodifiableList(new ArrayList(versionList));
}
+ /**
+ * Constructs a new instance with the given major and minor versions and a patch version of 0.
+ *
+ * @param majorVersion the major version
+ * @param minorVersion the minor version
+ */
+ public ServerVersion(final int majorVersion, final int minorVersion) {
+ this(asList(majorVersion, minorVersion, 0));
+ }
+
/**
* Gets the version list.
*
diff --git a/src/main/com/mongodb/UnacknowledgedBulkWriteResult.java b/src/main/com/mongodb/UnacknowledgedBulkWriteResult.java
index 1f9274629c9..c85ace96297 100644
--- a/src/main/com/mongodb/UnacknowledgedBulkWriteResult.java
+++ b/src/main/com/mongodb/UnacknowledgedBulkWriteResult.java
@@ -43,6 +43,11 @@ public int getRemovedCount() {
throw getUnacknowledgedWriteException();
}
+ @Override
+ public boolean isModifiedCountAvailable() {
+ throw getUnacknowledgedWriteException();
+ }
+
@Override
public int getModifiedCount() {
throw getUnacknowledgedWriteException();
diff --git a/src/main/com/mongodb/UpdateCommandMessage.java b/src/main/com/mongodb/UpdateCommandMessage.java
index bb901d7f415..a147334963d 100644
--- a/src/main/com/mongodb/UpdateCommandMessage.java
+++ b/src/main/com/mongodb/UpdateCommandMessage.java
@@ -46,11 +46,15 @@ protected UpdateCommandMessage writeTheWrites(final OutputBuffer buffer, final i
writer.encodeDocument(getCommandEncoder(), update.getQuery());
writer.writeName("u");
writer.encodeDocument(encoder, update.getUpdateDocument());
- writer.writeBoolean("multi", update.isMulti());
- writer.writeBoolean("upsert", update.isUpsert());
+ if (update.isMulti()) {
+ writer.writeBoolean("multi", update.isMulti());
+ }
+ if (update.isUpsert()) {
+ writer.writeBoolean("upsert", update.isUpsert());
+ }
writer.popMaxDocumentSize();
writer.writeEndDocument();
- if (maximumCommandDocumentSizeExceeded(buffer, commandStartPosition)) {
+ if (exceedsLimits(buffer.getPosition() - commandStartPosition, i + 1)) {
writer.reset();
nextMessage = new UpdateCommandMessage(getWriteNamespace(), getWriteConcern(), updates.subList(i, updates.size()),
getCommandEncoder(), encoder, getSettings());
diff --git a/src/main/com/mongodb/WriteCommandResultHelper.java b/src/main/com/mongodb/WriteCommandResultHelper.java
index 87ba861336d..25f89358305 100644
--- a/src/main/com/mongodb/WriteCommandResultHelper.java
+++ b/src/main/com/mongodb/WriteCommandResultHelper.java
@@ -20,6 +20,9 @@
import java.util.Collections;
import java.util.List;
+import static com.mongodb.WriteRequest.Type.REPLACE;
+import static com.mongodb.WriteRequest.Type.UPDATE;
+
final class WriteCommandResultHelper {
static boolean hasError(final CommandResult commandResult) {
@@ -29,7 +32,7 @@ static boolean hasError(final CommandResult commandResult) {
static BulkWriteResult getBulkWriteResult(final WriteRequest.Type type, final CommandResult commandResult) {
int count = getCount(commandResult);
List upsertedItems = getUpsertedItems(commandResult);
- return new AcknowledgedBulkWriteResult(type, count - upsertedItems.size(), getModifiedCount(commandResult), upsertedItems);
+ return new AcknowledgedBulkWriteResult(type, count - upsertedItems.size(), getModifiedCount(type, commandResult), upsertedItems);
}
static BulkWriteException getBulkWriteException(final WriteRequest.Type type, final CommandResult commandResult) {
@@ -84,8 +87,12 @@ private static int getCount(final CommandResult commandResult) {
return commandResult.getInt("n");
}
- private static Integer getModifiedCount(final CommandResult commandResult) {
- return commandResult.getInt("nModified", 0);
+ private static Integer getModifiedCount(final WriteRequest.Type type, final CommandResult commandResult) {
+ Integer modifiedCount = (Integer) commandResult.get("nModified");
+ if (modifiedCount == null && !(type == UPDATE || type == REPLACE)) {
+ modifiedCount = 0;
+ }
+ return modifiedCount;
}
private static DBObject getErrInfo(final DBObject response) {
diff --git a/src/main/com/mongodb/gridfs/GridFS.java b/src/main/com/mongodb/gridfs/GridFS.java
index 86a9f82ded9..457ebc98bf4 100644
--- a/src/main/com/mongodb/gridfs/GridFS.java
+++ b/src/main/com/mongodb/gridfs/GridFS.java
@@ -49,7 +49,7 @@ public class GridFS {
/**
* file's chunk size
*/
- public static final int DEFAULT_CHUNKSIZE = 256 * 1024;
+ public static final int DEFAULT_CHUNKSIZE = 255 * 1024;
/**
* file's max chunk size
@@ -296,16 +296,23 @@ public void remove( String filename ){
/**
* removes all files matching the given query
+ *
* @param query
- * @throws MongoException
+ * @throws MongoException
*/
- public void remove( DBObject query ){
- if(query == null) {
- throw new IllegalArgumentException("query can not be null");
- }
- for ( GridFSDBFile f : find( query ) ){
- f.remove();
+ public void remove(DBObject query) {
+ if (query == null) {
+ throw new IllegalArgumentException("query can not be null");
+ }
+ // can't remove chunks without files_id thus keep them
+ List filesIds = new ArrayList();
+ for (GridFSDBFile f : find(query)) {
+ filesIds.add((ObjectId) f.getId());
}
+ // remove files from bucket
+ getFilesCollection().remove(query);
+ // then remove chunks
+ getChunksCollection().remove(new BasicDBObject("files_id", new BasicDBObject("$in", filesIds)));
}
diff --git a/src/test/com/mongodb/BulkWriteBatchCombinerSpecification.groovy b/src/test/com/mongodb/BulkWriteBatchCombinerSpecification.groovy
index de507f72f01..1c81881f3cb 100644
--- a/src/test/com/mongodb/BulkWriteBatchCombinerSpecification.groovy
+++ b/src/test/com/mongodb/BulkWriteBatchCombinerSpecification.groovy
@@ -48,6 +48,18 @@ class BulkWriteBatchCombinerSpecification extends Specification {
result == new AcknowledgedBulkWriteResult(INSERT, 1, 0, [])
}
+ def 'should handle null modifiedCount'() {
+ def runResults = new BulkWriteBatchCombiner(new ServerAddress(), ACKNOWLEDGED)
+ runResults.addResult(new AcknowledgedBulkWriteResult(UPDATE, 1, null, []), new IndexMap.RangeBased().add(0, 0))
+ runResults.addResult(new AcknowledgedBulkWriteResult(INSERT, 1, 0, []), new IndexMap.RangeBased().add(0, 0))
+
+ when:
+ def result = runResults.getResult()
+
+ then:
+ result == new AcknowledgedBulkWriteResult(1, 1, 0, null, [])
+ }
+
def 'should sort upserts'() {
given:
def runResults = new BulkWriteBatchCombiner(new ServerAddress(), ACKNOWLEDGED)
diff --git a/src/test/com/mongodb/BulkWriteOperationSpecification.groovy b/src/test/com/mongodb/BulkWriteOperationSpecification.groovy
index 77dde79d9c0..53b545f860e 100644
--- a/src/test/com/mongodb/BulkWriteOperationSpecification.groovy
+++ b/src/test/com/mongodb/BulkWriteOperationSpecification.groovy
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2008 - 2013 MongoDB Inc.
+ * Copyright (c) 2008-2014 MongoDB Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed 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
*
@@ -17,56 +17,105 @@
package com.mongodb
import org.bson.types.ObjectId
+import spock.lang.Unroll
+import static com.mongodb.Fixture.isReplicaSet
+import static com.mongodb.Fixture.isServerStartedWithJournalingDisabled
+import static com.mongodb.Fixture.isSharded
+import static com.mongodb.Fixture.isStandalone
+import static com.mongodb.Fixture.serverIsAtLeastVersion
import static com.mongodb.WriteRequest.Type.INSERT
import static com.mongodb.WriteRequest.Type.REMOVE
import static com.mongodb.WriteRequest.Type.REPLACE
import static com.mongodb.WriteRequest.Type.UPDATE
+import static org.junit.Assume.assumeTrue
+@Unroll
class BulkWriteOperationSpecification extends FunctionalSpecification {
def 'when no document with the same id exists, should insert the document'() {
given:
- def builder = collection.initializeOrderedBulkOperation();
- builder.insert(new BasicDBObject('_id', 1))
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(new BasicDBObject('_id', 1))
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
result == new AcknowledgedBulkWriteResult(INSERT, 1, [])
- result.getUpserts() == []
collection.findOne() == new BasicDBObject('_id', 1)
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when a document contains a key with an illegal character, inserting it should throw IllegalArgumentException'() {
+ given:
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(new BasicDBObject('$set', 1))
+
+ when:
+ operation.execute()
+
+ then:
+ thrown(IllegalArgumentException)
+
+ where:
+ ordered << [true, false]
}
def 'when a document with the same id exists, should throw an exception'() {
given:
def document = new BasicDBObject('_id', 1)
collection.insert(document)
- def builder = collection.initializeOrderedBulkOperation();
- builder.insert(document)
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(document)
when:
- builder.execute()
+ operation.execute()
then:
def ex = thrown(BulkWriteException)
ex.getWriteErrors().get(0).code == 11000
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when a document with no _id is inserted, the _id should be generated by the driver'() {
+ given:
+ def operation = initializeBulkOperation(ordered)
+ def document = new BasicDBObject()
+ operation.insert(document)
+
+ when:
+ def result = operation.execute()
+
+ then:
+ result == new AcknowledgedBulkWriteResult(INSERT, 1, [])
+ document._id instanceof ObjectId
+ collection.findOne() == document
+
+ where:
+ ordered << [true, false]
}
def 'when documents match the query, a remove of one should remove one of them'() {
given:
collection.insert(new BasicDBObject('x', true))
collection.insert(new BasicDBObject('x', true))
- def builder = collection.initializeOrderedBulkOperation()
- builder.find(new BasicDBObject('x', true)).removeOne()
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject('x', true)).removeOne()
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
result == new AcknowledgedBulkWriteResult(REMOVE, 1, [])
collection.count() == 1
+
+ where:
+ ordered << [true, false]
}
def 'when documents match the query, a remove should remove all of them'() {
@@ -74,121 +123,279 @@ class BulkWriteOperationSpecification extends FunctionalSpecification {
collection.insert(new BasicDBObject('x', true))
collection.insert(new BasicDBObject('x', true))
collection.insert(new BasicDBObject('x', false))
- def builder = collection.initializeOrderedBulkOperation()
- builder.find(new BasicDBObject('x', true)).remove()
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject('x', true)).remove()
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
result == new AcknowledgedBulkWriteResult(REMOVE, 2, [])
- collection.count() == 1
+ collection.count(new BasicDBObject('x', false)) == 1
+
+ where:
+ ordered << [true, false]
}
- def 'when a document matches the query, an update of one should update that document'() {
+ def 'when an update document contains a non $-prefixed key, update should throw IllegalArgumentException'() {
+ given:
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject()).update(new BasicDBObject('$set', new BasicDBObject('x', 1)).append('y', 2))
+
+ when:
+ operation.execute()
+
+ then:
+ thrown(IllegalArgumentException)
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when an update document contains a non $-prefixed key, updateOne should throw IllegalArgumentException'() {
+ given:
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject()).updateOne(new BasicDBObject('$set', new BasicDBObject('x', 1)).append('y', 2))
+
+ when:
+ operation.execute()
+
+ then:
+ thrown(IllegalArgumentException)
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when multiple document match the query, updateOne should update only one of them'() {
given:
collection.insert(new BasicDBObject('x', true))
collection.insert(new BasicDBObject('x', true))
- def builder = collection.initializeOrderedBulkOperation()
- builder.find(new BasicDBObject('x', true)).updateOne(new BasicDBObject('$set', new BasicDBObject('y', 1)))
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject('x', true)).updateOne(new BasicDBObject('$set', new BasicDBObject('y', 1)))
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
- result == new AcknowledgedBulkWriteResult(UPDATE, 1, 1, [])
- collection.count(new BasicDBObject('y', 1)) == 1
+ result == new AcknowledgedBulkWriteResult(UPDATE, 1, expectedModifiedCount(1), [])
+ collection.find(new BasicDBObject('y', 1), new BasicDBObject('x', 1).append('_id', 0)).toArray() == [new BasicDBObject('x', true)]
+
+ where:
+ ordered << [true, false]
}
- def 'when documents match the query, an update should update all of them'() {
+ def 'when documents match the query, an update should update all of them'() {
given:
collection.insert(new BasicDBObject('x', true))
collection.insert(new BasicDBObject('x', true))
collection.insert(new BasicDBObject('x', false))
- def builder = collection.initializeOrderedBulkOperation()
- builder.find(new BasicDBObject('x', true)).update(new BasicDBObject('$set', new BasicDBObject('y', 1)))
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject('x', true)).update(new BasicDBObject('$set', new BasicDBObject('y', 1)))
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
- result == new AcknowledgedBulkWriteResult(UPDATE, 2, 2, [])
+ result == new AcknowledgedBulkWriteResult(UPDATE, 2, expectedModifiedCount(2), [])
collection.count(new BasicDBObject('y', 1)) == 2
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when no document matches the query, updateOne with upsert should insert a document'() {
+ given:
+ def id = new ObjectId()
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject('_id', id)).upsert().updateOne(new BasicDBObject('$set', new BasicDBObject('x', 2)))
+
+ when:
+ def result = operation.execute()
+
+ then:
+ result == new AcknowledgedBulkWriteResult(UPDATE, 0, expectedModifiedCount(0), [new BulkWriteUpsert(0, id)])
+ collection.findOne() == new BasicDBObject('_id', id).append('x', 2)
+
+ where:
+ ordered << [true, false]
}
- def 'when no document matches the query, an update with upsert should insert a document'() {
+ def 'when no document matches the query, update with upsert should insert a document'() {
given:
def id = new ObjectId()
- def builder = collection.initializeOrderedBulkOperation()
- builder.find(new BasicDBObject('_id', id)).upsert().updateOne(new BasicDBObject('$set', new BasicDBObject('x', 2)))
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject('_id', id)).upsert().update(new BasicDBObject('$set', new BasicDBObject('x', 2)))
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
- result == new AcknowledgedBulkWriteResult(UPDATE, 0, [new BulkWriteUpsert(0, id)])
+ result == new AcknowledgedBulkWriteResult(UPDATE, 0, expectedModifiedCount(0), [new BulkWriteUpsert(0, id)])
collection.findOne() == new BasicDBObject('_id', id).append('x', 2)
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when documents matches the query, update with upsert should update all of them'() {
+ given:
+ collection.insert(new BasicDBObject('x', true))
+ collection.insert(new BasicDBObject('x', true))
+ collection.insert(new BasicDBObject('x', false))
+
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject('x', true)).upsert().update(new BasicDBObject('$set', new BasicDBObject('y', 1)))
+
+ when:
+ def result = operation.execute()
+
+ then:
+ result == new AcknowledgedBulkWriteResult(UPDATE, 2, expectedModifiedCount(2), [])
+ collection.count(new BasicDBObject('y', 1)) == 2
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when a document contains a key with an illegal character, replacing a document with it should throw IllegalArgumentException'() {
+ given:
+ def id = new ObjectId()
+ def operation = initializeBulkOperation(ordered)
+ def query = new BasicDBObject('_id', id)
+ operation.find(query).upsert().replaceOne(new BasicDBObject('$set', new BasicDBObject('x', 1)))
+
+ when:
+ operation.execute()
+
+ then:
+ thrown(IllegalArgumentException)
+
+ where:
+ ordered << [true, false]
}
def 'when no document matches the query, a replace with upsert should insert a document'() {
given:
def id = new ObjectId()
- def builder = collection.initializeOrderedBulkOperation()
+ def operation = initializeBulkOperation(ordered)
def query = new BasicDBObject('_id', id)
- builder.find(query).upsert().replaceOne(new BasicDBObject('_id', id).append('x', 2))
+ operation.find(query).upsert().replaceOne(new BasicDBObject('_id', id).append('x', 2))
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
- result == new AcknowledgedBulkWriteResult(UPDATE, 0, [new BulkWriteUpsert(0, id)])
+ result == new AcknowledgedBulkWriteResult(UPDATE, 0, expectedModifiedCount(0), [new BulkWriteUpsert(0, id)])
collection.findOne() == new BasicDBObject('_id', id).append('x', 2)
+
+ where:
+ ordered << [true, false]
}
- def 'when a document matches the query, an update with upsert should update that document'() {
+ def 'when multiple documents match the query, replaceOne should replace one of them'() {
+ given:
+ collection.insert(new BasicDBObject('x', true))
+ collection.insert(new BasicDBObject('x', true))
+
+ def operation = initializeBulkOperation(ordered)
+ def replacement = new BasicDBObject('y', 1).append('x', false)
+ operation.find(new BasicDBObject('x', true)).replaceOne(replacement)
+
+ when:
+ def result = operation.execute()
+
+ then:
+ result == new AcknowledgedBulkWriteResult(UPDATE, 1, expectedModifiedCount(1), [])
+ collection.find(new BasicDBObject('x', false), new BasicDBObject('_id', 0)).toArray() == [replacement]
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when a document matches the query, updateOne with upsert should update that document'() {
given:
def id = new ObjectId()
collection.insert(new BasicDBObject('_id', id))
- def builder = collection.initializeOrderedBulkOperation()
- builder.find(new BasicDBObject('_id', id)).upsert().updateOne(new BasicDBObject('$set', new BasicDBObject('x', 2)))
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject('_id', id)).upsert().updateOne(new BasicDBObject('$set', new BasicDBObject('x', 2)))
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
- result == new AcknowledgedBulkWriteResult(UPDATE, 1, 1, [])
+ result == new AcknowledgedBulkWriteResult(UPDATE, 1, expectedModifiedCount(1), [])
collection.findOne() == new BasicDBObject('_id', id).append('x', 2)
+
+ where:
+ ordered << [true, false]
}
def 'when a document matches the query, a replace with upsert should update that document'() {
given:
collection.insert(new BasicDBObject('_id', 1))
- def builder = collection.initializeOrderedBulkOperation()
- builder.find(new BasicDBObject('_id', 1)).upsert().replaceOne(new BasicDBObject('_id', 1).append('x', 2))
+ def operation = initializeBulkOperation(ordered)
+ operation.find(new BasicDBObject('_id', 1)).upsert().replaceOne(new BasicDBObject('_id', 1).append('x', 2))
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
- result == new AcknowledgedBulkWriteResult(REPLACE, 1, 1, [])
+ result == new AcknowledgedBulkWriteResult(REPLACE, 1, expectedModifiedCount(1), [])
collection.findOne() == new BasicDBObject('_id', 1).append('x', 2)
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when a replacement document is 16MB, the document is still replaced'() {
+ given:
+ collection.insert(new BasicDBObject('_id', 1))
+
+ def operation = collection.initializeOrderedBulkOperation()
+ operation.find(new BasicDBObject('_id', 1)).upsert().replaceOne(new BasicDBObject('_id', 1)
+ .append('x', new byte[1024 * 1024 * 16 - 30]))
+
+ when:
+ def result = operation.execute()
+
+ then:
+ result == new AcknowledgedBulkWriteResult(REPLACE, 1, expectedModifiedCount(1), [])
+ collection.count() == 1
+ }
+
+ def 'when two update documents together exceed 16MB, the documents are still updated'() {
+ given:
+ def operation = collection.initializeOrderedBulkOperation()
+ operation.find(new BasicDBObject('_id', 1))
+ .update(new BasicDBObject('$set', new BasicDBObject('x', new byte[1024 * 1024 * 8])));
+ operation.find(new BasicDBObject('_id', 2))
+ .update(new BasicDBObject('$set', new BasicDBObject('x', new byte[1024 * 1024 * 8])));
+
+ when:
+ def result = operation.execute()
+
+ then:
+ result == new AcknowledgedBulkWriteResult(UPDATE, 0, expectedModifiedCount(0), [])
+ collection.count() == 0
}
+
def 'should handle multi-length runs of ordered insert, update, replace, and remove'() {
given:
collection.insert(getTestInserts())
- def builder = collection.initializeOrderedBulkOperation()
- addWritesToBuilder(builder)
+ def operation = collection.initializeOrderedBulkOperation()
+ addWritesToOperation(operation)
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
- result == new AcknowledgedBulkWriteResult(2, 4, 2, 4, [])
+ result == new AcknowledgedBulkWriteResult(2, 4, 2, expectedModifiedCount(4), [])
collection.findOne(new BasicDBObject('_id', 1)) == new BasicDBObject('_id', 1).append('x', 2)
collection.findOne(new BasicDBObject('_id', 2)) == new BasicDBObject('_id', 2).append('x', 3)
@@ -205,11 +412,11 @@ class BulkWriteOperationSpecification extends FunctionalSpecification {
collection.getDB().requestStart()
collection.insert(getTestInserts())
- def builder = collection.initializeOrderedBulkOperation()
- addWritesToBuilder(builder)
+ def operation = initializeBulkOperation(ordered)
+ addWritesToOperation(operation)
when:
- def result = builder.execute(WriteConcern.UNACKNOWLEDGED)
+ def result = operation.execute(WriteConcern.UNACKNOWLEDGED)
collection.insert(new BasicDBObject('_id', 9))
then:
@@ -225,36 +432,42 @@ class BulkWriteOperationSpecification extends FunctionalSpecification {
cleanup:
collection.getDB().requestDone()
+
+ where:
+ ordered << [true, false]
}
def 'error details should have correct index on ordered write failure'() {
given:
- def builder = collection.initializeOrderedBulkOperation()
- builder.insert(new BasicDBObject('_id', 1))
- builder.find(new BasicDBObject('_id', 1)).updateOne(new BasicDBObject('$set', new BasicDBObject('x', 3)))
- builder.insert(new BasicDBObject('_id', 1))
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(new BasicDBObject('_id', 1))
+ operation.find(new BasicDBObject('_id', 1)).updateOne(new BasicDBObject('$set', new BasicDBObject('x', 3)))
+ operation.insert(new BasicDBObject('_id', 1))
when:
- builder.execute()
+ operation.execute()
then:
def ex = thrown(BulkWriteException)
ex.writeErrors.size() == 1
ex.writeErrors[0].index == 2
ex.writeErrors[0].code == 11000
+
+ where:
+ ordered << [true, false]
}
def 'should handle multi-length runs of unordered insert, update, replace, and remove'() {
given:
- collection.insert(getTestInserts());
- def builder = collection.initializeUnorderedBulkOperation()
- addWritesToBuilder(builder)
+ collection.insert(getTestInserts())
+ def operation = collection.initializeUnorderedBulkOperation()
+ addWritesToOperation(operation)
when:
- def result = builder.execute()
+ def result = operation.execute()
then:
- result == new AcknowledgedBulkWriteResult(2, 4, 2, 4, [])
+ result == new AcknowledgedBulkWriteResult(2, 4, 2, expectedModifiedCount(4), [])
collection.findOne(new BasicDBObject('_id', 1)) == new BasicDBObject('_id', 1).append('x', 2)
collection.findOne(new BasicDBObject('_id', 2)) == new BasicDBObject('_id', 2).append('x', 3)
@@ -266,17 +479,110 @@ class BulkWriteOperationSpecification extends FunctionalSpecification {
collection.findOne(new BasicDBObject('_id', 8)) == new BasicDBObject('_id', 8)
}
+ def 'should split when the number of writes is larger than the match write batch size'() {
+ given:
+ def operation = initializeBulkOperation(ordered)
+ (0..2000).each {
+ operation.insert(new BasicDBObject())
+ }
+
+ when:
+ operation.execute()
+
+ then:
+ collection.find().count() == 2001
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'should split when the message size would exceed the max command message size'() {
+ given:
+ def operation = collection.initializeUnorderedBulkOperation()
+ (0..5).each {
+ operation.insert(new BasicDBObject('binary', new byte[1024 * 1024 * 4]))
+ }
+
+ when:
+ operation.execute()
+
+ then:
+ collection.count() == 6
+ }
+
+ def 'should throw correct BulkWriteException when the message size would exceed the max command message size'(boolean ordered) {
+ given:
+ def operation = initializeBulkOperation(ordered)
+ (0..5).each {
+ operation.insert(new BasicDBObject('_id', it).append('binary', new byte[1024 * 1024 * 4]))
+ }
+ operation.insert(new BasicDBObject('_id', 0)) // duplicate key
+ operation.insert(new BasicDBObject('_id', 6))
+
+ when:
+ operation.execute()
+
+ then:
+ def ex = thrown(BulkWriteException)
+ ex.writeErrors.size() == 1
+ ex.writeErrors[0].index == 6
+ ex.writeResult == new AcknowledgedBulkWriteResult(INSERT, ordered ? 6 : 7, []) // for ordered, last doc will not be inserted
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'should insert all documents when the number of inserts is larger than the match write batch size '(boolean ordered) {
+ given:
+ def operation = initializeBulkOperation(ordered)
+ (0..1001).each {
+ operation.insert(new BasicDBObject('_id', it))
+ }
+
+ when:
+ operation.execute()
+
+ then:
+ collection.count == 1002
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'should throw correct BulkWriteException when the number of writes is larger than the match write batch size '(boolean ordered) {
+ given:
+ def operation = initializeBulkOperation(ordered)
+ (0..999).each {
+ operation.insert(new BasicDBObject('_id', it))
+ }
+
+ operation.insert(new BasicDBObject('_id', 0)) // duplicate key
+ operation.insert(new BasicDBObject('_id', 1000))
+
+ when:
+ operation.execute()
+
+ then:
+ def ex = thrown(BulkWriteException)
+ ex.writeErrors.size() == 1
+ ex.writeErrors[0].index == 1000
+ ex.writeResult == new AcknowledgedBulkWriteResult(INSERT, ordered ? 1000 : 1001, []) // for ordered, last doc will not be inserted
+
+ where:
+ ordered << [true, false]
+ }
+
def 'error details should have correct index on unordered write failure'() {
given:
collection.insert(getTestInserts())
- def builder = collection.initializeUnorderedBulkOperation()
- builder.insert(new BasicDBObject('_id', 1))
- builder.find(new BasicDBObject('_id', 2)).updateOne(new BasicDBObject('$set', new BasicDBObject('x', 3)))
- builder.insert(new BasicDBObject('_id', 3))
+ def operation = collection.initializeUnorderedBulkOperation()
+ operation.insert(new BasicDBObject('_id', 1))
+ operation.find(new BasicDBObject('_id', 2)).updateOne(new BasicDBObject('$set', new BasicDBObject('x', 3)))
+ operation.insert(new BasicDBObject('_id', 3))
when:
- builder.execute()
+ operation.execute()
then:
def ex = thrown(BulkWriteException)
@@ -287,103 +593,207 @@ class BulkWriteOperationSpecification extends FunctionalSpecification {
ex.writeErrors[1].code == 11000
}
+ def 'when there is a duplicate key error and a write concern error, both should be reported'() {
+ assumeTrue(isReplicaSet())
+
+ given:
+ collection.insert(getTestInserts())
+
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(new BasicDBObject('_id', 7))
+ operation.insert(new BasicDBObject('_id', 1)) // duplicate key
+
+ when:
+ operation.execute(new WriteConcern(4, 1)) // This is assuming that it won't be able to replicate to 4 servers in 1 ms
+
+ then:
+ def ex = thrown(BulkWriteException)
+ ex.writeErrors.size() == 1
+ ex.writeErrors[0].index == 1
+ ex.writeErrors[0].code == 11000
+ ex.writeConcernError != null
+
+ where:
+ ordered << [false]
+ }
+
+ def 'when w > 1 write concern is used on a standalone server with write commands, CommandFailureException is thrown'() {
+ assumeTrue(isStandalone() && serverIsAtLeastVersion(2.6))
+
+ given:
+ def operation = collection.initializeUnorderedBulkOperation()
+ operation.insert(new BasicDBObject('_id', 1))
+
+ when:
+ operation.execute(new WriteConcern(2, 1))
+
+ then:
+ thrown(CommandFailureException)
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when w > 1 write concern is used on a standalone server without write commands, BulkWriteException is thrown'() {
+ assumeTrue(isStandalone() && !serverIsAtLeastVersion(2.6))
+
+ given:
+ def operation = collection.initializeUnorderedBulkOperation()
+ operation.insert(new BasicDBObject('_id', 1))
+ operation.insert(new BasicDBObject('_id', 2))
+
+ when:
+ operation.execute(new WriteConcern(2, 1))
+
+ then:
+ def e = thrown(BulkWriteException)
+ e.writeResult == new AcknowledgedBulkWriteResult(INSERT, 2, [])
+ e.writeConcernError != null
+ e.writeConcernError.getDetails().containsField('wnote')
+
+ where:
+ ordered << [true, false]
+ }
+
+ def 'when j write concern is used on a server without journaling or write commands, BulkWriteException is thrown'() {
+ assumeTrue(!isSharded() && isServerStartedWithJournalingDisabled() && !serverIsAtLeastVersion(2.6))
+
+ given:
+ def operation = collection.initializeUnorderedBulkOperation()
+ operation.insert(new BasicDBObject('_id', 1))
+ operation.insert(new BasicDBObject('_id', 2))
+
+ when:
+ operation.execute(WriteConcern.JOURNALED)
+
+ then:
+ def e = thrown(BulkWriteException)
+ e.writeResult == new AcknowledgedBulkWriteResult(INSERT, 2, [])
+ e.writeConcernError != null
+ e.writeConcernError.getDetails().containsField('jnote')
+
+ where:
+ ordered << [true, false]
+ }
+
def 'execute should throw IllegalStateException when already executed'() {
given:
- def builder = collection.initializeOrderedBulkOperation();
- builder.insert(new BasicDBObject('_id', 1))
- builder.execute()
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(new BasicDBObject('_id', 1))
+ operation.execute()
when:
- builder.execute()
+ operation.execute()
then:
thrown(IllegalStateException)
+
+ where:
+ ordered << [true, false]
}
def 'execute with write concern should throw IllegalStateException when already executed'() {
given:
- def builder = collection.initializeOrderedBulkOperation();
- builder.insert(new BasicDBObject('_id', 1))
- builder.execute()
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(new BasicDBObject('_id', 1))
+ operation.execute()
when:
- builder.execute(WriteConcern.ACKNOWLEDGED)
+ operation.execute(WriteConcern.ACKNOWLEDGED)
then:
thrown(IllegalStateException)
+
+ where:
+ ordered << [true, false]
}
def 'insert should throw IllegalStateException when already executed'() {
given:
- def builder = collection.initializeOrderedBulkOperation();
- builder.insert(new BasicDBObject('_id', 1))
- builder.execute()
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(new BasicDBObject('_id', 1))
+ operation.execute()
when:
- builder.insert(new BasicDBObject())
+ operation.insert(new BasicDBObject())
then:
thrown(IllegalStateException)
+
+ where:
+ ordered << [true, false]
}
def 'find should throw IllegalStateException when already executed'() {
given:
- def builder = collection.initializeOrderedBulkOperation();
- builder.insert(new BasicDBObject('_id', 1))
- builder.execute()
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(new BasicDBObject('_id', 1))
+ operation.execute()
when:
- builder.find(new BasicDBObject());
+ operation.find(new BasicDBObject())
then:
thrown(IllegalStateException)
+
+ where:
+ ordered << [true, false]
}
// just need to check one case here, since the others are checked above
def 'should throw IllegalStateException when already executed with write concern'() {
given:
- def builder = collection.initializeOrderedBulkOperation();
- builder.insert(new BasicDBObject('_id', 1))
- builder.execute(WriteConcern.ACKNOWLEDGED)
+ def operation = initializeBulkOperation(ordered)
+ operation.insert(new BasicDBObject('_id', 1))
+ operation.execute(WriteConcern.ACKNOWLEDGED)
when:
- builder.execute()
+ operation.execute()
then:
thrown(IllegalStateException)
+
+ where:
+ ordered << [true, false]
}
def 'should throw IllegalStateException when executing an empty bulk operation'() {
given:
- def builder = collection.initializeOrderedBulkOperation();
+ def operation = initializeBulkOperation(ordered)
when:
- builder.execute()
+ operation.execute()
then:
thrown(IllegalStateException)
+
+ where:
+ ordered << [true, false]
}
def 'should throw IllegalStateException when executing an empty bulk operation with a write concern'() {
given:
- def builder = collection.initializeOrderedBulkOperation();
+ def operation = initializeBulkOperation(ordered)
when:
- builder.execute(WriteConcern.ACKNOWLEDGED)
+ operation.execute(WriteConcern.ACKNOWLEDGED)
then:
thrown(IllegalStateException)
+
+ where:
+ ordered << [true, false]
}
- private static void addWritesToBuilder(BulkWriteOperation builder) {
- builder.find(new BasicDBObject('_id', 1)).updateOne(new BasicDBObject('$set', new BasicDBObject('x', 2)))
- builder.find(new BasicDBObject('_id', 2)).updateOne(new BasicDBObject('$set', new BasicDBObject('x', 3)))
- builder.find(new BasicDBObject('_id', 3)).removeOne()
- builder.find(new BasicDBObject('_id', 4)).removeOne()
- builder.find(new BasicDBObject('_id', 5)).replaceOne(new BasicDBObject('_id', 5).append('x', 4))
- builder.find(new BasicDBObject('_id', 6)).replaceOne(new BasicDBObject('_id', 6).append('x', 5))
- builder.insert(new BasicDBObject('_id', 7))
- builder.insert(new BasicDBObject('_id', 8))
+ private static void addWritesToOperation(BulkWriteOperation operation) {
+ operation.find(new BasicDBObject('_id', 1)).updateOne(new BasicDBObject('$set', new BasicDBObject('x', 2)))
+ operation.find(new BasicDBObject('_id', 2)).updateOne(new BasicDBObject('$set', new BasicDBObject('x', 3)))
+ operation.find(new BasicDBObject('_id', 3)).removeOne()
+ operation.find(new BasicDBObject('_id', 4)).removeOne()
+ operation.find(new BasicDBObject('_id', 5)).replaceOne(new BasicDBObject('_id', 5).append('x', 4))
+ operation.find(new BasicDBObject('_id', 6)).replaceOne(new BasicDBObject('_id', 6).append('x', 5))
+ operation.insert(new BasicDBObject('_id', 7))
+ operation.insert(new BasicDBObject('_id', 8))
}
private static List getTestInserts() {
@@ -396,4 +806,11 @@ class BulkWriteOperationSpecification extends FunctionalSpecification {
]
}
+ private BulkWriteOperation initializeBulkOperation(boolean ordered) {
+ ordered ? collection.initializeOrderedBulkOperation() : collection.initializeUnorderedBulkOperation()
+ }
+
+ private static Integer expectedModifiedCount(final int expectedCountForServersThatSupportIt) {
+ (serverIsAtLeastVersion(2.5)) ? expectedCountForServersThatSupportIt : null
+ }
}
\ No newline at end of file
diff --git a/src/test/com/mongodb/DBCollectionTest.java b/src/test/com/mongodb/DBCollectionTest.java
index 6626304c7f9..717f4df4fe7 100644
--- a/src/test/com/mongodb/DBCollectionTest.java
+++ b/src/test/com/mongodb/DBCollectionTest.java
@@ -21,6 +21,7 @@
import org.junit.Test;
import java.net.UnknownHostException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -49,6 +50,17 @@ public void testMultiInsert() {
c.insert(inserted1,inserted2);
}
+ @Test
+ public void testLargeMultiInsert() {
+ List documents = new ArrayList();
+ for (int i = 0; i < 1001; i++) {
+ documents.add(new BasicDBObject());
+ }
+
+ collection.insert(documents);
+ assertEquals(1001, collection.count());
+ }
+
@Test
public void testCappedCollection() {
String collectionName = "testCapped";
@@ -181,6 +193,12 @@ public void testDropIndex(){
c.dropIndexes();
assertEquals( 1 , c.getIndexInfo().size() );
+ c.createIndex( new BasicDBObject( "x" , 1 ) );
+ assertEquals( 2 , c.getIndexInfo().size() );
+
+ c.dropIndexes();
+ assertEquals( 1 , c.getIndexInfo().size() );
+
c.ensureIndex( new BasicDBObject( "x" , 1 ) );
assertEquals( 2 , c.getIndexInfo().size() );
@@ -190,6 +208,18 @@ public void testDropIndex(){
c.dropIndex( new BasicDBObject( "x" , 1 ) );
assertEquals( 2 , c.getIndexInfo().size() );
+ c.dropIndexes();
+ assertEquals( 1 , c.getIndexInfo().size() );
+
+ c.createIndex( new BasicDBObject( "x" , 1 ) );
+ assertEquals( 2 , c.getIndexInfo().size() );
+
+ c.createIndex( new BasicDBObject( "y" , 1 ) );
+ assertEquals( 3 , c.getIndexInfo().size() );
+
+ c.dropIndex( new BasicDBObject( "x" , 1 ) );
+ assertEquals( 2 , c.getIndexInfo().size() );
+ c.dropIndexes();
}
@Test
@@ -246,7 +276,15 @@ public void testEnsureIndex(){
c.ensureIndex( new BasicDBObject( "x" , 1 ) , new BasicDBObject( "unique" , true ) );
assertEquals( 2 , c.getIndexInfo().size() );
- assertEquals( Boolean.TRUE , c.getIndexInfo().get(1).get( "unique" ) );
+ DBObject indexInfo = c.getIndexInfo().get(1);
+ assertEquals("x_1", indexInfo.get("name"));
+
+ c.drop();
+
+ c.createIndex(new BasicDBObject("x", 1), new BasicDBObject("unique", true));
+ indexInfo = c.getIndexInfo().get(1);
+ assertEquals( Boolean.TRUE , indexInfo.get("unique") );
+ assertEquals("x_1", indexInfo.get("name"));
}
@Test
@@ -259,22 +297,47 @@ public void testEnsureNestedIndex(){
assertEquals( 1 , c.getIndexInfo().size() );
c.ensureIndex( new BasicDBObject("x.y", 1), "nestedIdx1", false);
assertEquals( 2 , c.getIndexInfo().size() );
+ assertEquals( "nestedIdx1" , c.getIndexInfo().get(1).get("name") );
+
+ c.drop();
+ c.createIndex(new BasicDBObject("x.y", 1), new BasicDBObject("name", "nestedIdx1").append("unique", false));
+ assertEquals(2, c.getIndexInfo().size());
+ assertEquals( "nestedIdx1" , c.getIndexInfo().get(1).get("name") );
}
- @Test(expected = MongoException.DuplicateKey.class)
- public void testIndexExceptions(){
+ @Test(expected = DuplicateKeyException.class)
+ public void testEnsureIndexExceptions(){
collection.insert(new BasicDBObject("x", 1));
collection.insert(new BasicDBObject("x", 1));
collection.ensureIndex(new BasicDBObject("y", 1));
collection.resetIndexCache();
- collection.ensureIndex(new BasicDBObject("y", 1)); // make sure this doesn't throw
+ try {
+ collection.ensureIndex(new BasicDBObject("y", 1)); // make sure this doesn't throw
+ } catch (Exception e) {
+ fail("Trying to create an existing index should not fail.");
+ }
collection.resetIndexCache();
collection.ensureIndex(new BasicDBObject("x", 1), new BasicDBObject("unique", true));
}
+ @Test(expected = DuplicateKeyException.class)
+ public void testCreateIndexExceptions(){
+ collection.insert(new BasicDBObject("x", 1));
+ collection.insert(new BasicDBObject("x", 1));
+
+ collection.createIndex(new BasicDBObject("y", 1));
+ try {
+ collection.createIndex(new BasicDBObject("y", 1)); // make sure this doesn't throw
+ } catch (Exception e) {
+ fail("Trying to create an existing index should not fail.");
+ }
+
+ collection.createIndex(new BasicDBObject("x", 1), new BasicDBObject("unique", true));
+ }
+
@Test
public void testMultiInsertNoContinue() {
collection.setWriteConcern(WriteConcern.NORMAL);
@@ -499,8 +562,9 @@ public void testParallelScan() throws UnknownHostException {
collection.insert(new BasicDBObject("_id", i));
}
- List cursors = collection.parallelScan(ParallelScanOptions.builder().numCursors(3).batchSize(1000).build());
- assertEquals(3, cursors.size());
+ int numCursors = 10;
+ List cursors = collection.parallelScan(ParallelScanOptions.builder().numCursors(numCursors).batchSize(1000).build());
+ assertTrue(cursors.size() <= numCursors);
for (Cursor cursor : cursors) {
while (cursor.hasNext()) {
diff --git a/src/test/com/mongodb/DBCursorTest.java b/src/test/com/mongodb/DBCursorTest.java
index ea099ed3136..0facb58a51f 100644
--- a/src/test/com/mongodb/DBCursorTest.java
+++ b/src/test/com/mongodb/DBCursorTest.java
@@ -499,6 +499,32 @@ public void testMaxTimeForIterator() {
}
}
+ @Test
+ public void testMaxTimeDuringGetMore() {
+ assumeFalse(isSharded(getMongoClient()));
+ checkServerVersion(2.5);
+ for (int i=0; i < 20; i++) {
+ collection.insert(new BasicDBObject("x", 1));
+ }
+
+ DBCursor cursor = new DBCursor(collection, new BasicDBObject("x", 1), new BasicDBObject(), ReadPreference.primary());
+ cursor.batchSize(10);
+ cursor.maxTime(1, SECONDS);
+ cursor.next();
+
+ enableMaxTimeFailPoint();
+ try {
+ while(cursor.hasNext()) {
+ cursor.next();
+ }
+ fail("Show have thrown");
+ } catch (MongoExecutionTimeoutException e) {
+ assertEquals(50, e.getCode());
+ } finally {
+ disableMaxTimeFailPoint();
+ }
+ }
+
@Test
public void testMaxTimeForIterable() {
assumeFalse(isSharded(getMongoClient()));
diff --git a/src/test/com/mongodb/DBTest.java b/src/test/com/mongodb/DBTest.java
index ff8f710bd00..e388ba7e0de 100644
--- a/src/test/com/mongodb/DBTest.java
+++ b/src/test/com/mongodb/DBTest.java
@@ -25,6 +25,7 @@
import java.net.UnknownHostException;
import java.util.Arrays;
+import java.util.List;
import static com.mongodb.ReadPreference.primary;
import static com.mongodb.ReadPreference.primaryPreferred;
@@ -37,6 +38,7 @@
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
@@ -461,6 +463,110 @@ public void shouldNotThrowAnExceptionOnCommandFailure() {
assertThat(commandResult, hasFields(new String[]{"serverUsed", "ok", "errmsg"}));
}
+ @Test
+ public void shouldAddReadOnlyUser() {
+ String userName = "newUser";
+ String pwd = "pwd";
+ getDatabase().addUser(userName, pwd.toCharArray(), true);
+ try {
+ assertCorrectUserExists(userName, pwd, true, getDatabase());
+ } finally {
+ getDatabase().removeUser(userName);
+ }
+ }
+
+ @Test
+ public void shouldAddReadWriteUser() {
+ String userName = "newUser";
+ String pwd = "pwd";
+ getDatabase().addUser(userName, pwd.toCharArray(), false);
+ try {
+ assertCorrectUserExists(userName, pwd, false, getDatabase());
+ } finally {
+ getDatabase().removeUser(userName);
+ }
+ }
+
+ @Test
+ public void shouldAddReadWriteAdminUser() throws UnknownHostException {
+ String userName = "newUser";
+ String pwd = "pwd";
+ MongoClient mongoClient = new MongoClient(getMongoClientURI());
+ DB adminDB = mongoClient.getDB("admin");
+ adminDB.addUser(userName, pwd.toCharArray(), false);
+ try {
+ assertTrue(adminDB.authenticate(userName, pwd.toCharArray()));
+ assertCorrectUserExists(userName, pwd, false, adminDB);
+ } finally {
+ adminDB.removeUser(userName);
+ mongoClient.close();
+ }
+ }
+
+
+ @Test
+ public void shouldAddReadOnlyAdminUser() throws UnknownHostException {
+ String readWriteUserName = "newUserReadWrite";
+ String readOnlyUserName = "newUser";
+ String pwd = "pwd";
+ MongoClient mongoClient = new MongoClient(getMongoClientURI());
+ DB adminDB = mongoClient.getDB("admin");
+ adminDB.addUser(readWriteUserName, pwd.toCharArray(), false);
+ adminDB.authenticate(readWriteUserName, pwd.toCharArray());
+ adminDB.addUser(readOnlyUserName, pwd.toCharArray(), true);
+ try {
+ assertCorrectUserExists(readOnlyUserName, pwd, true, adminDB);
+ } finally {
+ adminDB.removeUser(readOnlyUserName);
+ adminDB.removeUser(readWriteUserName);
+ mongoClient.close();
+ }
+ }
+
+ @Test
+ public void shouldRemoveUser() {
+ String userName = "newUser";
+ getDatabase().addUser(userName, "pwd".toCharArray(), true);
+ getDatabase().removeUser(userName);
+ assertThatUserIsRemoved(userName, getDatabase());
+ }
+
+ private void assertThatUserIsRemoved(final String userName, final DB database) {
+ if (serverIsAtLeastVersion(2.6)) {
+ CommandResult usersInfo = database.command(new BasicDBObject("usersInfo", userName));
+ assertEquals(0, ((List) usersInfo.get("users")).size());
+ }
+ else {
+ assertNull(database.getCollection("system.users").findOne(new BasicDBObject("user", userName)));
+ }
+ }
+
+
+ private void assertCorrectUserExists(final String userName, final String password, final boolean isReadOnly, final DB database) {
+ if (serverIsAtLeastVersion(2.6, database.getMongo())) {
+ CommandResult usersInfo = database.command(new BasicDBObject("usersInfo", userName));
+ DBObject user = (DBObject) ((List) usersInfo.get("users")).get(0);
+ assertEquals(userName, user.get("user"));
+ assertEquals(database.getName(), user.get("db"));
+ assertEquals(getExpectedRole(isReadOnly, database), ((DBObject) ((List) user.get("roles")).get(0)).get("role"));
+ }
+ else {
+ assertEquals(new BasicDBObject("user", userName).append("readOnly", isReadOnly)
+ .append("pwd", getDatabase()._hash(userName, password.toCharArray())),
+ database.getCollection("system.users").findOne(new BasicDBObject("user", userName),
+ new BasicDBObject("_id", 0)));
+ }
+ }
+
+ private String getExpectedRole(final boolean isReadOnly, final DB database) {
+ if (database.getName().equals("admin")) {
+ return isReadOnly ? "readAnyDatabase" : "root";
+ } else {
+ return isReadOnly ? "read" : "dbOwner";
+ }
+ }
+
+
private DB getReplicaSetDB() throws UnknownHostException {
Mongo mongo = new MongoClient(Arrays.asList(new ServerAddress("127.0.0.1"), new ServerAddress("127.0.0.1", 27018)));
return mongo.getDB("database-" + System.nanoTime());
diff --git a/src/test/com/mongodb/Fixture.java b/src/test/com/mongodb/Fixture.java
index 75b0b5f8058..b6231cc8b2a 100644
--- a/src/test/com/mongodb/Fixture.java
+++ b/src/test/com/mongodb/Fixture.java
@@ -17,6 +17,7 @@
package com.mongodb;
import java.net.UnknownHostException;
+import java.util.List;
/**
* Helper class for the acceptance tests.
@@ -62,6 +63,39 @@ public static boolean serverIsAtLeastVersion(double version) {
return Double.parseDouble(serverVersion.substring(0, 3)) >= version;
}
+ public static boolean isStandalone() {
+ return !isReplicaSet() && !isSharded();
+ }
+
+ public static boolean isSharded() {
+ CommandResult isMasterResult = runIsMaster();
+ Object msg = isMasterResult.get("msg");
+ return msg != null && msg.equals("isdbgrid");
+ }
+
+ public static boolean isReplicaSet() {
+ return runIsMaster().get("setName") != null;
+ }
+
+ public static boolean isServerStartedWithJournalingDisabled() {
+ return serverStartedWithBooleanOption("--nojournal", "nojournal");
+ }
+
+ private static boolean serverStartedWithBooleanOption(final String commandLineOption, final String configOption) {
+ CommandResult res = getMongoClient().getDB("admin").command(new BasicDBObject("getCmdLineOpts", 1));
+ res.throwOnError();
+ if (res.containsField("parsed") && ((DBObject) res.get("parsed")).containsField(configOption)) {
+ return (Boolean) ((DBObject) res.get("parsed")).get(configOption);
+ } else {
+ return ((List) res.get("argv")).contains(commandLineOption);
+ }
+ }
+
+ private static CommandResult runIsMaster() {
+ // Check to see if this is a replica set... if not, get out of here.
+ return getMongoClient().getDB("admin").command(new BasicDBObject("ismaster", 1));
+ }
+
static class ShutdownHook extends Thread {
@Override
public void run() {
diff --git a/src/test/com/mongodb/JavaClientTest.java b/src/test/com/mongodb/JavaClientTest.java
index d60a88f4f1f..7d5eacaa748 100644
--- a/src/test/com/mongodb/JavaClientTest.java
+++ b/src/test/com/mongodb/JavaClientTest.java
@@ -44,6 +44,7 @@
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
+import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -491,7 +492,7 @@ public void testMapReduceInlineSecondary() throws Exception {
return;
}
- Mongo mongo = new MongoClient(Arrays.asList(new ServerAddress("127.0.0.1", 27017), new ServerAddress("127.0.0.1", 27018)),
+ Mongo mongo = new MongoClient(asList(new ServerAddress("127.0.0.1", 27017), new ServerAddress("127.0.0.1", 27018)),
MongoClientOptions.builder().writeConcern(WriteConcern.UNACKNOWLEDGED).build());
int size = getReplicaSetSize(mongo);
@@ -914,6 +915,24 @@ public void testWriteResultOnUnacknowledgedUpdate(){
assertTrue(res.isLazy());
}
+ @Test
+ public void testWriteResultOnInsert(){
+ WriteResult res = collection.insert(new BasicDBObject());
+ assertEquals(0, res.getN());
+ assertFalse(res.isUpdateOfExisting());
+ assertNull(res.getUpsertedId());
+ assertFalse(res.isLazy());
+ }
+
+ @Test
+ public void testWriteResultOnInsertList(){
+ WriteResult res = collection.insert(Arrays.asList(new BasicDBObject(), new BasicDBObject()));
+ assertEquals(0, res.getN());
+ assertFalse(res.isUpdateOfExisting());
+ assertNull(res.getUpsertedId());
+ assertFalse(res.isLazy());
+ }
+
@Test
public void testWriteResultOnUpdate(){
collection.insert(new BasicDBObject("_id", 1));
@@ -1101,7 +1120,7 @@ public void testBadKey(){
} catch (IllegalArgumentException e) {}
try {
- final List list = Arrays.asList(new BasicDBObject("$a", 1));
+ final List list = asList(new BasicDBObject("$a", 1));
c.save(new BasicDBObject("a", list));
fail("Bad key was accepted");
} catch (IllegalArgumentException e) {}
diff --git a/src/test/com/mongodb/MongoClientOptionsTest.java b/src/test/com/mongodb/MongoClientOptionsTest.java
index 48ce26cb537..cc554cf2b2a 100644
--- a/src/test/com/mongodb/MongoClientOptionsTest.java
+++ b/src/test/com/mongodb/MongoClientOptionsTest.java
@@ -26,6 +26,7 @@
import static java.lang.System.getProperty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class MongoClientOptionsTest {
@@ -60,6 +61,7 @@ public void testBuilderDefaults() {
assertEquals(getProperty("com.mongodb.slaveAcceptableLatencyMS") != null
? parseInt(getProperty("com.mongodb.slaveAcceptableLatencyMS")) : 15,
options.getAcceptableLatencyDifference());
+ assertNull(options.getRequiredReplicaSetName());
}
@Test
@@ -225,6 +227,7 @@ public void testBuilderBuild() {
builder.heartbeatConnectTimeout(53);
builder.heartbeatSocketTimeout(54);
builder.heartbeatThreadCount(4);
+ builder.requiredReplicaSetName("test");
SocketFactory socketFactory = SSLSocketFactory.getDefault();
builder.socketFactory(socketFactory);
@@ -269,6 +272,7 @@ public DBDecoder create() {
assertEquals(socketFactory, options.getSocketFactory());
assertEquals(encoderFactory, options.getDbEncoderFactory());
assertEquals(decoderFactory, options.getDbDecoderFactory());
+ assertEquals("test", options.getRequiredReplicaSetName());
}
@Test
diff --git a/src/test/com/mongodb/MongoClientURITest.java b/src/test/com/mongodb/MongoClientURITest.java
index 7f23517e872..0cca48624b0 100644
--- a/src/test/com/mongodb/MongoClientURITest.java
+++ b/src/test/com/mongodb/MongoClientURITest.java
@@ -201,16 +201,22 @@ public void testSSLOption() {
public void testOptions() {
MongoClientURI uAmp = new MongoClientURI("mongodb://localhost/?" +
"maxPoolSize=10&waitQueueMultiple=5&waitQueueTimeoutMS=150&" +
+ "minPoolSize=7&maxIdleTimeMS=1000&maxLifeTimeMS=2000&" +
+ "replicaSet=test&" +
"connectTimeoutMS=2500&socketTimeoutMS=5500&autoConnectRetry=true&" +
"slaveOk=true&safe=false&w=1&wtimeout=2500&fsync=true");
assertOnOptions(uAmp.getOptions());
MongoClientURI uSemi = new MongoClientURI("mongodb://localhost/?" +
"maxPoolSize=10;waitQueueMultiple=5;waitQueueTimeoutMS=150;" +
+ "minPoolSize=7;maxIdleTimeMS=1000;maxLifeTimeMS=2000;" +
+ "replicaSet=test;" +
"connectTimeoutMS=2500;socketTimeoutMS=5500;autoConnectRetry=true;" +
"slaveOk=true;safe=false;w=1;wtimeout=2500;fsync=true");
assertOnOptions(uSemi.getOptions());
MongoClientURI uMixed = new MongoClientURI("mongodb://localhost/test?" +
"maxPoolSize=10&waitQueueMultiple=5;waitQueueTimeoutMS=150;" +
+ "minPoolSize=7&maxIdleTimeMS=1000;maxLifeTimeMS=2000&" +
+ "replicaSet=test;" +
"connectTimeoutMS=2500;socketTimeoutMS=5500&autoConnectRetry=true;" +
"slaveOk=true;safe=false&w=1;wtimeout=2500;fsync=true");
assertOnOptions(uMixed.getOptions());
@@ -304,11 +310,15 @@ public void testMultipleIPV6ServersWithPorts() {
@SuppressWarnings("deprecation")
private void assertOnOptions(MongoClientOptions options) {
assertEquals(10, options.getConnectionsPerHost(), 10);
+ assertEquals(7, options.getMinConnectionsPerHost());
+ assertEquals(1000, options.getMaxConnectionIdleTime());
+ assertEquals(2000, options.getMaxConnectionLifeTime());
assertEquals(5, options.getThreadsAllowedToBlockForConnectionMultiplier());
assertEquals(150, options.getMaxWaitTime());
assertEquals(5500, options.getSocketTimeout());
assertTrue(options.isAutoConnectRetry());
assertEquals(new WriteConcern(1, 2500, true), options.getWriteConcern());
assertEquals(ReadPreference.secondaryPreferred(), options.getReadPreference());
+ assertEquals("test", options.getRequiredReplicaSetName());
}
}
diff --git a/src/test/com/mongodb/MongoOptionsTest.java b/src/test/com/mongodb/MongoOptionsTest.java
index 9b1e05781fa..9bf0a978a98 100644
--- a/src/test/com/mongodb/MongoOptionsTest.java
+++ b/src/test/com/mongodb/MongoOptionsTest.java
@@ -61,6 +61,7 @@ public void testCopy() throws Exception {
options.minConnectionsPerHost = 5;
options.maxConnectionIdleTime = 50000;
options.maxConnectionLifeTime = 500000;
+ options.requiredReplicaSetName = "set1";
final MongoOptions copy = options.copy();
assertEquals(options.connectionsPerHost, copy.connectionsPerHost);
@@ -86,6 +87,7 @@ public void testCopy() throws Exception {
assertEquals(options.minConnectionsPerHost, copy.minConnectionsPerHost);
assertEquals(options.maxConnectionIdleTime, copy.maxConnectionIdleTime);
assertEquals(options.maxConnectionLifeTime, copy.maxConnectionLifeTime);
+ assertEquals(options.requiredReplicaSetName, copy.requiredReplicaSetName);
}
@Test
@@ -114,6 +116,7 @@ public void testGetterSetters() throws Exception {
options.setReadPreference(ReadPreference.secondary());
options.setCursorFinalizerEnabled(true);
options.setAlwaysUseMBeans(true);
+ options.requiredReplicaSetName = "set1";
assertEquals(options.getConnectionsPerHost(), 100);
assertEquals(options.getThreadsAllowedToBlockForConnectionMultiplier(), 101);
@@ -135,6 +138,7 @@ public void testGetterSetters() throws Exception {
assertEquals(options.getReadPreference(), ReadPreference.secondary());
assertEquals(options.isCursorFinalizerEnabled(), true);
assertEquals(options.isAlwaysUseMBeans(), true);
+ assertEquals(options.getRequiredReplicaSetName(), "set1");
}
@Test
diff --git a/src/test/com/mongodb/StickyHAShardedClusterServerSelectorTest.java b/src/test/com/mongodb/MongosHAServerSelectorTest.java
similarity index 95%
rename from src/test/com/mongodb/StickyHAShardedClusterServerSelectorTest.java
rename to src/test/com/mongodb/MongosHAServerSelectorTest.java
index cc12269c3f5..8736499ba4a 100644
--- a/src/test/com/mongodb/StickyHAShardedClusterServerSelectorTest.java
+++ b/src/test/com/mongodb/MongosHAServerSelectorTest.java
@@ -34,16 +34,16 @@
import static org.junit.Assert.assertEquals;
-public class StickyHAShardedClusterServerSelectorTest {
+public class MongosHAServerSelectorTest {
- private StickyHAShardedClusterServerSelector selector;
+ private MongosHAServerSelector selector;
private ServerDescription first;
private ServerDescription secondConnecting;
private ServerDescription secondConnected;
@Before
public void setUp() throws UnknownHostException {
- selector = new StickyHAShardedClusterServerSelector();
+ selector = new MongosHAServerSelector();
first = ServerDescription.builder()
.state(Connected)
.address(new ServerAddress())
diff --git a/src/test/com/mongodb/SecondaryReadTest.java b/src/test/com/mongodb/SecondaryReadTest.java
index c2ff8ae1de1..ed19ea97c05 100644
--- a/src/test/com/mongodb/SecondaryReadTest.java
+++ b/src/test/com/mongodb/SecondaryReadTest.java
@@ -17,6 +17,7 @@
package com.mongodb;
import com.mongodb.util.TestCase;
+import org.junit.Ignore;
import org.junit.Test;
import java.util.ArrayList;
@@ -36,6 +37,7 @@ public class SecondaryReadTest extends TestCase {
* Assert that the percentage of reads to each secondary does not deviate by more than 1 %
*/
@Test
+ @Ignore
public void testSecondaryReadBalance() throws Exception {
if (!isReplicaSet(cleanupMongo)) {
return;
diff --git a/src/test/com/mongodb/WriteCommandHelperSpecification.groovy b/src/test/com/mongodb/WriteCommandHelperSpecification.groovy
index 7ae38726531..c805a0ddcb0 100644
--- a/src/test/com/mongodb/WriteCommandHelperSpecification.groovy
+++ b/src/test/com/mongodb/WriteCommandHelperSpecification.groovy
@@ -22,6 +22,8 @@ import static com.mongodb.WriteCommandResultHelper.getBulkWriteException
import static com.mongodb.WriteCommandResultHelper.getBulkWriteResult
import static com.mongodb.WriteCommandResultHelper.hasError
import static com.mongodb.WriteRequest.Type.INSERT
+import static com.mongodb.WriteRequest.Type.REMOVE
+import static com.mongodb.WriteRequest.Type.REPLACE
import static com.mongodb.WriteRequest.Type.UPDATE
class WriteCommandHelperSpecification extends Specification {
@@ -41,6 +43,26 @@ class WriteCommandHelperSpecification extends Specification {
.getUpserts()
}
+ def 'should not have modified count for update with no nModified field in the result'() {
+ expect:
+ !getBulkWriteResult(UPDATE, getCommandResult(new BasicDBObject('n', 1))).isModifiedCountAvailable()
+ }
+
+ def 'should not have modified count for replace with no nModified field in the result'() {
+ expect:
+ !getBulkWriteResult(REPLACE, getCommandResult(new BasicDBObject('n', 1))).isModifiedCountAvailable()
+ }
+
+ def 'should have modified count of 0 for insert with no nModified field in the result'() {
+ expect:
+ 0 == getBulkWriteResult(INSERT, getCommandResult(new BasicDBObject('n', 1))).getModifiedCount()
+ }
+
+ def 'should have modified count of 0 for remove with no nModified field in the result'() {
+ expect:
+ 0 == getBulkWriteResult(REMOVE, getCommandResult(new BasicDBObject('n', 1))).getModifiedCount()
+ }
+
def 'should not have error if writeErrors is empty and writeConcernError is missing'() {
expect:
!hasError(getCommandResult(new BasicDBObject()));
diff --git a/src/test/com/mongodb/util/TestCase.java b/src/test/com/mongodb/util/TestCase.java
index a1059694249..014a851da42 100644
--- a/src/test/com/mongodb/util/TestCase.java
+++ b/src/test/com/mongodb/util/TestCase.java
@@ -101,7 +101,11 @@ protected static DB getDatabase() {
* @return true if server is at least specified version
*/
protected boolean serverIsAtLeastVersion(double version) {
- String serverVersion = (String) cleanupMongo.getDB("admin").command("serverStatus").get("version");
+ return serverIsAtLeastVersion(version, cleanupMongo);
+ }
+
+ protected boolean serverIsAtLeastVersion(double version, Mongo mongo) {
+ String serverVersion = (String) mongo.getDB("admin").command("serverStatus").get("version");
return Double.parseDouble(serverVersion.substring(0, 3)) >= version;
}