Commit 5f91323
authored
fix(api): prevent MongoDB duplicate key failures during concurrent upserts (JhaSourav07#550)
## Description
Fixes JhaSourav07#541
This PR resolves a MongoDB concurrency race condition inside
`/api/track-user` that could trigger `E11000 duplicate key` exceptions
during simultaneous requests for the same username.
Previously, concurrent requests executing:
findOneAndUpdate(..., { upsert: true })
could race against the unique `username` index.
Under high concurrency:
- multiple requests detected the document as missing simultaneously
- multiple insert attempts were triggered
- MongoDB threw `E11000 duplicate key` errors
- the API incorrectly returned `500 Internal Server Error`
This PR introduces safe duplicate-key race handling and treats these
collisions as successful no-op operations.
---
## Changes Implemented
### Graceful Duplicate-Key Handling
Wrapped the upsert operation inside a dedicated `try/catch` block and
safely handled MongoDB duplicate-key race conditions:
if (
upsertError &&
typeof upsertError === 'object' &&
'code' in upsertError &&
upsertError.code === 11000
) {
return NextResponse.json({ success: true });
}
This ensures:
- concurrent duplicate inserts no longer fail
- the endpoint remains idempotent
- safe race conditions do not return HTTP 500
### Added Regression Test Coverage
Added a dedicated concurrency regression test that:
- simulates `E11000` duplicate-key collisions
- verifies `200 OK` responses
- validates successful API behavior
- ensures no unnecessary error logging occurs
---
## Files Changed
- `app/api/track-user/route.ts`
- `app/api/track-user/route.test.ts`
---
## Root Cause
The route previously treated MongoDB duplicate-key collisions as fatal
server errors.
However, under concurrent upsert operations:
- one request may successfully insert the username
- a second simultaneous request may fail the unique index check
- MongoDB throws `E11000 duplicate key error`
This is an expected concurrency race and should be treated as a safe
no-op instead of an internal server failure.
---
## Validation
Successfully verified with:
- `npm run test`
- `npm run lint`
- `npm run build`
### Results
- 22/22 test suites passed
- 276/276 tests passed
- Production build compiled successfully
---
## Concurrency Validation
Verified successfully using:
- simultaneous identical username submissions
- rapid repeated POST requests
- mocked MongoDB duplicate-key collisions
Confirmed:
- no HTTP 500 responses occur
- duplicate races resolve safely
- API behavior remains stable and idempotent
---
## Impact
This PR fixes:
- MongoDB `E11000` concurrency crashes
- unnecessary API failures
- duplicate insert race instability
- noisy database exception behavior
while preserving:
- existing API behavior
- response structure
- TypeScript safety
- backward compatibility
---
## Pillar
- [ ] 🎨 Pillar 1 — New Theme Design
- [ ] 📐 Pillar 2 — Geometric SVG Improvement
- [ ] 🕐 Pillar 3 — Timezone Logic Optimization
- [x] 🛠️ Other (Bug fix, refactoring, docs)
---
## Visual Preview
N/A — backend concurrency reliability fix
---
## Checklist before requesting a review:
- [x] I have read the `CONTRIBUTING.md` file.
- [x] I have tested these changes locally.
- [x] I have run `npm run lint` locally and resolved all errors.
- [x] My commits follow the Conventional Commits format.
- [x] I have made sure that I have only one commit in this PR.
- [x] All tests and production builds pass successfully.2 files changed
Lines changed: 78 additions & 9 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| |||
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
102 | | - | |
| 102 | + | |
103 | 103 | | |
104 | 104 | | |
105 | | - | |
| 105 | + | |
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| |||
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
127 | 172 | | |
128 | 173 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
55 | 79 | | |
56 | 80 | | |
57 | 81 | | |
| |||
0 commit comments