Repository navigation
benchmarks: the PostgreSQL-wire twin of the served SQL cells, with an answers-identical gate - #563
Merged
Merged
Conversation
… answers-identical gate DECISIONS #166/#171/#237(3): the served SQL cells (l1, l1tpc, l3s, l3d, l4) run a third time as a twin job whose /query SQL goes over ArcadeDB's postgres plugin through psycopg (never prepared, autocommit); writes, DDL, transactions and bulk loads stay on HTTP. Role stays `sensitivity` so landing and export keep the headline unchanged; `arcadedb_client_wire`=pgwire splits it from the lean twin in the freeze key and the exporter. The twin's server starts the plugin with explicit settings read back from the engine (the plugin list is matched as a tail: the server prepends AutoBackupSchedulerPlugin). pgwire_gate.py compares each res_<query>_digest with the headline's (the lane's own coercions) and keeps a mismatching row with `error` set. A resume no longer treats a twin as done because its headline is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… digest; the native-server smoke expects the third row l3s and l3d are checked against ground truth (recall_at_10), not by digest, so the gate compares the wire row's recall with the headline median within 0.01 and says so; l1 records neither and stays unchecked. smoke_native_server.sh now expects l1 to produce a headline, a lean and a wire row and checks the wire row's port, call count, gate and read-back postgres port. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The twin re-runs the whole cell (load, index build, queries) for a sensitivity row, so at the large tier (deep10m, tpch10, sf1full) it would add hours to a pass. build_jobs takes the invocation's scale and builds no wire twin when it is in LARGE_SCALES, which moves from make_2611_stages.py to runner.py (the generator reads it from there: one definition). The lean twin is unchanged at every scale. DECISIONS #166's "tier 1 only" is the ENGINE tier, which the twin already satisfies (ArcadeDB arms only); the size cut is an added cost decision, stated in PROTOCOL.md and CAMPAIGN.md row 6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tae898
force-pushed
the
bench/fourarm-pgwire
branch
from
November 3, 2026 16:17
793d710 to
952ef54
Compare
tae898
marked this pull request as ready for review
November 3, 2026 16:21
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #554 (
bench/arcadedb-import-26111), draft, never for main. Implements the PostgreSQL-wire twin of ArcadeDB's served SQL arms that DECISIONS #166, #171 and #237 (3) call for: the same served SQL cell runs a third time with its read statements on ArcadeDB's postgres plugin through psycopg, as a labelled sensitivity row beside the headline (officialarcadedb-driver0.2.0) and the lean HTTP twin. It is never a headline: the wire flatters ArcadeDB most (#166 addendum 2).What runs on the wire. A twin job per served SQL cell, built by
runner.build_jobslike the lean twin (runner.PGWIRE_SQL_ARMS, run_id ending_clientpgwire,BENCH_ARCADEDB_HTTP_CLIENT=pgwire).lean_http.PgSessionsendsPOST /querySQL over psycopg 3 (autocommit,prepare_threshold=None, so every execution is an unnamed Parse/Bind/Execute and the prepared-statement path of upstream ArcadeData#9009 is not used;:nameplaceholders become bound%(name)sparameters). Everything else stays on lean HTTP and is counted by reason:/command(writes, DDL,sqlscripttransactions),/batchbulk loads (#171 (c)), the server probe, Cypher, session ids, dict parameters. The twin therefore times the read and query phases only; its write-phase numbers are lean HTTP and are not to be quoted (arcadedb_pgwire_scopeon every wire row, PROTOCOL.md and CAMPAIGN.md row 6 say so).Cells covered (lane x arm, every workload the arm runs), at the small size tier only: the twin re-runs the whole cell including load and index build, so no wire twin is built at
runner.LARGE_SCALES(deep10m, tpch10, sf1full), the generator's own large tier (build_jobs(..., scale=)); the lean twin is unchanged at every scale. DECISIONS #166's "tier 1 only" is the engine tier, which the twin already meets (ArcadeDB arms only); the size cut is an added cost decision. In the paper-2611 four-arm roster that leaves l1tpc tpch1 and l3d small (stages qV03, qV04 and their native twins); l1, l3s and l4 are not in that roster.arcadedb_serverarcadedb_serverarcadedb_sparse_server,arcadedb_sparse_server_fp32arcadedb_dense_server,arcadedb_dense_server_int8arcadedb_ts_doc_server,arcadedb_ts_native_serverNot covered, on purpose: l2 (Cypher stays on HTTP, #162), e2 (SQL and Cypher in one cell, plus session transactions), lifecycle and restart (their timed quantity is open, close and restart), e4 (it measures the client axis itself), and the JVM-defaults arm. Both chains are covered: the JVM image and the native image take the same settings.
Plugin settings. Only the twin's server gets them, as
-Dtokens (JAVA_OPTS on the JVM image, command line on the native one):arcadedb.server.plugins=Postgres:com.arcadedb.postgres.PostgresProtocolPlugin(the plugin is opt-in), and the engine's own defaults written out so they are recorded:arcadedb.postgres.host=0.0.0.0,port=5432,ssl=DISABLED,debug=false,quotedIdentifiers=true,queryMaxRows=1000000. They are read back fromGET /server?mode=defaultintoserver_settingslike the other server settings, and the cell is refused if one is unlisted or differs (the server prependsAutoBackupSchedulerPlugin,to the plugin list at start, so that one setting matches as a tail). The cell is also refused if port 5432 does not accept a connection. The client recordsarcadedb_pgwire_client(psycopg version, no prepare, autocommit, text results),arcadedb_pgwire_callsandarcadedb_pgwire_fallback_reasonsfrom the lane's log.Answers-identical gate (
pgwire_gate.py). The lanes already record oneres_<query>_digestper deterministic query, outside the timers, under the lane's own declared coercions; the twin runs the same lane code, so its digests carry the same coercions. The gate compares them with the headline's for the same cell: equal passes; different, or a query the headline recorded and the wire row did not, fails the row; a headline that disagrees with itself leaves that query unchecked; no headline digests leaves the rowunchecked, never passed. A failed row is kept witherrorset and the reason recorded (never dropped). The runner stamps it as the twin cell ends against the headline rows in the results file, andpython3 pgwire_gate.py --rows results/runs.jsonlre-checks a whole file and exits 1 on any failure. Honest limits: l1tpc and l4 record digests; l3s and l3d record none and are checked byrecall_at_10, so their wire rows are compared within 0.01 of the headline median (stamped "not by digest"); l1 records neither, so its wire row staysuncheckeduntil that lane records digests (follow-up, not in this PR).Landing and export. The role stays
sensitivity, so every existing filter keeps the wire rows out of every headline.arcadedb_client_wire=pgwiresplits the two twins: the freeze key inmake_paper_tables(otherwise the lean and wire twins of one cell would replace each other onts_utc), the exporter (wire rows never enter the lean twin'sclient_twincolumns), and one generated sentence on tables whose served SQL cells ran on the wire. Also fixed on the way:--skip-done-sincekeyed a cell by (backend, rep), so a twin counted as done when its headline was; the key now includes the client.Tests (
test_pgwire_twin.py, 34). The digest gate (equal, unequal, missing, unchecked, self-disagreeing headline, declared absence, headline-only reference, recall lanes, the lane coercions applied to Decimal and datetime wire rows, the file gate's exit status); the role and wire stamp; the stage generator andbuild_jobsemitting the twin for exactly the served SQL cells and none for Cypher, e2, lifecycle, restart or e4; the session routing with a fake psycopg; the settings patch and read-back; the log parse; the freeze key and exporter split. Run against the base versions oflean_http.py,runner.py,make_paper_tables.pyandexport_web.py(new module and tests kept), 18 of 30 fail; the gate tests (9) cannot run withoutpgwire_gate.py, and two (no twin for Cypher and mixed cells, the lean twin set unchanged) are regression guards that hold on both.test_instrument_method.pynow selects the lean twins by client, because there are two kinds. The scale cut has its own tests (no wire twin at each large-tier scale, the lean twin kept, the generator roster wired at tpch1 and small only); 6 of the tests fail before that change. The fullbenchmarks/experimentssuite undertaskset -c 8-15on the rebased head: 1283 passed, 27 skipped.Smoke for the parent to queue (not run here; laptop cores are queued work). On a measurement lane, at this PR's head, once the engine image and wheel for the chain are set (the existing native-server smoke now expects the third row for l1):
Expected: the last line
SMOKE PASS native server armand exit 0, with, forl1/arcadedb_server, three rows (oneheadline, twosensitivity, one of themarcadedb_client_wire=pgwire), the wire row witharcadedb_pgwire_port_opentrue,arcadedb_pgwire_callsabove 0,server_settings["arcadedb.postgres.port"]=5432, and a gate status ofunchecked(l1 records no digest). For a digest-checked wire row, run l1tpc at its smallest staged scale with the same environment the script exports:Expected: three cell lines ending
-> ok(l1tpc_arcadedb_server_olap_tpch1_r1, the same with_clientleanand_clientpgwire), thenok l1tpc_arcadedb_server_olap_clientpgwire_tpch1_r1: N checkedwith N at least 5 and1 pg-wire twin rows, 0 failed, exit 0. If the wire returns a different answer for a query, that row is kept witherrorstartingpg-wire answers differ from the headline:and the command exits 1.Not verified here: nothing in this PR has run against a live server. Two things only a live cell can answer: whether the plugin accepts psycopg's bound parameters and list parameters (
vectorNeighbors(:idx, :q, ...), upstream ArcadeData#9007/ArcadeData#9008 are the known list-typing issues) and whether the settings read back as written; both surface as a failed twin row with a reason, not as a silent number.🤖 Generated with Claude Code