Commit 802a21a
authored
fix(next): prevent transaction race condition in renderDocument parallel operations (#14652)
## Description
Fixes `MongoExpiredSessionError` caused by transaction state mutation
leaking between parallel operations in `renderDocument`.
## Problem
When `renderDocument` runs `getDocumentPermissions` and `getIsLocked` in
parallel via `Promise.all`, they share the same `req` object. When
`getDocumentPermissions` calls `docAccessOperation` →
`initTransaction()`, it **mutates** `req.transactionID`. This mutation
is visible to `getIsLocked`, which then tries to use the same
transaction. When the permission check completes and commits its
transaction, `getIsLocked` fails with an expired session error.
This is because `getIsLocked` may still be trying to use the
transactionID it (involuntarily) received from `getDocumentPermissions`,
even though `getDocumentPermissions` already closed it.
## Solution
Use `isolateObjectProperty(req, 'transactionID')` to give each parallel
operation its own isolated transaction state. This creates a Proxy where
`transactionID` mutations go to a delegate object instead of the shared
parent req.
```ts
// Before - shared req, mutations leak
await Promise.all([
getDocumentPermissions({ req }), // Mutates req.transactionID
getIsLocked({ req }), // Sees the mutation → ERROR
])
```
```ts
// After - isolated transactionID
const reqForPermissions = isolateObjectProperty(req, 'transactionID')
const reqForLockCheck = isolateObjectProperty(req, 'transactionID')
await Promise.all([
getDocumentPermissions({ req: reqForPermissions }), // Isolated
getIsLocked({ req: reqForLockCheck }), // Isolated
])
```
If parent `req` already has a transaction, it's preserved (no isolation
applied).
## Testing
This PR does not include tests, as it's very difficult to reliably test
for this race condition. I reproduced this issue using this PR:
#14631 as it just happened to
speed up `getDocumentPermissions` just enough to trigger this race
condition more often.1 parent 87137fe commit 802a21a
1 file changed
+27
-5
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| |||
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
143 | 165 | | |
144 | 166 | | |
145 | 167 | | |
| |||
155 | 177 | | |
156 | 178 | | |
157 | 179 | | |
158 | | - | |
| 180 | + | |
159 | 181 | | |
160 | 182 | | |
161 | 183 | | |
162 | 184 | | |
163 | 185 | | |
164 | | - | |
| 186 | + | |
165 | 187 | | |
166 | 188 | | |
167 | | - | |
| 189 | + | |
168 | 190 | | |
169 | 191 | | |
170 | 192 | | |
171 | 193 | | |
172 | 194 | | |
173 | | - | |
| 195 | + | |
174 | 196 | | |
175 | 197 | | |
176 | 198 | | |
| |||
0 commit comments