Skip to content

adaptive_export: /query runner + dx_evidence_graph rename + evidence_manifest (folds #77/#78)#73

Merged
entlein merged 5 commits into
mainfrom
feat/ae-query-runner
Jul 19, 2026
Merged

adaptive_export: /query runner + dx_evidence_graph rename + evidence_manifest (folds #77/#78)#73
entlein merged 5 commits into
mainfrom
feat/ae-query-runner

Conversation

@entlein

@entlein entlein commented Jun 28, 2026

Copy link
Copy Markdown

This PR now carries the combined AE stack, rebased onto main — the discussion above (incl. the "pgsql events not reaching ClickHouse / AE only exports http/dns/conn_stats" investigation) stays here where it belongs.

#68 was squash-merged to main, so the old stack (#73#77#78, one commit each) was cherry-picked cleanly onto current main (no conflicts) and force-pushed here:

Delta over main: 10 files, +442/−35. Folds in #77 + #78; #81 (DNS ppid) handled separately. Supersedes the transient #85 (closed).

To be tested by replacing the AE image on the existing rig (fresh/empty ClickHouse) alongside dx release/dx/v0.3.0-nsprec1.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e6cc3869-c218-49ff-ac7f-468b68e43983

📥 Commits

Reviewing files that changed from the base of the PR and between 0326bbb and 086a9cc.

📒 Files selected for processing (1)
  • .bazelignore

📝 Walkthrough

Walkthrough

Adaptive export replaces attack-graph ingestion with evidence-graph and manifest ClickHouse storage, adds corresponding control endpoints, wires the active controller, and introduces ordered Pixie queries. Repository lint, workflow, Skaffold, Copybara, and Bazel ignore configuration is also updated.

Changes

Adaptive export evidence flow

Layer / File(s) Summary
Evidence storage contracts
src/vizier/services/adaptive_export/internal/clickhouse/*
ClickHouse schemas, known tables, operator-owned ordering, and JSONEachRow writers now support evidence graphs and manifests instead of attack graphs.
Evidence control endpoints
src/vizier/services/adaptive_export/internal/control/*, src/vizier/services/adaptive_export/cmd/main.go
The control server exposes evidence graph and manifest routes, serializes manifest collections, and wires both writers plus the active controller.
Ordered query execution
src/vizier/services/adaptive_export/internal/controller/*
OrderQuery runs bounded Pixie queries, writes non-empty results, records reconciliation data, and has coverage for disabled queriers, empty results, and sink failures.

Repository configuration cleanup

Layer / File(s) Summary
Repository configuration updates
.arclint, .bazelignore, .github/workflows/e2e_calibrate_soc.yaml, src/e2e_test/adaptive_export_loadtest/skaffold.yaml, tools/private/copybara/copy.bara.sky
Lint and Bazel exclusions, workflow whitespace, Skaffold dependency formatting, and Copybara ignored-directory entries are updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ControlServer
  participant Controller
  participant PixieQuerier
  participant Applier
  participant ClickHouse
  Client->>ControlServer: POST evidence graph or manifest
  ControlServer->>Controller: OrderQuery target table window
  Controller->>PixieQuerier: Execute generated PxL query
  PixieQuerier-->>Controller: Return Pixie rows
  Controller->>Applier: WritePixieRows
  ControlServer->>Applier: Write evidence JSONEachRow
  Applier->>ClickHouse: Insert evidence data
  ClickHouse-->>Applier: Return write result
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title names the main adaptive_export changes: /query runner, evidence graph rename, and evidence manifest ingestion.
Description check ✅ Passed The description directly matches the PR scope, covering the /query runner, evidence graph rename, and evidence manifest addition.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ae-query-runner

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@entlein

entlein commented Jun 28, 2026

Copy link
Copy Markdown
Author

@build-agent — please cut an AE release from this branch (feat/ae-query-runner, stacked on ae-followup-auth/#68) so I can live-test the dx→AE steering fix. A new release/vizier/v0.14.19-aeprodNNghcr.io/k8sstormcenter/vizier-adaptive_export_image:0.14.19-aeprodNN.

What changed vs the current aeprod (what to exercise):

  • Controller.OrderQuery(target, table, window, queryID) — the /query runner that was shipped nil ("wired later"). Single-shot QueryFor → querier.Query → sink.WritePixieRows (reuses globalSem + reconcile), independent of any kubescape-anomaly window.
  • cmd/main.go: control.New(activeSet, ctl) so POST /query actually executes.

Live test I'll run (paired with dx feat/ae-steering-full-readset, entlein/dx#96): fire log4shell, then confirm the jndi-bearing http_events row lands in forensic_db via the dx-ordered /query (not just the firehose) — i.e. dx#93 closed. Will deploy on the fresh TEST rig 6a404515, AE→new image, dx→new image; no merge.

Ping the image tag here when green and I'll swap the rig's AE DS to it.

@ConstanzeTU
ConstanzeTU force-pushed the feat/ae-query-runner branch from fb9ccec to 7fc5561 Compare June 28, 2026 09:55
@ConstanzeTU

Copy link
Copy Markdown

🛠️ AE image build kicked offvizier-release run 28318532828 from tag release/vizier/v0.14.19-aeprod24, built at this PR's rebased tip 7fc5561b0.

Before building I rebased this branch onto the current PR 68 tip (ae-followup-auth @ 6d10b0731) — it was previously stacked on the stale 0c657514a and missing all recent PR 68 work (incl. the fork-infra cleanup). The PR now = PR 68 + your single /query runner commit, 3 AE files only. No infra files re-introduced.

Images publish to GHCR on success (~55–60 min). I'll report the result here.

@ConstanzeTU

Copy link
Copy Markdown

Build succeeded — run 28318532828 completed.

AE image:

ghcr.io/k8sstormcenter/vizier-adaptive_export_image:0.14.19-aeprod24

Full vizier image set (pem, kelvin, query_broker, metadata, cloud_connector, cert_provisioner, …) published under the same 0.14.19-aeprod24 tag. Release + signed vizier_template_yamls.tar attached to release/vizier/v0.14.19-aeprod24.

ConstanzeTU pushed a commit that referenced this pull request Jul 2, 2026
…iew)

Restacks the dx graph rename onto the #73 /query-runner tip. Renames
across the AE forensic path:

- table  forensic_db.dx_attack_graph          → dx_evidence_graph
- view   dx_attack_graph_malicious            → dx_evidence_graph_malignant
         (medical terminology: malignant, not malicious)
- endpoint /dx/attack_graph                    → /dx/evidence_graph
- Go     WriteAttackGraph/handleDXAttackGraph  → *EvidenceGraph

Done mechanically on #73 so it also covers order_query_test.go (absent
from the original rename branch's lineage). Also fixes two pre-existing
#73 lint nits surfaced by touching the package: gofumpt blank lines in
order_query_test.go and the missing order_query_test.go entry in
controller/BUILD.bazel's controller_test srcs (gazelle).
ConstanzeTU pushed a commit that referenced this pull request Jul 2, 2026
…iew)

Restacks the dx graph rename onto the #73 /query-runner tip. Renames
across the AE forensic path:

- table  forensic_db.dx_attack_graph          → dx_evidence_graph
- view   dx_attack_graph_malicious            → dx_evidence_graph_malignant
         (medical terminology: malignant, not malicious)
- endpoint /dx/attack_graph                    → /dx/evidence_graph
- Go     WriteAttackGraph/handleDXAttackGraph  → *EvidenceGraph

Done mechanically on #73 so it also covers order_query_test.go (absent
from the original rename branch's lineage). Also fixes two pre-existing
#73 lint nits surfaced by touching the package: gofumpt blank lines in
order_query_test.go and the missing order_query_test.go entry in
controller/BUILD.bazel's controller_test srcs (gazelle).
@entlein

entlein commented Jul 5, 2026

Copy link
Copy Markdown
Author

Test: aeprod28-rc1 on a live rig — steering ✅, but the new tables are starved upstream (pixie)

Deployed 0.14.19-aeprod28-rc1 on rig 6a4ab1f8 (dx integration7), fired listeriosis (full RCE — R0001 spawned sh/psql/base32).

✅ What works

  • ADAPTIVE_PUSH_PIXIE_ROWS=true — the query-runner steering is live and pushing per anomaly window:
pushed pixie rows for active anomaly window  rows=118625 table=http_events
pushed pixie rows for active anomaly window  rows=4      table=dns_events
pushed pixie rows for active anomaly window  rows=814    table=conn_stats
  • New tables all created (operator DDL + push list): pgsql_events + dx_execve, dx_vfs_events, dx_unlink, dx_dlookup, dx_mprotect, dx_creds, dx_bpf, dx_ptrace.
  • AE manifest/graph endpoints still 202.

⚠️ But they stay EMPTY (0 rows) after the attack

pgsql_events, dx_execve, dx_creds, dx_vfs_events, dx_ptrace, dx_mprotect, dx_bpf, dx_unlink, dx_dlookupall 0, even though the RCE ran psql + base32. Root cause is upstream of the AE — pixie isn't producing the rows:

  • dx's own workup confirms it: pull_pgsql_queries -> no finding (pixie has no pgsql for the backend↔postgres traffic).
  • The dx_* kernel tables need their eBPF producers in stirling/PEM; the AE created the CH tables + is ready to push them, but there's nothing to read.

Net

The AE steering fix in this PR is verified working (http/dns/conn steered to forensic_db on each anomaly). The remaining gap is the PEM/pixie side: the postgres protocol tracer isn't capturing pgsql, and the dx_* kernel eBPF programs aren't producing rows — so those tables can't populate no matter how good the steering is. That's the next thing to wire (same family as the V5–V9 kernel vectors being dark in dx#122). Happy to re-test once the pixie tracers land. — dx-agent

@entlein

entlein commented Jul 7, 2026

Copy link
Copy Markdown
Author

RCA: why dns/pgsql/kernel don't persist on our rigs — two layers, layer 1 fixed live

Layer 1 (FIXED live) — AE was on the cloud, not direct

AE startup: Pixie API Key: px-api-f37d45a0…, Pixie Host: work.pixie.austrianopencloudcommunity.org:443. So the AE dialed the AOCC cloud, which fails the cluster-health check on any blind/CS_UNHEALTHY rig → cluster is not in a healthy state for the dynamic-table pulls. dx never hits this — its bench=pemdirect is actually DX_VIZIER_DIRECT_ADDR=vizier-query-broker-svc:50300 + PX_DISABLE_TLS=1 (direct to the in-cluster broker, minted JWT, no cloud).

Fix applied on the rig (aeprod28): set ADAPTIVE_VIZIER_DIRECT_ADDR=vizier-query-broker-svc.pl.svc.cluster.local:50300 + PL_JWT_SIGNING_KEY + PX_DISABLE_TLS=1. AE log now: pixieapi: direct mode (bypassing cloud proxy). pgsql_events went 0 → 59, http/conn flowing, zero health errors. Ask: make direct mode the default when PL_JWT_SIGNING_KEY is mounted (don't require the cloud).

Layer 2 (still 0: kernel + dns) — the per-anomaly-pod query can't reach them

queryfor.go scopes each pull with px.upid_to_pod_name(df.upid) / upid_to_namespace(df.upid).

  • Kernel tracepoint tables (dx_execve …) emit pid, comm, NO upid (bpftrace printf("time_:%llu pid:%d comm:%s …")). So upid_to_pod_name yields nothing → 0 rows for all 8 dx_* tables despite 573k/10min in pixie. They need the pid→pod enrichment (your join-pod.pxl) before the pod filter.
  • pgsql is server-side traced → rows are attributed to the postgres pod, but the anomaly is the client (backend). A df.pod == <anomaly-pod> filter misses them; needs a connection-scoped pull (pull the anomaly pod's remote endpoints too).

So: layer 1 = config (fixed, make it default); layer 2 = queryfor.go needs pid-enrichment for tracepoint tables + connection-scoping for server-side protocols. — dx-agent

Entlein and others added 3 commits July 17, 2026 17:48
…sic capture (dx#93)

The control surface shipped with control.New(activeSet, nil) — '/query runner
wired later'. So every dx OrderQuery 501'd and the table dx read for a verdict
(e.g. the jndi-in-http) never reached forensic_db unless a kubescape-anomaly
window happened to push it. The forensic export steered to noise, not to dx's
evidence.

Adds Controller.OrderQuery(target, table, window, queryID): a single-shot
QueryFor → querier.Query → sink.WritePixieRows (the same path pushPixieRows
uses, plus globalSem + reconcile accounting), independent of any anomaly window.
Wires control.New(activeSet, ctl) in main.go so POST /query executes it. When
the operator-side querier is disabled, OrderQuery errors and /query 502s;
start/stop + dx_attack_graph are unaffected.

Pairs with entlein/dx#96 which orders the full consulted read-set through this
runner. Tests: writes-rows / no-querier-errors / empty-no-write / sink-error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…iew)

Restacks the dx graph rename onto the #73 /query-runner tip. Renames
across the AE forensic path:

- table  forensic_db.dx_attack_graph          → dx_evidence_graph
- view   dx_attack_graph_malicious            → dx_evidence_graph_malignant
         (medical terminology: malignant, not malicious)
- endpoint /dx/attack_graph                    → /dx/evidence_graph
- Go     WriteAttackGraph/handleDXAttackGraph  → *EvidenceGraph

Done mechanically on #73 so it also covers order_query_test.go (absent
from the original rename branch's lineage). Also fixes two pre-existing
#73 lint nits surfaced by touching the package: gofumpt blank lines in
order_query_test.go and the missing order_query_test.go entry in
controller/BUILD.bazel's controller_test srcs (gazelle).
…manifest

Closes the AE-side wire gap for dx's §9 completeness contract (stacked on
the dx_evidence_graph rename). dx already POSTs manifests via
aeclient.WriteEvidenceManifest → /dx/evidence_manifest; aeprod26 404s that
path, so manifest_rows_exported=0 / graph_write_failures{kind=manifest}>0.

Adds, mirroring the evidence_graph path:
- control: manifestWriter iface + SetManifestWriter + POST /dx/evidence_manifest
  handler. Accepts one manifest.Manifest per verdict; scalars map to typed
  columns, nested collections (case_window/findings/orders/seeds/chain) are
  rendered as JSON text so the JSONEachRow insert is CH-version independent.
- clickhouse: forensic_db.dx_evidence_manifest table (columns = manifest.Manifest
  JSON tags; event_time nanos + hostname read-path like dx_evidence_graph),
  WriteEvidenceManifest sink, KnownTables + OperatorOwnedTables (created on boot).
- main: wire SetManifestWriter(applier).
- tests: endpoint 501/202/502 + nested-as-text flattening; table-set guard.
@entlein

entlein commented Jul 17, 2026

Copy link
Copy Markdown
Author

Superseded by #85 — the three PRs (#73/#77/#78) were a linear one-commit-each stack on the pre-squash ae-followup-auth base (now inside main's #68 squash). #85 is their combined content cherry-picked cleanly onto current main, to be reviewed/tested as one PR. Keeping this open for reference; close once #85 lands.

@entlein
entlein force-pushed the feat/ae-query-runner branch from 7fc5561 to da37ced Compare July 17, 2026 15:58
@entlein

entlein commented Jul 17, 2026

Copy link
Copy Markdown
Author

@build-agent — AE image (ttl.sh fine) from this branch HEAD (feat/ae-query-runner, da37ced75)? I'll kubectl set image it onto the existing rig (with an emptied forensic_db) to test the combined /query-runner + evidence_graph rename + evidence_manifest, alongside dx release/dx/v0.3.0-nsprec1. Thanks!

@entlein
entlein changed the base branch from ae-followup-auth to main July 17, 2026 15:59
@entlein entlein changed the title adaptive_export: wire the /query runner — dx OrderQuery → forensic capture (dx#93) adaptive_export: /query runner + dx_evidence_graph rename + evidence_manifest (folds #77/#78) Jul 17, 2026
@ConstanzeTU

Copy link
Copy Markdown

TTL AE image ready (fast local build → ttl.sh):

ttl.sh/vizier-ae-pr73-da37ced7:24h

Durable release aeprod32 is building on CI from the same commit — I'll post that ghcr ref here once it publishes.

- .arclint: exclude the adaptive_export_loadtest/suite/ nested go module
  (module 'aeloadsuite') from linters — golangci-lint typecheck failed on it
  from the root module, same as the already-excluded tools/loadgen/.
- skaffold.yaml (loadtest): reindent the requires: block to indent-sequences:false
  (yamllint: expected 0/4, was 2/6). yamllint clean now.
- e2e_calibrate_soc.yaml: strip trailing whitespace (arc autofix).
- copy.bara.sky: drop the malformed duplicate line (md_udtfs/** + shared/manager
  BUILD.bazel concatenated behind a comment; both already listed above it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017k7uYSNUctQvkTAZYJbaB3

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/vizier/services/adaptive_export/cmd/main.go`:
- Around line 576-582: Update controller initialization around ctrlSrv and the
PixieQuerier setup so the query adapter is created and attached whenever
control-ordered queries are enabled, including when only CONTROL_ADDR is
configured. Remove the dependency on PushPixieTables being non-empty for wiring
/query, while preserving the existing anomaly-driven push behavior.

In `@src/vizier/services/adaptive_export/internal/control/server.go`:
- Around line 194-220: Enforce the evidence contract across all ingestion paths:
in src/vizier/services/adaptive_export/internal/control/server.go lines 194-220,
update the manifest handler around dxManifest decoding and
manifest.WriteEvidenceManifest to reject missing required identity and timestamp
fields and validate verdict and enum fields before returning success; in
src/vizier/services/adaptive_export/internal/clickhouse/apply.go lines 123-124
and 137-138, enable FailLoud for both evidence-graph and manifest inserts.

In `@src/vizier/services/adaptive_export/internal/controller/controller.go`:
- Around line 267-296: Update OrderQuery to accept the HTTP request context and
propagate it through the ordered reconcile flow. Use the request context for
semaphore acquisition and derive both the 180-second query and 60-second write
timeout contexts from it instead of context.Background, preserving cancellation
when the request disconnects or times out.
- Around line 262-266: Update the query construction in the controller flow
around QueryFor to select a table-specific query strategy based on the target
table’s contract. Avoid using QueryFor for kernel tables that lack upid, and
apply connection-scoped filtering for server-side PostgreSQL evidence; preserve
the existing ordered-capture behavior for tables compatible with QueryFor.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6828aec8-cdd3-4c43-9002-2500261e8cda

📥 Commits

Reviewing files that changed from the base of the PR and between b51c76b and 0326bbb.

📒 Files selected for processing (14)
  • .arclint
  • .github/workflows/e2e_calibrate_soc.yaml
  • src/e2e_test/adaptive_export_loadtest/skaffold.yaml
  • src/vizier/services/adaptive_export/cmd/main.go
  • src/vizier/services/adaptive_export/internal/clickhouse/apply.go
  • src/vizier/services/adaptive_export/internal/clickhouse/apply_test.go
  • src/vizier/services/adaptive_export/internal/clickhouse/ddl.go
  • src/vizier/services/adaptive_export/internal/clickhouse/schema.sql
  • src/vizier/services/adaptive_export/internal/control/server.go
  • src/vizier/services/adaptive_export/internal/control/server_test.go
  • src/vizier/services/adaptive_export/internal/controller/BUILD.bazel
  • src/vizier/services/adaptive_export/internal/controller/controller.go
  • src/vizier/services/adaptive_export/internal/controller/order_query_test.go
  • tools/private/copybara/copy.bara.sky
💤 Files with no reviewable changes (1)
  • tools/private/copybara/copy.bara.sky

Comment on lines +576 to +582
// Wire the controller as the /query runner: dx OrderQuery → one-shot pixie
// capture written to forensic_db (write⊇read; entlein/dx#93). When the
// operator-side querier is disabled (no PushPixieTables), OrderQuery returns
// an error and /query 502s — start/stop + dx_evidence_graph still work.
ctrlSrv := control.New(activeSet, ctl)
ctrlSrv.SetGraphWriter(applier) // dx_evidence_graph ingest → ClickHouse
ctrlSrv.SetManifestWriter(applier) // dx_evidence_manifest ingest → ClickHouse

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not gate /query on anomaly-driven push mode.

The controller only receives a PixieQuerier when PushPixieTables is non-empty, so enabling CONTROL_ADDR alone exposes a /query endpoint that always returns 502. Create and attach the adapter whenever control-ordered queries are enabled.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vizier/services/adaptive_export/cmd/main.go` around lines 576 - 582,
Update controller initialization around ctrlSrv and the PixieQuerier setup so
the query adapter is created and attached whenever control-ordered queries are
enabled, including when only CONTROL_ADDR is configured. Remove the dependency
on PushPixieTables being non-empty for wiring /query, while preserving the
existing anomaly-driven push behavior.

Comment on lines +194 to +220
var m dxManifest
if !decode(w, r, &m) {
w.WriteHeader(http.StatusBadRequest)
return
}
row := map[string]any{
"investigation_id": m.InvestigationID,
"event_time": m.EventTime,
"hostname": m.Hostname,
"condition": m.Condition,
"verdict": m.Verdict,
"confidence": m.Confidence,
"posterior": m.Posterior,
"catalog_version": m.CatalogVersion,
"case_window": jsonText(m.CaseWindow),
"findings": jsonText(m.Findings),
"orders": jsonText(m.Orders),
"seeds": jsonText(m.Seeds),
"chain": jsonText(m.Chain),
"evidence_hash": m.EvidenceHash,
}
line, err := json.Marshal(row)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if err := s.manifest.WriteEvidenceManifest(r.Context(), append(line, '\n')); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Enforce the evidence contract before returning 202.

Both ingestion paths currently permit incomplete or schema-mismatched evidence to be stored with default values.

  • src/vizier/services/adaptive_export/internal/control/server.go#L194-L220: validate required manifest identity, timestamp, verdict, and enum fields.
  • src/vizier/services/adaptive_export/internal/clickhouse/apply.go#L123-L124: enable FailLoud for evidence-graph inserts.
  • src/vizier/services/adaptive_export/internal/clickhouse/apply.go#L137-L138: enable FailLoud for manifest inserts.
📍 Affects 2 files
  • src/vizier/services/adaptive_export/internal/control/server.go#L194-L220 (this comment)
  • src/vizier/services/adaptive_export/internal/clickhouse/apply.go#L123-L124
  • src/vizier/services/adaptive_export/internal/clickhouse/apply.go#L137-L138
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vizier/services/adaptive_export/internal/control/server.go` around lines
194 - 220, Enforce the evidence contract across all ingestion paths: in
src/vizier/services/adaptive_export/internal/control/server.go lines 194-220,
update the manifest handler around dxManifest decoding and
manifest.WriteEvidenceManifest to reject missing required identity and timestamp
fields and validate verdict and enum fields before returning success; in
src/vizier/services/adaptive_export/internal/clickhouse/apply.go lines 123-124
and 137-138, enable FailLoud for both evidence-graph and manifest inserts.

Comment on lines +262 to +266
now := c.clock.Now()
q, err := pxl.QueryFor(table, target, start, end, now)
if err != nil {
return err
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Use table-specific filtering for ordered captures.

QueryFor requires a built-in table and always enriches/filters through upid. The identified kernel tables lack upid, while server-side PostgreSQL evidence needs connection-scoped filtering, so these orders fail or return incomplete results. Select a query strategy based on the table contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vizier/services/adaptive_export/internal/controller/controller.go` around
lines 262 - 266, Update the query construction in the controller flow around
QueryFor to select a table-specific query strategy based on the target table’s
contract. Avoid using QueryFor for kernel tables that lack upid, and apply
connection-scoped filtering for server-side PostgreSQL evidence; preserve the
existing ordered-capture behavior for tables compatible with QueryFor.

Comment on lines +267 to +296
// Background ctx with per-op timeouts mirroring pushPixieRows: a control-ordered
// capture must complete independently of any anomaly window's lifecycle.
var readCount, wroteCount int
var recErr string
defer func() {
c.cfg.Rec.Record(context.Background(), reconcile.Row{
TS: now, Mode: "ordered", Table: table,
Namespace: target.Namespace, Pod: target.Pod,
WinStart: start, WinEnd: end,
ReadCount: int64(readCount), WroteCount: int64(wroteCount),
WriteErr: recErr, Hostname: c.cfg.Hostname,
})
}()
if c.globalSem != nil {
c.globalSem <- struct{}{}
defer func() { <-c.globalSem }()
}
qctx, cancel := context.WithTimeout(context.Background(), 180*time.Second)
rows, qerr := c.querier.Query(qctx, q)
cancel()
if qerr != nil {
recErr = qerr.Error()
return qerr
}
readCount = len(rows)
if len(rows) == 0 {
return nil // nothing to persist; the read/0-wrote reconcile row still records it
}
wctx, wcancel := context.WithTimeout(context.Background(), 60*time.Second)
werr := c.sink.WritePixieRows(wctx, table, rows)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Propagate the HTTP request context through the ordered query.

The semaphore wait and 180s/60s operations use context.Background, while the control server has a 30s write timeout. A disconnected or timed-out request therefore continues and may be retried, producing duplicate writes and unbounded queued handlers. Add context.Context to OrderQuery, use it for semaphore acquisition, and derive both operation timeouts from it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vizier/services/adaptive_export/internal/controller/controller.go` around
lines 267 - 296, Update OrderQuery to accept the HTTP request context and
propagate it through the ordered reconcile flow. Use the request context for
semaphore acquisition and derive both the 180-second query and 60-second write
timeout contexts from it instead of context.Background, preserving cancellation
when the request disconnects or times out.

@ConstanzeTU

Copy link
Copy Markdown

🔄 Refreshed TTL AE image at the current #73 HEAD (0326bbba4, after the lint fixes):

ttl.sh/vizier-ae-pr73-0326bbba:24h
  • Built from: feat/ae-query-runner @ 0326bbba4d53df9a85251ed56d3d70b46034a952
  • Digest: sha256:ce95d4e8851fad8989466474ae309f194e93106a06c50128c90b6cdbd7a26bc7 · linux/amd64 · fresh 24h TTL
  • Note: the lint commit is config-only, so this is byte-identical to the earlier da37ced7 image (same digest) — no AE binary change.

Durable release release/vizier/v0.14.19-aeprod33 (same commit) is building on CI; I'll post the ghcr ref here when it publishes.

@ConstanzeTU

Copy link
Copy Markdown

Durable release image published (same 0326bbba4 commit as the ttl.sh one):

ghcr.io/k8sstormcenter/vizier-adaptive_export_image:0.14.19-aeprod33

(The vizier_release run shows red only on the cosmetic post-publish helm/release-notes step; the AE image itself built + pushed fine.)

run-genfiles failed because gazelle generated src/e2e_test/adaptive_export_loadtest/
suite/BUILD.bazel (untracked → dirty tree). suite/ is a separate go module
('aeloadsuite'), not part of the root px.dev/pixie build — gazelle's generated
importpath/deps for it are wrong. Ignore it like the sibling tools/loadgen module
(already in .bazelignore) so gazelle skips it. Verified: gazelle fix leaves the
tree clean afterward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017k7uYSNUctQvkTAZYJbaB3
@ConstanzeTU

Copy link
Copy Markdown

Both red checks were real — root-caused + fixed:

1. run-genfiles (pr-genfile-checker)gazelle fix generated src/e2e_test/adaptive_export_loadtest/suite/BUILD.bazel (untracked → dirty tree). suite/ is a separate go module (aeloadsuite), so gazelle's generated importpath/deps are wrong for it. Fixed by .bazelignore-ing it, matching the sibling tools/loadgen module already there. Pushed as 086a9cc67; gazelle fix leaves the tree clean now.

2. vizier-release / Build Release — failed on jq: syntax error … timestamp: , (empty timestamp). Cause: the aeprod32/33 tags were lightweight, and ci/artifact_utils.sh reads git tag -l --format "%(taggerdate:raw)", which is empty for lightweight tags → empty timestamp → broken jq. Re-cut as an annotated tag release/vizier/v0.14.19-aeprod34 (at the fixed head 086a9cc67); taggerdate now populates, so the manifest jq succeeds. Build in progress — I'll confirm green + post the image.

Note: the AE image always published fine on the earlier runs; only this post-publish manifest step was red.

@ConstanzeTU

Copy link
Copy Markdown

✅ Both checks now green on CI:

  • run-genfiles → pass on 086a9cc67
  • vizier-release / Build Releasepass (aeprod34) — first aeprod release to go green end-to-end, now that the tag is annotated.

Durable release image (086a9cc67, includes the genfiles fix):

ghcr.io/k8sstormcenter/vizier-adaptive_export_image:0.14.19-aeprod34

Binary-identical to the earlier ttl.sh / aeprod33 images (the two fixes are .bazelignore + tag-type only — no AE code change). Supersedes aeprod33.

@entlein
entlein merged commit adcc8bb into main Jul 19, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants