This project demonstrates race conditions that occur in DocumentDB (and consequently in Azure Cosmos DB for MongoDB vCore) but not in standard MongoDB when performing concurrent upsert operations.
This reproducer runs 100 iterations of concurrent upsert operations on the same document using two different MongoDB connections. Each iteration:
- Deletes the test document to start fresh
- Concurrently executes two upsert operations:
- Connection 1: Sets
isProxy: false
and unsetsdeleted
field - Connection 2: Adds a
profileId
to theprofileIds
array
- Connection 1: Sets
- Verifies that both operations succeeded by checking the final document
- Node.js (v18+)
- Docker and Docker Compose
- npm or yarn
- Clone/download this repository
- Install dependencies:
npm install
-
Start MongoDB container:
npm run docker:up
-
Run the test:
npm run test-mongodb
Expected Result: ✅ 100% success rate, 0% data loss
- Run the test:
npm run test-documentdb
Expected Result: ❌ ~2% success rate, ~98% data loss
🧪 Race Condition Test
Testing with: mongodb://localhost:27017/
✅ Run 1: Success
✅ Run 2: Success
...
✅ Run 100: Success
Results: 100 successes, 0 failures
Data loss rate: 0.0%
🧪 Race Condition Test
Testing with: mongodb://***:***@localhost:10260/?tls=true&tlsAllowInvalidCertificates=true
✅ Run 1: Success
❌ Run 2: Missing isProxy
❌ Run 3: Missing profileIds
...
❌ Run 100: Missing profileIds
Results: 2 successes, 98 failures
Data loss rate: 98.0%
Script | Description |
---|---|
npm run test-mongodb |
Test against MongoDB (localhost:27017) |
npm run test-documentdb |
Test against DocumentDB (localhost:10260) |
npm run docker:up |
Start both MongoDB and DocumentDB containers |
npm run docker:down |
Stop and remove all containers |
You can also run tests manually with custom connection strings:
# Test with MongoDB
npx tsx minimal.ts "mongodb://localhost:27017/"
# Test with DocumentDB
npx tsx minimal.ts "mongodb://alice:hunter2@localhost:10260/?tls=true&tlsAllowInvalidCertificates=true"
# Test with your own connection string
npx tsx minimal.ts "your-connection-string-here"
The race condition occurs when two concurrent upsert operations target the same document:
- Connection 1 upserts with:
{ $unset: { deleted: "" }, $set: { isProxy: false } }
- Connection 2 upserts with:
{ $addToSet: { profileIds: "some-id" } }
Both operations should be atomic and the final document should contain both changes:
{
"_id": "test-id",
"isProxy": false,
"profileIds": ["some-profile-id"]
}
One operation often overwrites the other, resulting in partial data loss:
// Missing isProxy field
{
"_id": "test-id",
"profileIds": ["some-profile-id"]
}
// OR missing profileIds field
{
"_id": "test-id",
"isProxy": false
}