Skip to content

refactor: full api --> ingest --> clickhouse --> dlq refactor - #182

Merged
EricAndrechek merged 15 commits into
mainfrom
clickhouse-robust-insert
May 27, 2026
Merged

refactor: full api --> ingest --> clickhouse --> dlq refactor#182
EricAndrechek merged 15 commits into
mainfrom
clickhouse-robust-insert

Conversation

@EricAndrechek

@EricAndrechek EricAndrechek commented May 25, 2026

Copy link
Copy Markdown
Member

Summary

Fully refactored the API --> nats --> ingest --> clickhouse --> DLQ flow to drop bento for better control + speed, dropped false negative api parsing, retrying clickhouse fails (not entire batch on DLQ).

Test plan

  • ...

Related Issues

Closes #91, closes #34, starts building what's needed for #50, starts on phase 1 of #175

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file go Pull requests that update go code area/api HTTP handlers, routing, middleware area/ingest Ingest pipeline (Bento, batching, DLQ) area/query Structured query AST, SQL builder area/cache Local / shared / tiered caching area/sdk TypeScript SDK (clients/ts/) area/infra CI, build, deploy, Docker, release labels May 25, 2026
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Concurrent batch ingestion for faster array inserts.
    • Dead Letter Queue (DLQ) routing for failed rows.
    • Stream/controller: immediate SSE "connected" feedback and a new connection-await API.
  • Performance Improvements

    • Fast table-level authorization executed before payload parsing.
    • Cache invalidation now accepts precomputed version keys for more efficient invalidation.
  • Tests & Docs

    • Added/expanded end-to-end and stress tests; updated ingest documentation and examples.

Walkthrough

Replaces Bento with a native IngestWorker (NATS→batched ClickHouse inserts, per-row fallback, DLQ routing), refactors cache invalidation to accept version-key lists, adds NATS PublishOpt helpers and test mocks, broadens discovery validation, makes TS batch inserts concurrent, and updates tests/docs/build config accordingly.

Changes

Ingest Pipeline Modernization

Layer / File(s) Summary
All changes (single review pass)
go.mod, internal/cache/*, internal/ingest/*, internal/mq/*, internal/testutil/*, internal/discovery/*, clients/ts/*, tests/e2e/*, tests/integration/*, internal/api/*, docs/*, .golangci.yml, Makefile, .air.toml
Comprehensive set of edits: add native IngestWorker and tests; remove Bento pipeline and Bento tests; change Cache.InvalidateCache to accept versionKeys []string and adapt LocalCache/VersionManager/tests; introduce PublishOpt and WithHeader plus EmbeddedNATS/Publisher signature updates; expand test utilities with MockCache/MockJetStream/MockRoundTripper; broaden discovery isTypeCompatible and move validation tests to validation_test.go; update TS SDK to dispatch array inserts concurrently and add StreamController.connected; adjust multiple E2E and integration tests and documentation; update build/lint config and Makefile formatting scope.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 PR title accurately describes the main refactoring effort: replacing Bento with a native ingest worker, implementing per-row failure isolation, and improving the end-to-end flow from API through ClickHouse to DLQ.
Description check ✅ Passed The PR description is related to the changeset, explaining the removal of Bento, addition of retry logic, per-row failure isolation, and referencing relevant issue numbers.
Linked Issues check ✅ Passed The PR successfully addresses both linked issues: #91 (per-row failure isolation, poison pill handling via DLQ) and #34 (Bento replacement with native Go worker), with comprehensive implementation of retry logic and cache invalidation.
Out of Scope Changes check ✅ Passed All changes are within scope: cache API refactoring to support version keys, authorization enhancements (fast/deep auth), validation expansion, e2e/integration tests, documentation updates, and ingest worker implementation are all directly supporting the core refactoring objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch clickhouse-robust-insert
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch clickhouse-robust-insert

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

@coderabbitai
coderabbitai Bot requested a review from taitelee May 25, 2026 03:54
@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation area/docs Documentation, site/, README labels May 25, 2026

@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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/api/ingest.go (2)

61-83: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent table-enumeration via auth-before-existence checks.

Line 61-66 runs schema existence checks before policy evaluation, which lets unauthorized callers distinguish unknown tables (404) from known-but-forbidden (403). Evaluate authorization first (using the provided table name), then only reveal existence to authorized callers.

Suggested fix
- schema := h.Registry.Get(table)
- if schema == nil {
-   slog.WarnContext(ctx, "unknown table requested", "table", table)
-   writeJSONError(w, http.StatusNotFound, fmt.Sprintf("unknown table: %s", table))
-   return
- }
-
 // FAST AUTH: Table-level policy check (Before spending CPU parsing JSON)
 var perms *policy.ResolvedPermissions
 var role string
@@
   if !perms.Allowed {
     slog.WarnContext(ctx, "policy enforcement rejected request", "role", role, "table", table)
     writeJSONError(w, http.StatusForbidden, "forbidden")
     return
   }
 }
+
+schema := h.Registry.Get(table)
+if schema == nil {
+  slog.WarnContext(ctx, "unknown table requested", "table", table)
+  writeJSONError(w, http.StatusNotFound, fmt.Sprintf("unknown table: %s", table))
+  return
+}

86-92: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject trailing tokens after JSON decode in internal/api/ingest.go.

json.Decoder.Decode(&data) will stop after the first JSON value; without an EOF/trailing-token check, malformed multi-value bodies can be accepted and then pass schema validation. Add a second Decode expecting io.EOF (and import io) so trailing garbage returns 400, matching the existing pattern in internal/api/query.go.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 389411c2-cd6e-4f22-bc73-a9581d789577

📥 Commits

Reviewing files that changed from the base of the PR and between 4170074 and 3295dbd.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (25)
  • clients/ts/src/table.ts
  • cmd/wavehouse/main.go
  • go.mod
  • internal/api/ingest.go
  • internal/cache/cache.go
  • internal/cache/cache_test.go
  • internal/cache/local.go
  • internal/cache/local_test.go
  • internal/cache/version_manager.go
  • internal/discovery/discovery_test.go
  • internal/discovery/validation.go
  • internal/discovery/validation_test.go
  • internal/ingest/bento.go
  • internal/ingest/bento_test.go
  • internal/ingest/worker.go
  • internal/mq/embedded.go
  • internal/mq/mq.go
  • internal/testutil/mocks.go
  • tests/e2e/sdk/batching.test.ts
  • tests/e2e/sdk/cache.test.ts
  • tests/e2e/sdk/dlq.test.ts
  • tests/e2e/sdk/ingest.test.ts
  • tests/e2e/sdk/streaming.test.ts
  • tests/e2e/sdk/stress.test.ts
  • tests/integration/setup_test.go
💤 Files with no reviewable changes (5)
  • internal/ingest/bento.go
  • internal/ingest/bento_test.go
  • tests/integration/setup_test.go
  • internal/discovery/discovery_test.go
  • internal/cache/cache_test.go
📜 Review details
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI
🧰 Additional context used
📓 Path-based instructions (9)
tests/e2e/sdk/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

E2E tests in tests/e2e/sdk/*.test.ts exercise the full pipeline and use helpers from tests/e2e/sdk/helpers.ts; run with make test-e2e

Files:

  • tests/e2e/sdk/batching.test.ts
  • tests/e2e/sdk/stress.test.ts
  • tests/e2e/sdk/streaming.test.ts
  • tests/e2e/sdk/cache.test.ts
  • tests/e2e/sdk/dlq.test.ts
  • tests/e2e/sdk/ingest.test.ts
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go interfaces for core behaviors (Cache, Deduplicator, Publisher, Subscriber) to enable interface-first design with multiple implementations for standalone and clustered modes
Apply gofumpt strict formatting enforced by CI
Use structured logging with log/slog (JSON handler) instead of other logging approaches
Return errors and wrap with fmt.Errorf("context: %w", err) instead of panicking
Do not use global state; pass dependencies explicitly through constructor injection
Use lowercase, single-word (or abbreviated) package names in Go
Every new function should have corresponding test cases
Aim for 80%+ coverage on new code; project-wide CI enforces 80% minimum via merged unit + integration + e2e profiles
Go version 1.26 with strict gofumpt formatting enforced by CI; use golangci-lint v2.11.4 pinned in Makefile; most dev tools pinned in go.mod via tool directives

Files:

  • internal/mq/embedded.go
  • internal/cache/local.go
  • internal/mq/mq.go
  • internal/cache/version_manager.go
  • internal/discovery/validation_test.go
  • internal/discovery/validation.go
  • internal/testutil/mocks.go
  • internal/cache/local_test.go
  • internal/api/ingest.go
  • internal/ingest/worker.go
  • cmd/wavehouse/main.go
  • internal/cache/cache.go
internal/cache/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

TieredCache uses golang.org/x/sync/singleflight to prevent cache stampede

Files:

  • internal/cache/local.go
  • internal/cache/version_manager.go
  • internal/cache/local_test.go
  • internal/cache/cache.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Use table-driven tests with t.Run(tt.name, ...) for test cases in Go
Use shared mocks from internal/testutil/ (MockPublisher, MockCache, MockDeduplicator, MockSubscriber) instead of creating ad-hoc mocks
Use testutil.NewTestSchemaRegistry(tables) or discovery.NewSchemaRegistryFromMap(tables) for schema-aware tests

Files:

  • internal/discovery/validation_test.go
  • internal/cache/local_test.go
clients/ts/**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use pnpm >= 11.1 and Node.js 22 LTS (pinned via .nvmrc) for SDK and E2E test harnesses; SDK and tests shell out to pnpm

Files:

  • clients/ts/src/table.ts
internal/api/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/api/**/*.go: Use Chi v5 for HTTP routing in WaveHouse
Use Bearer-token-only CORS posture: never emit Access-Control-Allow-Credentials, rely on Authorization: Bearer headers, not cookies
Schema-driven ingest: POST /v1/ingest/{table} accepts flat JSON body, validates against discovered schema (unknown fields rejected, types checked, nullable constraints enforced), returns 200 immediately after optional dedup + MQ publish
/health returns 200 after first successful schema Refresh; /ready returns 503 with latest diagnostic until Refresh succeeds

Files:

  • internal/api/ingest.go
internal/api/**/*ingest*.go

📄 CodeRabbit inference engine (AGENTS.md)

If NATS stream is full on ingest, return 503 + Retry-After instead of blocking

Files:

  • internal/api/ingest.go
internal/ingest/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/ingest/**/*.go: Ingest is insert-only: the EventMessage wire format carries {table_name, received_timestamp, data} and nothing else; non-insert mutations (DELETE/UPDATE/TRUNCATE) go through POST /v1/admin/query under admin/service role
Per-table batching: the Bento ingest pipeline groups events by table name and performs dynamic INSERTs using schema column order; each table batch is independent
Failed batch inserts are published to a separate NATS stream (WAVEHOUSE_DLQ) with subjects dlq.

to prevent silent data loss; DLQ is controlled by dlq.enabled config
Active Sweeper purges NATS messages that are both ACKed (written to ClickHouse) and older than the gap window

Files:

  • internal/ingest/worker.go
cmd/wavehouse/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Schema-discovery failure on boot is non-fatal: record api.BootState diagnostic, bind :8080, and retry via SchemaRegistry.RetryRefresh (exp backoff 2s → 60s) in background

Files:

  • cmd/wavehouse/main.go
🔇 Additional comments (17)
internal/mq/mq.go (1)

54-69: LGTM!

internal/mq/embedded.go (1)

111-123: LGTM!

internal/testutil/mocks.go (1)

25-33: LGTM!

internal/cache/cache.go (1)

24-28: LGTM!

Also applies to: 57-62

internal/cache/local.go (1)

53-58: LGTM!

internal/cache/local_test.go (1)

120-125: LGTM!

internal/cache/version_manager.go (1)

28-32: LGTM!

internal/ingest/worker.go (6)

44-61: LGTM!


63-110: LGTM!


112-161: LGTM!


163-228: LGTM!


230-266: LGTM!


268-311: LGTM!

cmd/wavehouse/main.go (1)

282-296: LGTM!

tests/e2e/sdk/ingest.test.ts (1)

36-36: LGTM!

Also applies to: 64-64, 114-114, 151-151, 203-203, 258-258, 341-341

tests/e2e/sdk/stress.test.ts (1)

1-60: LGTM!

go.mod (1)

114-114: LGTM!

Comment thread clients/ts/src/table.ts
Comment thread cmd/wavehouse/main.go Outdated
Comment thread internal/discovery/validation.go
Comment thread internal/ingest/worker.go Outdated
Comment thread tests/e2e/sdk/batching.test.ts Outdated
Comment thread tests/e2e/sdk/cache.test.ts Outdated
Comment thread tests/e2e/sdk/dlq.test.ts Outdated
Comment thread tests/e2e/sdk/streaming.test.ts Outdated
@github-project-automation github-project-automation Bot moved this from Backlog to In review in WaveHouse Task Board May 25, 2026
@gemini-code-assist

Copy link
Copy Markdown

Warning

Gemini encountered an error creating the summary. You can try again by commenting /gemini summary.

@github-actions github-actions Bot added area/pipes Named query pipes and removed documentation Improvements or additions to documentation area/docs Documentation, site/, README labels May 25, 2026

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/ingest/worker.go (1)

325-331: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Do not ack the source message when DLQ publish fails.

If PublishMsg fails at Line 325, Line 331 still DoubleAcks the original message, which can permanently lose that event.

Proposed fix
 	_, pubErr := w.js.PublishMsg(ctx, msg)
 	if pubErr != nil {
 		w.logger.ErrorContext(ctx, "NATS DLQ publish failed", "subject", subject, "error", pubErr)
+		return
 	}
 
 	// DoubleAck original message so NATS doesn't redeliver the corrupt data
 	_ = pm.natsMsg.DoubleAck(ctx)

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b2d542d6-ea19-4ca8-8eb6-b674d95de47e

📥 Commits

Reviewing files that changed from the base of the PR and between 3295dbd and 142607f.

📒 Files selected for processing (19)
  • .golangci.yml
  • clients/ts/src/stream/controller.ts
  • clients/ts/src/table.ts
  • cmd/wavehouse/main.go
  • internal/api/dlq.go
  • internal/api/dlq_test.go
  • internal/api/ingest.go
  • internal/api/pipes.go
  • internal/api/pipes_test.go
  • internal/api/stream_sse.go
  • internal/api/structured_query.go
  • internal/ingest/worker.go
  • internal/pipes/pipes.go
  • tests/e2e/sdk/batching.test.ts
  • tests/e2e/sdk/cache.test.ts
  • tests/e2e/sdk/dlq.test.ts
  • tests/e2e/sdk/streaming.test.ts
  • tests/e2e/sdk/stress.test.ts
  • tests/integration/dlq_test.go
💤 Files with no reviewable changes (1)
  • tests/e2e/sdk/stress.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI
🧰 Additional context used
📓 Path-based instructions (12)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go interfaces for core behaviors (Cache, Deduplicator, Publisher, Subscriber) to enable interface-first design with multiple implementations for standalone and clustered modes
Apply gofumpt strict formatting enforced by CI
Use structured logging with log/slog (JSON handler) instead of other logging approaches
Return errors and wrap with fmt.Errorf("context: %w", err) instead of panicking
Do not use global state; pass dependencies explicitly through constructor injection
Use lowercase, single-word (or abbreviated) package names in Go
Every new function should have corresponding test cases
Aim for 80%+ coverage on new code; project-wide CI enforces 80% minimum via merged unit + integration + e2e profiles
Go version 1.26 with strict gofumpt formatting enforced by CI; use golangci-lint v2.11.4 pinned in Makefile; most dev tools pinned in go.mod via tool directives

Files:

  • internal/api/stream_sse.go
  • internal/api/structured_query.go
  • internal/pipes/pipes.go
  • internal/api/dlq.go
  • internal/api/dlq_test.go
  • internal/api/pipes.go
  • internal/api/pipes_test.go
  • internal/api/ingest.go
  • internal/ingest/worker.go
  • tests/integration/dlq_test.go
  • cmd/wavehouse/main.go
internal/api/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/api/**/*.go: Use Chi v5 for HTTP routing in WaveHouse
Use Bearer-token-only CORS posture: never emit Access-Control-Allow-Credentials, rely on Authorization: Bearer headers, not cookies
Schema-driven ingest: POST /v1/ingest/{table} accepts flat JSON body, validates against discovered schema (unknown fields rejected, types checked, nullable constraints enforced), returns 200 immediately after optional dedup + MQ publish
/health returns 200 after first successful schema Refresh; /ready returns 503 with latest diagnostic until Refresh succeeds

Files:

  • internal/api/stream_sse.go
  • internal/api/structured_query.go
  • internal/api/dlq.go
  • internal/api/dlq_test.go
  • internal/api/pipes.go
  • internal/api/pipes_test.go
  • internal/api/ingest.go
internal/api/**/*stream*.go

📄 CodeRabbit inference engine (AGENTS.md)

Use NATS DeliverByStartTime for SSE/WS gap-fill; no in-process ring buffer

Files:

  • internal/api/stream_sse.go
internal/pipes/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Named query pipes: pre-defined SQL templates with parameter binding, role restrictions, and caching; stored in NATS KV with .sql file directory bootstrap

Files:

  • internal/pipes/pipes.go
tests/e2e/sdk/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

E2E tests in tests/e2e/sdk/*.test.ts exercise the full pipeline and use helpers from tests/e2e/sdk/helpers.ts; run with make test-e2e

Files:

  • tests/e2e/sdk/dlq.test.ts
  • tests/e2e/sdk/cache.test.ts
  • tests/e2e/sdk/batching.test.ts
  • tests/e2e/sdk/streaming.test.ts
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Use table-driven tests with t.Run(tt.name, ...) for test cases in Go
Use shared mocks from internal/testutil/ (MockPublisher, MockCache, MockDeduplicator, MockSubscriber) instead of creating ad-hoc mocks
Use testutil.NewTestSchemaRegistry(tables) or discovery.NewSchemaRegistryFromMap(tables) for schema-aware tests

Files:

  • internal/api/dlq_test.go
  • internal/api/pipes_test.go
  • tests/integration/dlq_test.go
**/api/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/api/**/*_test.go: Use testutil.MakeJWT(t, claims) and testutil.MakeExpiredJWT(t, claims) for JWT authentication tests
Use testutil.AssertJSONResponse(t, rec, status, expected) and testutil.AssertJSONContains(t, rec, status, substring) for HTTP handler assertions

Files:

  • internal/api/dlq_test.go
  • internal/api/pipes_test.go
clients/ts/**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use pnpm >= 11.1 and Node.js 22 LTS (pinned via .nvmrc) for SDK and E2E test harnesses; SDK and tests shell out to pnpm

Files:

  • clients/ts/src/table.ts
  • clients/ts/src/stream/controller.ts
internal/api/**/*ingest*.go

📄 CodeRabbit inference engine (AGENTS.md)

If NATS stream is full on ingest, return 503 + Retry-After instead of blocking

Files:

  • internal/api/ingest.go
internal/ingest/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

internal/ingest/**/*.go: Ingest is insert-only: the EventMessage wire format carries {table_name, received_timestamp, data} and nothing else; non-insert mutations (DELETE/UPDATE/TRUNCATE) go through POST /v1/admin/query under admin/service role
Per-table batching: the Bento ingest pipeline groups events by table name and performs dynamic INSERTs using schema column order; each table batch is independent
Failed batch inserts are published to a separate NATS stream (WAVEHOUSE_DLQ) with subjects dlq.

to prevent silent data loss; DLQ is controlled by dlq.enabled config
Active Sweeper purges NATS messages that are both ACKed (written to ClickHouse) and older than the gap window

Files:

  • internal/ingest/worker.go
tests/integration/**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

Integration tests must be marked with //go:build integration and use Docker testcontainers for ClickHouse; E2E tests use the SDK as the primary test harness in tests/e2e/sdk/

Files:

  • tests/integration/dlq_test.go
cmd/wavehouse/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Schema-discovery failure on boot is non-fatal: record api.BootState diagnostic, bind :8080, and retry via SchemaRegistry.RetryRefresh (exp backoff 2s → 60s) in background

Files:

  • cmd/wavehouse/main.go
🧠 Learnings (3)
📚 Learning: 2026-05-20T01:02:00.784Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 164
File: internal/api/router_test.go:289-350
Timestamp: 2026-05-20T01:02:00.784Z
Learning: In WaveHouse’s internal API tests (files matching internal/api/**/*_test.go), follow the existing separation-of-concerns convention for testing the RequireRole middleware: inject `ContextKeyRole` directly into the request `context.Context` instead of using `testutil.MakeJWT`/JWT-driven flows. Do not refactor role-gate tests to use JWT tokens—JWT parsing and token handling are covered separately in `middleware_test.go` (the dedicated JWT parsing tests), and mixing those concerns would expand the failure surface and reduce isolation.

Applied to files:

  • internal/api/dlq_test.go
  • internal/api/pipes_test.go
📚 Learning: 2026-05-23T01:23:59.268Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 174
File: internal/api/ingest_test.go:111-111
Timestamp: 2026-05-23T01:23:59.268Z
Learning: In WaveHouse Go tests in internal/api/**/*_test.go, use internal/testutil.AssertJSONErrorResponse(t, w) for HTTP error-path JSON assertions. Do not use (or reintroduce) package-local assertJSONErrorResponse helpers. AssertJSONErrorResponse verifies the response Content-Type is application/json, includes the X-Content-Type-Options: nosniff header, and that the JSON body contains an "error" field.

Applied to files:

  • internal/api/dlq_test.go
  • internal/api/pipes_test.go
📚 Learning: 2026-05-20T20:30:15.808Z
Learnt from: taitelee
Repo: Wave-RF/WaveHouse PR: 172
File: internal/api/pipes_test.go:106-118
Timestamp: 2026-05-20T20:30:15.808Z
Learning: For WaveHouse pipes authorization allowlist checks, fix the empty-role fail-open behavior by (1) removing any outer guard that prevents allowlist evaluation when the incoming `role` is `""` (e.g., don’t short-circuit with `if role != "" { ... }`), and (2) during allowlist scanning, ensure only non-empty allowlist entries can match—e.g., require `ar != "" && ar == role` (so a malformed allowlist like `["" ]` cannot grant access to an empty incoming role via `"" == ""`).

Applied to files:

  • internal/api/pipes.go
  • internal/api/pipes_test.go
🧬 Code graph analysis (6)
tests/e2e/sdk/dlq.test.ts (1)
tests/e2e/sdk/helpers.ts (3)
  • waitForCondition (88-99)
  • chQuery (109-125)
  • testId (103-105)
internal/api/dlq.go (1)
internal/query/ident.go (2)
  • SafeEncodeNATS (11-24)
  • SafeDecodeNATS (27-31)
clients/ts/src/stream/controller.ts (1)
clients/ts/src/types.ts (1)
  • StreamSubscriber (41-50)
internal/api/pipes_test.go (2)
internal/pipes/pipes.go (1)
  • NewMemoryStore (294-303)
internal/api/pipes.go (1)
  • NewPipesHandler (26-28)
tests/integration/dlq_test.go (1)
internal/query/ident.go (1)
  • SafeEncodeNATS (11-24)
tests/e2e/sdk/streaming.test.ts (1)
clients/ts/src/stream/controller.ts (1)
  • status (67-69)
🔇 Additional comments (9)
clients/ts/src/table.ts (1)

51-63: Unbounded concurrent insert fan-out remains.

This concern was already raised earlier for this segment (add concurrency limiting/chunking).

internal/api/structured_query.go (1)

56-56: LGTM!

internal/pipes/pipes.go (1)

211-212: LGTM!

tests/e2e/sdk/dlq.test.ts (1)

14-15: LGTM!

Also applies to: 20-20, 44-47, 51-52, 58-60, 63-66

internal/api/dlq.go (1)

8-8: LGTM!

Also applies to: 11-11, 40-40, 51-55

internal/api/dlq_test.go (1)

76-77: LGTM!

Also applies to: 107-107

tests/e2e/sdk/cache.test.ts (1)

89-90: TTL units are still inflated by * 1000 and can exceed test timing bounds.

This appears to be the same unresolved issue already raised in prior review feedback.

Also applies to: 110-110

tests/e2e/sdk/streaming.test.ts (1)

53-53: LGTM!

Also applies to: 85-87, 91-91, 132-132, 167-167

cmd/wavehouse/main.go (1)

432-434: LGTM!

Comment thread clients/ts/src/stream/controller.ts Outdated
Comment thread clients/ts/src/stream/controller.ts
Comment thread internal/api/pipes_test.go
Comment thread internal/api/pipes.go
Comment thread internal/api/stream_sse.go Outdated
@coderabbitai coderabbitai Bot added the documentation Improvements or additions to documentation label May 25, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 25, 2026
@EricAndrechek
EricAndrechek merged commit 7140df8 into main May 27, 2026
8 checks passed
@EricAndrechek
EricAndrechek deleted the clickhouse-robust-insert branch May 27, 2026 18:41
@github-project-automation github-project-automation Bot moved this from In review to Done in WaveHouse Task Board May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api HTTP handlers, routing, middleware area/cache Local / shared / tiered caching area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release area/ingest Ingest pipeline (Bento, batching, DLQ) area/pipes Named query pipes area/query Structured query AST, SQL builder area/sdk TypeScript SDK (clients/ts/) dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation go Pull requests that update go code

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

feat(ingest): Prevent infinite retry loops on permanent ClickHouse failures (Poison Pills) Bento Vs Custom Buffer

2 participants