[SERVER-8496] _id values generated by mongod are not thread safe across databases, may cause mongod to generate duplicate ids for a single collection (resulting in insert failure) Created: 10/Feb/13  Updated: 11/Jul/16  Resolved: 13/Feb/13

Status: Closed
Project: Core Server
Component/s: Concurrency, Storage
Affects Version/s: 2.2.3, 2.3.2
Fix Version/s: 2.4.0-rc1

Type: Bug Priority: Major - P3
Reporter: Aaron Staple Assignee: Eliot Horowitz (Inactive)
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Operating System: ALL
Participants:

 Description   

DataFileMgr::insert is typically called with a database level write lock, for example when an insert operation request is received on the wire receivedInsert() will acquire such a lock and call checkAndInsert() which calls DataFileMgr::insertWithObjMod() which calls DataFileMgr::insert().

The implementation of DataFileMgr::insert() checks for an existing id in the document to be inserted. If no _id is found, a new ObjectId is generated and added. The implementation for adding an ObjectId uses the static global variables idToInsert and idToInsert. First the OID member of idToInsert_ is initialized with its init() method. Then the idToInsert object is byte copied into the on disk representation of the document to be inserted. If two threads are concurrently inserting into two separate databases, possible race conditions include:

  • the two init() calls precede the two copies into the doc store, resulting in duplicate _ids in different databases
  • the two init() calls overlap. OID::init() is not thread safe for a shared OID object, and this may create an ObjectId value unanticipated by the OID implementation. (In particular the bytes of the counter portion of the id are set individually not in one atomic assignment.) The resulting unexpected id value may conflict with a prior or future document's _id in the same collection, resulting in an insert failure when the second a conflicting document is inserted.

Test

#include <iostream>
#include <cstdlib>
 
#include "mongo/client/dbclient.h"
 
using namespace std;
using namespace mongo;
 
int main( int argc, const char **argv ) {
 
    const char *port = "27017";
    const char *db = argv[ 1 ];
 
    mongo::DBClientConnection conn;
    string errmsg;
    if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) {
        cout << "couldn't connect : " << errmsg << endl;
        return EXIT_FAILURE;
    }
 
    string ns = string( db ) + ".coll";
    int i = 0;
    while( true ) {
      conn.insert( ns, BSON( "a" << 1 ) );
      if ( i % 1000 == 0 ) {
        BSONObj err = conn.getPrevError();
        if ( !err[ "err" ].isNull() ) {
          log() << "err: " << err << endl;
          conn.resetError();
        }
      }
      ++i;
    }
 
}
 

Run two instances of the test concurrently (on different databases using the first command line arg). It can take several minutes to observe the first dup key error.

This bug will only manifest when clients do not insert _id fields and mongod generates _ids itself.



 Comments   
Comment by auto [ 13/Feb/13 ]

Author:

{u'date': u'2013-02-13T04:59:06Z', u'name': u'Eliot Horowitz', u'email': u'eliot@10gen.com'}

Message: SERVER-8496: make sure _id generated by server on insert is unique across dbs
Branch: master
https://github.com/mongodb/mongo/commit/3a8bf9f32314d1625fe57086a010d783eba97316

Comment by Aaron Staple [ 10/Feb/13 ]

Putting in 2.4.0-rc1 for triage. This issue is in 2.2 as well as 2.3.

Generated at Thu Feb 08 03:17:36 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.