How to set MongoDB ReadPreference in Spring MVC's contextConfigLocation
Asked Answered
M

10

5

I am connecting to a MongoDB sharding server via mongodb java driver in Spring MVC. I am using the following versions:

  • spring-webmvc-3.2.1.RELEASE
  • mongo-java-driver/2.10.0/mongo-java-driver-2.10.0
  • spring-data-mongodb-1.2.0.RELEASE

My Mongo options are set in the contextConfigLocation file mvc-dispatcher-servlet.xml

<mongo:mongo host="mongo.sample.com" port="30000">
     <mongo:options auto-connect-retry="true"
                    slave-ok="true"/>
</mongo:mongo>

It works pretty well, but the slave-ok is deprecated by come.MongoDB.ReadPreference. I just wonder if there is any way to set the readPreference for Spring MVC in the contextConfiLocation file.

Meryl answered 24/10, 2013 at 2:45 Comment(4)
You can set it via mongoTemplate for sureFlessel
Looking at the schema (springframework.org/schema/data/mongo/spring-mongo-1.0.xsd) you can only set slave-ok, there's no "secondary preferred" option (which is the replacement). You'll be OK using slave-ok, it's still supported in the Mongo driver despite deprecation. But you can definitely do it on the MongoTemplate by setting the read preference to secondary.Doctorate
@Trisha, you are right that Slave-ok is just supported. Do it in MongoTemplate programmatically to secondary also works, but is it too much overhead if I call setReadPreference() every time when I wanna get a collection from MongoTemplate?Meryl
No, you only have to set it on the collection (or database) once, then it will be used as the default on that collection (or database & therefore all collections) from then on,Doctorate
K
8

Expanding @Trisha's response in to an answer: "Do it in MongoTemplate programmatically" by setting the ReadPreference to SECONDARY.

MongoTemplate template = new MongoTemplate(...);
template.setReadPreference(com.mongodb.ReadPreference.SECONDARY);
Kc answered 24/2, 2014 at 20:18 Comment(0)
C
9

Declare the following bean

<bean id="readPreferenceSecondary" class="com.mongodb.TaggableReadPreference.SecondaryReadPreference">
</bean>

and

you inject this in your mongotemplate

<bean id="mongoTemplateProdDb" class="org.springframework.data.mongodb.core.MongoTemplate" >
        <property name="readPreference" ref="readPreferenceSecondary"></property>
</bean>
Christoperchristoph answered 7/7, 2014 at 15:35 Comment(0)
K
8

Expanding @Trisha's response in to an answer: "Do it in MongoTemplate programmatically" by setting the ReadPreference to SECONDARY.

MongoTemplate template = new MongoTemplate(...);
template.setReadPreference(com.mongodb.ReadPreference.SECONDARY);
Kc answered 24/2, 2014 at 20:18 Comment(0)
T
4

In case you are using spring-data-mongodb and have some requirement to use multiple Read Preferences based on find query, you can create multiple Mongo Templates and/or Repositories like

    @EnableMongoRepositories(basePackages = {
            "com.you.repo.package" }, mongoTemplateRef = "mongoTemplateOne")    
    @Configuration
    public class MongoConfig {

    @Bean(name="mongoTemplateOne")
    public MongoTemplate getMongoTemplateOne() throws UnknownHostException {
        MongoTemplate templateOne = new MongoTemplate(new SimpleMongoDbFactory(new MongoClientURI("YOUR_MONGO_URL")));
        templateOne.setReadPreference(ReadPreference.secondaryPreferred());

        //setting WriteConcern but not relevant for this thread
        templateOne.setWriteConcernResolver(yourWriteConcernResolver());
        return templateOne;
    }

    @Bean(name = "mongoTemplateTwo")
    public MongoTemplate getMongoTemplateTwo() throws UnknownHostException {
        MongoTemplate templateTwo = new MongoTemplate(new SimpleMongoDbFactory(new MongoClientURI("YOUR_MONGO_URL")));
        templateTwo.setReadPreference(ReadPreference.secondaryPreferred());
        return templateTwo;
    }


    private WriteConcernResolver yourWriteConcernResolver() {
        return action -> {
            if (action.getCollectionName()
                    .equals("your_collecton")
                    && (action.getMongoActionOperation() == MongoActionOperation.SAVE
                            || action.getMongoActionOperation() == MongoActionOperation.UPDATE)) {
                return WriteConcern.MAJORITY;
            }
            return action.getDefaultWriteConcern();
        };
    }
Tweeter answered 4/7, 2018 at 5:5 Comment(0)
M
2

In case you have more than one secondary (replica-set) you can be more specific and tell the mongo driver explicitly which of the secondaries you want to read from, using tags.

On the mongo side you run this command:

db.getMongo().setReadPref('secondaryPreferred',
                          [{"tagName":"TagVal1"},
                            {"tagName":"TagVal2"},
                            {}])

In the code it looks like this:

MongoTemplate template = new MongoTemplate(...)
template.setReadPreference(ReadPreference.secondaryPreferred("your DBObject that reflect your mongo tag names");

Hope it helps.

Maun answered 8/7, 2015 at 10:58 Comment(0)
O
2

Here is one more way to do this using Mongo Repositories

@Configuration
@EnableMongoRepositories
class ApplicationConfig extends AbstractMongoClientConfiguration {

    @Autowired
    private Environment env;

    @Value("${spring.data.mongodb.uri}")
    public String mongoUri;


    @Override
    protected String getDatabaseName() {
        return env.getProperty("spring.data.mongodb.database");
    }

    @Override
    protected void configureClientSettings(MongoClientSettings.Builder builder) {
        builder.applyConnectionString(new ConnectionString(mongoUri)).readPreference(ReadPreference.secondary());
    }

}

sample app available @ https://github.com/prashanthmadi/cosmosdb-mongodb-readpreference-springboot-java/blob/main/src/main/java/azure/cosmosdb/mongodb/spring/cosmosdbmongodb/ApplicationConfig.java

Oleograph answered 9/9, 2021 at 13:1 Comment(0)
M
0

If there is a need to mix between primary and secondary for reads depending on the collection, you can set the ReadPreference on the DBCollection object. This helps to avoid complex multiple MongoTemplate configuration. Instead, set collection level preference like below once in the application lifetime. All the reads for that specific collection will go to secondary, while for other collections it goes to primary.

DBCollection dbCollection = mongoTemplate.getCollection(mongoTemplate.getCollectionName(collection));
dbCollection.setReadPreference(ReadPreference.secondaryPreferred());

If you want to know different options to achieve it, please check Spring data mongodb secondary reads

Massenet answered 7/1, 2019 at 8:58 Comment(0)
B
0

As of spring-mongo-2.0.xsd, slave-ok has been entirely removed, but support has been added for XML config of ReadPreference. Here's the XML from the original question translated for the current XSD:

<mongo:mongo-client host="mongo.sample.com" port="30000">
     <mongo:client-options read-preference="SECONDARY_PREFERRED" />
</mongo:mongo-client>
Bola answered 16/9, 2019 at 17:54 Comment(0)
S
0

Or you can define at the query level with annotation in the repository interface:

@Meta(flags = CursorOption.SECONDARY_READS)
Stream<MyClass> findBySomeField(String someField);

Which is really useful when using spring generated queries from method names or annotations like @Query or @Aggregation

Slavonic answered 12/5, 2023 at 0:12 Comment(1)
Nice! Doesn't answer the question directly but this is the way to do it in modern spring-data-mongo repositories!Heuser
G
0

If you are using SpringBoot's "MongoRepository" interface you can just add the @ReadPreference("secondaryPreferred") to the method.

public interface MyRepository extends MongoRepository<MyItem, String>
{
    @ReadPreference("secondaryPreferred")
    public Optional<MyItem> findById(String id);
Grith answered 25/9 at 21:6 Comment(0)
G
0

If you have to use Spring's "MongoOperations", like I have to because we have a collection with a dot (.) in its name, and I am not allowed to rename the collection. You can still set the read preference on the "Aggregation" Object via "AggregationOptions". Here is an example:

AggregationOptions aggOps = AggregationOptions.builder().readPreference(
                com.mongodb.ReadPreference.secondaryPreferred()).build();
        
Aggregation agg = Aggregation.newAggregation(match, buildRuntimeProjection()).withOptions(aggOps);

And by the way you can use both MongoRepository and MongoOperations in the same class. I am using both in a test app I am writing.

Grith answered 25/9 at 21:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.