Commit 60b80ec
improvement(tables): race-free row-count trigger + scoped tx timeouts (#4289)
* improvement(tables): race-free row-count trigger + scoped tx timeouts
* fix(tables): close upsert race + serialize replaceTableRows
Two concurrency bugs flagged by review:
1. `upsertRow` insert path: removing FOR UPDATE broke serialization between
the initial existing-row SELECT and the INSERT. Two concurrent upserts
on the same conflict target could both find no match, then both insert,
producing a duplicate that bypasses the app-level unique check. Fixed
by re-checking for the matching row *after* acquiring the per-table
advisory lock; if a racing tx already inserted, switch to UPDATE.
2. `replaceTableRows`: under READ COMMITTED each tx's DELETE uses its own
MVCC snapshot, so two concurrent replaces could each DELETE only the
rows visible at their start, then both INSERT, leaving the table with
the union of both row sets. Fixed by acquiring the per-table advisory
lock at the start of the tx to serialize replaces against each other
and against auto-position inserts.
Also updated a stale docstring on `upsertRow` that still referenced the
removed FOR UPDATE.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(tables): serialize explicit-position inserts with advisory lock
The `(table_id, position)` index is non-unique. Concurrent explicit-
position inserts at the same slot can both observe the slot empty, both
skip the shift, then each INSERT — producing duplicate `(table_id,
position)` rows.
Take the per-table advisory lock in the explicit-position branches of
`insertRow` and `batchInsertRows` (the auto-position branches already do
this). Updated the test that asserted the explicit path skipped the lock,
and added coverage for `batchInsertRows` with explicit positions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(tables): dedupe upsert UPDATE path + extract nextAutoPosition
Two pure cleanups on the round-1 changes:
1. Extract `nextAutoPosition(trx, tableId)` — the `SELECT coalesce(max(
position), -1) + 1` pattern was repeated in three call sites
(`insertRow` auto branch, `batchInsertRows` auto branch, `upsertRow`
insert branch). One helper, same behavior.
2. Consolidate `upsertRow` update path. The initial-SELECT match and the
post-lock re-select match previously had two literal duplicates of the
same UPDATE + return block. Resolve `matchedRowId` first, then run one
UPDATE branch. Lock is still only acquired when we don't find a match
on the first pass.
No behavior change. 98/98 table unit tests unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>1 parent af4be77 commit 60b80ec
11 files changed
Lines changed: 15990 additions & 168 deletions
File tree
- apps/sim
- app
- api
- table/[tableId]/rows
- v1/tables/[tableId]/rows
- workspace/[workspaceId]/tables/[tableId]/hooks
- hooks/queries
- lib/table
- __tests__
- packages/db/migrations
- meta
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
69 | 75 | | |
70 | 76 | | |
71 | 77 | | |
| |||
328 | 334 | | |
329 | 335 | | |
330 | 336 | | |
| 337 | + | |
331 | 338 | | |
332 | 339 | | |
333 | 340 | | |
| |||
349 | 356 | | |
350 | 357 | | |
351 | 358 | | |
| 359 | + | |
352 | 360 | | |
353 | 361 | | |
354 | 362 | | |
| |||
398 | 406 | | |
399 | 407 | | |
400 | 408 | | |
401 | | - | |
402 | | - | |
403 | | - | |
404 | | - | |
405 | | - | |
406 | | - | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
407 | 417 | | |
408 | 418 | | |
409 | 419 | | |
410 | 420 | | |
411 | | - | |
| 421 | + | |
412 | 422 | | |
413 | 423 | | |
414 | 424 | | |
| |||
424 | 434 | | |
425 | 435 | | |
426 | 436 | | |
427 | | - | |
| 437 | + | |
428 | 438 | | |
429 | 439 | | |
430 | 440 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
74 | 80 | | |
75 | 81 | | |
76 | 82 | | |
| |||
219 | 225 | | |
220 | 226 | | |
221 | 227 | | |
| 228 | + | |
222 | 229 | | |
223 | 230 | | |
224 | 231 | | |
| |||
268 | 275 | | |
269 | 276 | | |
270 | 277 | | |
271 | | - | |
272 | | - | |
273 | | - | |
274 | | - | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
275 | 307 | | |
276 | | - | |
277 | | - | |
278 | | - | |
279 | | - | |
280 | | - | |
| 308 | + | |
281 | 309 | | |
282 | 310 | | |
283 | 311 | | |
| |||
292 | 320 | | |
293 | 321 | | |
294 | 322 | | |
295 | | - | |
| 323 | + | |
296 | 324 | | |
297 | 325 | | |
298 | 326 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| 37 | + | |
37 | 38 | | |
38 | 39 | | |
39 | 40 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| 41 | + | |
| 42 | + | |
41 | 43 | | |
42 | 44 | | |
43 | 45 | | |
44 | 46 | | |
45 | | - | |
| 47 | + | |
| 48 | + | |
46 | 49 | | |
47 | 50 | | |
48 | 51 | | |
| |||
64 | 67 | | |
65 | 68 | | |
66 | 69 | | |
| 70 | + | |
67 | 71 | | |
68 | 72 | | |
69 | 73 | | |
70 | 74 | | |
71 | 75 | | |
72 | 76 | | |
| 77 | + | |
73 | 78 | | |
74 | 79 | | |
75 | 80 | | |
| |||
98 | 103 | | |
99 | 104 | | |
100 | 105 | | |
| 106 | + | |
101 | 107 | | |
102 | 108 | | |
103 | 109 | | |
| |||
114 | 120 | | |
115 | 121 | | |
116 | 122 | | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
117 | 127 | | |
118 | 128 | | |
119 | 129 | | |
120 | 130 | | |
121 | 131 | | |
122 | 132 | | |
123 | 133 | | |
124 | | - | |
| 134 | + | |
125 | 135 | | |
126 | | - | |
| 136 | + | |
127 | 137 | | |
128 | 138 | | |
129 | 139 | | |
130 | 140 | | |
131 | 141 | | |
132 | | - | |
| 142 | + | |
133 | 143 | | |
134 | 144 | | |
135 | 145 | | |
| |||
209 | 219 | | |
210 | 220 | | |
211 | 221 | | |
| 222 | + | |
212 | 223 | | |
213 | 224 | | |
214 | | - | |
| 225 | + | |
215 | 226 | | |
216 | 227 | | |
217 | 228 | | |
| |||
223 | 234 | | |
224 | 235 | | |
225 | 236 | | |
| 237 | + | |
226 | 238 | | |
227 | 239 | | |
228 | 240 | | |
| |||
393 | 405 | | |
394 | 406 | | |
395 | 407 | | |
396 | | - | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
397 | 413 | | |
398 | 414 | | |
399 | 415 | | |
| |||
0 commit comments