feat(db): support explicit Postgres DSNs#759
Conversation
🔐 Codex Security Review
Review SummaryOverall Risk: MEDIUM Findings[MEDIUM] DB_DSN deployments remain blocked by bundled TimescaleDB health
NotesNo auth bypass, SQL injection, command injection, cryptostealing/pool hijack, or protobuf wire-format issues were found in the scoped diff. Focused Go tests could not be run because this sandbox is read-only and Go could not create module/build cache directories. Generated by Codex Security Review | |
1f85998 to
000a186
Compare
There was a problem hiding this comment.
Pull request overview
Adds support for providing an explicit PostgreSQL connection string via DB_DSN, while keeping the existing DB_USERNAME/DB_PASSWORD/DB_NAME/DB_ADDRESS/DB_SSL_MODE configuration path. It also expands DB retry behavior to classify failover-like errors, optionally reset idle pools on those errors, and selectively retry read-only QueryContext calls.
Changes:
- Add
DB_DSNsupport to DB config, including DSN parsing/validation and multi-host safety checks. - Extend
RetryDBwith failover-class error detection, optional pool reset, and read-only query retry behavior. - Wire retry/pool-reset options through Fleet’s SQL store constructors and deployment compose/docs to pass through
DB_DSN.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| server/internal/infrastructure/db/config.go | Adds explicit DSN support, DSN validation, and multi-host/read-write targeting checks. |
| server/internal/infrastructure/db/config_test.go | Adds unit coverage for DSN precedence, validation, multi-host detection, and non-echo invalid DSNs. |
| server/internal/infrastructure/db/database_connection.go | Validates config before opening and logs/returns connection context using ConnectionTarget(). |
| server/internal/infrastructure/db/retry.go | Adds failover-class error classification, pool reset option, and read-only query detection for failover retries. |
| server/internal/infrastructure/db/retry_test.go | Adds tests for failover classification, pool reset behavior, and read-vs-write retry semantics. |
| server/internal/handlers/minerproxy/handler.go | Threads RetryDBOption variadic options through handler construction. |
| server/internal/domain/stores/sqlstores/sql_connection_manager.go | Propagates RetryDB options into SQLConnectionManager via a type alias. |
| server/internal/domain/stores/sqlstores/user.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/transactor.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/site.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/session.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/schedule.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/pool.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/notification_history.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/infrastructure_device.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/fleetnodepairing.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/fleetnodeenrollment.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/fleetnodeauth.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/error.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/discovered_device.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/device.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/curtailment.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/collection.go | Adds an options-aware constructor variant to pass RetryDB options while keeping existing signature. |
| server/internal/domain/stores/sqlstores/building.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/apikey.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/alert_channel.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/stores/sqlstores/activity.go | Updates constructor to accept optional connection manager options. |
| server/internal/domain/miner/service.go | Allows passing RetryDB options into the miner service’s connection manager. |
| server/cmd/fleetd/main.go | Wires a pool-reset function into store constructors and the miner proxy handler. |
| server/cmd/fleetd/main_test.go | Adds coverage that DB_DSN is loaded into the embedded DB config. |
| server/docker-compose.base.yaml | Passes DB_DSN through the base compose environment. |
| deployment-files/docker-compose.yaml | Passes DB_DSN through the deployment compose environment. |
| deployment-files/README.md | Documents DB_DSN override behavior and the multi-host read-write targeting requirement. |
- scope multi-host guard to explicit DSNs while checking parsed fallbacks - build legacy DSNs with userinfo escaping - clarify failover retry log fields
- scope multi-host guard to explicit DSNs while checking parsed fallbacks - build legacy DSNs with userinfo escaping - clarify failover retry log fields
afadb5e to
e66395b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e66395b913
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
097b8d8 to
5631372
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5631372a07
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
5631372 to
6dd434a
Compare
6dd434a to
947dd61
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 947dd61bc8
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
873b4e6 to
1323dfa
Compare
1323dfa to
e6db4a1
Compare
Reviewable diff: +248/-36 across 21 files (excludes generated, test, and story files).
Summary
Fleet can now take an explicit PostgreSQL DSN through
DB_DSNwhile preserving the existingDB_USERNAME/DB_PASSWORD/DB_NAME/DB_ADDRESS/DB_SSL_MODEconfiguration path. This gives on-prem HA deployments a topology-neutral way to point Fleet at a writable PostgreSQL endpoint, including pgx multi-host DSNs that usetarget_session_attrs=read-write.The PR also adds reset-on-failover behavior at Fleet-owned database boundaries. When those paths observe a failover-class error, they drop idle connections so the next operation can reconnect through the HA endpoint; they do not replay ambiguous writes or transactions.
Stack: #768
fix(db): require shared transaction helpers-> #759 this PR. This PR's diff is relative to #768; reviewers should take #768's shared-transaction-helper guard as the upstream context and review this PR for explicit DSN support plus HA pool reset behavior.How it works
At startup,
DB_DSNwins when it is set; otherwise Fleet builds the same legacy PostgreSQL URL from the existing DB fields. The final DSN is parsed by pgx before Fleet opens the connection. Malformed explicit DSNs return a generic invalid-DSN error, pgx configs with more than one distincthost:portendpoint requiretarget_session_attrs=read-write, andhostaddris rejected for now with an explicit "use host" error.Once Fleet opens the shared
*sql.DB, it registers an idle-pool reset callback for that connection. The reset callback is then picked up by the DB paths this PR wires into:RetryDBExecContext/QueryContext, prepared sqlc complete-operation retries, non-transaction store queries returned bySQLConnectionManager.GetQueries(ctx), anddb.WithTransaction(...)begin/action/commit handling. Structured PostgreSQL errors are classified by SQLSTATE first, so non-failoverPgErrors do not fall through to any fallback classifier. Unstructured errors classify as failover only when they match typed/sentinel driver or network failures. Existing serialization/deadlock retry behavior remains unchanged; failover-class errors reset the pool and return the original failure to the caller.Transaction-bound store contexts still use the transaction's
*sqlc.Queries; the transaction helper owns failover reset for begin, body, and commit errors. Code that bypasses Fleet's DB setup or constructs raw sqlc handles directly is outside this automatic reset boundary.Diagrams
Areas of the code involved
server/internal/infrastructure/db/config.goDB_DSN, DSN selection, pgx parsed-config validation, generic malformed-DSN errors, multi-host read-write targeting, and explicithostaddrrejectionserver/internal/infrastructure/db/database_connection.go,pool_reset.goserver/internal/infrastructure/db/retry.goserver/internal/infrastructure/db/failover_reset_querier.go,prepared_querier.go:onequeriesserver/internal/infrastructure/db/with_transaction.go%wserver/internal/domain/stores/sqlstores/**,server/internal/domain/authz/reconcile.go*sqlc.Queriestosqlc.Querierwhere those helpers can receive reset-aware query handlesGetQueries(ctx)while non-transaction paths get the decoratorserver/docker-compose.base.yaml,deployment-files/docker-compose.yaml,deployment-files/README.mdDB_DSNserver/internal/infrastructure/dbandserver/cmd/fleetdKey technical decisions & trade-offs
DB_DSNopt-in and preserve legacy DB fieldstarget_session_attrs=read-writeonly when pgx's final config has more than one distinct endpointhostaddrfor now instead of partially supporting ithost*sql.DB:onecoverageScanerrors without a custom row wrapper; it still relies on stores using the normalSQLConnectionManagerpathTesting & validation
Current stacked validation after simplifying DSN validation:
source ../bin/activate-hermit && go test ./internal/infrastructure/db -run 'TestConfig|TestDSN|TestIsFailoverPostgresError|TestRetrier|TestRetryDB|TestWithTransaction'source ../bin/activate-hermit && go test ./cmd/fleetd -run 'TestFleetdLoadsExplicitDBDSNFromEnv|TestConfig'source ../bin/activate-hermit && go test ./internal/domain/stores/sqlstores ./internal/domain/authz -run '^$'Per local testing policy, DB-backed integration tests were not run locally; CI should cover those surfaces.