Read Requests from Secondary not working for ReplicaSet

I setup a ReplicaSet (1 Primary and 2 Secondary) via Docker Compose. The ReplicaSet is up. However, the read requests are not getting directed to the secondary nodes. The Primary node seems to be receiving the read requests. I checked the query types for each node using mongostat. The secondary only gets updates as part of the replication step from the Primary. Can anyone please tell me what is causing this behaviour ? I have provided the shell script which I am calling to create the replicaset below

setup.sh:

#!/bin/bash

#MONGODB1=`ping -c 1 mongo1 | head -1  | cut -d "(" -f 2 | cut -d ")" -f 1`
#MONGODB2=`ping -c 1 mongo2 | head -1  | cut -d "(" -f 2 | cut -d ")" -f 1`
#MONGODB3=`ping -c 1 mongo3 | head -1  | cut -d "(" -f 2 | cut -d ")" -f 1`

MONGODB1=mongodb1
MONGODB2=mongodb2
MONGODB3=mongodb3

echo "**********************************************" ${MONGODB1}
echo "Waiting for startup.."
until curl http://${MONGODB1}:27017/serverStatus\?text\=1 2>&1 | grep uptime | head -1; do
  printf '.'
  sleep 1
done

# echo curl http://${MONGODB1}:27017/serverStatus\?text\=1 2>&1 | grep uptime | head -1
echo ${MONGODB1} " Started.."
echo "**************Replica Set configuration Begin*********"


echo SETUP.sh time now: `date +"%T" `
mongo --host ${MONGODB1}:27017 <<EOF
var cfg = {
    "_id": "rs0",
    "protocolVersion": 1,
    "version": 1,
    "members": [
        {
            "_id": 0,
            "host": "${MONGODB1}:27017",
            "priority": 2
        },
        {
            "_id": 1,
            "host": "${MONGODB2}:27017",
            "priority": 0
        },
        {
            "_id": 2,
            "host": "${MONGODB3}:27017",
            "priority": 0
        }
    ],settings: {chainingAllowed: true}
};

rs.initiate(cfg, { force: true });
rs.reconfig(cfg, { force: true });

rs.slaveOk();
db.getMongo().setReadPref('secondary');
db.getMongo().setSlaveOk();

echo "**************Replica Set configuration Complete*********"

EOF


until mongo --host ${MONGODB1}:27017 --eval 'db.isMaster().primary' | tail -1 | grep ${MONGODB1}; do
  printf 'Waiting for Replica Set to Intialize Primary.....'
  sleep 1
done

echo "Primary intialized...."
echo "**************Index Creation Begin*********"

mongo --host ${MONGODB1}:27017 <<EOF

use orion;
db.createCollection("entities");
db.entities.createIndex({"_id.servicePath": 1, "_id.id": 1, "_id.type": 1}, {unique: true});
db.entities.createIndex({"_id.type": 1}); db.entities.createIndex({"_id.id": 1});
use orion-iotteststand;
db.createCollection("entities");
db.entities.createIndex({"_id.servicePath": 1, "_id.id": 1, "_id.type": 1}, {unique: true});
db.entities.createIndex({"_id.type": 1}); db.entities.createIndex({"_id.id": 1});
use iotagentul;
db.createCollection("devices");
db.devices.createIndex({"_id.service": 1, "_id.id": 1, "_id.type": 1});

echo "**************Index Creation Complete*********"

EOF

Hi Sourabh_Swain,

Have you set the read preference on the connection string when you are doing the query? Could you share the connection string and query?

1 Like

The read preference is not set on the connections string while doing the query. Wanted to know from a more general perspective, can’t the default behaviour be setup to always read from secondary (without setting it up via the connection string) ?

Try with https://docs.mongodb.com/manual/reference/method/Mongo.setReadPref/.

I did try that in the Mongo shell. However, after I disconnect and connect back to the Mongo shell and run getReadPref(), it is blank. So my understanding is this is readPreference is connection oriented. How to set it by default for all connections so that all queries are directed based on this setting?

Hi @Sourabh_Swain,

The default behaviour is to read from the current primary for strong consistency, so if you want non-default behaviour (and eventual consistency) you need to specify a secondary read preference on your connection string or the commands you are invoking.

It also looks like your current connection string is missing the replicaSet option, which is important to make sure a replica set connection (supporting read preferences & failover) is established as opposed to a direct connection to a single member of your replica set.

Regards,
Stennie

Thank you for the suggestion. While doing rs.init(cfg), I am specifying the readPreference as secondary in the cfg. Isn’t that enough to set the non-default behaviour ? Or does it need to be done every time I setup a connection to the Database ?

Hi @Sourabh_Swain,

Read preferences are set per client connection. You can set a read preference in your connection string or after connection using an option like setReadPreference() in the mongo shell.

rs.initiate(cfg, { force: true });
rs.reconfig(cfg, { force: true });

The force option should only be used when you need to Reconfigure a Replica Set with Unavailable Members. You should not use this with rs.initiate() and there is also no need to call rs.reconfigure() immediately after an rs.initiate().

rs.slaveOk();
db.getMongo().setReadPref(‘secondary’);
db.getMongo().setSlaveOk();

All of these commands are being executed in the context of the current mongo shell connection, so have no effect on future connections in new invocations of mongo.

Regards,
Stennie

1 Like