Handling connection errors and reconnection mechanisms
Analysis of Connection Error Causes
When Mongoose connects to a MongoDB database, various error scenarios may be encountered. Common error types include:
- Network Issues: Server unreachable, DNS resolution failure, firewall blocking ports
- Authentication Failure: Incorrect username/password, insufficient permissions
- Server Configuration Issues: MongoDB service not started, connection limit reached
- Timeout Issues: Network latency causing connection timeouts
mongoose.connect('mongodb://localhost:27017/mydb', {
auth: { username: 'user', password: 'wrongpassword' }
}).catch(err => {
console.error('Authentication failed:', err.message);
// Output: Authentication failed: Authentication failed
});
Basic Error Handling Mechanism
Mongoose provides multiple ways to capture connection errors. The most basic error handling is using catch
after the connect()
method:
mongoose.connect(uri)
.then(() => console.log('Connection successful'))
.catch(err => console.error('Connection failed:', err));
Alternatively, listen for the error
event on the connection object:
const db = mongoose.connection;
db.on('error', (err) => {
console.error('Database connection error:', err.message);
});
Automatic Reconnection Implementation
Mongoose has built-in automatic reconnection but requires proper parameter configuration:
mongoose.connect(uri, {
autoReconnect: true, // Deprecated, enabled by default in Mongoose 5.x+
reconnectTries: Number.MAX_VALUE, // Infinite retries
reconnectInterval: 1000, // Retry every 1 second
bufferCommands: false // Fail immediately when disconnected instead of buffering commands
});
More granular control can be achieved by listening to events:
const db = mongoose.connection;
db.on('disconnected', () => {
console.log('Connection lost, attempting to reconnect...');
mongoose.connect(uri, { useNewUrlParser: true });
});
db.on('reconnected', () => {
console.log('Successfully reconnected to the database');
});
db.on('reconnectFailed', () => {
console.error('Reconnection failed, check database status');
});
Advanced Reconnection Strategies
For production environments, implementing an exponential backoff algorithm is recommended:
let retryAttempts = 0;
const MAX_RETRIES = 5;
const BASE_DELAY = 1000;
function connectWithRetry() {
return mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
}).catch(err => {
if (retryAttempts < MAX_RETRIES) {
const delay = BASE_DELAY * Math.pow(2, retryAttempts);
retryAttempts++;
console.log(`Connection failed, retrying in ${delay}ms...`);
return new Promise(resolve =>
setTimeout(resolve, delay)
).then(() => connectWithRetry());
}
throw err;
});
}
connectWithRetry().then(() => {
console.log('Final connection successful');
});
Connection State Management
Properly handle connection states when maintaining application state:
let isConnected = false;
let isConnecting = false;
mongoose.connection.on('connected', () => {
isConnected = true;
isConnecting = false;
});
mongoose.connection.on('disconnected', () => {
isConnected = false;
});
mongoose.connection.on('connecting', () => {
isConnecting = true;
});
function ensureConnection() {
if (isConnected) return Promise.resolve();
if (isConnecting) {
return new Promise((resolve) => {
const check = () => {
if (isConnected) resolve();
else setTimeout(check, 100);
};
check();
});
}
return connectWithRetry();
}
Connection Handling in Transactions
Special attention is needed for connection states during transactions:
async function performTransaction() {
const session = await mongoose.startSession();
session.startTransaction();
try {
await ensureConnection(); // Ensure connection is active
// Execute transaction operations
await Model1.create([{...}], { session });
await Model2.updateMany({...}, {...}, { session });
await session.commitTransaction();
} catch (err) {
await session.abortTransaction();
throw err;
} finally {
session.endSession();
}
}
Connection Handling in Cluster Environments
More factors need to be considered for replica sets or sharded clusters:
mongoose.connect('mongodb://node1,node2,node3/dbname?replicaSet=myReplSet', {
useNewUrlParser: true,
useUnifiedTopology: true,
readPreference: 'secondaryPreferred',
connectTimeoutMS: 30000,
socketTimeoutMS: 30000,
serverSelectionTimeoutMS: 5000,
heartbeatFrequencyMS: 10000,
retryWrites: true,
retryReads: true
});
mongoose.connection.on('fullsetup', () => {
console.log('Connected to all replica set nodes');
});
mongoose.connection.on('all', () => {
console.log('Connected to all available nodes');
});
Connection Pool Optimization
Optimize connection pool for better performance:
mongoose.connect(uri, {
poolSize: 10, // Default: 5
maxPoolSize: 20,
minPoolSize: 2,
socketTimeoutMS: 45000,
waitQueueTimeoutMS: 10000,
maxIdleTimeMS: 30000
});
Monitor connection pool status:
setInterval(() => {
const poolStats = mongoose.connection.getClient().s.topology.pool;
console.log({
available: poolStats.availableConnections,
waiting: poolStats.waiting,
active: poolStats.active
});
}, 5000);
Special Handling in Test Environments
Different connection strategies may be needed for testing:
function getConnectionOptions() {
if (process.env.NODE_ENV === 'test') {
return {
autoIndex: false,
connectTimeoutMS: 5000,
socketTimeoutMS: 5000,
serverSelectionTimeoutMS: 5000,
retryWrites: false
};
}
return {
autoIndex: true,
connectTimeoutMS: 30000,
socketTimeoutMS: 30000,
serverSelectionTimeoutMS: 30000,
retryWrites: true
};
}
mongoose.connect(uri, getConnectionOptions());
Cloud Service Adaptation
Special configurations for cloud services like MongoDB Atlas:
mongoose.connect(process.env.ATLAS_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
retryWrites: true,
w: 'majority',
ssl: true,
sslValidate: true,
sslCA: require('fs').readFileSync('./ca.pem'),
readConcern: { level: 'majority' },
writeConcern: { w: 'majority', j: true }
});
Connection Health Checks
Implement proactive health check mechanisms:
async function checkConnectionHealth() {
try {
await mongoose.connection.db.admin().ping();
return { healthy: true };
} catch (err) {
return {
healthy: false,
error: err.message,
stack: err.stack
};
}
}
// Periodic checks
setInterval(async () => {
const health = await checkConnectionHealth();
if (!health.healthy) {
console.error('Database health check failed:', health.error);
// Trigger reconnection logic
}
}, 30000);
Multiple Database Connection Handling
Manage multiple database connections simultaneously:
const primaryDB = mongoose.createConnection(primaryURI, {
useNewUrlParser: true,
retryWrites: true
});
const secondaryDB = mongoose.createConnection(secondaryURI, {
useNewUrlParser: true,
readPreference: 'secondary'
});
primaryDB.on('error', handlePrimaryDBError);
secondaryDB.on('error', handleSecondaryDBError);
function handlePrimaryDBError(err) {
console.error('Primary DB error:', err);
setTimeout(() => {
primaryDB.openUri(primaryURI);
}, 5000);
}
Connection Closure and Cleanup
Properly close connections to release resources:
async function gracefulShutdown() {
try {
await mongoose.connection.close(false); // force parameter set to false
console.log('MongoDB connection closed gracefully');
process.exit(0);
} catch (err) {
console.error('Error closing connection:', err);
process.exit(1);
}
}
process.on('SIGINT', gracefulShutdown);
process.on('SIGTERM', gracefulShutdown);
Logging and Monitoring
Implement detailed connection logging:
mongoose.set('debug', function(collectionName, methodName, ...methodArgs) {
console.log(`Mongoose: ${collectionName}.${methodName}`, methodArgs);
});
// Custom monitoring
const connectionEvents = ['connecting', 'connected', 'disconnecting', 'disconnected', 'close', 'reconnected', 'error'];
connectionEvents.forEach(event => {
mongoose.connection.on(event, () => {
logConnectionStatus(event);
sendMetricsToMonitoringSystem(event);
});
});
function logConnectionStatus(event) {
console.log(`Connection state changed: ${event} at ${new Date().toISOString()}`);
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn