Deploying MongoDB Replica Sets: Configuration, Troubleshooting, and Client Integration
Replica Sets provide MongoDB with automatic failover and data redundancy through a primary-secondary architecture. A typical deployment consists of a primary node handling write operations, secondary nodes maintaining copies for read scaling and failover, and an arbiter participating in elections without storing data.
Environment Preparation
Create isolated directories for each node type:
mkdir -p /var/mongo/data/{primary,secondary,arbiter}
mkdir -p /var/mongo/logs
touch /var/mongo/logs/{primary,secondary,arbiter}.log
Node Configuration
Create separate configuration files for each role. The arbiter requires minimal storage allocation.
primary.conf:
dbpath=/var/mongo/data/primary
logpath=/var/mongo/logs/primary.log
pidfilepath=/var/mongo/primary.pid
port=27017
bind_ip=127.0.0.1
replSet=rs0
oplogSize=2048
fork=true
logappend=true
directoryperdb=true
noprealloc=true
secondray.conf:
dbpath=/var/mongo/data/secondary
logpath=/var/mongo/logs/secondary.log
pidfilepath=/var/mongo/secondary.pid
port=27018
bind_ip=127.0.0.1
replSet=rs0
oplogSize=2048
fork=true
logappend=true
directoryperdb=true
noprealloc=true
arbiter.conf:
dbpath=/var/mongo/data/arbiter
logpath=/var/mongo/logs/arbiter.log
pidfilepath=/var/mongo/arbiter.pid
port=27019
bind_ip=127.0.0.1
replSet=rs0
fork=true
logappend=true
noprealloc=true
Starting Instances
Launch each mongod process with its respective configuration:
mongod --config /var/mongo/primary.conf
mongod --config /var/mongo/secondary.conf
mongod --config /var/mongo/arbiter.conf
Successful initialization displays: child process started successfully, parent exiting.
Troubleshooting Startup Failures
Exit Code 1
Occurs when the daemon cannot access specified paths. Verify absolute paths in configuration files—relative paths like ~/mongo/data often cause permission issues. Use full system paths (/home/user/mongo/data).
Exit Code 100 Indicates an unclean shutdown left a lock file. Resolve by:
rm /var/mongo/data/primary/mongod.lock
mongod --repair --dbpath /var/mongo/data/primary
Replica Set Initialization
Connect to any node and initiate the set:
mongo --port 27017
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "127.0.0.1:27017", priority: 3 },
{ _id: 1, host: "127.0.0.1:27018", priority: 2 },
{ _id: 2, host: "127.0.0.1:27019", arbiterOnly: true }
]
})
Priority determines primary eligibility; the arbiter requires arbiterOnly: true. Verify status with rs.status(), looking for "stateStr": "PRIMARY" and "stateStr": "SECONDARY".
Python Client Integration
Connect to specific nodes or use the replica set name for automatic failover:
from pymongo import MongoClient
# Direct connection to primary
primary = MongoClient('127.0.0.1', 27017)
primary_db = primary.inventory
products = primary_db.items
# Insert documents
products.insert_one({"sku": "A123", "qty": 100})
products.insert_many([{"sku": "B456", "qty": 50}, {"sku": "C789", "qty": 200}])
# Read from secondary with read preference
secondary = MongoClient('127.0.0.1', 27018, readPreference='secondary')
for item in secondary.inventory.items.find():
print(item)
Automated Database Provisioning
This utility initializes a content management database with appropriate indexes:
#!/usr/bin/env python
from pymongo import MongoClient, ASCENDING, DESCENDING
import sys
DB_NAME = "content_platform"
HOST = "localhost"
PORT = 27017
INDEX_SPEC = {
"articles": {
("slug", ASCENDING): {"unique": True, "name": "unique_slug"},
("author_id", ASCENDING): {"name": "author_index"},
("tags", ASCENDING): {"name": "tag_search"},
("published_date", DESCENDING): {"name": "date_sort"}
},
"authors": {
("email", ASCENDING): {"unique": True, "name": "email_unique"},
("last_name", ASCENDING): {"name": "lastname_lookup"}
}
}
def provision_database():
client = MongoClient(HOST, PORT)
# Clean slate for demonstration
client.drop_database(DB_NAME)
db = client[DB_NAME]
# Apply indexes
for collection, indexes in INDEX_SPEC.items():
for key, options in indexes.items():
db[collection].create_index([key] if isinstance(key, str) else list(key), **options)
print(f"Database {DB_NAME} provisioned with indexes")
client.close()
if __name__ == "__main__":
provision_database()
The script drops existing data, recreates the database, and applies compound and single-field indexes for optimal query performance.