Skip to content

Report the unresolvable chunk when the gc mark phase fails#1561

Merged
timsehn merged 1 commit into
masterfrom
bug/gc-mark-failure-diagnostics
Jul 7, 2026
Merged

Report the unresolvable chunk when the gc mark phase fails#1561
timsehn merged 1 commit into
masterfrom
bug/gc-mark-failure-diagnostics

Conversation

@timsehn

@timsehn timsehn commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #1547 / #1559, reviving the diagnostic output from the closed #1556 as a standalone change (per request) — without #1556's test-build counters, debug SQL registration, or CI stress gate.

What it does

dolt_gc() mark failures now report exactly what could not be resolved instead of a bare gc mark phase failed:

gc mark phase failed: missing chunk 010a8957... parent=2b5f0fcf... source=child parent_type=working-set child_index=0 rc=12
  • source names the edge that produced the hash: refs-table, branch-commit, branch-working-set, tag-commit, pending, session, or child (an edge inside a stored chunk).
  • For child edges: the parent chunk hash, its classified type (including the conflicts / constraint-violations types added in Fix GC classification of commit chunks #1559), the child ordinal, and parent_level/parent_items when the parent is a prolly node.
  • rc distinguishes not-found (12) from corrupt reads (11).

This exact output is what let the #1547 reporter capture an actionable diagnostic from their production store; keeping it means the next field report starts with the offending bytes instead of a guess.

Testing

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Version-Control Performance Ceilings

Runs: median of 3 executions per benchmark, excluding fixture setup. The
working-set fixtures use 800 committed tables with 125 rows each
(100000 rows total); data-dirty cases update 1 row in 40
tables, and schema-dirty cases update 1 row plus add a column in 20 tables.
Branch tests use 300 branches, checkout uses a clean 400-table
branch switch over 100000 rows, and merge
tests use 100000-row tables with 2000 changed or conflicting
rows per side.

Benchmark Median ms Ceiling ms Result
status_clean_many_tables 82 200 PASS
status_dirty_many_tables 84 200 PASS
diff_regular_working_one_table 75 150 PASS
diff_regular_working_many_tables 84 200 PASS
diff_stat_working_many_tables 84 200 PASS
diff_schema_working_many_tables 85 200 PASS
branch_list_many_branches 23 100 PASS
branch_create_delete 31 100 PASS
checkout_branch_clean 90 200 PASS
merge_data_no_conflicts 50 150 PASS
merge_schema_no_conflicts 22 100 PASS
merge_data_conflicts 133 250 PASS
merge_data_conflicts_with_resolve 133 250 PASS

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Sysbench-Style Benchmark: Doltlite vs SQLite

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 24,892 28,025 1.13
oltp_range_select 11,064 11,517 1.04
oltp_sum_range 9,809 11,323 1.15
oltp_order_range 2,637 2,915 1.11
oltp_distinct_range 3,666 3,949 1.08
oltp_index_scan 3,907 4,976 1.27
select_random_points 10,184 11,358 1.12
select_random_ranges 3,029 4,009 1.32
covering_index_scan 4,259 4,035 0.95
groupby_scan 31,499 34,140 1.08
index_join 5,830 7,698 1.32
index_join_scan 3,324 4,604 1.39
types_table_scan 1,127,890 1,224,221 1.09
table_scan 1,261,414 1,341,806 1.06
oltp_read_only 103,697 113,030 1.09
Average 1.15

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 183,766 243,798 1.33
oltp_insert 15,784 28,460 1.80
oltp_update_index 51,983 89,078 1.71
oltp_update_non_index 36,814 58,658 1.59
oltp_delete_insert 47,049 71,560 1.52
oltp_write_only 22,822 45,169 1.98
types_delete_insert 25,178 40,060 1.59
oltp_read_write 66,961 103,072 1.54
Average 1.63

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 106,863 55,610 0.52
oltp_range_select 19,952 14,419 0.72
oltp_sum_range 18,050 14,043 0.78
oltp_order_range 3,606 3,212 0.89
oltp_distinct_range 4,537 4,235 0.93
oltp_index_scan 12,276 8,085 0.66
select_random_points 19,272 14,160 0.73
select_random_ranges 11,388 6,772 0.59
covering_index_scan 12,685 7,243 0.57
groupby_scan 32,812 34,528 1.05
index_join 10,216 9,837 0.96
index_join_scan 4,405 5,061 1.15
types_table_scan 1,102,108 1,222,425 1.11
table_scan 1,261,629 1,343,313 1.06
oltp_read_only 220,931 153,876 0.70
Average 0.83

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 195,743 266,197 1.36
oltp_insert 22,052 35,553 1.61
oltp_update_index 77,659 115,065 1.48
oltp_update_non_index 57,597 80,344 1.39
oltp_delete_insert 66,760 92,165 1.38
oltp_write_only 43,212 64,135 1.48
types_delete_insert 39,348 52,002 1.32
oltp_read_write 86,274 123,589 1.43
Average 1.43

File-Backed (autocommit)

Each statement runs as its own transaction — exposes per-commit
fixed costs that the wrapped-in-BEGIN/COMMIT tests amortize away.
SQLite uses WAL mode with synchronous=FULL in this section so
the comparison uses SQLite's durable WAL autocommit path.

Reads

Reads have no commit cost; these are the same SQL files as the
File-Backed Reads section, included here for symmetry and to
catch any per-statement overhead doltlite pays on the read path.

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 50,806 55,255 1.09
oltp_range_select 13,709 14,382 1.05
oltp_sum_range 12,088 13,878 1.15
oltp_order_range 3,008 3,180 1.06
oltp_distinct_range 3,940 4,239 1.08
oltp_index_scan 6,650 7,994 1.20
select_random_points 13,137 14,090 1.07
select_random_ranges 5,714 6,777 1.19
covering_index_scan 6,856 7,005 1.02
groupby_scan 31,573 34,542 1.09
index_join 7,455 9,747 1.31
index_join_scan 3,689 5,053 1.37
types_table_scan 1,100,365 1,211,140 1.10
table_scan 1,252,466 1,341,317 1.07
oltp_read_only 140,370 153,681 1.09
Average 1.13

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 14,150 61,554 4.35
oltp_insert_ac 17,021 80,013 4.70
oltp_update_index_ac 18,695 94,488 5.05
oltp_update_non_index_ac 16,640 69,048 4.15
oltp_delete_insert_ac 17,523 83,152 4.75
oltp_write_only_ac 16,852 82,637 4.90
types_delete_insert_ac 15,842 73,225 4.62
oltp_read_write_ac 21,317 94,316 4.42
Average 4.62

100000 rows, median of 5 invocations per test; autocommit writes use 9, workload-only timing via host monotonic clock when available.

Performance Ceiling Check (2.5x individual, 2x average; autocommit writes: 6x / 5x)

All tests within ceilings.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Sysbench-Style Benchmark (composite PK): Doltlite vs SQLite

Companion to the classic Sysbench-Style Benchmark. Every workload here
runs against tables with a 2-column INTEGER PRIMARY KEY(a, b) WITHOUT ROWID.

Individual ratios gated at 2.5×; section averages gated at 2×. Autocommit writes use 6× / 5×.

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 32,171 40,837 1.27
oltp_range_select 18,014 22,968 1.28
oltp_sum_range 17,760 21,166 1.19
oltp_order_range 3,416 3,776 1.11
oltp_distinct_range 4,587 4,883 1.06
oltp_index_scan 4,476 5,940 1.33
select_random_points 27,660 31,913 1.15
select_random_ranges 7,615 8,811 1.16
covering_index_scan 4,110 4,113 1.00
groupby_scan 35,782 39,038 1.09
index_join 8,233 10,339 1.26
index_join_scan 4,155 5,557 1.34
types_table_scan 1,064,292 1,261,772 1.19
table_scan 1,272,940 1,327,868 1.04
oltp_read_only 152,670 171,877 1.13
Average 1.17

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 241,541 347,927 1.44
oltp_insert 18,739 36,379 1.94
oltp_update_index 70,302 120,504 1.71
oltp_update_non_index 52,333 85,609 1.64
oltp_delete_insert 51,504 98,409 1.91
oltp_write_only 27,908 57,624 2.06
types_delete_insert 33,131 55,343 1.67
oltp_read_write 102,425 157,994 1.54
Average 1.74

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 102,439 64,793 0.63
oltp_range_select 27,569 25,996 0.94
oltp_sum_range 26,158 24,207 0.93
oltp_order_range 4,400 4,194 0.95
oltp_distinct_range 5,550 5,295 0.95
oltp_index_scan 11,994 8,849 0.74
select_random_points 39,479 38,311 0.97
select_random_ranges 15,261 11,564 0.76
covering_index_scan 11,537 7,142 0.62
groupby_scan 37,119 39,576 1.07
index_join 12,961 13,030 1.01
index_join_scan 5,593 6,013 1.08
types_table_scan 1,119,816 1,246,506 1.11
table_scan 1,399,173 1,365,210 0.98
oltp_read_only 264,573 215,180 0.81
Average 0.90

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 255,903 378,641 1.48
oltp_insert 27,145 46,558 1.72
oltp_update_index 103,075 153,288 1.49
oltp_update_non_index 81,514 108,325 1.33
oltp_delete_insert 80,153 122,137 1.52
oltp_write_only 54,055 80,128 1.48
types_delete_insert 51,057 70,578 1.38
oltp_read_write 135,923 182,068 1.34
Average 1.47

File-Backed (autocommit)

Each statement runs as its own transaction — exposes per-commit
fixed costs that the wrapped-in-BEGIN/COMMIT tests amortize away.
SQLite uses WAL mode with synchronous=FULL in this section so
the comparison uses SQLite's durable WAL autocommit path.

Reads

Reads have no commit cost; these are the same SQL files as the
File-Backed Reads section, included here for symmetry and to
catch any per-statement overhead doltlite pays on the read path.

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 58,333 66,325 1.14
oltp_range_select 22,593 26,094 1.15
oltp_sum_range 21,803 24,382 1.12
oltp_order_range 4,041 4,292 1.06
oltp_distinct_range 5,024 5,350 1.06
oltp_index_scan 7,579 9,033 1.19
select_random_points 34,799 37,312 1.07
select_random_ranges 10,663 11,626 1.09
covering_index_scan 6,932 7,189 1.04
groupby_scan 36,888 39,649 1.07
index_join 10,527 13,216 1.26
index_join_scan 4,971 6,192 1.25
types_table_scan 1,075,704 1,237,264 1.15
table_scan 1,400,795 1,349,101 0.96
oltp_read_only 195,733 213,272 1.09
Average 1.11

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 23,224 82,798 3.57
oltp_insert_ac 25,457 103,747 4.08
oltp_update_index_ac 27,597 116,835 4.23
oltp_update_non_index_ac 23,021 93,683 4.07
oltp_delete_insert_ac 24,756 106,255 4.29
oltp_write_only_ac 25,726 101,620 3.95
types_delete_insert_ac 21,547 90,141 4.18
oltp_read_write_ac 33,612 110,516 3.29
Average 3.96

100000 rows, median of 5 invocations per test; autocommit writes use 9, workload-only timing via host monotonic clock when available.

Performance Ceiling Check (2.5x individual, 2x average; autocommit writes: 6x / 5x)

All tests within ceilings.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Sysbench-Style Benchmark (BLOB PK): Doltlite vs SQLite

Companion to the classic Sysbench-Style Benchmark. Every workload here
runs against tables with a 16-byte big-endian BLOB PRIMARY KEY.

Individual ratios gated at 2.5×; section averages gated at 2×. Autocommit writes use 6× / 5×.

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 26,090 29,897 1.15
oltp_range_select 11,788 12,728 1.08
oltp_sum_range 11,373 11,950 1.05
oltp_order_range 2,718 2,981 1.10
oltp_distinct_range 3,699 3,925 1.06
oltp_index_scan 4,066 5,085 1.25
select_random_points 16,256 19,054 1.17
select_random_ranges 3,418 4,634 1.36
covering_index_scan 3,705 3,958 1.07
groupby_scan 31,163 32,429 1.04
index_join 6,256 8,701 1.39
index_join_scan 3,645 5,240 1.44
types_table_scan 1,062,257 1,163,551 1.10
table_scan 1,282,598 1,285,572 1.00
oltp_read_only 107,171 118,913 1.11
Average 1.16

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 192,718 276,827 1.44
oltp_insert 17,014 32,463 1.91
oltp_update_index 60,667 115,949 1.91
oltp_update_non_index 41,780 71,858 1.72
oltp_delete_insert 42,322 87,767 2.07
oltp_write_only 23,671 52,497 2.22
types_delete_insert 26,988 45,768 1.70
oltp_read_write 71,626 115,779 1.62
Average 1.82

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 57,232 41,279 0.72
oltp_range_select 15,448 14,086 0.91
oltp_sum_range 14,969 13,523 0.90
oltp_order_range 3,168 3,151 0.99
oltp_distinct_range 4,107 4,107 1.00
oltp_index_scan 7,667 6,721 0.88
select_random_points 20,457 20,542 1.00
select_random_ranges 6,810 5,892 0.87
covering_index_scan 7,462 5,425 0.73
groupby_scan 31,940 32,776 1.03
index_join 8,630 9,983 1.16
index_join_scan 4,243 5,703 1.34
types_table_scan 1,070,029 1,161,249 1.09
table_scan 1,292,368 1,278,452 0.99
oltp_read_only 154,281 135,819 0.88
Average 0.97

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 257,562 354,782 1.38
oltp_insert 46,555 65,358 1.40
oltp_update_index 199,931 213,595 1.07
oltp_update_non_index 155,077 142,517 0.92
oltp_delete_insert 163,758 171,832 1.05
oltp_write_only 125,109 120,667 0.96
types_delete_insert 103,445 112,424 1.09
oltp_read_write 171,441 182,780 1.07
Average 1.12

File-Backed (autocommit)

Each statement runs as its own transaction — exposes per-commit
fixed costs that the wrapped-in-BEGIN/COMMIT tests amortize away.
SQLite uses WAL mode with synchronous=FULL in this section so
the comparison uses SQLite's durable WAL autocommit path.

Reads

Reads have no commit cost; these are the same SQL files as the
File-Backed Reads section, included here for symmetry and to
catch any per-statement overhead doltlite pays on the read path.

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 35,669 40,653 1.14
oltp_range_select 13,200 14,114 1.07
oltp_sum_range 12,987 13,293 1.02
oltp_order_range 2,970 3,167 1.07
oltp_distinct_range 3,882 4,149 1.07
oltp_index_scan 5,534 6,639 1.20
select_random_points 18,411 20,370 1.11
select_random_ranges 4,714 5,821 1.23
covering_index_scan 5,226 5,352 1.02
groupby_scan 31,568 32,412 1.03
index_join 7,431 9,944 1.34
index_join_scan 4,083 5,558 1.36
types_table_scan 1,058,888 1,160,040 1.10
table_scan 1,285,284 1,265,279 0.98
oltp_read_only 119,190 133,924 1.12
Average 1.12

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 24,083 85,835 3.56
oltp_insert_ac 25,211 106,815 4.24
oltp_update_index_ac 27,880 124,209 4.46
oltp_update_non_index_ac 26,879 103,255 3.84
oltp_delete_insert_ac 24,987 105,165 4.21
oltp_write_only_ac 25,749 107,897 4.19
types_delete_insert_ac 24,093 97,801 4.06
oltp_read_write_ac 31,316 114,072 3.64
Average 4.02

100000 rows, median of 5 invocations per test; autocommit writes use 9, workload-only timing via host monotonic clock when available.

Performance Ceiling Check (2.5x individual, 2x average; autocommit writes: 6x / 5x)

All tests within ceilings.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Sysbench-Style Benchmark (TEXT PK): Doltlite vs SQLite

Companion to the classic Sysbench-Style Benchmark. Every workload here
runs against tables with a 32-char hex TEXT PRIMARY KEY (UUID-shaped).

Individual ratios gated at 2.5×; section averages gated at 2×. Autocommit writes use 6× / 5×.

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 28,668 37,338 1.30
oltp_range_select 11,607 15,088 1.30
oltp_sum_range 10,971 14,233 1.30
oltp_order_range 2,706 3,108 1.15
oltp_distinct_range 3,733 4,196 1.12
oltp_index_scan 4,263 5,981 1.40
select_random_points 16,249 20,796 1.28
select_random_ranges 3,599 5,111 1.42
covering_index_scan 4,542 4,364 0.96
groupby_scan 31,039 33,887 1.09
index_join 6,680 8,817 1.32
index_join_scan 4,018 5,278 1.31
types_table_scan 1,051,464 1,208,123 1.15
table_scan 1,192,961 1,292,308 1.08
oltp_read_only 113,225 137,091 1.21
Average 1.23

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 232,375 352,847 1.52
oltp_insert 21,162 39,054 1.85
oltp_update_index 68,415 129,287 1.89
oltp_update_non_index 46,658 85,080 1.82
oltp_delete_insert 48,904 101,702 2.08
oltp_write_only 27,791 60,092 2.16
types_delete_insert 31,645 53,983 1.71
oltp_read_write 80,834 137,073 1.70
Average 1.84

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 98,764 61,675 0.62
oltp_range_select 20,312 17,919 0.88
oltp_sum_range 19,229 17,182 0.89
oltp_order_range 3,757 3,487 0.93
oltp_distinct_range 4,831 4,576 0.95
oltp_index_scan 11,986 8,889 0.74
select_random_points 26,051 24,148 0.93
select_random_ranges 10,971 7,787 0.71
covering_index_scan 12,421 7,139 0.57
groupby_scan 32,690 34,349 1.05
index_join 11,572 11,028 0.95
index_join_scan 5,408 5,797 1.07
types_table_scan 1,042,123 1,202,922 1.15
table_scan 1,195,818 1,289,090 1.08
oltp_read_only 215,204 173,852 0.81
Average 0.89

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 247,587 382,029 1.54
oltp_insert 43,229 51,436 1.19
oltp_update_index 109,248 165,756 1.52
oltp_update_non_index 92,949 109,693 1.18
oltp_delete_insert 86,666 130,805 1.51
oltp_write_only 90,635 83,655 0.92
types_delete_insert 53,792 73,589 1.37
oltp_read_write 147,980 159,643 1.08
Average 1.29

File-Backed (autocommit)

Each statement runs as its own transaction — exposes per-commit
fixed costs that the wrapped-in-BEGIN/COMMIT tests amortize away.
SQLite uses WAL mode with synchronous=FULL in this section so
the comparison uses SQLite's durable WAL autocommit path.

Reads

Reads have no commit cost; these are the same SQL files as the
File-Backed Reads section, included here for symmetry and to
catch any per-statement overhead doltlite pays on the read path.

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 52,390 62,158 1.19
oltp_range_select 15,228 17,716 1.16
oltp_sum_range 14,287 17,318 1.21
oltp_order_range 3,210 3,482 1.08
oltp_distinct_range 4,271 4,575 1.07
oltp_index_scan 6,986 9,033 1.29
select_random_points 20,440 24,118 1.18
select_random_ranges 6,306 7,735 1.23
covering_index_scan 7,395 7,147 0.97
groupby_scan 31,878 34,392 1.08
index_join 8,521 11,029 1.29
index_join_scan 4,851 5,755 1.19
types_table_scan 1,042,979 1,205,598 1.16
table_scan 1,196,716 1,294,097 1.08
oltp_read_only 148,551 173,766 1.17
Average 1.16

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 21,153 76,834 3.63
oltp_insert_ac 24,014 93,032 3.87
oltp_update_index_ac 27,235 111,740 4.10
oltp_update_non_index_ac 24,108 89,731 3.72
oltp_delete_insert_ac 25,198 103,463 4.11
oltp_write_only_ac 23,821 100,431 4.22
types_delete_insert_ac 20,899 89,739 4.29
oltp_read_write_ac 29,790 106,523 3.58
Average 3.94

100000 rows, median of 5 invocations per test; autocommit writes use 9, workload-only timing via host monotonic clock when available.

Performance Ceiling Check (2.5x individual, 2x average; autocommit writes: 6x / 5x)

All tests within ceilings.

A bare "gc mark phase failed" gives a field report nothing to act on.
Track each queued hash's provenance through the mark walk and name the
missing chunk, its parent hash, the edge source (refs table, branch
tip/working set, tag, pending, session, or a child edge inside a stored
chunk), the parent's chunk type — with level/item count for prolly
nodes — the child ordinal, and the result code. This is the diagnostic
layer from the closed #1556 without its test-build counters and stress
gate; its field capture is what pinpointed the #1547 misclassification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@timsehn timsehn force-pushed the bug/gc-mark-failure-diagnostics branch from 35d4d2f to 9ab2f39 Compare July 7, 2026 00:06
@timsehn timsehn merged commit 628ece6 into master Jul 7, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant