Skip to content

fix: fail fast on transient DB errors in custom domain verification#2047

Merged
bflad merged 1 commit intomainfrom
bflad/age-1630-bug-do-not-create-domains-on-transient-lookup-errors
Apr 2, 2026
Merged

fix: fail fast on transient DB errors in custom domain verification#2047
bflad merged 1 commit intomainfrom
bflad/age-1630-bug-do-not-create-domains-on-transient-lookup-errors

Conversation

@bflad
Copy link
Copy Markdown
Member

@bflad bflad commented Mar 31, 2026

Previously, any error from GetCustomDomainByDomain triggered domain creation. Now only pgx.ErrNoRows does — other errors (e.g. connection failures) return immediately instead of creating spurious domain records.

Introduces a dns.Resolver interface whose method signatures match *net.Resolver, enabling deterministic DNS testing without network calls. MockResolver exposes a Resolver() method that returns a real *net.Resolver whose Dial function routes queries through configurable mock functions via in-memory net.Pipe connections with TCP-framed dnsmessage wire protocol. This mirrors the approach used internally by Go's own net package tests (fakeDNSServer in dnsclient_unix_test.go). An alternative using miekg/dns to spin up a real localhost UDP server was considered — it would simplify the wire format handling but adds an external dependency and real sockets per test; the stdlib-only dial approach was chosen to avoid both.

Adds lint rules via forbidigo banning direct net.Lookup* calls in favor of dns.NewNetResolver().

Linear: https://linear.app/speakeasy/issue/AGE-1630/bug-do-not-create-domains-on-transient-lookup-errors

@bflad bflad requested a review from a team as a code owner March 31, 2026 20:38
@linear
Copy link
Copy Markdown

linear bot commented Mar 31, 2026

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 31, 2026

🦋 Changeset detected

Latest commit: 65726e5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
server Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gram-docs-redirect Ready Ready Preview, Comment Apr 2, 2026 0:48am

Request Review

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 0 potential issues.

View 1 additional finding in Devin Review.

Open in Devin Review

@bflad bflad force-pushed the bflad/age-1630-bug-do-not-create-domains-on-transient-lookup-errors branch from 2b7c319 to 6c4d7de Compare April 1, 2026 14:09
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +155 to +176
func TestVerifyCustomDomain_TransientDBError(t *testing.T) {
t.Parallel()

const orgID = "org-transient"
const domain = "transient.example.com"
ctx, ti := newTestInstance(t, orgID, domain)
activity := newActivity(t, ti)

// Close the pool to simulate a transient DB error during GetCustomDomainByDomain
ti.conn.Close()

err := activity.Do(ctx, activities.VerifyCustomDomainArgs{
OrgID: orgID,
Domain: domain,
CreatedBy: urn.NewPrincipal(urn.PrincipalTypeUser, "test-user"),
})
require.Error(t, err)

// The error should NOT be about domain creation — it should be the lookup failure.
// Before the fix, this would have attempted to create a domain.
assert.NotContains(t, err.Error(), "error creating custom domain")
}
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot Apr 1, 2026

Choose a reason for hiding this comment

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

🚩 Test for transient DB error exercises Begin() failure, not the new switch default case

In TestVerifyCustomDomain_TransientDBError (line 155-176), ti.conn.Close() closes the entire connection pool. When activity.Do runs, d.db.Begin(ctx) at verify_custom_domain.go:68 fails immediately because the pool is closed, returning the error "failed to access custom domains" before reaching the new switch at line 77. The test assertion NotContains(t, err.Error(), "error creating custom domain") passes, but it doesn't actually exercise the new default branch at line 102-103. To truly test a transient error during GetCustomDomainByDomain, the test would need to inject a failure after Begin succeeds (e.g., via a custom DBTX wrapper that fails on the query). The sanity-check test TestGetCustomDomainByDomain_ReturnsErrNoRows at line 349-356 is a good addition that validates the ErrNoRows assumption, but the transient error scenario's specific code path remains untested.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Custom dbtx wrapper, eh? Not sure the juice is worth the squeeze right now, its bad enough this had like zero testing before.

@bflad bflad force-pushed the bflad/age-1630-bug-do-not-create-domains-on-transient-lookup-errors branch from 6c4d7de to 4f3b70b Compare April 1, 2026 16:30
devin-ai-integration[bot]

This comment was marked as resolved.

@bflad
Copy link
Copy Markdown
Member Author

bflad commented Apr 2, 2026

Rebasing and fixing the things Devin found.

https://linear.app/speakeasy/issue/AGE-1630/bug-do-not-create-domains-on-transient-lookup-errors

Previously, any error from `GetCustomDomainByDomain` triggered domain
creation. Now only `pgx.ErrNoRows` does — other errors (e.g. connection
failures) return immediately instead of creating spurious domain records.

Introduces a `dns.Resolver` interface whose method signatures match
`*net.Resolver`, enabling deterministic DNS testing without network
calls. `MockResolver` exposes a `Resolver()` method that returns a real
`*net.Resolver` whose `Dial` function routes queries through
configurable mock functions via in-memory `net.Pipe` connections with
TCP-framed `dnsmessage` wire protocol. This mirrors the approach used
internally by Go's own `net` package tests (`fakeDNSServer` in
`dnsclient_unix_test.go`). An alternative using `miekg/dns` to spin up
a real localhost UDP server was considered — it would simplify the wire
format handling but adds an external dependency and real sockets per
test; the stdlib-only dial approach was chosen to avoid both.

Adds lint rules via `forbidigo` banning direct `net.Lookup*` calls in
favor of `dns.NewNetResolver()`.
@bflad bflad force-pushed the bflad/age-1630-bug-do-not-create-domains-on-transient-lookup-errors branch from 4f3b70b to 65726e5 Compare April 2, 2026 12:48
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

@bflad bflad merged commit c0d3215 into main Apr 2, 2026
36 checks passed
@bflad bflad deleted the bflad/age-1630-bug-do-not-create-domains-on-transient-lookup-errors branch April 2, 2026 12:58
@github-actions github-actions bot locked and limited conversation to collaborators Apr 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants