Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
7cb3a19
Anser skeleton - header file and size calculation
leborchuk Aug 14, 2026
46ae325
Fix build
leborchuk Aug 14, 2026
a705311
Fix build - use blobs, not hash function
leborchuk Aug 14, 2026
ef9e34b
Fix compile issue in a debug mode
Aug 17, 2026
5855ca4
Add working with slots
leborchuk Aug 17, 2026
5b232a5
Add anserservice.c
leborchuk Aug 17, 2026
05a6afc
Add GUC to unsync section
leborchuk Aug 17, 2026
b4abc43
Add regress tests to check anser functionality
leborchuk Aug 17, 2026
4314851
Fix compile issues in tests
leborchuk Aug 17, 2026
7ef43cd
Potential fix for pull request finding
leborchuk Aug 17, 2026
b126e01
Fix issues found by Copilot
leborchuk Aug 17, 2026
a0adaa6
Fix pg_regress output
Aug 17, 2026
ae74392
Switch from arena to DSM
leborchuk Aug 17, 2026
aeeedc3
Fix regression in tests
leborchuk Aug 17, 2026
d901eff
Add bloom filters union
leborchuk Aug 17, 2026
de0d71f
Fix build
leborchuk Aug 17, 2026
693d17f
Add Bloom Filter Producer/Consumer executor nodes
leborchuk Aug 18, 2026
fe06fcd
Add GUC from anser runtime filters
leborchuk Aug 18, 2026
da4bdfa
Add regression tests for bloom filters
leborchuk Aug 18, 2026
a25fd7a
Fix tests - use Datum, not int
leborchuk Aug 18, 2026
ee65d69
Fix expected output
Aug 18, 2026
c0dbce4
Add network interaction to anser
leborchuk Aug 19, 2026
12d3a5c
Fix anser service compile
leborchuk Aug 19, 2026
22fe395
Place GUC to unsync section
leborchuk Aug 19, 2026
3af0eb3
Add anser functions to bgworker.c
leborchuk Aug 19, 2026
713b30b
Fix test with sweeping cancelled channels
leborchuk Aug 19, 2026
825c807
Fix regress output
Aug 19, 2026
71c9181
Fix issue with old data in memory structures
leborchuk Aug 19, 2026
d96b5ed
Adjust trailing spaces
Aug 19, 2026
adf69a7
Add planner hook for anser
leborchuk Aug 20, 2026
e57114f
Add custom scan node
leborchuk Aug 20, 2026
8cafc6f
Add usage anser in PlanTree
leborchuk Aug 20, 2026
1148c7d
Fix issue with bogus varno: -2 = OUTER_VAR
leborchuk Aug 20, 2026
2ba3e85
Make expected output the same as result
Aug 20, 2026
b652637
Fix hang in producer/consumer getting data
leborchuk Aug 20, 2026
4856215
Fix NULL filter
leborchuk Aug 20, 2026
e1a644b
Fix range overflow in blommfilters
leborchuk Aug 20, 2026
7ba8b70
Switch to folding data on a master, not on segments
leborchuk Aug 21, 2026
bf92d23
Do not transfer CANCELLED state in PENDING
leborchuk Aug 21, 2026
3227f37
Refactor code and move hard parts to separate functions
leborchuk Aug 21, 2026
47ebc97
Switch to use the same DSM area for consumers
leborchuk Aug 27, 2026
41ddeb7
Remove our own create_bloom - reuse existing code
leborchuk Aug 28, 2026
aaf5beb
Remove unused functions about internal details
leborchuk Aug 28, 2026
af9d1ce
Do not add set method
leborchuk Aug 28, 2026
4642ab5
Add backward authentication
leborchuk Aug 28, 2026
ebdf608
Use ExplainPropertyText
leborchuk Aug 30, 2026
67baa97
Fix regress tests
leborchuk Aug 30, 2026
e3a8448
Add walk description
leborchuk Aug 31, 2026
5943d4d
Polish Anser comments for review
leborchuk Aug 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contrib/pax_storage/src/cpp/contrib/googlebench
Submodule googlebench updated 135 files
2 changes: 1 addition & 1 deletion contrib/pax_storage/src/cpp/contrib/googletest
Submodule googletest updated 61 files
+13 −27 BUILD.bazel
+2 −2 CMakeLists.txt
+21 −28 MODULE.bazel
+4 −7 README.md
+7 −36 WORKSPACE
+29 −47 ci/linux-presubmit.sh
+4 −7 ci/macos-presubmit.sh
+13 −25 ci/windows-presubmit.bat
+50 −70 docs/advanced.md
+13 −0 docs/faq.md
+20 −53 docs/gmock_cook_book.md
+2 −2 docs/primer.md
+30 −23 docs/quickstart-bazel.md
+1 −2 docs/reference/actions.md
+2 −9 docs/reference/assertions.md
+4 −7 docs/reference/matchers.md
+1 −98 docs/reference/testing.md
+4 −32 fake_fuchsia_sdk.bzl
+20 −80 googlemock/include/gmock/gmock-actions.h
+76 −321 googlemock/include/gmock/gmock-matchers.h
+8 −8 googlemock/include/gmock/gmock-more-actions.h
+6 −4 googlemock/include/gmock/gmock-spec-builders.h
+5 −0 googlemock/include/gmock/internal/gmock-internal-utils.h
+0 −1 googlemock/include/gmock/internal/gmock-port.h
+4 −4 googlemock/src/gmock-cardinalities.cc
+7 −55 googlemock/test/gmock-actions_test.cc
+2 −2 googlemock/test/gmock-function-mocker_test.cc
+27 −210 googlemock/test/gmock-matchers-arithmetic_test.cc
+5 −62 googlemock/test/gmock-matchers-comparisons_test.cc
+19 −317 googlemock/test/gmock-matchers-containers_test.cc
+23 −82 googlemock/test/gmock-matchers-misc_test.cc
+10 −39 googlemock/test/gmock-more-actions_test.cc
+1 −1 googlemock/test/gmock-pp_test.cc
+4 −3 googlemock/test/gmock-spec-builders_test.cc
+1 −1 googlemock/test/gmock_link_test.h
+1 −1 googletest/README.md
+1 −1 googletest/cmake/internal_utils.cmake
+0 −7 googletest/include/gtest/gtest-assertion-result.h
+3 −3 googletest/include/gtest/gtest-matchers.h
+39 −95 googletest/include/gtest/gtest-param-test.h
+0 −39 googletest/include/gtest/gtest-printers.h
+65 −61 googletest/include/gtest/gtest-typed-test.h
+2 −2 googletest/include/gtest/gtest.h
+23 −27 googletest/include/gtest/internal/gtest-internal.h
+14 −48 googletest/include/gtest/internal/gtest-param-util.h
+62 −8 googletest/include/gtest/internal/gtest-port.h
+0 −4 googletest/src/gtest-internal-inl.h
+34 −157 googletest/src/gtest.cc
+17 −51 googletest/test/BUILD.bazel
+0 −38 googletest/test/googletest-fail-if-no-test-linked-test-with-disabled-test_.cc
+0 −38 googletest/test/googletest-fail-if-no-test-linked-test-with-enabled-test_.cc
+0 −169 googletest/test/googletest-fail-if-no-test-linked-test.py
+0 −19 googletest/test/googletest-filter-unittest.py
+16 −95 googletest/test/googletest-json-output-unittest.py
+0 −70 googletest/test/googletest-param-test-test.cc
+1 −31 googletest/test/googletest-printers-test.cc
+2 −2 googletest/test/googletest-setuptestsuite-test_.cc
+4 −52 googletest/test/gtest_unittest.cc
+26 −67 googletest/test/gtest_xml_output_unittest.py
+1 −21 googletest/test/gtest_xml_output_unittest_.cc
+7 −7 googletest_deps.bzl
2 changes: 1 addition & 1 deletion dependency/yyjson
Submodule yyjson updated 470 files
2 changes: 1 addition & 1 deletion gpcontrib/gpcloud/test/googletest
Submodule googletest updated 288 files
2 changes: 1 addition & 1 deletion src/backend/cdb/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)

SUBDIRS := motion dispatcher endpoint
SUBDIRS := motion dispatcher endpoint anser


OBJS = cdbappendonlystorageformat.o \
Expand Down
33 changes: 33 additions & 0 deletions src/backend/cdb/anser/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#-------------------------------------------------------------------------
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Makefile--
# Makefile for Anser subsystem
#
#-------------------------------------------------------------------------

subdir = src/backend/cdb/anser
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global

override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)

OBJS = anser.o anserservice.o

include $(top_srcdir)/src/backend/common.mk
295 changes: 295 additions & 0 deletions src/backend/cdb/anser/anser-pr1-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# Anser Subsystem — PR 1: Channel Map + Background Services

## Background

This is the first PR in a series that brings an **Anser-style adaptive
information sharing framework** (per the VLDB paper *"Anser: Adaptive
Information Sharing Framework of AnalyticDB"*, p3636-wu) to Apache
Cloudberry. The end goal is to gather **bloom filters from producers**
(hash-join build sides) and deliver them to **consumers** (probe sides)
so data can be filtered out *before* an expensive hash join is
performed.

The paper's model has three roles:

| Paper concept | Role |
|---|---|
| **Publisher** | Operator that collects runtime information (e.g. a bloom filter from the build side of a hash join) |
| **Subscriber** | Operator that consumes it (e.g. a filter above the probe side) |
| **Channel manager** | Logical part: publisher↔subscriber relationship map + information lifecycle state |
| **Channel service** | Physical part: push-based transfer, expected-partition counting, cancellation flags, memory caps |

## Scope of this PR

**In scope:**

1. A **shared-memory channel map** holding producer/consumer properties
and information state (the channel manager).
2. **Two coordinator-only background services** (the channel service):
- a *gather* service that receives data from producers;
- a *send* service that makes aggregated data available to consumers.
3. A **publish/consume C API** over a shared-memory queue, organized
around registered **conditions** (see the identity model below).
4. A **test module** with debug SQL functions so the whole pipeline is
testable end-to-end immediately.

**Out of scope (later PRs):** bloom-filter payloads and merging, planner
PubNode/SubNode matching (including generation of the condition symbols,
see below), executor integration, segment-local services, network (RPC)
transport.

## Identity model: queries, conditions, channels

A single query may carry **multiple channels**, and a channel must be
matched to specific producers/consumers in the plan tree. The model:

- **Query identity** — `(gp_session_id, gp_command_count)` (*ssid +
ccnt*), the standard Cloudberry way to identify a running command;
no separate `query_id` is introduced.
- **Condition** — the unit of registration. Per the paper (§3.3), the
global context is a key-value hash map whose keys and values are
*"symbols … generated by the optimizer through algebraic equivalence
that uniquely mark an attribute of the same sub-expression"* — i.e.
the precise matching key is an optimizer-generated equivalence-class
symbol of the (join) attribute, **not** a table name + join condition
(that is only an intuitive approximation). Since planner integration
is a later PR, in this PR the condition carries an **opaque
fixed-length condition key** (bytes chosen by the caller / test
module); the planner PR will populate it with the real
equivalence-class symbols.
- **Channel** — one registered condition inside one query:
`channel key = (ssid, ccnt, condition_id)`. Each channel has one set
of producers and **one or more consumers** — one producer's result
can be consumed by multiple subscribers in different parts of the
plan tree (the paper's one-to-many passing).

Registration flow: a query registers its conditions up front (one per
piece of adaptive information), producers attach and publish, and
specific parts of the plan tree subscribe to the condition they need.

## Architecture

```mermaid
flowchart LR
subgraph Producers
P1[Producer backend 1]
P2[Producer backend N]
end
subgraph Shmem[Shared memory]
RB[Inbound ring buffer]
CM[Channel map - dynahash + data arena]
end
subgraph AuxProcs[Coordinator aux processes]
GS[Anser gather service]
SS[Anser send service]
end
C1[Consumer backend 1]
C2[Consumer backend M]

P1 -- AnserPublish --> RB
P2 -- AnserPublish --> RB
RB -- drain --> GS
GS -- append payload, count producers, mark READY --> CM
GS -- set latch --> SS
SS -- publish READY info, recycle CONSUMED --> CM
CM -- AnserConsume - wait with timeout --> C1
CM -- AnserConsume - wait with timeout --> C2
```

### Channel lifecycle

```mermaid
stateDiagram-v2
[*] --> PENDING: AnserRegisterCondition
PENDING --> COLLECTING: first producer payload
COLLECTING --> READY: all expected producers reported
COLLECTING --> CANCELLED: producer timeout / cancel
PENDING --> CANCELLED: timeout / AnserCancelChannel / AnserCancelQuery
READY --> CANCELLED: AnserCancelChannel - consumption not worth it
READY --> CONSUMED: all consumers done
CONSUMED --> [*]: entry + data slice recycled
CANCELLED --> [*]: entry recycled, all consumers unblock
```

Cancellation semantics (three levels):

1. **Whole query** — `AnserCancelQuery(ssid, ccnt)`: query failed or
finished; all its channels are cancelled/recycled.
2. **Single channel** — `AnserCancelChannel(channel)`: the step
completed, or a subscriber decides the data is not worth consuming
(cost outweighs benefit, per the paper's SubOperator thresholds).
3. **Producer failure** — if a producer fails or times out, the gather
service marks the channel `CANCELLED`, which unblocks **all**
consumers of that producer at once (the paper's cancellation flag,
distinguishing "empty info" from "failed/cancelled").

## New code

### `src/backend/cdb/anser/` + `src/include/cdb/anser.h`

New subdirectory, modeled on [`src/backend/cdb/endpoint/`](../src/backend/cdb/endpoint/).

**`anser.c` — the channel manager:**

- Shmem **dynahash** of channel entries keyed by
`(ssid, ccnt, condition_id)`. Each entry holds: the opaque condition
key, state enum (`PENDING → COLLECTING → READY/CANCELLED →
CONSUMED`), cancellation flag, expected/done producer counts,
consumer count/done, data length, and an offset into a fixed data
arena.
- **Fixed shmem pool** sized at postmaster start:
`gp_anser_max_channels` hash entries +
`gp_anser_max_channels × gp_anser_max_info_size` data arena.
On pool exhaustion, registration first runs an **orphan sweep** —
recycling entries in terminal states (`CONSUMED`/`CANCELLED`) and
entries whose owning session/command is no longer alive — and
retries; only if the pool is still full does registration fail with
a `WARNING`. Publishers are advisory (information tunes performance,
never affects correctness), so failing open is safe. LRU eviction
per the paper's 200MB budget is a follow-up.
- An **inbound message ring buffer** (producers → gather service) plus
shared latches to wake the two services.
- **Locking:** two named individual LWLocks added to
[`lwlocknames.txt`](../src/backend/storage/lmgr/lwlocknames.txt) —
`AnserChannelLock` (channel hash + arena) and `AnserRingLock` (ring
buffer) — following the existing `ParallelCursorEndpointLock`
precedent (entry 59). *(Correction vs. the draft plan: a named-tranche
LWLock is the extension mechanism; in-core subsystems use
lwlocknames.txt.)*
- **Public API:**
- `AnserShmemSize()` / `AnserShmemInit()`
- `AnserRegisterCondition(ssid, ccnt, condition_key, n_producers)`
→ channel handle; a query registers each of its conditions once
- `AnserSubscribe(channel)` — a consumer (a specific part of the
plan tree) subscribes to a registered condition; many subscribers
per channel are allowed
- `AnserPublish(channel, payload, len)` — producer enqueues its
partial result on the ring
- `AnserConsume(channel, ...)` — *weak dependency*: block up to
`gp_anser_timeout_ms`, then cancel this subscription and proceed
- `AnserChannelGetState(channel)`
- `AnserCancelChannel(channel)` — cancel one channel (step done, or
consumption judged not beneficial)
- `AnserCancelQuery(ssid, ccnt)` — cancel and recycle all channels
of a query (query failure or normal end)

**`anserservice.c` — the two auxiliary processes:**

- **Gather service:** drains the ring buffer, appends payloads to the
channel's arena slice, increments the done-producer count; when all
expected producers have reported → state `READY` → set the send
service's latch. On producer timeout or cancellation flag →
`CANCELLED`, unblocking all consumers of that producer.
- **Send service:** for `READY` channels, makes the aggregated
information available to consumers (wakes waiters); once all
consumers are done → `CONSUMED` → recycle entry and data slice.
Also cancels channels stuck beyond the timeout so blocked
subscribers unblock.
- Both are standard latch-wait loops: `WaitLatch` with
`WL_EXIT_ON_PM_DEATH`, `SIGHUP` config reload, `SIGTERM` exit.

**`Makefile`** — modeled on [`cdb/endpoint/Makefile`](../src/backend/cdb/endpoint/Makefile).

### Test module `src/test/modules/anser/`

Modeled on [`src/test/modules/test_shm_mq/`](../src/test/modules/test_shm_mq/):
a small extension exposing SQL wrappers over the in-core API —
`anser_test_register_condition()`, `anser_test_subscribe()`,
`anser_test_publish()`, `anser_test_consume()`, `anser_test_state()`,
`anser_test_cancel_channel()`, `anser_test_cancel_query()` — plus a
regression test covering:

1. the happy path: register condition → subscribe ×M → publish ×N →
`READY` → consume ×M → `CONSUMED` → entry recycled;
2. multiple conditions in one query, cancelled together by
`anser_test_cancel_query()`;
3. single-channel cancel: `READY` → `anser_test_cancel_channel()` →
consumers return without data;
4. the timeout path: register → (no publish) → consume blocks →
`CANCELLED` after `gp_anser_timeout_ms`, all consumers unblock;
5. pool exhaustion: fill the pool with terminal-state entries, verify
the orphan sweep recycles them and registration still succeeds.

## Touched existing files

| File | Change |
|---|---|
| [`src/backend/cdb/Makefile`](../src/backend/cdb/Makefile) | Add `anser` to `SUBDIRS` |
| [`src/backend/storage/lmgr/lwlocknames.txt`](../src/backend/storage/lmgr/lwlocknames.txt) | Add `AnserChannelLock`, `AnserRingLock` |
| [`src/backend/storage/ipc/ipci.c`](../src/backend/storage/ipc/ipci.c) | Hook `AnserShmemSize()` into `CalculateShmemSize` next to `EndpointShmemSize()` (line ~226) and `AnserShmemInit()` under `!IsUnderPostmaster` next to `EndpointShmemInit()` (line ~446); both no-ops when `gp_anser_enable` is off |
| [`src/backend/postmaster/postmaster.c`](../src/backend/postmaster/postmaster.c) | Two new `PMAuxProcList` entries (gather + send), same idiom as the `pg_cron launcher` entry (line 442); start rule: `gp_anser_enable && Gp_role == GP_ROLE_DISPATCH` (coordinator-only, like `PgCronStartRule`) |
| [`src/include/postmaster/postmaster.h`](../src/include/postmaster/postmaster.h) | `MaxPMAuxProc` base constant `4 → 6` (line 110: `(4 + IC_PROXY_NUM_BGWORKER + FTS_NUM_BGWORKER)`) |
| [`src/backend/utils/misc/guc_gp.c`](../src/backend/utils/misc/guc_gp.c) | New GUCs (see below). *(Correction vs. the draft plan: Cloudberry `gp_*` GUCs live in `guc_gp.c`, not `guc_tables.c`.)* |
| [`src/test/modules/Makefile`](../src/test/modules/Makefile) + `meson.build` | Add the `anser` test module |

### GUCs

| GUC | Default | Context | Purpose |
|---|---|---|---|
| `gp_anser_enable` | `off` | `PGC_POSTMASTER` | Master switch. When off: **no shmem allocated, no workers registered**; enabling requires a restart. |
| `gp_anser_max_channels` | `128` | `PGC_POSTMASTER` | Channel hash size / arena slot count (sizes shmem) |
| `gp_anser_max_info_size` | `1MB` | `PGC_POSTMASTER` | Per-record memory cap, per the paper (sizes shmem) |
| `gp_anser_timeout_ms` | `1000` | `PGC_USERSET` | Weak-dependency wait budget for consumers |

The three shmem-sizing GUCs must be `PGC_POSTMASTER` because the pool
is fixed at postmaster start.

## Deliberate simplifications vs. the paper

Documented in code comments:

1. **Coordinator-only** — no segment-local services or RPC transport
yet (paper §3.3, "Information transmission").
2. **Fixed pool + orphan sweep** instead of the paper's 200MB
LRU-evicted service budget (§3.4).
3. **"Aggregation" is byte-concatenation** — real bloom-filter union
arrives with the bloom-filter PR (§3.2, information merging).
4. **Opaque condition keys** — the optimizer-generated
algebraic-equivalence symbols (§3.3) arrive with the planner PR;
until then the caller supplies the key bytes.
5. **ACK/retry is trivial in-shmem** — the cancellation flag and
expected-producer counting do follow the paper (§3.3).

## Validation

1. Compile `src/backend/cdb/anser` and the full backend.
2. Run the test module's regression suite
(`make -C src/test/modules/anser installcheck`).
3. Smoke-test on a demo cluster with `gp_anser_enable=on`:
- both aux processes visible in `ps` on the coordinator;
- nothing starts with the GUC off, or on segments regardless.
4. Report exactly what was run; do not claim untested results.

## Risks and review notes

- Touches **postmaster startup** and **shared-memory sizing** — both
high-risk areas; changes are additive and fully gated behind an
off-by-default `PGC_POSTMASTER` GUC.
- No SQL-visible behavior, catalog, on-disk, or protocol changes.
- `MaxPMAuxProc` bump affects the static aux-worker array; verify no
other list consumers assume the old count.
- The consume path blocks backends (bounded by `gp_anser_timeout_ms`);
the timeout-cancel path in the send service is the safety net and
must be covered by the regression test.
- The orphan sweep must take `AnserChannelLock` exclusively and be
careful about liveness checks for `(ssid, ccnt)` owners so it never
recycles a channel still in use.
Loading
Loading