Selective RLS measurements (10M profile)

Tested source commit: 455351ca049a6dac73852fd90f0fd0a97d6556e4. Installed library SHA-256: d33cf6a74a76c3c8a9d7e46cff251cb9b501dde953d6d5523e9e62ab469c399d. This was an optimized PostgreSQL 17.11 Linux aarch64 development build (pg17 development, without pg_test). Build and runtime facts are separately attested in runtime-attestation.json; the library hash does not identify its features by itself.

All four cases completed ten measured samples after three warmups. Exact-result, percentile, strategy and resource validation passed within the frozen budgets. The source and library stayed unchanged and the owned cluster stopped cleanly. These measurements cover this profile, not every query shape or a latency SLA.

Case Selected strategy p95 total (ms) Maximum policy-source rows
Identity one-hop, automatic Lazy 2792.5595 3
Same identity query, forced eager Eager 64947.0115 30000000
Sparse-policy whole-source scan Eager 28408.12585 300000
Identity one-hop, no RLS Lazy 321.46415 0

Automatic and forced eager identity queries produced the same one-row signature in every sample. Automatic execution performed no repeated GQL read recheck; forced eager and no-RLS execution rechecked one matched row, and the whole-source case rechecked ten. samples.csv and summary.csv retain every measured timing, signature, memory charge, work charge and recheck count.

The fixture contains ten million scalar nodes plus ten million composite nodes, with two N-1 relationship mappings: 20 million nodes and 19,999,998 relationships in total. The profile name is not the total graph node count. The CSR build took 374614.234252 ms and reported 1953.179370880127 MiB of projection memory.

The container had four CPUs and a 6 GiB memory limit inside a Docker Desktop VM configured with 12 GiB. The cgroup lifetime memory.peak reached exactly 6 GiB and memory.events.max increased from 0 to 31679, indicating pressure at the container limit. No OOM or OOM-kill event occurred. This run does not demonstrate spare container memory. A previous preparation attempt with the same container limit on an approximately 8 GiB outer VM was killed by a global VM OOM during build, before any query samples. It is retained as a separate preparation failure, not a latency observation.

Query workspace was 2048 MiB, the graph memory limit was 4096 MiB, the work limit was 100 million units, and query spill remained 4096 MiB. Only fixture building used a 16384 MiB spill allowance, reset before query execution. The effective settings are retained in work-governor.csv, build-spill-setting.csv and query-spill-setting.csv. The frozen query acceptance budgets were unchanged.

Reproduction

Use the tested commit, the attested development-only release build and a fresh disposable PostgreSQL 17 cluster with the recorded CPU/memory envelope. Configure fsync=on, autovacuum=off, TCP listening off, and the effective graph governors in runtime-attestation.json. Use an owned Unix socket and ordinary libpq connection settings. The maintained runner does not explicitly ANALYZE the fixture.

Copy graph/tests/heavy/rls_large_table_baseline.sh outside the clean checkout. Set only the copy’s SCRIPT_DIR to the absolute source graph/tests/heavy directory, so its source provenance still names the clean checkout. Around its single SELECT * FROM graph.build(); preparation statement, add:

SET graph.spill_disk_limit_mb = 16384;
SELECT * FROM graph.build();
RESET graph.spill_disk_limit_mb;

Record current_setting('graph.spill_disk_limit_mb') immediately before and after that build, as in the retained setting CSVs. No query SQL or query budget changes are needed. The original runner, adapted runner and adaptation diff hashes are recorded in validation.json; local copy/capture paths affect the adapted hashes.

NODE_COUNT=10000000 COMPOSITE_COUNT=10000000 SAMPLES=10 WARMUPS=3 QUERY_MEMORY_MB=2048 PERSIST_ON_BUILD=on STATEMENT_TIMEOUT_MS=6000000 RUN_PROFILE=p5_release SKIP_INSTALL=1 PG_VERSION_FEATURE=pg17 DBNAME=pggraph_fresh_measurement ROLE_NAME=pggraph_fresh_reader OUTPUT_DIR=/path/to/fresh-output PG_CONFIG=/path/to/pg_config bash /path/to/copied-runner.sh

Percentiles use PostgreSQL percentile_cont interpolation. Visibility source_rows counts returned policy rows, not all PostgreSQL rows examined or hydration work. Zero GQL rechecks does not imply zero hydration SQL. Governor memory is distinct from process RSS and cgroup lifetime memory. The 1M and 10M profiles used different source commits and resource envelopes, so their timings are not a controlled scaling comparison.

Measurement CSVs and plans are copied byte-for-byte. redactions.json records only local host/path removal from run metadata and the validator’s source path. selection-provenance.json identifies original file and validation hashes; selection itself is not validation. Relative log references in attempts.csv identify omitted original logs. Cluster files and local qualification wrappers are also omitted. SHA256SUMS covers all selected files except itself.