-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: 3.4.1
-
Component/s: None
-
None
-
None
-
None
-
None
-
None
-
None
-
None
In my failover test, retryWrites is turned on. "not master" errors are observed in a very high frequency.
On server side, I use a 3-node replica-set, setup by docker. There are 2 test files written in javascript and python respectively. What these test files do is to insert documents repeatedly for every 3 seconds. When these tests are running, I run 'rs.stepDown()' on the primary node from time to time. I expect that the insertions will be successful, assuming that the replica-set election doesn't take more than 30 seconds (default serverSelectionTimeoutMS) to complete. My tests show following result
- Nodejs version test, with retryWrites always turned on, received a lot of 'not master' error. The error rate is basically equal to the rate in python version test with retryWrites is turned off.
- Python version test, if retryWrites is not turned off explicitly, received almost no error, unless the replica-set takes too much time to elect a new primary.
- Nodejs version test may be blocked in 'await insertOne()', if the insertion has no wtimeout option.
Mongodb: 4.2.2
Nodejs driver: mongodb 3.4.1
Python driver: pymongo 3.10
Mongodb Replica-Set docker stack file
version: '3.4' services: mdb1: image: mongo:4.2 volumes: - ./test/mongodb-rs1:/data/db command: mongod --replSet gs --wiredTigerCacheSizeGB 1 networks: - flowers mdb2: image: mongo:4.2 volumes: - ./test/mongodb-rs2:/data/db command: mongod --replSet gs --wiredTigerCacheSizeGB 1 networks: - flowers mdb3: image: mongo:4.2 volumes: - ./test/mongodb-rs3:/data/db command: mongod --replSet gs --wiredTigerCacheSizeGB 1 networks: - flowers exp4: image: "node:12" user: node volumes: - ./test/nodejs:/usr/src/app networks: - flowers working_dir: /usr/src/app stdin_open: true tty: true command: bash exp5: image: "python" volumes: - ./test/python:/usr/src/app networks: - flowers working_dir: /usr/src/app stdin_open: true tty: true command: bash networks: flowers:
nodejs test
var MongoClient = require('mongodb').MongoClient;
var errs = 0, total = 0;
async function write(db, start) {
console.log('Writing', start);
total ++;
try {
await db.collection('test').insertOne(
{ time: start, by: 'nodejs' }, {wtimeout: 40000});
var now = new Date();
console.log(`Insert finished in ${now.getTime() - start.getTime()}, <= ${start}, Stats <${errs}/${total}>`);
} catch (error) {
errs ++;
console.error('Insertion test error:', start, error);
console.log(`Stats: <${errs}/${total}>`);
}
}
function delay(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), ms);
});
}
(async function(){
var url = 'mongodb://mdb1:27017,mdb2:27017,mdb3:27017/geekmooc?replicaSet=gs&retryWrites=true&w=majority&serverSelectionTimeoutMS=30000';
console.log(`Connecting to ${url}`);
try {
var client = await MongoClient.connect(url, {
useNewUrlParser: true, useUnifiedTopology: true,
replicaSet: 'gs',
});
var db = client.db('geekmooc');
while (true) {
await write(db, new Date());
await delay(3000);
}
} catch (error) {
console.error('Connection failure', error);
}
})();
Python test
import random, string, datetime, sys, time, logging, traceback
import pymongo
logger = logging.getLogger("test")
out_hdlr = logging.StreamHandler(sys.stdout)
out_hdlr.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
out_hdlr.setLevel(logging.INFO)
logger.addHandler(out_hdlr)
logger.setLevel(logging.INFO)MONGO_URI = 'mongodb://mdb1:27017,mdb2:27017,mdb3:27017/geekmooc?replicaSet=gs&retryWrites=false&w=majority&serverSelectionTimeoutMS=30000'logger.info("Attempting to connect...")
client = pymongo.MongoClient(MONGO_URI)
logger.info("Connected")
db = client['test']
coll = db['test']
collection = coll.with_options(write_concern=pymongo.WriteConcern(w=2, wtimeout=40000))
i = 0
errs = 0
total = 0
while True:
try:
total += 1
start = datetime.datetime.utcnow()
logger.info("Inserting: %s" %start)
text = ''.join(random.choices(string.ascii_uppercase + string.digits, k = 3))
doc = { "idx": i, "date" : start, "text" : text}
i += 1
id = collection.insert_one(doc).inserted_id
logger.info("Record inserted - id: %s, stats: %d/%d" %(str(id), errs, total))
time.sleep(3)
except Exception as e: # pymongo.errors.ConnectionFailure
errs += 1
logger.error("Exception seen: " + str(e))
traceback.print_exc(file = sys.stdout)
logger.info("Retrying...")logger.info("Done...")
client.close()