Skip to content

feat(db): accept a read replica ID in DB operation commands#177

Draft
aprimakina wants to merge 4 commits into
mainfrom
feat/db-connect-replica-id
Draft

feat(db): accept a read replica ID in DB operation commands#177
aprimakina wants to merge 4 commits into
mainfrom
feat/db-connect-replica-id

Conversation

@aprimakina

@aprimakina aprimakina commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The tiger db connection commands (connection-string, connect/psql, test-connection, schema) and the MCP db_execute_query / db_schema tools now accept a read replica set ID wherever a service ID is accepted. A replica ID connects to that replica's endpoint using the parent primary's credentials (replicas share them); a primary ID behaves as before.

The API resolves a replica ID via the normal service-get endpoint, returning the replica's endpoint plus a forked_from link to its parent — so credentials (and password reset) resolve against the parent.

The db connection commands (connection-string, connect/psql,
test-connection, schema) and the db_execute_query / db_schema MCP tools
now resolve a read replica set ID as well as a service ID: it's tried as
a service first, then matched against the project's read replica sets.
A replica ID connects to that replica's endpoint while credentials
resolve against the parent service (replicas share the primary's
credentials).

When --pooled is requested for a replica that has no pooler, all
surfaces warn and fall back to a direct connection. Service-management
commands still treat a replica ID as not found.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aprimakina aprimakina self-assigned this Jul 15, 2026
@aprimakina aprimakina changed the title feat(db): accept a read replica ID wherever a service ID is accepted feat(db): accept a read replica ID in DB operations commands Jul 15, 2026
aprimakina and others added 3 commits July 15, 2026 12:06
Read replica ID resolution now:
- validates a matched replica is active and has an endpoint, returning a
  clear "not active" / "no endpoint" error instead of failing late at
  connect time. A new ErrReplicaNotFound sentinel distinguishes a real
  no-match (fall back to the service error) from a matched-but-unusable
  replica (surface it).
- only scans the project's services when the service lookup failed with a
  404; other failures (auth, network, 5xx) are surfaced as-is via the new
  common.IsNotFound helper, avoiding a wasted service-list call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… scan

The API resolves a read replica ID through the normal service-get endpoint,
returning the replica's own endpoint plus a forked_from link to its parent
(is_standby, service_id). Use that instead of scanning the project's services:

- common.ResolveConnectionTarget turns a fetched service into a
  ConnectionTarget{Connect, Credential, IsReplica}. A standby replica connects
  to its own endpoint but resolves credentials (and password reset) against the
  parent primary, fixing the "password authentication failed" error when
  connecting to a replica by ID.
- Removes the ResolveServiceOrReplica/FindReplicaByID list-scan machinery, the
  IsNotFound helper, GetReplicaConnectionDetails, ConnectToService/ConnectToTarget.
- Consolidates the pooler policy: ConnectionTarget.Details owns the
  build-and-RequirePooler rule; ReplicaPoolerWarning owns the replica guard;
  resolveConnectTarget handles both the by-ID replica and the interactive menu.
- getServiceDetails delegates to the shared common.GetService.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the redundant "in place of the service ID" and doubled "read replica"
wording from the db connection commands' help and the README, and remove the
read-only/lag aside from `db connect` help (kept in the MCP tool description).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aprimakina aprimakina force-pushed the feat/db-connect-replica-id branch from a4104cd to 57e8469 Compare July 15, 2026 11:59
@aprimakina aprimakina requested a review from Askir July 15, 2026 12:06
@aprimakina aprimakina changed the title feat(db): accept a read replica ID in DB operations commands feat(db): accept a read replica ID in DB operation commands Jul 15, 2026

@Askir Askir left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Naming confused me a lot here but I think otherwise its good.

What happens if you use update-password, save-password and tiger db create role with a read replica? For the record I think its fine if update-password and create role fail (its a read replica after all so read only) but it should probably have a good error. For save-password I genuinely am not sure if it should override the parents password or error out.

Comment thread internal/tiger/cmd/db.go
}

service, err := getServiceDetailsFunc(cmd, cfg, args)
target, err := resolveConnectionTarget(cmd, cfg, args)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We have resolveConnectionTaget and resolveConnectTarget now doing wildly different things. But reading as if someone typo'd.
Maybe we could clarify these two? One is the connection chooser and returns ConnectionDetails as the result, the other one is the lookup of the ID

Comment on lines +16 to +20
type ConnectionTarget struct {
Connect api.Service
Credential api.Service
IsReplica bool
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I find this poorly named, it took me a long time while reading to understand that Credential is only the CredentialService/ the service where we resolve the credentials from rather than the actual credentials.

I think it would make sense to at least call the two ConnectionService and CredentialService. Sorry for being a bit pedantic about naming, here. I try not to do that usually but I really struggled understanding what's happening here 😅

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