Skip to content

fix(columnar): append filter output under one mutation lease per storage epoch - #2073

Merged
justinjoy merged 1 commit into
mainfrom
claude/filter-append-lease-regression
Oct 4, 2026
Merged

justinjoy merged 1 commit into
mainfrom
claude/filter-append-lease-regression

Conversation

@justinjoy

Copy link
Copy Markdown
Collaborator

Summary

Main's Sanitizers / ubuntu-latest / gcc job times out tdd_recursive (30 s) and session (180 s) on 2-vCPU GitHub-hosted runners. The cause is the #2037 mutation-lease series, not #2058, which was merged in the same push:

tdd_recursive, pinned to 2 CPUs, sanitizer debug time
a8b5b5d (before #2037) 7.7 s
c53c3c3 (end of #2037) 16.6 s
b63f39d (#2058) 17.1 s (+3 %, noise)
main 20.8–26 s

User CPU tripled, so the extra time is compute, not waiting. perf put the mutation-set acquire, validate and finish functions above 40 %. They are reached from the filter operator, which appended each selected row through col_rel_append_row and so took a full mutation lease per row. CI attributed the break to #2058 because that push's runs alternated between a fast self-hosted runner, where they passed, and a hosted one, where they timed out.

This PR adds a held-lease batch appender (col_rel_append_batch_begin/row/end). It acquires one lease per storage epoch and renews it only before a row that would replace storage again; the append path uses the same predicate. Every filter.c fill now appends through it, and every exit ends the batch before the output is destroyed or published. Filter results, growth policy and error codes are unchanged.

Measurements

Pinned to 2 CPUs, sanitizer debug build:

test a8b5b5d main this PR
tdd_recursive 8.8 s 26.0 s 10.9 s
session 61.6 s 187.0 s 104.7 s

This restores the filter operator's append cost. session is still about 1.7x its baseline. Per-cell col_rel_set (join and aggregation) and other per-row col_rel_append_row callers still take a lease each. That work is tracked in #2072.

Tests

  • relation_mutation_set: the batch appender is compared against per-row col_rel_append_row on columns (including FLOAT -0.0), capacities and generation deltas. It takes at most one lease per storage transition, and readers get EBUSY while it is active.
  • The right-side filter and wl_columnar_filter_op, on all three fills: at most 16 mutation sets for 4096 rows, where per-row appends need at least 3945. Each checks the selected rows and timestamps and a clean destroy. These tests fail against main's filter.c.
  • Full meson test: 416 OK, 0 failed. The full suite under the CI sanitizer configuration (gcc, -Db_sanitize=address,undefined, debug): 414 OK, 0 failed. --suite tidy: 4/4.

Review

Ran through the implementation harness with an independent Architect, Critic and Reviewer.

  • The Reviewer blocked the first candidate on a test-only leak, which the ASan run also caught. It is fixed, and the Reviewer approved the second candidate.
  • The Architect and Critic both approved tree 7895ed30, which is this commit's tree.

Refs #2072

…age epoch

Since the #2037 mutation-lease series, col_rel_append_row acquires,
validates and finishes a full single-relation mutation set for every row,
and the filter operator appended its selected rows that way. Under the
main sanitizer job (gcc, ASan/UBSan, debug) on 2-vCPU hosted runners,
tdd_recursive and session then exceeded their 30 s and 180 s limits.
Pinned to two CPUs locally, tdd_recursive went from 8.8 s at a8b5b5d to
26.0 s on main, with user CPU tripling. perf put col_rel_mutation_set_acquire,
col_rel_mutation_lease_validate and their helpers above 40 % of the run,
reached through wl_columnar_filter_op and fill_filtered_rel. PR #2058,
merged in the same push, measured within noise of its parent.

Add a held-lease batch appender: col_rel_append_batch_begin acquires the
lease, col_rel_append_batch_row appends exactly as col_rel_append_row
would, and col_rel_append_batch_end finishes the lease. A lease may
publish one storage transition, so the appender starts a fresh lease only
before a row that would replace storage again. That condition is
col_rel_append_replaces_storage, which the append path now shares.
Generation publication, copy-on-write, admission and per-row denial
evidence stay in the append path, unchanged. The writer gates are held
across the fill, so readers get EBUSY until the batch ends.

Every fill in filter.c now appends through a batch: the timestamped
simple-predicate path, the untimestamped bulk copy, the compiled slow
path and both loops of fill_filtered_rel. Each exit ends the batch before
the output is destroyed or published, through one discard helper on the
error paths. Filter results, growth policy and error codes are unchanged.
An empty selection now takes one lease where it took none.

Measured locally, pinned to two CPUs, sanitizer debug build:

  test             a8b5b5d    main   this change
  tdd_recursive       8.8 s  26.0 s        10.9 s
  session            61.6 s 187.0 s       104.7 s

This restores the filter operator's append cost. session is still about
1.7x its pre-#2037 time, because col_rel_set per cell (join.c
col_join_append_pair, ops.c aggregation) and other per-row
col_rel_append_row callers still take a lease each.

Tests in relation_mutation_set:
- The batch appender against per-row col_rel_append_row: identical
  columns including FLOAT -0.0, capacities and generation deltas, at most
  one lease per storage transition, and reader exclusion while active.
- The right-side filter and wl_columnar_filter_op on all three fills:
  at most 16 mutation sets for 4096 rows where per-row appends need at
  least 3945, the selected rows and timestamps, and a clean destroy. These
  fail against the previous filter.c.

docs/THREADING.md audits the new test-only nonce peek (220 sites).
@justinjoy
justinjoy merged commit aa99a42 into main Oct 4, 2026
30 checks passed
@justinjoy
justinjoy deleted the claude/filter-append-lease-regression branch October 4, 2026 23:47
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