Skip to content

refactor(app): extract the process wiring from main.go into internal/app - #585

Merged
taitelee merged 9 commits into
mainfrom
app-wiring
Sep 17, 2026
Merged

taitelee merged 9 commits into
mainfrom
app-wiring

Conversation

@taitelee

Copy link
Copy Markdown
Member

Summary

Pulls the ~550 lines of hand wiring and settings-reload hooks out of cmd/wavehouse/main.go into a new internal/app package. app.New builds every component from the boot config and the settings directory, Run drives the long-lived ones (ingest worker, sweeper, hub bridge, keepalive wheel, schema refresh, SIGHUP + directory watcher, API server, Prometheus sidecar) under one erxt is cancelled or a component fails, and Close releases whatNew opened in reverse order. Each component is wired in one place, with the settings store handed to its wiring function whole, so the per-tenant store registry (#583, story 2) lands there rather than in main.

  • main.go shrinks to argv dispatch, logger, config.Load, CheckDataDir, app.New, app.Run; run(ctx) takes the signal context and is unit-tested, so
    the .testcoverage.yml exclude for it is removed.
  • tests/integration boots through the same app.New against its testcontainer instead of a hand-built handler subset that had drifted from the binary.
  • Two exits made stricter: a graceful stop now waits for the ingerain within server.shutdown_timeout (open connectionsforce-closed at the deadline), and the mq.max_bytes_gb reload hook bounds its JetStream calls to 10s. The e2e orchestrator's kill budget is raised to 20s to
    match.
  • Docs, CHANGELOG, labeler (area/app), and stale main.go code comments synced.

Test plan

  • New unit tests: degraded boot without ClickHouse, dedupe on/off, Prometheus inline vs sidecar, Run stops cleanly on cancel, listen failure names the component, settings reload drives the relocated hooks, run(ctx) boot/refusal paths

Related Issues

Closes #140. Part of #583 (story 0).

@github-actions github-actions Bot added documentation Improvements or additions to documentation go Pull requests that update go code area/observability Metrics, logs, traces, health, profiling area/api HTTP handlers, routing, middleware area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release labels Sep 15, 2026
@taitelee taitelee moved this from Backlog to In progress in WaveHouse Task Board Sep 15, 2026
@coderabbitai

coderabbitai Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

📝 Summary

Summary by CodeRabbit

  • Improvements

    • Improved application startup and shutdown coordination.
    • Graceful shutdown now drains active work before releasing stores and flushing telemetry.
    • Open event streams close promptly during shutdown and reconnect automatically.
    • Schema discovery retries recoverable failures while reporting degraded health.
    • Configuration reloads preserve the previous valid configuration when rejected.
    • Increased the service stop grace period to accommodate shutdown processing.
  • Documentation

    • Updated deployment, architecture, configuration, development, API, and pipeline documentation.

Walkthrough

The pull request moves process construction and lifecycle management into internal/app. The command entry point handles configuration, logging, and signals. Integration tests boot production wiring, and shutdown behavior, coverage, deployment settings, and documentation are updated.

Changes

Application wiring extraction

Layer / File(s) Summary
App lifecycle contract
internal/app/app.go, internal/app/app_test.go
Adds application construction, execution, cleanup, accessors, and lifecycle tests.
Component wiring and shutdown
internal/app/wire.go, internal/api/stream.go, internal/mq/embedded.go, internal/observability/provider.go
Wires runtime components, reload hooks, HTTP serving, SSE shutdown, discovery retries, and bounded cleanup.
Entrypoint and production-backed integration setup
cmd/wavehouse/*, tests/integration/*
Reduces the command entry point to configuration, logging, signal handling, and app lifecycle calls. Integration setup now uses the production app on a real listener.
Coverage, configuration, and architecture references
.github/labeler.yml, .testcoverage.yml, AGENTS.md, CHANGELOG.md, docs/src/content/docs/*, scripts/*, deployments/compose/standalone.yaml, config.yaml
Updates labels, coverage rules, lifecycle references, shutdown documentation, grace periods, comments, and orchestrator timing.

Priority: ➖ Normal

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

Change: Refactor

Sequence Diagram(s)

sequenceDiagram
  participant Signal as signal context
  participant Main as cmd/wavehouse
  participant App as internal/app
  participant Runtime as HTTP and worker components
  Signal->>Main: cancel on SIGINT or SIGTERM
  Main->>App: New(config, build info, log level, listener)
  App->>Runtime: construct and start wired components
  Main->>App: Run(ctx)
  Runtime->>App: return on cancellation or component error
  Main->>App: Close()
  App->>Runtime: release resources in reverse order
Loading

Suggested reviewers: ericandrechek

Merge Risk: 🟡 Moderate · up to 08324

A shutdown-time SIGHUP can delay exit, and a stalled liveness response can hang integration setup indefinitely. These lifecycle and test reliability issues should be fixed before merge.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Issue [#140] requires App.New, App.Run, App.Shutdown, and App.Close. internal/app.App still provides New, Run, and Close(ctx), but no Shutdown method. The extraction, integration reu… Implement App.Shutdown(ctx) with the configured graceful-stop behavior and use it for process shutdown. Add unit coverage for successful App.New boot and the OTel-disabled path.
Out of Scope Changes check ⚠️ Warning Issue [#140] defines a wiring refactor, excludes subsystem implementation changes, and requires existing external behavior to remain unchanged. internal/api/stream.go adds StreamHandler.Closing an… Remove the functional SSE changes from this pull request, or track them in a separate issue and pull request. Keep changes required for the [#140] wiring extraction and its lifecycle tests.
Docstring Coverage ⚠️ Warning Docstring coverage is 49.21% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 63 functions across 19 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: moving process wiring from main.go into internal/app.
Description check ✅ Passed The description is directly related to the changeset and covers the app extraction, lifecycle methods, integration test reuse, shutdown changes, documentation updates, and test plan.
Full details: Linked Issues check

Explanation

Issue [#140] requires App.New, App.Run, App.Shutdown, and App.Close. internal/app.App still provides New, Run, and Close(ctx), but no Shutdown method. The extraction, integration reuse, reduced main.go, coverage-exclusion removal, and lifecycle tests are present. The reviewed test summary states that app tests use closed ClickHouse addresses, so they cover degraded boot rather than the required successful App.New path. It also does not show explicit OTel-disabled unit coverage.

Full details: Out of Scope Changes check

Explanation

Issue [#140] defines a wiring refactor, excludes subsystem implementation changes, and requires existing external behavior to remain unchanged. internal/api/stream.go adds StreamHandler.Closing and changes active SSE requests to terminate when application shutdown starts. This is a functional API subsystem and external-behavior change, not application wiring. The lifecycle timeout, orchestration, integration-test, documentation, and comment changes have a direct connection to the extraction.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
✨ Simplify code
  • Commit to this branch
  • Create a new PR

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.

@github-actions

github-actions Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

📚 Docs preview is live → https://686decc1-wavehouse-docs.wave-rf.workers.dev

  • Commit — 0832429: test: repoint the OTel flush comment at App.Close and wait for the stop context after a component failure
  • Author — @taitelee
  • Committed — 2026-09-17 09:11 (UTC-04:00)
  • Deployed — 2026-09-17 09:29 EDT

@github-code-quality

github-code-quality Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Go

Go

The overall line coverage in commit 0832429 in the app-wiring branch remains at 91%, unchanged from commit e462135 in the main branch.

Show a line coverage summary of the most impacted files.
File main e462135 app-wiring 0832429 +/-
internal/api/stream.go 58% 52% -6%
cmd/wavehouse/main.go 66% 63% -3%
internal/app/wire.go 0% 86% +86%
internal/app/app.go 0% 89% +89%

Updated September 17, 2026 13:30 UTC

coderabbitai[bot]
coderabbitai Bot previously requested changes Sep 15, 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: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 6a153dd1-a751-4b5d-9ee0-4f219958510d

📥 Commits

Reviewing files that changed from the base of the PR and between e462135 and 5fd2a2d.

📒 Files selected for processing (26)
  • .github/labeler.yml
  • .testcoverage.yml
  • AGENTS.md
  • CHANGELOG.md
  • cmd/wavehouse/main.go
  • cmd/wavehouse/main_test.go
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/configuration.mdx
  • docs/src/content/docs/development.md
  • docs/src/content/docs/ingest-pipeline.md
  • internal/api/boot_chain_test.go
  • internal/api/router.go
  • internal/app/app.go
  • internal/app/app_test.go
  • internal/app/wire.go
  • internal/mq/embedded.go
  • internal/observability/provider.go
  • internal/settings/watch_test.go
  • internal/stream/subscriber.go
  • scripts/cov/main.go
  • scripts/orchestrator/main.go
  • tests/integration/dlq_test.go
  • tests/integration/ingest_test.go
  • tests/integration/otel_test.go
  • tests/integration/query_test.go
  • tests/integration/setup_test.go
💤 Files with no reviewable changes (1)
  • .testcoverage.yml

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Docs preview
  • GitHub Check: E2E tests
  • GitHub Check: Coverage
  • GitHub Check: Lint
🧰 Additional context used
📓 Path-based instructions (2)
In MDX, leave a blank line between a JSX tag and a code fence.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/configuration.mdx
WH001 applies to every tracked Markdown file, with no carve-out

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • CHANGELOG.md
  • docs/src/content/docs/development.md
  • docs/src/content/docs/ingest-pipeline.md
  • AGENTS.md
  • docs/src/content/docs/architecture.md
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-15T19:03:45.435Z
Learning: Run `make lint` and `make test` before considering work complete.
📚 Learning: 2026-08-13T12:17:52.620Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 470
File: docs/src/content/docs/reverse-proxy.mdx:137-144
Timestamp: 2026-08-13T12:17:52.620Z
Learning: For Wave-RF/WaveHouse documentation, verify claims about implementation control flow against the authoritative implementation source (for example, internal/auth/auth.go) rather than relying solely on docs/** content. Documentation may lag behind or paraphrase behavior, so control-flow claims should be confirmed in source code.

Applied to files:

  • docs/src/content/docs/configuration.mdx
📚 Learning: 2026-06-26T12:23:22.696Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 346
File: internal/stream/subscriber_test.go:9-28
Timestamp: 2026-06-26T12:23:22.696Z
Learning: In this Go repository, prefer table-driven tests (e.g., `[]struct{...}` with `t.Run(...)`) only for tests that cover multiple scenarios/inputs and can be cleanly enumerated. Do not artificially rewrite a clear single-scenario sequential behavioral-flow test into a table-driven form just to fit the pattern; if there’s only one meaningful scenario, keep the test as a straightforward linear flow (as in `TestSubscriber_SendDeliversThenDropsWhenFull`).

Applied to files:

  • cmd/wavehouse/main_test.go
  • internal/app/app_test.go
📚 Learning: 2026-06-10T15:01:09.027Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: docs/src/content/docs/development.md:0-0
Timestamp: 2026-06-10T15:01:09.027Z
Learning: In this repo’s Markdown review (all .md files), do not flag capitalization/style issues for literal paths starting with ".github/" (or any substring that is a path beginning with ".github/"). Treat ".github" as the correct lowercase dotfile directory name, even when it appears inside prose or code spans; automated checks such as LanguageTool’s "(GITHUB)" rule commonly produce false positives for this literal filesystem path.

Applied to files:

  • CHANGELOG.md
  • AGENTS.md
🪛 ast-grep (0.45.3)
internal/app/wire.go

[warning] 488-492: This http.Server is constructed without a ReadTimeout. Without a read timeout, a slow or malicious client can hold connections open indefinitely (e.g. a Slowloris attack), exhausting server resources and causing a denial of service. Set ReadTimeout (and ideally ReadHeaderTimeout, WriteTimeout, and IdleTimeout) on the http.Server to bound how long the server waits while reading a request.
Context: http.Server{
Addr: fmt.Sprintf(":%d", a.cfg.Server.Port),
Handler: a.handler,
ReadHeaderTimeout: readHeaderTimeout,
}
Note: [CWE-400] Uncontrolled Resource Consumption.

(http-server-missing-read-timeout-go)


[warning] 536-536: A log/format call (log.Print/Printf/Println, the Fatal/Panic variants, fmt.Sprintf, or a structured logger's Info/Warn/Error/Debug method) is given a message built by concatenating a string literal with a non-literal value such as request data. Unsanitized, attacker-controlled input written to logs enables log forging / CRLF injection: an attacker can inject newlines to spoof log entries or break log parsers. Do not concatenate raw input into the log message; pass it as a separate structured field/argument (e.g. 'log.Printf("user: %s", user)' or 'logger.Info("login", "user", user)') and strip or escape newline characters first.
Context: slog.Info("starting "+name, "addr", ln.Addr().String())
Note: [CWE-117] Improper Output Neutralization for Logs.

(log-injection-request-data-concat-go)


[warning] 547-547: A log/format call (log.Print/Printf/Println, the Fatal/Panic variants, fmt.Sprintf, or a structured logger's Info/Warn/Error/Debug method) is given a message built by concatenating a string literal with a non-literal value such as request data. Unsanitized, attacker-controlled input written to logs enables log forging / CRLF injection: an attacker can inject newlines to spoof log entries or break log parsers. Do not concatenate raw input into the log message; pass it as a separate structured field/argument (e.g. 'log.Printf("user: %s", user)' or 'logger.Info("login", "user", user)') and strip or escape newline characters first.
Context: slog.Info("shutting down " + name)
Note: [CWE-117] Improper Output Neutralization for Logs.

(log-injection-request-data-concat-go)


[warning] 551-551: A log/format call (log.Print/Printf/Println, the Fatal/Panic variants, fmt.Sprintf, or a structured logger's Info/Warn/Error/Debug method) is given a message built by concatenating a string literal with a non-literal value such as request data. Unsanitized, attacker-controlled input written to logs enables log forging / CRLF injection: an attacker can inject newlines to spoof log entries or break log parsers. Do not concatenate raw input into the log message; pass it as a separate structured field/argument (e.g. 'log.Printf("user: %s", user)' or 'logger.Info("login", "user", user)') and strip or escape newline characters first.
Context: slog.Error(name+" shutdown error; closing open connections", "error", err)
Note: [CWE-117] Improper Output Neutralization for Logs.

(log-injection-request-data-concat-go)

🪛 LanguageTool
docs/src/content/docs/configuration.mdx

[typographical] ~143-~143: The word ‘When’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ... you can run traces-only, logs-only, etc. Prometheus exposition is configured in ...

(WRB_QUESTION_MARK)


[style] ~143-~143: Since ownership is already implied, this phrasing may be redundant.
Context: ... Prometheus exposition is configured in its own top-level prometheus b...

(PRP_OWN)

CHANGELOG.md

[uncategorized] ~23-~23: The official name of this software platform is spelled with a capital “H”.
Context: ...on/setup_test.go, .testcoverage.yml, .github/labeler.yml): app.New` builds every c...

(GITHUB)

AGENTS.md

[grammar] ~27-~27: Please add a punctuation mark at the end of paragraph.
Context: ...and the signal context; everything else is internal/app Seventeen internal pack...

(PUNCTUATION_PARAGRAPH_END)

docs/src/content/docs/architecture.md

[style] ~90-~90: Since ownership is already implied, this phrasing may be redundant.
Context: ...ons.Listenerlets one serve the API on its own listener instead ofserver.port`. - **...

(PRP_OWN)


[typographical] ~129-~129: Consider using an em dash in dialogues and enumerations.
Context: - discovery.go — SchemaRegistry que...

(DASH_RULE)


[style] ~129-~129: This sentence is over 40 words long. Consider splitting it up, as shorter sentences make the text easier to read.
Context: ...e SSE announcement are both built from. Each refresh also records the server version (SELECT version()), joins system.tables for each table's create_table_query (kept in-process as TableSchema.DDL and marked json:"-" — an external-engine table renders its wiring in that statement — endpoint, bucket/host, database, username, access key id — so it must never reach /v1/ops/schema; ClickHouse masks the password as [HIDDEN] from ~23.9, so what is withheld here is the topology), reads each column's default_expression and 1-based position alongside its type, discovers the server's default time zone (SELECT timezone()) and bakes every DateTime/DateTime64 column's canonicalization spec (precision + resolved zone) into the cached schema, so the per-record ingest path parses no type strings and loads no zones (#372). Supports periodic auto-refresh, on-d...

(TOO_LONG_SENTENCE)

🔇 Additional comments (26)
.github/labeler.yml (1)

6-10: LGTM!

AGENTS.md (1)

29-29: LGTM!

Also applies to: 32-32, 69-69, 393-407, 424-426

CHANGELOG.md (1)

23-24: LGTM!

docs/src/content/docs/architecture.md (1)

48-55: LGTM!

Also applies to: 86-91, 129-129, 148-150

docs/src/content/docs/configuration.mdx (1)

143-143: LGTM!

docs/src/content/docs/development.md (1)

348-348: LGTM!

Also applies to: 454-454

docs/src/content/docs/ingest-pipeline.md (1)

158-185: LGTM!

Also applies to: 235-235

internal/api/boot_chain_test.go (1)

94-94: LGTM!

internal/api/router.go (1)

46-46: LGTM!

Also applies to: 120-120

internal/mq/embedded.go (1)

66-67: LGTM!

internal/observability/provider.go (1)

70-70: LGTM!

internal/settings/watch_test.go (1)

81-81: LGTM!

internal/stream/subscriber.go (1)

59-59: LGTM!

scripts/cov/main.go (1)

76-78: LGTM!

Also applies to: 303-304, 708-710

scripts/orchestrator/main.go (1)

311-317: LGTM!

Also applies to: 320-320

tests/integration/otel_test.go (1)

218-218: LGTM!

Also applies to: 313-314

internal/app/app.go (1)

1-191: LGTM!

internal/app/app_test.go (1)

1-348: LGTM!

tests/integration/ingest_test.go (1)

33-33: LGTM!

Also applies to: 57-57, 94-94, 135-135

cmd/wavehouse/main.go (2)

112-115: LGTM!

Also applies to: 131-138, 168-185, 187-197


163-167: 📐 Maintainability & Code Quality

The auth-posture handling remains in internal/app/wire.go: lines 379–382 emit both JWT/JWKS warnings, lines 385–389 handle the trimmed OperatorKey and recovery warning, and line 398 passes the trimmed value to the authenticator. The extraction did not remove this behavior.

cmd/wavehouse/main_test.go (1)

30-41: LGTM!

Also applies to: 45-53, 57-75, 77-110, 112-150

tests/integration/setup_test.go (2)

139-154: LGTM!

Also applies to: 162-196, 198-213, 226-239, 241-273, 380-380


214-225: 🩺 Stability & Availability

internal/settings/seed/config.json defines exactly the six patched clickhouse fields and the single mq.max_bytes_gb field. settings.Store.ClickHouse() dereferences those six fields, and MQConfig contains only MaxBytesGB. The patch therefore does not drop any required settings, so merging the seed objects is unnecessary.

tests/integration/dlq_test.go (1)

27-27: LGTM!

Also applies to: 72-72, 117-117

tests/integration/query_test.go (1)

110-110: LGTM!

Also applies to: 137-137

Comment thread internal/app/wire.go
Comment thread internal/app/wire.go
Comment thread tests/integration/setup_test.go
@github-project-automation github-project-automation Bot moved this from In progress to In review in WaveHouse Task Board Sep 15, 2026
@taitelee

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

♻️ Duplicate comments (1)
internal/app/wire.go (1)

278-278: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Use a fresh timeout context for the rollback.

If api.EnsureDLQStream fails because the shared ctx reaches mqResizeTimeout, embedded.Resize(ctx, applied) receives an expired context. The rollback then cannot run, and the ingest and DLQ limits remain inconsistent until another settings adoption. Create a new bounded context for the rollback.

Proposed fix
-			if err := embedded.Resize(ctx, applied); err != nil {
+			rollbackCtx, rollbackCancel := context.WithTimeout(context.Background(), mqResizeTimeout)
+			defer rollbackCancel()
+			if err := embedded.Resize(rollbackCtx, applied); err != nil {

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: c380b2f3-6d86-4cf8-bf66-09d6cd39ab09

📥 Commits

Reviewing files that changed from the base of the PR and between 5fd2a2d and 678d883.

📒 Files selected for processing (2)
  • internal/app/wire.go
  • tests/integration/setup_test.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-15T20:05:19.381Z
Learning: Never force-push or rebase a PR branch
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-15T20:05:19.381Z
Learning: Never hand-write markers or `--no-verify`
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-15T20:05:19.381Z
Learning: Run `make lint` and `make test` before considering work complete.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-15T20:05:19.381Z
Learning: Validate locally before every push
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-15T20:05:19.381Z
Learning: Agents must create PRs with `gh pr create --draft`.
🪛 ast-grep (0.45.3)
internal/app/wire.go

[warning] 499-503: This http.Server is constructed without a ReadTimeout. Without a read timeout, a slow or malicious client can hold connections open indefinitely (e.g. a Slowloris attack), exhausting server resources and causing a denial of service. Set ReadTimeout (and ideally ReadHeaderTimeout, WriteTimeout, and IdleTimeout) on the http.Server to bound how long the server waits while reading a request.
Context: http.Server{
Addr: fmt.Sprintf(":%d", a.cfg.Server.Port),
Handler: a.handler,
ReadHeaderTimeout: readHeaderTimeout,
}
Note: [CWE-400] Uncontrolled Resource Consumption.

(http-server-missing-read-timeout-go)

🔇 Additional comments (1)
tests/integration/setup_test.go (1)

155-160: LGTM!

coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 15, 2026
@taitelee
taitelee marked this pull request as ready for review September 15, 2026 20:16
@taitelee
taitelee requested review from a team and EricAndrechek September 15, 2026 20:16

@EricAndrechek EricAndrechek left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall really good stuff. My findings:

  • Ports should be handed out etc by the test orchestrator to find an open port and use that etc, and/or use test containers, so ideally we shouldn't have a port dynamically picked by a test at all. If a port IS taken and is being handed to our process, ie we are testing what happens when wavehouse is trying to be deployed on a server where an old process is running and holds that port, my take is it should fail right away. I think that is what your test in app_test.go is trying to test for, but on my mac I guess this behavior is slightly different and so that turns into a hang instead of failing. TBH I don't know enough about the networking specifics between Darwin/BSD and GNU+Linux to know what/why the behaviors there are different, so I had Claude help me debug that one a bit and it gave some suggestions in-line on that file.
  • On stopping the server (ie SIGTERM) we should stop accepting any new in-flight messages and end all SSE connections effectively right away, end all the workers and subscribers to NATS events, watchers/fsnotify, etc, and then hopefully by then any remaining in-flight messages trying to insert or querying a complex query etc have finished and have returned, otherwise I guess probably non-gracefully end them? Then the whole first step should be done in ~10 seconds, with more cleanup needed after that too like closing connections to clickhouse, pebble/remote dedupe store if an interface exists for that (ie future happening soonish), closing MQ (right now in process later remote we disconnect from), caches, OTEL cleanup, etc – and ideally this final cleanup/next steps should also be bounded by like a 10 second context or something – potentially we may want to move the OTeL cleanup to actually happen independently at the very end with its own context too so we get debug info etc from the cleanup process too. Not really sure what the best path is, but I think a 2/3 tiered shutdown process like this is probably/hopefully sufficient?
  • As I understand it, we setup during normal operation SIGTERM/SIGINT to start the kill/cleanup process described above, and SIGHUP is set to trigger it to refresh, so to read in new config/settings for instance, as (as I understand it) that is the norm when deploying on something like nomad for instance, where it can auto SIGHUP when a variable/config changes etc. But I think that that isn't fully happening properly or being handled/ignored correctly once a SIGTERM/SIGINT is triggered and the shutdown process has started – like a SIGTERM and then another SIGTERM I think should kill the process right away basically, or maybe kill the current tier its in shutting down right away? Idk for sure, tbd on that – but then SIGHUP should just be ignored altogether at that stage I think.

Then one other thing I want to mention now and we can discuss/decide if we want this to happen in this PR or not, but I am wondering if this app package is the right spot for us to start to decide which implementation of an abstraction gets used and whether or not it even is booted or not, etc. Will discuss more in-person.

Comment thread internal/app/app_test.go Outdated
Comment thread internal/app/wire.go
Comment thread internal/app/wire.go Outdated
Comment thread internal/app/wire.go Outdated
Comment thread internal/app/app.go Outdated
…nore SIGHUP and exit on a second signal during the stop

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

⚠️ Outside the diff (2)

🟠 Major · Apply the liveness deadline to each HTTP request.

tests/integration/setup_test.go:257
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Apply the liveness deadline to each HTTP request.

setup passes context.Background() to waitForLive. The bound net/http.DefaultClient has no response timeout. If /livez accepts a connection but does not respond, Do can block beyond the loop's 30-second deadline, so the integration suite can hang.

Create a context with the computed deadline and use it for each request.

Proposed fix
 func waitForLive(ctx context.Context, baseURL string, timeout time.Duration) error {
 	deadline := time.Now().Add(timeout)
+	pollCtx, cancel := context.WithDeadline(ctx, deadline)
+	defer cancel()
 	last := "no response"
 	for time.Now().Before(deadline) {
-		req, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+"/livez", nil)
+		req, err := http.NewRequestWithContext(pollCtx, http.MethodGet, baseURL+"/livez", nil)
🟡 Minor · Do not reload after cancellation.

internal/app/wire.go:451-452
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Do not reload after cancellation.

When ctx.Done() and hup are both ready, select can choose hup. Line 452 then calls a.store.Reload after shutdown starts. This can delay Run while reload hooks execute.

Check ctx.Err() before Reload and return when shutdown has started.

Proposed fix
 			case <-hup:
+				if ctx.Err() != nil {
+					return nil
+				}
 				a.store.Reload("sighup")

Based on PR objectives, SIGHUP must be ignored during stopping.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 975ae879-2fdb-4966-910a-f7c54d83bede

📥 Commits

Reviewing files that changed from the base of the PR and between 678d883 and 517aaa3.

📒 Files selected for processing (14)
  • CHANGELOG.md
  • cmd/wavehouse/main.go
  • cmd/wavehouse/main_test.go
  • deployments/compose/standalone.yaml
  • docs/src/content/docs/api.md
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/configuration.mdx
  • docs/src/content/docs/deployment.md
  • internal/api/stream.go
  • internal/app/app.go
  • internal/app/app_test.go
  • internal/app/wire.go
  • scripts/orchestrator/main.go
  • tests/integration/setup_test.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
WH001 applies to every tracked Markdown file, with no carve-out

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/configuration.mdx
  • CHANGELOG.md
  • docs/src/content/docs/api.md
  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/architecture.md
In MDX, leave a blank line between a JSX tag and a code fence.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/configuration.mdx
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-16T15:19:39.771Z
Learning: Run `make lint` and `make test` before considering work complete.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-16T15:19:39.771Z
Learning: Every new function should have corresponding test cases.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-16T15:19:39.771Z
Learning: Validate locally before every push
📚 Learning: 2026-08-13T12:17:52.620Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 470
File: docs/src/content/docs/reverse-proxy.mdx:137-144
Timestamp: 2026-08-13T12:17:52.620Z
Learning: For Wave-RF/WaveHouse documentation, verify claims about implementation control flow against the authoritative implementation source (for example, internal/auth/auth.go) rather than relying solely on docs/** content. Documentation may lag behind or paraphrase behavior, so control-flow claims should be confirmed in source code.

Applied to files:

  • docs/src/content/docs/configuration.mdx
📚 Learning: 2026-06-10T15:01:09.027Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: docs/src/content/docs/development.md:0-0
Timestamp: 2026-06-10T15:01:09.027Z
Learning: In this repo’s Markdown review (all .md files), do not flag capitalization/style issues for literal paths starting with ".github/" (or any substring that is a path beginning with ".github/"). Treat ".github" as the correct lowercase dotfile directory name, even when it appears inside prose or code spans; automated checks such as LanguageTool’s "(GITHUB)" rule commonly produce false positives for this literal filesystem path.

Applied to files:

  • CHANGELOG.md
🪛 LanguageTool
CHANGELOG.md

[uncategorized] ~23-~23: The official name of this software platform is spelled with a capital “H”.
Context: ...on/setup_test.go, .testcoverage.yml, .github/labeler.yml): app.New` builds every c...

(GITHUB)

docs/src/content/docs/architecture.md

[style] ~90-~90: Since ownership is already implied, this phrasing may be redundant.
Context: ...ons.Listenerlets one serve the API on its own listener instead ofserver.port`. - **...

(PRP_OWN)

🔇 Additional comments (13)
CHANGELOG.md (1)

23-23: LGTM!

docs/src/content/docs/architecture.md (4)

55-55: LGTM!


90-91: LGTM!


129-129: LGTM!


148-150: LGTM!

docs/src/content/docs/configuration.mdx (2)

45-45: LGTM!


143-143: LGTM!

scripts/orchestrator/main.go (1)

311-322: LGTM!

docs/src/content/docs/deployment.md (1)

313-323: LGTM!

docs/src/content/docs/api.md (1)

598-598: LGTM!

deployments/compose/standalone.yaml (1)

53-56: LGTM!

internal/app/app.go (1)

4-11: LGTM!

Also applies to: 68-73, 112-114, 174-180, 187-187

internal/app/app_test.go (1)

19-19: LGTM!

Also applies to: 117-117, 142-143, 214-235, 262-262, 361-366, 373-382, 383-410

Comment thread cmd/wavehouse/main.go Outdated
Comment thread internal/api/stream.go
Comment thread internal/app/wire.go Outdated
…on a second signal, and bound Close with a context
coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 16, 2026
@taitelee

Copy link
Copy Markdown
Member Author

@coderabbitai review

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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 1ebd3e0a-81bb-4516-8f2d-3b5ab3056569

📥 Commits

Reviewing files that changed from the base of the PR and between ecb2d22 and ae1e70a.

📒 Files selected for processing (4)
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/deployment.md
  • internal/app/app.go
  • internal/app/app_test.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
WH001 applies to every tracked Markdown file, with no carve-out

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/deployment.md
🧠 Learnings (1)
📚 Learning: 2026-06-26T12:23:22.696Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 346
File: internal/stream/subscriber_test.go:9-28
Timestamp: 2026-06-26T12:23:22.696Z
Learning: In this Go repository, prefer table-driven tests (e.g., `[]struct{...}` with `t.Run(...)`) only for tests that cover multiple scenarios/inputs and can be cleanly enumerated. Do not artificially rewrite a clear single-scenario sequential behavioral-flow test into a table-driven form just to fit the pattern; if there’s only one meaningful scenario, keep the test as a straightforward linear flow (as in `TestSubscriber_SendDeliversThenDropsWhenFull`).

Applied to files:

  • internal/app/app_test.go
🪛 LanguageTool
docs/src/content/docs/architecture.md

[style] ~90-~90: This phrase is redundant. Consider writing “last”.
Context: ...nt. The SIGHUP registration is released last of all. Handler, Registry, and MQ expose...

(LAST_OF_ALL)


[style] ~90-~90: Since ownership is already implied, this phrasing may be redundant.
Context: ...ons.Listenerlets one serve the API on its own listener instead ofserver.port`. - **...

(PRP_OWN)

🔇 Additional comments (2)
docs/src/content/docs/architecture.md (1)

55-55: LGTM!

Also applies to: 90-91, 129-129, 148-150

docs/src/content/docs/deployment.md (1)

318-318: LGTM!

Comment thread internal/app/app.go
@coderabbitai

coderabbitai Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed the last commit. Use @coderabbitai full review to rerun a review of the entire changeset.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@taitelee

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 17, 2026
EricAndrechek
EricAndrechek previously approved these changes Sep 17, 2026
Comment thread tests/integration/otel_test.go Outdated
Comment thread internal/app/app_test.go Outdated
@taitelee

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-project-automation github-project-automation Bot moved this from In review to In progress in WaveHouse Task Board Sep 17, 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.

Caution

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

⚠️ Outside diff range comments (1)

🟡 Minor · Restore the global OTel logger provider. · app_test.go:100-107

internal/app/app_test.go:100-107
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Restore the global OTel logger provider. observability.InitProvider installs a process-global logger provider when OTel logs are enabled. Successful shutdown closes that provider but does not restore the previous provider. Add the go.opentelemetry.io/otel/log/global import and restore it in guardGlobals.

Suggested change
	savedLP := global.GetLoggerProvider()
	t.Cleanup(func() {
		slog.SetDefault(savedLogger)
		otel.SetTextMapPropagator(savedProp)
		otel.SetTracerProvider(savedTP)
		otel.SetMeterProvider(savedMP)
		global.SetLoggerProvider(savedLP)
	})

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 0bf3469d-ddf4-468d-b730-eb50f8d2f95c

📥 Commits

Reviewing files that changed from the base of the PR and between 77c8557 and 0832429.

📒 Files selected for processing (2)
  • internal/app/app_test.go
  • tests/integration/otel_test.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-17T13:37:35.707Z
Learning: Run `make lint` and `make test` before considering work complete.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-09-17T13:37:35.707Z
Learning: DRY — one source of truth.
🔇 Additional comments (2)
internal/app/app_test.go (1)

391-394: LGTM!

tests/integration/otel_test.go (1)

218-218: LGTM!

Also applies to: 313-315

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/app Process wiring (internal/app): component build, run, release area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release area/observability Metrics, logs, traces, health, profiling 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.

refactor(cmd): extract app.App to dedupe main.go wiring + setup_test.go::buildServer

2 participants