[CXX-581] insertion with duplicate _id did not fail, it updated the document instead. Created: 15/Apr/15  Updated: 15/Apr/15  Resolved: 15/Apr/15

Status: Closed
Project: C++ Driver
Component/s: API
Affects Version/s: legacy-1.0.0
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Judy Han [X] Assignee: Adam Midvidy
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

using the following sample program, if I ran it, modify eventString values and compile and run again, both runs will succeed with data updated. I am expecting the second run to fail with "duplicate key error" .

1 #include "mongo/client/dbclient.h"
2 #include <iostream>
3 #include <vector>
4 #include <sys/time.h>
5
6 //#define RECORDS 5000000
7 //#define RECORDS 800
8 #define RECORDS 2
9 #define BULKSIZE 10000
10 const char *eventString = "abcdefg";
11 int main(int argc, char **argv) {
12 if (!mongo::client::initialize().isOK())

{ 13 std::cout << "mongo initialize failed" << std::endl; 14 }

15 std::vector<mongo::BSONObj> bulk_data;
16 mongo::DBClientConnection mongo;
17 mongo::HostAndPort mongoHostAndPort("localhost:27017");
18 mongo::ConnectionString mongoConnectionString(mongoHostAndPort);
19 //mongo.connect("localhost");
20 std::string errString; //NOLINT
21 if (!mongo.connect(mongoHostAndPort, errString))

{ 22 std::cout << "fail to connect mongoDB: " << errString; 23 return 1; 24 }

;
25 mongo.dropCollection("insert_test.col1");
26
27 struct timeval start;
28 gettimeofday(&start, NULL);
29
30 uint64_t id = 1;
31 int count = 0;
32 int mongodbFsync = 0;
33 int mongodbJournal = 0;
34 int mongodbWriteconcern = 1;
35 int mongodbWtimeoutInMilliseconds = 1;
36 mongo.createIndex("insert_test.collection1", BSON("EventId" << 1));
37 mongo.createIndex("insert_test.collection1", BSON("WalletId" << 1));
38 mongo.createIndex("insert_test.collection1", BSON("DeviceId" << 1));
39 try {
40 for (int i=1; i<=RECORDS; i++) {
41 count++;
42 //++id;
43 mongo::BSONObj record = BSON ("_id" << static_cast<long long int>(id)
44 << "EventId" << 2
45 << "WalletId" << 3
46 << "DeviceId" << 4
47 << "mystring" << eventString);
48
49 bulk_data.push_back(record);
50
51 if (i % BULKSIZE == 0) {
52 mongo.insert("insert_test.col1", bulk_data);
53 std::string e = mongo.getLastError();
54 if (!e.empty())

{ 55 std::cout << "insert failed for: " << e << std::endl; 56 }

57 bulk_data.clear();
58 count = 0;
59
60 }
61
62 }
63 if (count != 0) {
64 mongo.insert("insert_test.col1", bulk_data);
65 std::string e = mongo.getLastError();
66 if (!e.empty())

{ 67 std::cout << "insert failed for: " << e << std::endl; 68 }

69 bulk_data.clear();
70 count = 0;
71 }
71 }
72 } catch (const mongo::DBException& exc)

{ 73 std::cout << "Caught an exception during getLastError.\n" << "Exception's what()=" << exc.what () << std::endl; 74 }

catch (...)

{ 75 std::cout << "Caught an unknown exception during getLastError." << std::endl; 76 }

77 struct timeval end;
78 gettimeofday(&end, NULL);
79 int now = (end.tv_sec * 1000) + (int)(end.tv_usec/1000);
80 int elapsed_time = now - ((start.tv_sec * 1000) +
81 (int)(start.tv_usec/1000));
82
83 std::cout << "rate: " << RECORDS/(elapsed_time) << "/msec" << std::endl;
84
85 std::cout << "count:" << mongo.count("insert_test.col1") << std::endl;
86
87 std::auto_ptr<mongo::DBClientCursor> cursor = mongo.query("insert_test.col1", mongo::BSONObj() );
88 if (!cursor.get())

{ 89 std::cout << "query failure" << std::endl; 90 return EXIT_FAILURE; 91 }

92
93 int loopCount = 0;
94 while( cursor->more() ) {
95 std::cout << cursor->next().toString() << std::endl;
96 ++loopCount;
97 if (loopCount == 100)

{ 98 break; 99 }

100 }
101 mongo::client::shutdown();
102 return 0;
103 }



 Comments   
Comment by Judy Han [X] [ 15/Apr/15 ]

Hi Adam,
Thanks for the quick response and the explanation.
Here is the documentation I was referring to:
http://api.mongodb.org/cxx/current/classmongo_1_1_d_b_client_with_commands.html#aa17adcfed028b8dfadcfd19fcf7081aa
it mentions that:
Returns (of type string):
error message text, or empty string if no error.
Feel free to close the ticke now.
Thanks!
Judy

Comment by Adam Midvidy [ 15/Apr/15 ]

Hi Judy,

Yes it is expected behavior that OperationException would be thrown for a duplicate key error. The legacy driver does not have a separate exception type for duplicate key error, although some other drivers do. This is something that is unlikely to change given our desire to keep legacy driver changes limited to bug fixes.

Can you show me which documentation you believe this behavior contradicts?

And yes, getLastError is thread safe if each thread has its own connection. The server stores getLastError state on a per-connection basis.

Comment by Judy Han [X] [ 15/Apr/15 ]

sorry, I said "close" too quickly. Could you reopen it?

after removing line 25, for second run, I am expecting something like:
insert failed for: duplicate key error.

instead I got:
Caught an exception during getLastError.
Exception's what()=OperationException: { index: 0, code: 11000, errmsg: "E11000 duplicate key error dup key: { : 1 }", op:

{ _id: 1, EventId: 2, WalletId: 3, DeviceId: 4, mystring: "abcde" }

}

Is that expected? It does not match the documenation.

Another question I have is:
is getLastError() thread safe? Assuming each thread has its own connection.

Thanks!
Judy

Comment by Judy Han [X] [ 15/Apr/15 ]

oops, sorry, my bad. please close the ticket.

Comment by Adam Midvidy [ 15/Apr/15 ]

Hi Judy,

It appears you are dropping the collection "insert_test.col1" on line 25. As such the behavior you are seeing is expected.

Adam

Generated at Wed Feb 07 21:59:38 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.