From b5063cfd4e069913498e68e116ccc0e96a34865e Mon Sep 17 00:00:00 2001 From: Andrew Kenneth MacLeay Date: Tue, 5 Nov 2024 18:03:16 -0500 Subject: [PATCH 1/3] WIP: create feature to dynamically set readpreference on a call-by-call basis --- .../mongodb/api/MongoStaticOperations.groovy | 8 ++++++++ .../gorm/mongo/api/MongoStaticApi.groovy | 13 +++++++++++++ .../mapping/mongo/AbstractMongoSession.java | 18 +++++++++++++++++- 3 files changed, 38 insertions(+), 1 deletion(-) 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(); From 0b1c47a80b20ec796723f5f6cd577c4cce3b2d3b Mon Sep 17 00:00:00 2001 From: Andrew Kenneth MacLeay Date: Wed, 6 Nov 2024 10:54:20 -0500 Subject: [PATCH 2/3] wip add test --- .../groovy/grails/mongodb/MongoEntity.groovy | 4 +++ .../gorm/mongo/ReadPreferenceSpec.groovy | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 grails-datastore-gorm-mongodb/src/test/groovy/org/grails/datastore/gorm/mongo/ReadPreferenceSpec.groovy 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/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..5ed63213 --- /dev/null +++ b/grails-datastore-gorm-mongodb/src/test/groovy/org/grails/datastore/gorm/mongo/ReadPreferenceSpec.groovy @@ -0,0 +1,35 @@ +package org.grails.datastore.gorm.mongo + +import grails.gorm.annotation.Entity +import grails.mongodb.MongoEntity +import com.mongodb.ReadPreference +import grails.gorm.tests.GormDatastoreSpec + +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() + } +} + +@Entity +class PotatoFarm implements MongoEntity { + String id + String name + Integer potatoCount +} From 85861c00e9e95ed4492ee11ccd32155bc31b5e41 Mon Sep 17 00:00:00 2001 From: Andrew Kenneth MacLeay Date: Wed, 6 Nov 2024 12:02:10 -0500 Subject: [PATCH 3/3] wip --- .../gorm/mongo/ReadPreferenceSpec.groovy | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) 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 index 5ed63213..ddeae8f7 100644 --- 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 @@ -4,27 +4,34 @@ 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() + 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() + then: "It is primary" + rp == ReadPreference.primary() - when:"Ask for secondary" - def rp2 = PotatoFarm.withReadPreference("secondary") { - PotatoFarm.getCollection().getReadPreference() - } + when: "Ask for secondary" + def rp2 = PotatoFarm.withReadPreference("secondary") { + PotatoFarm.getCollection().getReadPreference() + } + + then: "It is secondary" + rp2 == ReadPreference.secondary() - 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