Description
Value::Text(Rc<str>) and Value::Blob(Rc<[u8]>)
(src/record/value.rs:15-17) make Value itself !Send. A result row
is a Vec<Value>, so no result can cross a thread boundary at all.
That blocks spec 013's embedding API outright. Its Req 4 asks for a
Send + Sync connection handle, and ADR-0034 proposes a worker thread
that "creates its Rc graph on a thread it owns and never lets it
leave" — but rows are precisely the thing that has to leave. Neither the
spec nor the ADR mentions this; both attribute the Send problem to the
pager alone.
What spike 014 (#682) measured
The change is far cheaper than the two ADRs that appear to forbid it
imply — and crucially, neither of them ruled on Value. ADR-0013
and ADR-0017 rejected Arc for the pager, on the grounds that one
Vm shares a page source across N cursors via cheap Rc clones, so
atomics would tax the Tier 0 read path. Value's payloads were never
the subject.
Measured on the spike branch:
- +22/-17 across 6 files. Nearly every construction site writes
Value::Text(s.to_string().into()), and .into() is identical for
Rc<str> and Arc<str>, so only the ~12 sites that name the type
needed editing (plus 4 orphaned imports).
- 1562 tests pass, 0 fail — identical to the
Rc baseline.
Value becomes Send + Sync, asserted at compile time.
- No measurable read-path cost.
full_drain/batch is
single-threaded with no boundary, so it isolates the tax: Rc runs
spanned 5.42-5.62 ms, Arc runs 5.24-5.33 ms. One comparison showed
Arc 5.6% faster, which is not credible — the honest reading is
that the difference is inside run-to-run variance.
- It removes the boundary copy entirely: handing rows over untouched
ran 5.13 ms against 7.34 ms for the owned-copy alternative, i.e.
~30% of the boundary cost disappears permanently.
Rc<dyn PageSource> and Rc<RefCell<Pager>> are untouched, so both
ADRs' actual subject matter is unaffected.
Scope
src/record/value.rs — Text(Arc<str>), Blob(Arc<[u8]>).
- The ~12 sites that name the type:
src/record/decode.rs,
src/vdbe/{cursor,hash_agg,result,sorter}.rs.
- A compile-time
Send + Sync assertion so the property cannot regress
silently.
- An ADR (see below).
Explicitly not in scope: any change to Rc<dyn PageSource> or
Rc<RefCell<Pager>>.
ADR required
Not a superseding ADR — a clarifying one. The natural reading of
ADR-0013 and ADR-0017 is that Arc anywhere was settled against, and
this ticket contradicts that reading while agreeing with what those
ADRs actually decided. That distinction needs writing down, with the
measurement, or the next person will either re-litigate it or assume it
was an oversight.
Complexity
Estimate: small
Reasoning: The change exists, compiles, and is measured on
spike/014_embedding_api. This re-lands it off main with the ADR and
the assertion a spike branch skipped. The care is in the ADR, not the
code.
Acceptance Criteria
Refs: 013/Req-4, #682, #678
Description
Value::Text(Rc<str>)andValue::Blob(Rc<[u8]>)(
src/record/value.rs:15-17) makeValueitself!Send. A result rowis a
Vec<Value>, so no result can cross a thread boundary at all.That blocks spec 013's embedding API outright. Its Req 4 asks for a
Send + Syncconnection handle, and ADR-0034 proposes a worker threadthat "creates its
Rcgraph on a thread it owns and never lets itleave" — but rows are precisely the thing that has to leave. Neither the
spec nor the ADR mentions this; both attribute the
Sendproblem to thepager alone.
What spike 014 (#682) measured
The change is far cheaper than the two ADRs that appear to forbid it
imply — and crucially, neither of them ruled on
Value. ADR-0013and ADR-0017 rejected
Arcfor the pager, on the grounds that oneVmshares a page source across N cursors via cheapRcclones, soatomics would tax the Tier 0 read path.
Value's payloads were neverthe subject.
Measured on the spike branch:
Value::Text(s.to_string().into()), and.into()is identical forRc<str>andArc<str>, so only the ~12 sites that name the typeneeded editing (plus 4 orphaned imports).
Rcbaseline.ValuebecomesSend + Sync, asserted at compile time.full_drain/batchissingle-threaded with no boundary, so it isolates the tax:
Rcrunsspanned 5.42-5.62 ms,
Arcruns 5.24-5.33 ms. One comparison showedArc5.6% faster, which is not credible — the honest reading isthat the difference is inside run-to-run variance.
ran 5.13 ms against 7.34 ms for the owned-copy alternative, i.e.
~30% of the boundary cost disappears permanently.
Rc<dyn PageSource>andRc<RefCell<Pager>>are untouched, so bothADRs' actual subject matter is unaffected.
Scope
src/record/value.rs—Text(Arc<str>),Blob(Arc<[u8]>).src/record/decode.rs,src/vdbe/{cursor,hash_agg,result,sorter}.rs.Send + Syncassertion so the property cannot regresssilently.
Explicitly not in scope: any change to
Rc<dyn PageSource>orRc<RefCell<Pager>>.ADR required
Not a superseding ADR — a clarifying one. The natural reading of
ADR-0013 and ADR-0017 is that
Arcanywhere was settled against, andthis ticket contradicts that reading while agreeing with what those
ADRs actually decided. That distinction needs writing down, with the
measurement, or the next person will either re-litigate it or assume it
was an oversight.
Complexity
Estimate: small
Reasoning: The change exists, compiles, and is measured on
spike/014_embedding_api. This re-lands it offmainwith the ADR andthe assertion a spike branch skipped. The care is in the ADR, not the
code.
Acceptance Criteria
ValueisSend + Sync, proven by a compile-time assertionRc<dyn PageSource>/Rc<RefCell<Pager>>unchangeddecide
make lint,cargo fmt --check,make check-mod-filescleanthe spike's noise floor is a few percent
Refs: 013/Req-4, #682, #678