sorebrek
sorebrek

Reputation: 55

How to set MongoDB ReadPreference in Spring MVC's contextConfigLocation

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

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.

Upvotes: 5

Views: 18228

Answers (10)

William Goff
William Goff

Reputation: 11

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.

Upvotes: 0

William Goff
William Goff

Reputation: 11

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

Upvotes: 0

Pla
Pla

Reputation: 497

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

Upvotes: 0

prashanth
prashanth

Reputation: 161

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

Upvotes: 2

htompkins
htompkins

Reputation: 421

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>

Upvotes: 0

Thilak
Thilak

Reputation: 726

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

Upvotes: 0

user10030061
user10030061

Reputation: 41

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

Upvotes: 4

Amazia Gur
Amazia Gur

Reputation: 1712

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.

Upvotes: 2

jaipster
jaipster

Reputation: 12007

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>

Upvotes: 9

JJ Zabkar
JJ Zabkar

Reputation: 3689

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

Upvotes: 8

Related Questions