-
Notifications
You must be signed in to change notification settings - Fork 1.1k
ReadPreference not applied consistently in concurrent usage of MongoTemplate [DATAMONGO-1061] #1982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Bogdan Apetrei commented Test scenario Input:
Results:
Note:
Test scenario Motivation:
Input:
Results:
Observation:
Test scenario
Input:
Result:
Observation:
|
Agoston Horvath commented I've just bumped into the exact same thing. I've made 2 mongoTemplates, one with readPreference: secondaryPreferred, and one with readPreference: primaryPreferred (for reads and writes, respectively). Dashboard however shows that all reads to to primary. Looking into it, it all boils down to MongoTemplate.prepareCollection() gets the cached DBCollection from the mongo driver and sets the read preference on it. This means that one can't have 2 different read preferences per collection. This is kind of a blocker, since for the usage pattern 'read-munge-persist', using optimistic locking, one needs a 'primaryPreferred' read preference, while for a regular 'read' pattern, the delay to the secondary nodes is acceptable. UPDATE: A sort workaround is to create 2 drivers for the same database, setting one of them with a global readpreference primaryPreferred, the other to secondaryPreferred. This is rather dirty though |
Bogdan Apetrei commented DATAMONGO-1061 - change in MongoTemplate for setting readPreference when multiple templates are used with different read preferences |
Bogdan Apetrei commented Hello Agoston, As a workaround I found that if the readPreference is set explicitly for both templates read operations will go fine. Example:
Indeed problem looks to be in MongoTemplate.prepareCollection() where we have:
Best regards, |
Agoston Horvath commented Bogdan, I wish it was that simple. But the mongo driver itself (as of version 3.2.1) caches the DBCollections in itself in DB.getCollection(): public DBCollection getCollection(final String name) {
DBCollection collection = collectionCache.get(name);
if (collection != null) {
return collection;
}
[...] So for now, having 2 drivers (with a new mongo driver instance in each) is the only option to circumvent this. I would also raise priority on this one. Very often, updates cannot be used because of complex rules, in which case, the only option is to read in the whole document, do the changes in java, and then use .save() (with a But if one switches to primaryPreferred, then also for simple queries the primary node is used, which effectively makes the replica slaves useless. This 2 clients hack works, but I think it is really kludgy for such a simple requirement |
Oliver Drotbohm commented Have you guys filed this with the MongoDB guys? I think that caching mutable objects is a receipt for disaster fundamentally. The only way I can imagine working around this on our side is synchronizing on the collection name but I don't think we'd want to do that for performance reasons. As indicated by Agoston Horvath, always setting the value doesn't really help as we're still subject to conflicts in concurrent scenarios |
Oliver Drotbohm commented I've filed a ticket with the MongoDB guys |
Bogdan Apetrei commented Hi Oliver, I agree with you, problem is now more complicated because of the changes done in MongoDB Java Driver (version 3.x) but in the same time it was observed on older versions of MongoDB Spring Data (ex: 1.6) with older version of MongoDB Java Driver (ex: 2.13) before caching was implemented in the driver. I think a fix in the MongoDB Spring Data would be required also after the fix in the MongoDB Java Driver. Thank you, |
Oliver Drotbohm commented If the MongoDB guys decide to leave things as they are (which is the way it currently looks), there's probably nothing we can realistically do except document the limitation. If the issue is fixed in the driver, all you need to do is to upgrade to a version of the driver including the fix |
Agoston Horvath commented Thanks for picking this up, and so quickly! Since there's no quick fix, here's the copy-pasteable @Configuration
@EnableMongoRepositories(basePackages = "com.bla")
public class MongoDBConfiguration extends AbstractMongoConfiguration {
@Override
protected String getDatabaseName() {
return database;
}
/**
* We make 2 clients - one with readPreference primaryPreferred, one with readPreference secondaryPreferred.
* This is to work around the limitation in mongo driver v3.2.1 that is able to set read preference per-collection only,
* not per-query.
*/
@Override
@Bean
public Mongo mongo() throws Exception {
final MongoClient client = isBlank(username) ?
new MongoClient(getServers(), getClientOptions()) :
new MongoClient(getServers(), singletonList(MongoCredential.createCredential(username, database, password)), getClientOptions());
client.setReadPreference(ReadPreference.secondaryPreferred());
return client;
}
@Bean
public Mongo mongoPrimary() throws Exception {
final MongoClient client = isBlank(username) ?
new MongoClient(getServers(), getClientOptions()) :
new MongoClient(getServers(), singletonList(MongoCredential.createCredential(username, database, password)), getClientOptions());
client.setReadPreference(ReadPreference.primaryPreferred());
return client;
}
/**
* mongoTemplate has its read preference to secondary, e.g. when consistency is no issue
* (this is the preferred template)
*/
@Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
}
/** Exact copy of mongoDbFactory(), but uses the writeMongo() client instead of the mongo() client
* NB: will need to re-copy this when updating spring-mongo-data */
@Bean
public MongoDbFactory mongoDbWriteFactory() throws Exception {
return new SimpleMongoDbFactory(mongoPrimary(), getDatabaseName(), getUserCredentials(), getAuthenticationDatabaseName());
}
/**
* mongoPrimaryTemplate has its read preference to primary, e.g. when reading from the DB as part of an update operation.
* (only use this when necessary, as it puts extra load on the master, e.g. to perform a read-update-write cycle with optimisitic locking)
*/
@Bean
public MongoTemplate mongoPrimaryTemplate() throws Exception {
return new MongoTemplate(mongoDbWriteFactory(), mappingMongoConverter());
}
} |
Fixed via #4286. |
Bogdan Apetrei opened DATAMONGO-1061 and commented
In the scenario when we have one mongodb factory used by two mongo templates defined as below:
Attachments:
Referenced from: pull request #386
The text was updated successfully, but these errors were encountered: