Reputation: 668
I'm having some trouble understanding how to correctly receive and handle DB constraint errors when interacting with MongoDB.
I'm using:
I'm using mongoTemplate.insert and giving it a "persistentTeam" object which represents a football team. I am custom generating the Document's _id field per some business rules. It's possible that the algorithm for generating this _id will match an existing object, in which case I want to fall into some error handling.
With default configuration, if I insert an object with a duplicate ID I get no error returned from the DB. This contradicts mongo documentation, which says the default mongo driver configuration is "ACKNOWLEDGED" which should raise an exception in this case: http://docs.mongodb.org/manual/release-notes/drivers-write-concern/ I looked at the 2.11 mongo driver code and sure enough, in Mongo.java default write concern is WriteConcern.NORMAL (equivalent to UNACKNOWLEDGED). Fine, I'll work around.
EDIT: Some additional notes on code/documentation disparity: Mongo Java Driver 2.10.0 introduced MongoClient.java which replaces Mongo.java. However, spring-framework-core 3.1.0.RELEASE does not integrate with this new class. This is why I'm not getting the expected default.
I updated my mongo factory:
<bean id="mongo" class="org.springframework.data.mongodb.core.MongoFactoryBean">
<property name="host" value="${db.url}" />
<property name="writeConcern" value="ACKNOWLEDGED" />
</bean>
Now when I attempt to insert an object with a duplicate primary key, I see the following error:
{"com.mongodb.MongoException$DuplicateKey: {
\"serverUsed\" : \"localhost/127.0.0.1:27017\" ,
\"err\" : \"E11000 duplicate key error index: Sports.persistentTeam.$_id_ dup key: { : 5019964551640473690 }\" ,
\"code\" : 11000 ,
\"n\" : 0 ,
\"connectionId\" : 21 , \"ok\" : 1.0}\n
\tat com.mongodb.CommandResult.getException(CommandResult.java:74)\n
\tat com.mongodb.CommandResult.throwOnError(CommandResult.java:110)\n
\tat com.mongodb.DBTCPConnector._checkWriteError(DBTCPConnector.java:102)\n
\tat com.mongodb.DBTCPConnector.say(DBTCPConnector.java:142)\n
\tat com.mongodb.DBTCPConnector.say(DBTCPConnector.java:115)\n
\tat com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:248)\n
\tat com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:204)\n
\tat com.mongodb.DBCollection.insert(DBCollection.java:76)\n
\tat com.mongodb.DBCollection.insert(DBCollection.java:60)\n
\tat com.mongodb.DBCollection.insert(DBCollection.java:105)\n
\tat org.springframework.data.mongodb.core.MongoTemplate$8.doInCollection(MongoTemplate.java:835)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:388)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.insertDBObject(MongoTemplate.java:830)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.doInsert(MongoTemplate.java:659)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:613)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:604)\n
\tat com.example.persistence.dao.MongoDataServiceDao.create(MongoDataServiceDao.java:96)\n
\tat com.example.persistence.PersistenceImpl.createObject(PersistenceImpl.java:944)\n
}
OK, great! It's all working. Well not really. When I try to insert something which should succeed, I get this error:
{
com.mongodb.WriteConcernException:
{ \"serverUsed\" : \"localhost/127.0.0.1:27017\" ,
\"n\" : 0 ,
\"connectionId\" : 20 ,
\"wnote\" : \"no replication has been enabled, so w=\\\"ACKNOWLEDGED\\\" won't work\" ,
\"err\" : \"norepl\" ,
\"ok\" : 1.0}
\n\tat com.mongodb.CommandResult.getException(CommandResult.java:77)\n
\tat com.mongodb.CommandResult.throwOnError(CommandResult.java:110)\n
\tat com.mongodb.DBTCPConnector._checkWriteError(DBTCPConnector.java:102)\n
\tat com.mongodb.DBTCPConnector.say(DBTCPConnector.java:142)\n
\tat com.mongodb.DBTCPConnector.say(DBTCPConnector.java:115)\n
\tat com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:248)\n
\tat com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:204)\n
\tat com.mongodb.DBCollection.insert(DBCollection.java:76)\n
\tat com.mongodb.DBCollection.insert(DBCollection.java:60)\n
\tat com.mongodb.DBCollection.insert(DBCollection.java:105)\n
\tat org.springframework.data.mongodb.core.MongoTemplate$8.doInCollection(MongoTemplate.java:835)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:388)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.insertDBObject(MongoTemplate.java:830)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.doInsert(MongoTemplate.java:659)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:613)\n
\tat org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:604)\n
\tat com.example.mongo.persistence.dao.MongoDataServiceDao.create(MongoDataServiceDao.java:96)\n
\tat com.example.persistence.PersistenceImpl.createObject(PersistenceImpl.java:944)\n
}
So this is a bit confusing. The error tells me I can't use ACKNOWLEDGED without replication. But documentaton doesn't say this. There are other WriteConcern modes which explicitly state they check for writes to replicas...ACKNOWLEDGED should only verify write to primary. And after all that, the write actually succeeded. What do I do here? Just bury this second exception and forget about it?
Upvotes: 0
Views: 4660
Reputation: 19000
This is because you're using the String variant of WriteConcern
.
That way, your driver regards it to be a replica tags set Write Concern. Hence the exception.
Use the int variant for WriteConcern
constructor and give it the value of 1 (as in {w=1}
) which is the the value for acknowledgment.
Upvotes: 2