使用connect-mongo存储会话时出现MongoError: E11000重复键错误
Hey there, I’ve run into this exact issue with large-scale apps using connect-mongo and MongoDB replica sets, so let’s break down what’s probably happening and how to fix it:
1. Check for Corrupted or Missing Unique Indexes
Connect-mongo relies on a unique index on the session ID (usually the _id field in the sessions collection) to prevent duplicates. If this index is corrupted, missing, or hasn’t properly synced across your replica set members, you’ll see this error even after cleaning sessions.
- First, verify the indexes in your MongoDB shell:
Look for a unique index ondb.sessions.getIndexes()_id(it should exist by default). If it’s missing or marked as invalid, rebuild it safely (without blocking your app):
The// Drop the corrupted index first (if needed) db.sessions.dropIndex("_id_") // Recreate the unique index in the background db.sessions.createIndex({ _id: 1 }, { unique: true, background: true })background: trueflag ensures this operation doesn’t lock up your collection during peak traffic.
2. Mitigate Race Conditions in Session Operations
High-traffic sites often face concurrent requests trying to create/update the same session at the same time. Connect-mongo’s default behavior might not handle these edge cases perfectly, leading to duplicate key errors.
- Use the
touchAfteroption: This adds a delay before updating unchanged sessions, reducing the chance of concurrent writes clashing. Example config:const sessionStore = new MongoStore({ mongoUrl: "your-replica-set-connection-string", touchAfter: 24 * 3600 // 24 hours in seconds }) - Add error handling for E11000 errors: Wrap session save operations in try-catch blocks, and when you catch a duplicate key error, fetch the existing session instead of retrying the insert. For example:
// In your route logic where you modify sessions try { await req.session.save(); } catch (err) { if (err.code === 11000) { // Fetch the existing session and use it const existingSession = await sessionStore.get(req.sessionID); req.session = existingSession; // Proceed with your logic or retry the save } else { throw err; // Re-throw other errors } }
3. Fix Replica Set Consistency Issues
If your connect-mongo config isn’t using a strong enough write concern, session writes might not propagate to all replica set members. This can lead to inconsistent state where one member thinks a session doesn’t exist, while another does—triggering duplicates.
- Update your mongo options to use majority write concern:
const sessionStore = new MongoStore({ mongoUrl: "your-replica-set-connection-string", mongoOptions: { w: "majority", // Ensure writes are replicated to most nodes wtimeoutMS: 5000 // Time out if majority isn't available } }) - Check for replica set lag: Run
rs.status()in the MongoDB shell to see if any secondary nodes are significantly behind the primary. Lagging members can cause read/write inconsistencies that lead to duplicate errors.
4. Clear Stale Client Cookies
Sometimes, users hold onto stale session IDs in their cookies that were already deleted from your database. When they send that ID again, your app might try to recreate the session—only to hit a duplicate if another request already recreated it.
- Set a reasonable
maxAgefor your session cookies: This ensures stale cookies expire automatically. Example in your session config:app.use(session({ store: sessionStore, secret: "your-session-secret", resave: false, saveUninitialized: false, cookie: { maxAge: 24 * 60 * 60 * 1000 // 24 hours } })) - Invalidate cookies when deleting sessions: When you manually clean up sessions server-side, send a response to expire the user’s session cookie.
- Implement session ID rotation: Periodically generate new session IDs for active users and invalidate old ones to reduce the chance of stale IDs causing conflicts.
5. Do a Full, Safe Session Collection Reset
If all else fails, a corrupted sessions collection might be the root cause. Before doing this, make sure to back up the collection and run it during low traffic hours (since it will log out all users):
// Backup the sessions collection first db.sessions.copyTo("sessions_backup") // Drop the existing collection db.sessions.drop() // Recreate the collection and required indexes db.sessions.createIndex({ _id: 1 }, { unique: true, background: true }) db.sessions.createIndex({ expires: 1 }, { expireAfterSeconds: 0 })
内容的提问来源于stack exchange,提问作者Elad Vider

