Mongo: Replica Set Features

by Mark Nielsen
Copyright October 2021


This is not intended to be a complete document on how to do TLS/SSL in mongo. Rather, links and a script is provided. We assume Mongo 5.0 on Ubuntu. An explanation of the steps of the script might be given. This is just a demo.
  1. Links
  2. Original Set
  3. More features

Links



Original set

This is the same steps from the other article.

function printPrimary { 
  count=`mongo --quiet --port 3001 mongo/functions.js| wc -l`
  while [ $count -lt 1 ]
  do
    echo "No primary yet: $count"
    sleep 2
    count=`mongo --quiet --port 3001 mongo/functions.js| wc -l`
  done
  port=`mongo --quiet --port 3001 mongo/functions.js | cut -d ':' -f2 `
  echo "port is on $port"
}

  # If restarting, kill the mogo processes and then do 
rm -rf  mongo/d1 mongo/d2 mongo/d3 mongo/logs
  # Setup 3 directories:
mkdir -p mongo/d1 mongo/d2 mongo/d3 mongo/logs
  # If not AWS,	  ignore.  
sudo  -s -- bash -c   '  echo "127.0.0.1 localhost ">> /etc/hosts '
  # Start once mongo instance
mongod --dbpath mongo/d1 --port 3001 --oplogSize 100 --wiredTigerCacheSizeGB 0.25 --replSet r --bind_ip 127.0.0.1 > mongo/logs/1.log 2>&1  & 
sleep 2
  # In  the mongo shell
mongo --port 3001 --eval "rs.initiate( { _id: 'r', version: 1, members: [ {_id :0, host: 'localhost:3001' } ] } )"

  # Start and add two other replica sets
mongod --dbpath mongo/d2 --port 3002 --oplogSize 100 --wiredTigerCacheSizeGB 0.25 --replSet r --bind_ip 127.0.0.1 > mongo/logs/2.log 2>&1  &
mongod --dbpath mongo/d3 --port 3003 --oplogSize 100 --wiredTigerCacheSizeGB 0.25 --replSet r --bind_ip 127.0.0.1 > mongo/logs/3.log 2>&1  &
sleep 2
mongo --port 3001 --eval "rs.add('localhost:3002')"
sleep 2
mongo --port 3001 --eval "rs.add('localhost:3003')"
sleep 2
  # Look at the status
mongo --port 3001 --eval "rs.status()"

echo "Let us sleep a really long time, 30 seconds"
sleep 30

/home/mark/temp# mongo --port 3001 --eval "rs.status()" | grep -i state

  # Lets setup the functions. 
echo "
cfg = rs.conf();
cfg.members[0].priority = 3;
cfg.members[1].priority = 2;
cfg.members[2].priority = 1;
rs.reconfig(cfg);
" >> mongo/reconfig.js

echo '
function pcheck() {
  s = rs.status()
  s.members.forEach( 
    function(myDoc) { 
      if (myDoc.stateStr=="PRIMARY") 
        { print( myDoc.stateStr + " " + myDoc.name ) }
      }
    );
}
pcheck()
' > mongo/functions.js

  # Lets print out the hosts in the replica set
mongo --port 3001 --eval "rs.status()" | grep name
  # Remove a host
mongo --port 3001 --eval "rs.remove('localhost:3003')"
sleep 1
mongo --port 3001 --eval "rs.status()" | grep name

  # Add host back in
mongo --port 3001 --eval "rs.add('localhost:3003')"
sleep 2
  # Add print out who is primary and secondary
mongo --port 3001 --eval "rs.status()" | egrep 'name|stateStr'

  # Redo the prorities, but in practice its no promise for stepdowns.
cat mongo/reconfig.js | mongo --port 3001 
mongo --port 3001 --eval "rs.conf()" | egrep 'host|priority'

mongo --port 3001 --eval "rs.stepDown({secondaryCatchUpPeriodSecs: 10 })"
sleep 5
printPrimary
mongo --port 3001 --eval "rs.status()" | egrep 'name|stateStr'

  # Not let's force it back to the first one --- this is bad in production. 
mongo --quiet  --port 3001 --eval "rs.remove('localhost:3003')"
sleep 5
printPrimary
mongo --quiet --port 3001 --eval "rs.remove('localhost:3002')"
sleep 5
printPrimary
mongo --quiet --port 3001 --eval "rs.status()" | egrep 'name|stateStr'

   # Now add them back in
mongo --quiet --port 3001 --eval "rs.add('localhost:3002')"
sleep 2
mongo --quiet --port 3001 --eval "rs.add('localhost:3003')"
sleep 2
  # Look at the status
mongo --quiet --port 3001 --eval "rs.status()" | egrep 'name|stateStr'


  echo "
  cfg = rs.conf();
  cfg.members[0].priority = 3;
  cfg.members[1].priority = 2;
  cfg.members[2].priority = 1;
  rs.reconfig(cfg);
  " > mongo/reconfig.js
  
cat mongo/reconfig.js | mongo --port 3001 --quiet
cat mongo/reconfig.js | mongo --port 3002 --quiet
cat mongo/reconfig.js | mongo --port 3003 --quiet




Replica Set Features

Like the first, we explore other features such as non failover slave, arbiter, and hidden (with slave delay).

We will also make a capped collection.

    
   #Make sure you have Replication_set.txt done a nd 3 mongos are running. 
   # We are going to make a 6 cluster, with a non failover slave, arbiter, and hidden.
   # We will also make a capped collection.

# for i in ${PIPESTATUS[@]}; do echo $i; done

margs=" --oplogSize 100 --wiredTigerCacheSizeGB 0.25 --replSet r "

function resetRep {

    echo "Trying reset replication configuration."  
    check=0
    cat mongo/reconfig.js | mongo --port 3001

    for i in ${PIPESTATUS[@]}; do
	if [ $i -gt 0 ]; then let check=$check+1; fi
    done

    count=1  
    while [ $check -gt 0 ]; do
	echo "Reconfig failed, sleeping 10 seconds, and retrying, count $count"
        check=0

	cat mongo/reconfig.js | mongo --port 3001
	for i in ${PIPESTATUS[@]}; do
	    if [ $i -gt 0 ]; then let check=$check+1; fi
	done
	let count = $count+1
    done
    
    if [ $check -gt 0 ]; then
	echo "Could not reconfigure replication, add more sleep time."
	exit
    fi
}

function checkCommand {
    error=$?
    if [ $error -gt 0 ]; then
      echo "Command did not work, aborting"
      exit
    fi

}

#-------------------------
killall mongod
sleep 2
killall -9 mongod
sleep 5

c=`ps auxw | grep mongod | grep -v grep | wc -l`
if [ $c -gt 0 ] ; then
    echo "not all mongod processes killed, aborting script"
fi;

rm -rf  mongo/logs
for i in 1 2 3 4 5 6; do
    rm -rf mongo/d$i
done

# Setup  directories:
mkdir -p mongo/d1 mongo/d2 mongo/d3 mongo/logs mongo/d4 mongo/d5 mongo/d6

echo "Starting 3 servers, need 1 minute"
mongod $margs --dbpath mongo/d1 --port 3001 > mongo/logs/1.log 2>&1 &
mongod $margs --dbpath mongo/d2 --port 3002 > mongo/logs/2.log 2>&1 &
mongod $margs --dbpath mongo/d3 --port 3003 > mongo/logs/3.log 2>&1 &
sleep 15
mongo --port 3001 --eval "rs.initiate( { _id: 'r', version: 1, members: [ {_id :0, host: 'localhost:3001' } ] } )"
checkCommand

sleep 5
mongo --port 3001 --eval "rs.add('localhost:3002')"
checkCommand

sleep 5
mongo --port 3001 --eval "rs.add('localhost:3003')"
checkCommand

sleep 30
mongo --port 3001 --eval "rs.status()" | egrep 'name|stateStr'
checkCommand
sleep 5

  # Lets setup the functions.
echo "
  cfg = rs.conf();
  cfg.members[0].priority = 3;
  cfg.members[1].priority = 2;
  cfg.members[2].priority = 1;
rs.reconfig(cfg);
  " > mongo/reconfig.js
resetRep

echo "Adding server 4"
mongod $margs --dbpath mongo/d4 --port 3004 > mongo/logs/4.log 2>&1  &
sleep 5
mongo --port 3001 --eval "rs.add({ host: 'localhost:3004', priority: 0})"

echo "Adding server 5"
mongod $margs --dbpath mongo/d5 --port 3005 > mongo/logs/5.log 2>&1 &
sleep 5
mongo --port 3001 --eval "rs.addArb('localhost:3005')"

echo "Adding server 6"
mongod $margs --dbpath mongo/d6 --port 3006 > mongo/logs/6.log 2>&1  &
sleep 5

mongo --port 3001 --eval "rs.add({ host: 'localhost:3006', hidden: 0, priority: 0 })"

echo "Sleeping 20 seconds to let everything catch up"
#sleep 20
mongo --port 3001 --eval "rs.status()" | egrep 'name|stateStr'
sleep 1

  # Lets setup the functions.
  echo "
  cfg = rs.conf();
  cfg.members[0].priority = 3;
  cfg.members[1].priority = 2;
  cfg.members[2].priority = 1;
  cfg.members[3].priority = 1;
  cfg.members[4].priority = 1;
  cfg.members[5].priority = 0;

cfg.members[5].hidden = true;
cfg.members[5].secondaryDelaySecs = 10;

rs.reconfig(cfg);
  " > mongo/reconfig.js

resetRep

sleep 10
echo "Working with collections"
  # Lets add some data. 

mongo --port 3001 --eval "db.Tcapped.drop();" test
mongo --port 3001 --eval "db.createCollection( 'Tcapped', { capped: true, size: 10 } )" test

echo "

function fillData() {
    db = db.getSiblingDB('test')
    db.no.insert({value: 1});
    db.no.drop();


    for( var i = 0; i < 10; i++ ) {
        db.no.insert( {value: i } );
    }

}
fillData()
" > mongo/fill.js

mongo --port 3001 test mongo/fill.js 

  # Should be 1000 entries
mongo  --quiet --port 3001 --eval "db.no.count()" test

  # Make it so we can connect to the secondaries for reads. 
  # We won't affect 2, will will make it so 3,4 and 6 are readable
  # 2 should not be readable.
  # We will issue the same command to the arbiter for fun to see what happens. 
  # In theory the command will work on the arbiter, but means nothing

  # This should fail
mongo --quiet --port 3002 --eval "db.no.count()" test
  # This should work
mongo --quiet --port 3003 --eval "rs.secondaryOk(); db.no.count()" test
  # This should work
mongo --quiet --port 3004 --eval "rs.secondaryOk(); db.no.count()" test
  # This should fail
mongo --quiet --port 3005 --eval "rs.secondaryOk(); db.no.count()" test

  # Refill the data.
mongo --quiet --port 3001 --eval "rs.secondaryOk(); db.dropDatabase()" test
sleep 10
mongo --quiet --port 3001 mongo/fill.js
  # What there's no data?
for i in 1 2 3 4 6 7 8 9 10 11 12; do
    sleep 1
    echo "count $i"
    mongo --quiet --port 3006 --eval "rs.secondaryOk(); db.no.count()" test
done

   #Verify the collection in capped
   # and list the first and last entry in the collection.

mongo --port 3001 --eval "db.Tcapped.drop();" test
mongo --port 3001 --eval "db.createCollection( 'Tcapped', { capped: true, size: 10 } )" test


mongo --port 3001 --eval "for( var i = 0; i < 20; i++ ) { db.Tcapped.insert( {value: i } ); } "
mongo --port 3001 --eval " db.Tcapped.createIndex({ "value": 1 },  { sparse: true, expireAfterSeconds: 360000 }) "
   
mongo --quiet --port 3001 --eval "rs.secondaryOk(); db.capped.isCapped()" test

echo "first entry"
mongo --quiet --port 3001 --eval "db.Tcapped.find().sort({_id:1}).limit(1);" test

echo "last entry"
mongo --quiet --port 3001 --eval "db.Tcapped.find().sort({_id:-1}).limit(1);" test