Commit d130f2c
committed
fix(client): fail closed in SF recovery and add crash-safe boundary manifest
SF crash recovery previously failed OPEN on operational errors:
- findFirst() < 0 (permission/transient enumeration error) was treated as
an empty directory, so the engine started fresh and openCleanRW(O_TRUNC)
truncated the existing durable log (sf-initial.sfa) and deleted the ack
watermark;
- findNext() < 0 silently accepted an incomplete directory listing;
- per-file openRW/mmap failures (EMFILE, ENOMEM, transient I/O) on valid
segments were swallowed by the same catch as data corruption and skipped;
- the FSN contiguity check only compares adjacent pairs, so a skipped
LEADING segment silently dropped unacked rows (ackedFsn seeded past
them) and a skipped TRAILING segment silently re-issued its FSNs to new
payloads (overlapping FSNs on disk).
Recovery now fails closed and proves slot integrity before mutating:
- Enumeration errors (findFirst/findNext < 0) abort startup.
- Operational open/mmap failures on recognized segments abort startup;
only positively-identified corruption (new MmapSegmentCorruptionException:
bad magic, sub-header size, negative baseSeq, unreadable header page) is
skippable, and quarantine renames (<name>.corrupt) are deferred until the
surviving chain validates, so a failed recovery mutates nothing.
Unsupported-version segments stay fatal (they belong to another client
build; renaming them would strand that build's frames).
- sf-manifest.bin: dual-slot, CRC-protected, generation-versioned boundary
record (headBase/activeBase) written on rotation and ack-driven trim.
Recovery validates the chain against the committed boundaries, which
catches missing leading/trailing segments that contiguity alone cannot.
Segments carry a manifest-required header flag so a deleted manifest next
to migrated segments fails closed. Boundary updates are monotonic
(clamped) and serialized with trim under the ring monitor; the trim path
recomputes the successor under that monitor so a concurrent rotation can
never make the head leapfrog a still-unacked sealed segment.
- Segment files are created with O_EXCL (new Files.openRWExclusive natives)
instead of O_TRUNC, so no code path can truncate an existing log.
- Crash-window protocol, verified state by state: fresh start orders
initial-segment durability -> manifest create -> flag stamp; rotation
syncs the promoted spare's rebased header before the manifest references
it and commits the manifest before any ring mutation; clean-drain close
durably collapses boundaries to head==active before unlinking and removes
the manifest last. Every window either recovers (equivalent empties from
a fresh-start kill, an empty active at the chain end after a rotation
crash, record-less manifest creation debris, drain-window survivors,
stale below-head files) or fails closed with intact evidence. The one
deliberate mutation on a failing path is quarantining a provably
record-less manifest (it contains no boundaries by construction; flagged
segments still fail closed afterwards).
Regression tests (SegmentRecoveryIntegrityTest, 24 cases) cover the full
required matrix: findFirst=-1, partial findNext=-1, openRW=-1, mmap
failure -- each asserting byte-for-byte slot preservation; the engine-level
O_TRUNC reproduction; leading/trailing/interior boundary loss; corrupt
stray quarantine with sibling recovery; deferred-quarantine no-mutation on
failure; flagged-segments-without-manifest; manifest creation-crash debris
self-heal (zero-byte and record-less); drain-crash spare survivor;
corrupt-active-with-empty-stand-in fail-closed; dual-slot generation
selection and torn-record fallback.
Independently reviewed for crash-consistency holes, lock ordering
(manager lock -> ring monitor -> manifest monitor, acyclic), unacked-frame
loss, and fd/mmap leaks; both review blockers (creation-debris brick,
drain-window spare brick) are fixed and regression-tested. Windows native
openRWExclusive0 uses CREATE_NEW; CI rebuilds platform binaries from the
.c change.1 parent e8c2dcd commit d130f2c
15 files changed
Lines changed: 1975 additions & 230 deletions
File tree
- core/src
- main
- c
- windows
- java/io/questdb/client
- cutlass/qwp/client/sf/cursor
- std
- test/java/io/questdb/client/test/cutlass/qwp/client/sf/cursor
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
68 | 75 | | |
69 | 76 | | |
70 | 77 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
106 | 115 | | |
107 | 116 | | |
108 | 117 | | |
| |||
Lines changed: 102 additions & 21 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
286 | 286 | | |
287 | 287 | | |
288 | 288 | | |
289 | | - | |
290 | | - | |
291 | | - | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
292 | 294 | | |
293 | 295 | | |
294 | 296 | | |
| |||
394 | 396 | | |
395 | 397 | | |
396 | 398 | | |
| 399 | + | |
397 | 400 | | |
398 | 401 | | |
399 | 402 | | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
400 | 410 | | |
401 | | - | |
| 411 | + | |
402 | 412 | | |
403 | 413 | | |
404 | | - | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
405 | 434 | | |
406 | 435 | | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
407 | 440 | | |
408 | | - | |
| 441 | + | |
409 | 442 | | |
410 | 443 | | |
411 | 444 | | |
| |||
1247 | 1280 | | |
1248 | 1281 | | |
1249 | 1282 | | |
1250 | | - | |
1251 | | - | |
1252 | | - | |
1253 | | - | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
1254 | 1288 | | |
1255 | | - | |
1256 | | - | |
1257 | | - | |
1258 | | - | |
1259 | | - | |
1260 | | - | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
1261 | 1301 | | |
1262 | 1302 | | |
1263 | 1303 | | |
| |||
1300 | 1340 | | |
1301 | 1341 | | |
1302 | 1342 | | |
1303 | | - | |
1304 | | - | |
1305 | | - | |
1306 | | - | |
1307 | | - | |
| 1343 | + | |
| 1344 | + | |
| 1345 | + | |
| 1346 | + | |
| 1347 | + | |
| 1348 | + | |
| 1349 | + | |
| 1350 | + | |
| 1351 | + | |
| 1352 | + | |
| 1353 | + | |
| 1354 | + | |
| 1355 | + | |
| 1356 | + | |
| 1357 | + | |
| 1358 | + | |
| 1359 | + | |
| 1360 | + | |
| 1361 | + | |
1308 | 1362 | | |
1309 | 1363 | | |
| 1364 | + | |
| 1365 | + | |
| 1366 | + | |
| 1367 | + | |
| 1368 | + | |
| 1369 | + | |
| 1370 | + | |
| 1371 | + | |
| 1372 | + | |
| 1373 | + | |
| 1374 | + | |
| 1375 | + | |
| 1376 | + | |
| 1377 | + | |
| 1378 | + | |
| 1379 | + | |
| 1380 | + | |
| 1381 | + | |
| 1382 | + | |
| 1383 | + | |
| 1384 | + | |
| 1385 | + | |
| 1386 | + | |
| 1387 | + | |
| 1388 | + | |
| 1389 | + | |
| 1390 | + | |
1310 | 1391 | | |
1311 | 1392 | | |
1312 | 1393 | | |
| |||
0 commit comments