Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to handle read timeouts in Spring Data MongoDB

So from time to time we see exceptions like these:

java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:150)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
at org.bson.io.Bits.readFully(Bits.java:48)
at org.bson.io.Bits.readFully(Bits.java:35)
at org.bson.io.Bits.readFully(Bits.java:30)
at com.mongodb.Response.<init>(Response.java:42)
at com.mongodb.DBPort$1.execute(DBPort.java:141)
at com.mongodb.DBPort$1.execute(DBPort.java:135)
at com.mongodb.DBPort.doOperation(DBPort.java:164)
at com.mongodb.DBPort.call(DBPort.java:135)
at com.mongodb.DBTCPConnector.innerCall(DBTCPConnector.java:292)
at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:271)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:84)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:66)
at com.mongodb.DBCollection.findOne(DBCollection.java:870)
at com.mongodb.DBCollection.findOne(DBCollection.java:844)
at com.mongodb.DBCollection.findOne(DBCollection.java:790)
at org.springframework.data.mongodb.core.MongoTemplate$FindOneCallback.doInCollection(MongoTemplate.java:2000)

Whats the best way to handle and recover from these in code? Do we need to put a 'retry' around each and every mongodb call?

like image 798
pdeva Avatar asked Jan 07 '15 22:01

pdeva


1 Answers

You can use MongoClientOptions object to set different optional connection parameters. You are looking at setting heart beat frequency to make sure driver retry for connection. Also set socket time out to make sure it does not continue for too long.

  1. MinHeartbeatFrequency: In the event that the driver has to frequently re-check a server's availability, it will wait at least this long since the previous check to avoid wasted effort. The default value is 10ms.
  2. HeartbeatSocketTimeout: Timeout for heart beat check
  3. SocketTimeout: Time out for connection

Reference API

To avoid too much code duplication, optionally you can follow some pattern as given below. Basic idea is to avoid any database connection related configuration littered everywhere in the projects.

/**
 * This class is an abstraction for all mongo connection config
 **/
 @Component
 public class MongoConnection{

    MongoClient mongoClient = null;

    ...

   @PostConstruct
   public void init() throws Exception {
            // Please watch out for deprecated methods in new version of driver.
            mongoClient = new MongoClient(new ServerAddress(url, port), 
                            MongoClientOptions.builder()
                            .socketTimeout(3000)
                            .minHeartbeatFrequency(25)
                            .heartbeatSocketTimeout(3000)
                            .build());
            mongoDb = mongoClient.getDB(db);
        .....   
   }

   public DBCollection getCollection(String name) {
        return mongoDb.getCollection(name);
    }
   }

Now you can use MongoConnection in DAO-s

@Repository
public class ExampleDao{

  @Autowired
  MongoConnection mongoConnection;

  public void insert(BasicDBObject document) {
     mongoConnection.getCollection("example").insert(document);
  }  
}

You can also implement all the database operations inside MongoConnection to introduce some common functionality across the board. For example add logging for all "inserts"

like image 50
kamoor Avatar answered Oct 09 '22 03:10

kamoor