Question

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.

Was it helpful?

Solution 2

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);

OTHER TIPS

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>

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();
        };
    }

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.

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

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

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>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top