Anser - runtime instrumentation - MVP with one bloomfilter - #1942
Anser - runtime instrumentation - MVP with one bloomfilter#1942leborchuk wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces the Anser runtime instrumentation subsystem for Cloudberry/Greenplum-style MPP execution, delivering an MVP runtime Bloom filter that is produced on segments, unioned on the coordinator, and consumed on segments to prune probe-side rows before hash joins—without changing the existing planning flow (post-plan tree injection).
Changes:
- Adds coordinator-resident Anser shared-memory channel map plus gather/send background workers, and a libpq-based segment→QD transport with token authentication.
- Implements plan-tree injection and executor support via CustomScan “Anser Bloom Producer/Consumer” nodes plus Bloom payload serialization/union helpers.
- Adds a comprehensive regression test module (
src/test/modules/anser) and wires it into Meson/Make and CI.
Reviewed changes
Copilot reviewed 44 out of 44 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/test/modules/meson.build | Adds anser test module to Meson build. |
| src/test/modules/Makefile | Adds anser test module to make-based test build. |
| src/test/modules/anser/test_anser.control | Defines test_anser extension for SQL-callable test helpers. |
| src/test/modules/anser/test_anser--1.0.sql | Registers SQL functions implemented by anser_test.c. |
| src/test/modules/anser/sql/test_anser.sql | Regression tests for channel lifecycle, payload correctness, libpq transport, and maintenance behavior. |
| src/test/modules/anser/sql/anser_runtime_filter.sql | Plan-shape and correctness regression for runtime filter injection and results stability. |
| src/test/modules/anser/meson.build | Builds/installs the test_anser shared module and regression schedule (Meson). |
| src/test/modules/anser/Makefile | Builds/installs the test_anser shared module and regression schedule (Make). |
| src/test/modules/anser/expected/test_anser.out | Expected output for test_anser regression. |
| src/test/modules/anser/expected/anser_runtime_filter.out | Expected output for runtime-filter plan/correctness regression. |
| src/test/modules/anser/anser_test.c | SQL-callable C helpers driving Anser APIs and libpq loopback tests. |
| src/include/utils/unsync_guc_name.h | Marks Anser GUCs as unsynchronized. |
| src/include/postmaster/postmaster.h | Increases auxiliary background worker count to accommodate Anser workers. |
| src/include/lib/bloomfilter.h | Adds bitset accessors and constructor-from-bitset API for deserialization. |
| src/include/executor/nodeAnserBloomFilter.h | Declares executor helper APIs for Bloom producer/consumer. |
| src/include/cdb/anserplan.h | Declares post-plan runtime-filter injection and CustomScan builders. |
| src/include/cdb/anserfilter.h | Declares Bloom part framing, serialization, deserialization, and fold API. |
| src/include/cdb/anserclient.h | Declares libpq client helpers for segment↔QD Anser transport. |
| src/include/cdb/anser.h | Introduces Anser shared-memory channel map API, GUCs, and service hooks. |
| src/include/catalog/pg_proc.dat | Adds built-in gp_anser_* functions for producer/publish/consume_wait transport. |
| src/include/catalog/catversion.h | Bumps catalog version for new built-ins. |
| src/backend/utils/misc/guc_gp.c | Adds Anser GUC definitions (enable/runtime_filter/limits/timeout/marker). |
| src/backend/utils/init/postinit.c | Registers CustomScan providers on backend init so dispatched plans resolve methods. |
| src/backend/storage/lmgr/lwlocknames.txt | Adds Anser LWLock names. |
| src/backend/storage/ipc/ipci.c | Accounts for and initializes Anser shared memory at postmaster start. |
| src/backend/postmaster/postmaster.c | Adds Anser gather/send background workers. |
| src/backend/postmaster/bgworker.c | Registers Anser worker entrypoints. |
| src/backend/optimizer/plan/planner.c | Calls AnserApplyRuntimeFilters() as a post-plan hook (ORCA + PG planner). |
| src/backend/libpq/auth.c | Adds gp_anser_conn startup marker parsing and token-based auth branch. |
| src/backend/lib/bloomfilter.c | Implements bitset accessors and bloom_create_from_bitset. |
| src/backend/executor/nodeAnserBloomFilterProduce.c | Implements executor helper for building/publishing per-producer Bloom parts. |
| src/backend/executor/nodeAnserBloomFilterConsume.c | Implements executor helper for consuming and reconstructing the merged Bloom filter. |
| src/backend/executor/Makefile | Links new executor helper objects. |
| src/backend/cdb/Makefile | Adds anser backend subdir to cdb build. |
| src/backend/cdb/anser/README.md | Documents architecture, transport/auth, GUCs, and channel state machine. |
| src/backend/cdb/anser/Makefile | Builds Anser subsystem objects. |
| src/backend/cdb/anser/anserservice.c | Implements coordinator-local gather/send worker main loops and error recovery. |
| src/backend/cdb/anser/anserplanexec.c | CustomScan execution providers for producer/consumer plan nodes + EXPLAIN stats. |
| src/backend/cdb/anser/anserplan.c | Plan-tree recognition and injection logic (hash join shape) + sizing and token registration. |
| src/backend/cdb/anser/anserfuncs.c | Implements built-in SQL functions backing the transport (producer/publish/consume_wait). |
| src/backend/cdb/anser/anserfilter.c | Implements Bloom payload framing, serialization/deserialization, and fold-in-place union. |
| src/backend/cdb/anser/anserclient.c | Implements libpq client transport used by segments to reach coordinator services. |
| .github/workflows/build-cloudberry.yml | Adds CI job to run src/test/modules/anser installcheck with gp_anser_enable=on. |
Suppressed comments (1)
src/test/modules/anser/anser_test.c:1076
- anser_test_dsm_free_on_cancel() disables the global Anser sweep (AnserSetSweepEnabled(false)) but never re-enables it in PG_FINALLY. Because sweep_enabled is global shared memory state, this can leak into later tests/sessions.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
what's difference with 6c41d27 impl? |
Runtime filter pushdown is intra-slice only. The RuntimeFilter executor node reaches the HashJoin's in-memory hash table via a plain executor pointer (node->hjstate->hj_HashTable, nodeRuntimeFilter.c:83-86); the Hash variant hands bloom scankeys to a registered SeqScan/DynamicSeqScanState in the same process. Nothing ever crosses a Motion or the network. Use the same hash function. Anser is cross-slice, cross-segment. Per-segment blooms are unioned on the coordinator into a global filter; any consumer anywhere can use it. That's the general MPP case from the paper — non-colocated joins, producer and consumer in different slices, even different joins sharing an equivalence-class condition_key. Could use different hash function. The built-in structurally cannot do any of that. But let's return to the example. For the proposed example you are right, existing approach is better. Explain with Explain with The interesting detail is that Another idea is why use separate step for filter out rows? We could push down all filters close to AM-level. And use it, for example in PAX or in the future iceberg approach. I want to implement it in the future but since we are talking about it here, add push-down to seq scan. The true meaning this PR is to add Anser, bloomfilters here just the tool for check how whole system works. I'm going to address all issues in other PR's, where I could just use working system. Here we have for about 8500 lines of the new code ... |
|
Pushed down filters to seq scan. Now execution plan looks like: I know right now it's better not use Anser, but it's the subject for future improvements. |
|
can we use |
Yes, thank you, really I could move it to the extension. The only tricky moment is with the authentication but I think I just could add hook. Converted PR to draft to rewrite code and tests. |
Two additive changes, both usable on their own, so that the Anser adaptive-information-sharing subsystem can live entirely in gpcontrib/anser instead of in the server. libpq/auth.c gains a pair of hooks for extensions that maintain their own internal connections: CustomAuthClaims_hook recognizes such a connection from a marker in its startup packet, and CustomAuthCheckPassword_hook validates the credential it sends as password. Both are consulted before pg_hba.conf, mirroring the existing PARALLEL RETRIEVE CURSOR path. The wire exchange stays in auth.c, so no static helper is exported. lib/bloomfilter.c gains accessors for the otherwise opaque filter -- bloom_bitset_bytes(), bloom_bitset_data() -- plus bloom_create_from_bitset(), which builds a filter and loads its bitset in one step, rejecting a wrongly-sized bitset instead of partially loading it. Together they let a caller outside the backend serialize and reconstruct a filter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Anser is a runtime pub/sub facility for MPP execution: producers on the segments publish a small piece of information about a running query (today a bloom filter over a join-build key), the coordinator unions the per-segment parts into one payload, and consumers on the segments receive it and prune work with it. State lives in a fixed coordinator-resident shared-memory channel map serviced by two background workers -- gather (drains producer submissions, unions parts, enforces the produce deadline) and send (delivers to waiting consumers, recycles channels). See gpcontrib/anser/README.md for the architecture, the state machine and the data flow. It is packaged as a shared_preload_libraries extension so that a kernel rebase does not have to carry it: everything is reached through an existing extensibility point -- shmem_request_hook and shmem_startup_hook for the shared state and its LWLock tranche, RegisterBackgroundWorker for the two services, planner_hook for the runtime-filter injection pass (which covers ORCA too, since ORCA is dispatched from inside standard_planner), RegisterCustomScanMethods for the injected nodes, DefineCustom*Variable for the anser.* GUCs, and the CustomAuth*_hook pair for segment -> coordinator connections. Segments cannot reach the coordinator's shared memory, so they open an ordinary libpq connection back to the QD and call anser.producer_begin / anser.publish / anser.consume_wait, authenticating with a per-session token instead of requiring pg_hba entries for segment hosts (the PARALLEL RETRIEVE CURSOR model; see src/anserauth.c). Every failure path is fail-open: a broken connection, an absent extension, an exhausted channel map or an expired produce deadline all degrade to unfiltered execution, never to a wrong result. Requires shared_preload_libraries='anser', anser.enable=on, and CREATE EXTENSION anser in each database that should use runtime filters -- the transport resolves its functions by name, so the catalog entries have to exist there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
anser_test is a second control file over the same library (its functions live in anser.so, so they act on the same shared state the services do) exposing the internal C API to the tests. It is test-only and superuser-gated; do not create it in production. Two regression files. anser_test covers the channel map and the network path end to end: producer/consumer accounting, the bloom part protocol and its in-place union, the produce timeout, the SQL functions driven through the live gather and send services, the libpq client helpers over loopback, session-token registration and rejection, multi-consumer partial delivery, and payload-DSM lifetime in the success, timeout and cancel cases. anser_runtime_filter covers the plan pass: the injected nodes appear (and disappear with the GUC off), and results are identical with the filter on and off under both optimizers, with pushdown, and for the datatype cases injection must refuse. The tests need the services live, which means postmaster-context settings, so installcheck first ensures shared_preload_libraries contains anser (appending, not overwriting) and anser.enable=on, then restarts the cluster. The CI matrix entry drives that through one target. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add control files to pom.xml since they do not contain APACHE header files
|
Could be reviewed, thank you. |
|
Alternative: reuse the dispatch connection instead of a backward libpq connection The QD↔QE dispatch connection is already bidirectional mid-execution. nextval() on a QE does exactly this: it sends a NOTIFY to the QD over its own frontend connection and waits for the reply on the same socket (src/backend/commands/sequence.c:2117-2176). The QD handles it inside the dispatcher's receive loop and writes the answer back with a custom message type (cdbdisp_async.c:1160-1195, send_sequence_response at :972). Bloom filter exchange is the same pattern with N producers and M consumers. Sketch:
What this removes: the segment→QD connection, the pg_hba bypass hooks, the token hash, QD shared memory and background workers, the three catalog functions and the catversion bump. Cancellation and error propagation come for free: any QE error makes the QD cancel all gangs, and the consumer's wait loop exits via CHECK_FOR_INTERRUPTS. That also resolves open question 3 about the consumer having no timeout. |
That's the MVP for the Anser https://vldb.org/pvldb/vol16/p3636-wu.pdf
Here we covered only scenario with adding bloomfilters to the query execution plan.
The main idea is as a follow
The overall execution plan should looks like (see
Custom Scannodes and their stat)The main architecture overview
See detailed description in [src/backend/cdb/anser/README.md]
Implementation consists of parts:
Right now working with bloomfilters is part of Anser, but maybe afterwards I will take it out to the new extension (depend of Anser). It's
Why MVP
For some queries using bloom filters leads to performance degradation. Usually it happens when there are no significant row dataset reduction after bloom filtering. We will address these issue in future researches/PRs.
Also I haven't checked all the cases where bloomfilters could be used.
So for now we support only one simple type of hashjoin, see details in
anser_hashjoin_keysandanser_resolve_build_scanfunctions.Open questions
How to enable && test it
Set
shared_preload_libraries = 'anser'anser.enable=onrestart cluster
test