diff --git a/grails-datastore-gorm-mongodb/src/main/groovy/grails/mongodb/MongoEntity.groovy b/grails-datastore-gorm-mongodb/src/main/groovy/grails/mongodb/MongoEntity.groovy index 55de176c..3160ff92 100644 --- a/grails-datastore-gorm-mongodb/src/main/groovy/grails/mongodb/MongoEntity.groovy +++ b/grails-datastore-gorm-mongodb/src/main/groovy/grails/mongodb/MongoEntity.groovy @@ -216,6 +216,10 @@ trait MongoEntity implements GormEntity, DynamicAttributes { currentMongoStaticApi().useDatabase(databaseName) } + static T withReadPreference(String readPreferenceString, Closure callable) { + currentMongoStaticApi().withReadPreference(readPreferenceString, callable) + } + /** * Counts the number of hits * @param query The query diff --git a/grails-datastore-gorm-mongodb/src/main/groovy/grails/mongodb/api/MongoStaticOperations.groovy b/grails-datastore-gorm-mongodb/src/main/groovy/grails/mongodb/api/MongoStaticOperations.groovy index 8daeffc1..814a5c3f 100644 --- a/grails-datastore-gorm-mongodb/src/main/groovy/grails/mongodb/api/MongoStaticOperations.groovy +++ b/grails-datastore-gorm-mongodb/src/main/groovy/grails/mongodb/api/MongoStaticOperations.groovy @@ -76,6 +76,14 @@ interface MongoStaticOperations extends GormStaticOperations { */ public T withCollection(String collectionName, Closure callable) + /** + * Use the given read preference for the scope of the closure call + * @param readPreferenceString The read preference as string (primary, secondaryPreferred, etc) + * @param callable The callable + * @return The result of the closure + */ + public T withReadPreference(String readPreferenceString, Closure callable) + /** * Use the given collection for this entity for the scope of the session * diff --git a/grails-datastore-gorm-mongodb/src/main/groovy/org/grails/datastore/gorm/mongo/api/MongoStaticApi.groovy b/grails-datastore-gorm-mongodb/src/main/groovy/org/grails/datastore/gorm/mongo/api/MongoStaticApi.groovy index 2c2af43a..b561c4fb 100644 --- a/grails-datastore-gorm-mongodb/src/main/groovy/org/grails/datastore/gorm/mongo/api/MongoStaticApi.groovy +++ b/grails-datastore-gorm-mongodb/src/main/groovy/org/grails/datastore/gorm/mongo/api/MongoStaticApi.groovy @@ -132,6 +132,19 @@ class MongoStaticApi extends GormStaticApi implements MongoAllOperations T withReadPreference(String readPreferenceString, Closure callable) { + withSession { AbstractMongoSession session -> + final previous = session.getReadPreference() + try { + session.setReadPreference(ReadPreference.valueOf(readPreferenceString)) + return callable.call() + } finally { + session.setReadPreference(previous) + } + } + } + @Override String useCollection(String collectionName) { withSession { AbstractMongoSession session -> diff --git a/grails-datastore-gorm-mongodb/src/main/groovy/org/grails/datastore/mapping/mongo/AbstractMongoSession.java b/grails-datastore-gorm-mongodb/src/main/groovy/org/grails/datastore/mapping/mongo/AbstractMongoSession.java index 0e52146b..da890831 100644 --- a/grails-datastore-gorm-mongodb/src/main/groovy/org/grails/datastore/mapping/mongo/AbstractMongoSession.java +++ b/grails-datastore-gorm-mongodb/src/main/groovy/org/grails/datastore/mapping/mongo/AbstractMongoSession.java @@ -14,6 +14,7 @@ */ package org.grails.datastore.mapping.mongo; +import com.mongodb.ReadPreference; import com.mongodb.WriteConcern; import com.mongodb.client.MongoClient; import org.bson.Document; @@ -45,6 +46,7 @@ public abstract class AbstractMongoSession extends AbstractSession protected final String defaultDatabase; protected MongoDatastore mongoDatastore; protected WriteConcern writeConcern = null; + protected ReadPreference readPreference = null; protected boolean errorOccured = false; protected Map mongoCollections = new ConcurrentHashMap(); protected Map mongoDatabases = new ConcurrentHashMap(); @@ -135,6 +137,14 @@ private WriteConcern getDeclaredWriteConcern(WriteConcern defaultConcern, Persis return writeConcern; } + public void setReadPreference(ReadPreference readPreference) { + this.readPreference = readPreference; + } + + public ReadPreference getReadPreference() { + return readPreference; + } + public MongoClient getNativeInterface() { return ((MongoDatastore)getDatastore()).getMongoClient(); } @@ -182,10 +192,16 @@ public com.mongodb.client.MongoCollection getCollection(PersistentEnti if(entity.isRoot()) { final String database = getDatabase(entity); final String collectionName = getCollectionName(entity); - return getNativeInterface() + com.mongodb.client.MongoCollection collection = getNativeInterface() .getDatabase(database) .getCollection(collectionName) .withCodecRegistry(getDatastore().getCodecRegistry()); + final ReadPreference readPreference = getReadPreference(); + if (readPreference != null) { + return collection.withReadPreference(readPreference); + } else { + return collection; + } } else { final PersistentEntity root = entity.getRootEntity(); diff --git a/grails-datastore-gorm-mongodb/src/test/groovy/org/grails/datastore/gorm/mongo/ReadPreferenceSpec.groovy b/grails-datastore-gorm-mongodb/src/test/groovy/org/grails/datastore/gorm/mongo/ReadPreferenceSpec.groovy new file mode 100644 index 00000000..ddeae8f7 --- /dev/null +++ b/grails-datastore-gorm-mongodb/src/test/groovy/org/grails/datastore/gorm/mongo/ReadPreferenceSpec.groovy @@ -0,0 +1,42 @@ +package org.grails.datastore.gorm.mongo + +import grails.gorm.annotation.Entity +import grails.mongodb.MongoEntity +import com.mongodb.ReadPreference +import grails.gorm.tests.GormDatastoreSpec +import org.junit.platform.commons.logging.LoggerFactory + +class ReadPreferenceSpec extends GormDatastoreSpec { + List getDomainClasses() { + [PotatoFarm] + } + + void "Gets read preference from collection correctly"() { + when: "Read read concern by default" + def rp = PotatoFarm.getCollection().getReadPreference() + + then: "It is primary" + rp == ReadPreference.primary() + + when: "Ask for secondary" + def rp2 = PotatoFarm.withReadPreference("secondary") { + PotatoFarm.getCollection().getReadPreference() + } + + then: "It is secondary" + rp2 == ReadPreference.secondary() + + when: "After asking for secondary" + def rp3 = PotatoFarm.getCollection().getReadPreference() + + then: "It returns to primary" + rp3 == ReadPreference.primary() + } +} + +@Entity +class PotatoFarm implements MongoEntity { + String id + String name + Integer potatoCount +}