Catalog B PR 2: SQL aggregation and native split/merge
This branch is based on merged PR #1417 at e05ed7f22. It extracts T07 and T08
from the combined Catalog B implementation. Expressions, field WHERE, HAVING,
SELECT DISTINCT, computed grouping, selectable time buckets and general sorting
remain in PRs 3 and 4. Legacy scalar COUNT(DISTINCT) keeps the merged PR 1 behavior;
extended exact count-distinct composition is not part of this PR.
Execution and compatibility
parameterized QL -> cached schema-resolved metadata -> fresh binding
-> authorization/admission/revision/lifecycle gates
-> eligible: sequential existing native aggregates -> keyed batch merge
otherwise: complete deduplicated native scan -> local hash aggregation
-> output aliases -> final LIMIT/OFFSET -> existing measure_result
Extended queries support named scalar grouping and multiple COUNT/SUM/MIN/MAX/ AVG/MEAN calls. Null keys differ from empty strings and zero. Counts are exact int64; integer SUM overflow fails. AVG uses sum and non-null count rather than the legacy native MEAN behavior. Repeated calls share work. Scalar empty input returns zero COUNT and null other aggregates; grouped empty input returns no rows.
Native eligibility is conservative: one measure group, a provable stage, one exact time interval and complete non-null entity identity. Unsafe integer sums, floating extrema, non-entity keys, residual time predicates and uncertain stage selection retain the complete local path. Native branches run sequentially and share the parent allowance; each source closes before the next opens. Partial failures do not return partial SQL groups. Local key/state growth reserves memory before allocation, and cleanup releases owners and reservations.
No protobuf definitions, RPCs, native operator implementations, persisted formats or native frame versions change. Memory protection remains active. Disk spilling and retained-result pagination remain deferred.
The generated QL-only contracts derive expected values from independent fixture arithmetic. Existing raw-query cases remain separate compatibility controls. T07 covers local grouping, null/empty semantics, shared calls and pagination; T08 compares cached, bypassed and forced-local execution, including branch reuse, lifecycle changes, cancellation, authorization/revision failures and recovery.
The extraction retains the main-branch compatibility fixes and the CI regression
fixes from PR 1. Shared benchmark fixture dispatch now lives in the test entrypoint,
so ordinary Go package compilation and vulnerability analysis do not depend on
helpers present only in _test.go files.
Isolated correctness checks (2026-10-21)
- Planner, QL operators, liaison, fixture generator and measure-data unit suites pass.
- The planner/operator/liaison/generator suites also pass with
-race -tags=querymetrics. - The unchanged deterministic generation check passes, preserving PR 1’s scalar count pagination and empty-entity regression cases alongside the new scenarios.
- Both independent measure/QL selections pass 530/530 cases in four-CPU, 8-GiB Docker containers: standalone in 73.315 s and distributed in 92.942 s. These include 79 new T07/T08 specs and the 451 inherited compatibility specs.
- Full
make lintpasses. All 78 relative documentation links/anchors resolve.
Reports are /mnt/d/qlb-pr2-standalone-report.json and
/mnt/d/qlb-pr2-distributed-report.json. Unit, race, generator and lint logs use
the /mnt/d/qlb-pr2- prefix. Other integration blocks were not selected.
Independent toolchain prerequisite
The Go 1.26.10 upgrade is isolated in PR #1425,
based directly on main. This aggregation branch changes neither go.mod nor
dependency versions. Merge the toolchain PR and rebase this branch to pick up the
security fix; it is no longer part of the aggregation diff.
On 2026-10-21, make vuln-check with inherited Go 1.26.9 reports reachable
GO-2026-6809 in crypto/x509. The
pre-aggregation PR 1 tree reports the same failure. Logs are
/mnt/d/qlb-pr2-vuln.log and /mnt/d/qlb-pr2-main-vuln-control.log.
This inherited-toolchain scan is not counted as passed.
Before extraction, the same aggregation implementation was separately verified
with Go 1.26.10: unit/race suites, 530 selected measure/QL cases per topology,
full lint, make check and make vuln-check passed. Those results remain
separate evidence under /mnt/d/qlb-pr2-go12610-; they do not imply that the
current Go 1.26.9 default passes the vulnerability scan. The standalone toolchain
PR also passes its own vulnerability scan, focused version/TLS/gRPC/pipeline
tests and make check on its main-based tree.
Aggregation benchmarks retain the frozen Go 1.26.9 protocol and are not relabeled as patched-toolchain results. After extraction, all 2,405 source and contract files match the measured source manifest again.
Evidence boundaries
T07 verification and T08 verification preserve historical measurements at their original revisions. The current branch includes #1413’s native aggregation optimization and PR 1’s ownership/accounting repairs; historical figures are not treated as measurements of this isolated head.
Fresh measurements use only the existing T07/T08 workloads, in four-CPU, 8-GiB Docker containers with no extra swap and Go 1.26.9. T07 compares dense local aggregation with its independent scalar reference. T08 compares the same SQL through native and forced-local execution, with existing individual-native RPC bundles as additional controls. Kernel and RPC observations remain separate; neither eligibility nor a specialized scalar comparison guarantees a speedup.
Fresh kernel measurements (2026-10-21)
All 40 existing variants completed five one-second samples each (200 observations),
with independent value/null checks and no cgroup OOM or memory-max events. The
following values are medians of five samples; the complete matrix is retained in
/mnt/d/qlb-pr2-bench/kernel-summary.json.
T07 runs 65,537 input rows in the first three rows below; the million-group rows use one million inputs. The scalar reference specializes Go map keys and omits the public operator lifecycle, typed key codec and tracked allocation checks.
| Groups | Shape | Dense ms | Scalar ms | Dense B/op | Scalar B/op |
|---|---|---|---|---|---|
| 1 | sum-count-avg | 6.925 | 1.128 | 2,872 | 384 |
| 1,024 | sum-count-avg | 7.945 | 2.243 | 138,296 | 321,784 |
| 65,537 | sum-count-avg | 42.125 | 31.060 | 11,574,845 | 20,981,057 |
| 1,000,000 | avg | 545.762 | 629.366 | 99,909,512 | 300,868,672 |
| 1,000,000 | sum-count-avg | 618.048 | 669.411 | 99,926,680 | 333,016,168 |
The general operator is slower at low group counts; high-cardinality allocation volume is lower. Both million-group shapes use two state lanes: 16,000,000 used state bytes, 16,777,216 state-capacity bytes and 41,165,888 key-capacity bytes. They fit the 128-MiB operator tracker, excluding caller-owned fixtures and the untimed scalar oracle. This is not a million-row RPC acceptance result.
T08 compares the complete native-group merge with its specialized scalar map. The table shows aligned branch rows; the permuted variants also pass and have similar measured cost. Both orders visit 2N input groups and 3N merge entries.
| Finalized groups | Batch merge | Scalar reference | Batch B/op |
|---|---|---|---|
| 1 | 6.147 µs | 0.130 µs | 4,096 |
| 1,024 | 725.915 µs | 248.269 µs | 160,472 |
| 65,537 | 59.607 ms | 27.840 ms | 12,632,837 |
This kernel comparison establishes no CPU speedup. Native pushdown can reduce RPC raw-row transport despite the more expensive general merge; that question is measured separately with the same SQL in native and forced-local modes.
Fresh local-aggregation RPC measurements
T07 completed five repetitions at each cardinality: 40 variant measurements and 2,400 timed queries. Each cardinality runs in a fresh process, with one RPC worker, 100 timed/5 warmup queries at 1,024 rows and 20 timed/3 warmup queries at 32,768 rows. Setup, visibility and full scalar checks are untimed. All values, structural counters and post-close ownership/reservation checks pass.
The table reports medians of the five repetition summaries. Allocated MB uses decimal bytes. CPU and allocated bytes cover the whole benchmark process, including client, liaison, data nodes and background work. Twenty large-query samples per repetition do not establish a precise tail-latency guarantee.
| Input rows | Shape | p50/p95 ms | Allocated MB/query | Process CPU ms/query |
|---|---|---|---|---|
| 1,024 | AVG, one group | 3.967 / 5.966 | 1.253 | 10.0 |
| 1,024 | COUNT/SUM/MIN/MAX, 1,024 groups | 10.980 / 16.616 | 4.399 | 22.4 |
| 1,024 | SUM/COUNT/AVG, N groups | 4.204 / 6.140 | 1.418 | 10.0 |
| 1,024 | Scalar SUM | 3.664 / 5.545 | 1.148 | 9.3 |
| 32,768 | AVG, one group | 40.647 / 44.270 | 30.395 | 79.5 |
| 32,768 | COUNT/SUM/MIN/MAX, 1,024 groups | 51.895 / 59.484 | 38.333 | 106.5 |
| 32,768 | SUM/COUNT/AVG, N groups | 46.745 / 53.571 | 34.350 | 86.0 |
| 32,768 | Scalar SUM | 37.153 / 39.518 | 27.066 | 75.0 |
This characterizes newly supported SQL without claiming a latency improvement
over main, which cannot execute those extended aggregate shapes. The full
repetition summary is /mnt/d/qlb-pr2-bench/t07-rpc-summary.json.
Fresh native-versus-local RPC comparison
T08 completed five repetitions at each cardinality: 110 variant measurements and 6,600 timed logical iterations. It uses the same schedules as T07 above. The individual-native controls time complete two/three-request bundles; their latency and CPU are not costs of a single native request.
| Raw rows | Shape | Pushdown p50 ms | Local p50 ms | p50 ratio | Allocation ratio | CPU ratio |
|---|---|---|---|---|---|---|
| 1,024 | Float AVG | 4.803 | 4.107 | 1.169 | 1.006 | 1.327 |
| 1,024 | Shared float SUM/COUNT/AVG | 5.011 | 3.999 | 1.253 | 0.969 | 1.474 |
| 1,024 | Integer MIN/MAX/COUNT | 7.284 | 4.255 | 1.712 | 1.453 | 2.061 |
| 1,024 | Integer AVG fallback control | 4.333 | 4.122 | 1.051 | 1.013 | 1.031 |
| 32,768 | Float AVG | 23.927 | 43.968 | 0.544 | 0.327 | 0.822 |
| 32,768 | Shared float SUM/COUNT/AVG | 24.643 | 45.082 | 0.547 | 0.326 | 0.764 |
| 32,768 | Integer MIN/MAX/COUNT | 38.779 | 43.472 | 0.892 | 0.509 | 1.229 |
| 32,768 | Integer AVG fallback control | 43.307 | 41.443 | 1.045 | 1.013 | 1.060 |
Ratios divide the five-run pushdown median by the corresponding local median. At 32,768 rows, float AVG/shared calls reduce observed p50 by about 45% and allocated bytes by about 67%. Integer extrema improve p50 by about 11% and allocation by about 49%, but increase process CPU by about 23%. All three native shapes are slower at 1,024 rows; the integer case also allocates more and uses more CPU. Eligibility is not a cost model or a universal speedup guarantee.
For the large float AVG/shared/integer-extrema cases, the five paired p50 ratios range from 0.442–0.594, 0.462–0.673 and 0.833–0.946 respectively. The fallback control executes the same local path in both modes and retains run variation; its differences are not an optimization effect. These are observations with small large-query sample counts, not a newly inferred statistical gate pass.
Combined benchmark provenance and reproduction
The two RPC protocols completed 9,000 timed logical iterations across 150 variant measurements and 20 fresh cardinality processes, with 300 nonempty CPU/heap profiles. This comprises 7,200 QL calls and 4,200 native Query calls (11,400 actual timed transport calls), because individual-native controls issue bundles. All independent result, structural-counter and post-close cleanup checks pass.
Nine captured RPC-container memory-event records and the kernel-container record
have zero OOM/max events. T07 repetition 1’s event record was not captured and is
unavailable, rather than counted as zero. Two untimed report merges were recovered
from retained shards after correcting the external wrapper; measured queries
were not rerun. The retained immutable wrapper uses env -u DQB_MODE for merge
mode and captures later memory-event records on exit.
Timing froze source at 199d95522. The source check confirms 2,405 tracked and
generated source/contract files match the manifests, including 1,141 production
files; both measured executable hashes match. Documentation updates after timing
do not change those sources. All artifacts are under /mnt/d/qlb-pr2-bench/,
including rpc-summary.json, t08-comparison-summary.json, kernel-summary.json,
latency samples, profiles, binary-sha256.txt, source manifests and exact runners.
Reproduce the kernels with BenchmarkAggregate(Kernel|MillionGroups) and
BenchmarkNativeAggregateMerge in pkg/query/ql, with -tags=querymetrics,
-benchmem -benchtime=1s -count=5 -run '^$'. The querybench qlb_t07 and qlb_t08
scenarios retain the protocols in their ticket records: cardinalities 1,024 and
32,768, one worker, the timed/warmup schedules above, and profiling enabled.
The existing Docker runner and its README describe the required environment.
This refresh adds no earlier-ticket matrix or new workload.
Process-wide peak reservations are not isolated native residency or a per-query lease size. Physical storage reads, native retained-frame bytes and total parent lease charges remain unavailable. No large-RPC success or historical T02 acceptance is inferred from the operator-only million-group measurements.