Skip to content

docs: how to parallelise a narrow sap_read_table extract, and what it is worth - #132

Closed
jrosskopf wants to merge 1 commit into
masterfrom
claude/document-narrow-table-partitioning
Closed

docs: how to parallelise a narrow sap_read_table extract, and what it is worth#132
jrosskopf wants to merge 1 commit into
masterfrom
claude/document-narrow-table-partitioning

Conversation

@jrosskopf

@jrosskopf jrosskopf commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Closes plan step 3.2 (RFC row-range partitioning) — evaluated, decided, documented.

The gate could not be satisfied, so I measured the proxy

The plan conditioned the refactor on "run Phase 0's scaling curve on production-class
hardware first". That hardware is not available. What could be measured is whether
splitting a narrow scan into row ranges helps at all.

DD02L, 164,664 rows, single column, three alternating runs each. Identical checksums
in both arms (2,719,013), so the comparison is valid:

arm median wall
one scan 8.25s
four range-partitioned scans 6.63s

1.24x, not 4x.

Confounded in both directions — the split arm pays four connection setups and makes
SAP evaluate four range filters, while a real in-scanner implementation would share
one setup and use ROWSKIPS. So this is suggestive, not decisive. It was also taken
on a trial under load average ~21.

Why I did not do the refactor

column_state_machines lives in the bind data, shared across the whole scan.
Row-range partitioning needs them per worker, which means moving them into a local
state and reworking Step(), the batch budget, the persistent-connection cache,
progress reporting, ActivateColumns(), the lock-step invariant
AreActiveStateMachineCaridnalitiesEqual(), HasMoreResults(), and the
residual-filter application.

That is a re-architecture of the scan path in which this same session found two
silent wrong-results bugs
— unapplied predicates, and truncation on a fully-filtered
batch. Against a measured 1.24x that cannot be soak-tested on a shared trial, that
trade does not look good.

The structural gap is real either way: a single-column extract gets no parallelism
at all. It is the payoff that is unproven, not the gap. A scaling curve on a
production-class system showing the knee well above 3 would reverse this.

What this PR delivers instead

The plan's own "either way" item — the recipe that works today with no code:

  • Why threads does nothing for a narrow extract (parallelism is per column;
    one column means one call and nothing to spread). Nothing said so, so the natural
    reaction to a slow narrow extract was to raise threads and watch it change nothing.
  • The range-partition recipe, now that fix(rfc): apply every predicate, and push far more of them to SAP #128 pushes range predicates to SAP so each
    branch reads only its slice rather than filtering locally.
  • The measured 1.2x, stated plainly so nobody expects 4x, plus the note that many
    small branches make it worse.
  • The two things that matter more than branch count: even boundaries (one long branch
    sets the wall time) and an indexed split key (otherwise you multiply the server's
    work rather than divide it).
  • That separate processes scale considerably better than either approach, since
    SAP-side concurrency limits apply per client program — with the caveat to ask Basis
    before pointing many parallel readers at production.

Docs only; no code changes.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

… is worth

sap_read_table parallelises per COLUMN -- one concurrent RFC_READ_TABLE call per
projected column. That is a good fit for a wide extract and no help at all for a
narrow one: a single-column scan issues one call and `threads` has nothing to spread.
Nothing said so, so the natural reaction to a slow narrow extract was to raise
`threads` and watch it change nothing.

Documents the range-partition recipe that works today with no code, now that #128
pushes range predicates to SAP so each branch reads only its slice.

States what it is actually worth rather than implying the branch count: measured on a
164,664-row single-column extract, four branches ran in 6.6s against 8.3s for the
single scan -- about 1.2x, not 4x. Each branch pays its own connection and metadata
round-trip and SAP evaluates each range separately, so splitting into many small
branches makes it worse. Even boundaries and an indexed split key matter more than how
many branches there are.

Also notes that separate processes scale considerably better than either approach,
since SAP-side concurrency limits apply per client program -- with the caveat to ask
Basis before pointing many parallel readers at production.
@jrosskopf

Copy link
Copy Markdown
Collaborator Author

Superseded by #133, which implements partitions rather than documenting the workaround.

The measurement here — 1.24x from a UNION ALL of range-filtered branches — turned out to understate what an in-scanner implementation achieves. #133 measures 2.7x at eight workers on the same extract, because it shares one connection setup and pages with ROWSKIPS instead of making SAP evaluate a range filter per branch. The proxy could not have told me that, which is the argument for having built it.

The documentation lands in #133 in updated form: it now points at partitions as the answer for a narrow extract, keeps the explanation of why threads does nothing there, and states the real curve and its knee.

@jrosskopf jrosskopf closed this Aug 31, 2026
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