Skip to content

[DPE-10881] feat(database): publish client endpoints from the manager (3/7) - #226

Open
marceloneppel wants to merge 2 commits into
feat/02-db-lifecyclefrom
feat/02-db-endpoints
Open

[DPE-10881] feat(database): publish client endpoints from the manager (3/7)#226
marceloneppel wants to merge 2 commits into
feat/02-db-lifecyclefrom
feat/02-db-endpoints

Conversation

@marceloneppel

@marceloneppel marceloneppel commented Aug 18, 2026

Copy link
Copy Markdown
Member

Issue

Third slice of the postgresql_provider.py migration. The client relation's read/write and read-only endpoints are still published by the charm, which is why ConfigManager currently calls back through a refresh_endpoints bridge.

Solution

Completes managers/database.py with update_endpoints(), plus CharmState.replicas_endpoint alongside the primary endpoint it already exposed.

The two substrates compute the addresses differently and both forms are kept:

  • VM reads the online Patroni members, skips members tagged nosync, takes the leader's database-address as the read/write endpoint and the replicas' as the read-only ones, falling back to the primary when there are no replicas. A stale leader unit missing from the peer data publishes its address verbatim (None:5432), exactly as the charm did, so a cutover diff stays empty in that corner. Handling on https://warthogs.atlassian.net/browse/DPE-10889.
  • K8s reads the <app>-primary and <app>-replicas Services.

One behaviour change. The read-only connection URI is now built from the read-only hosts rather than the read-only endpoints. On K8s, ro_endpoints already carried :5432, and interpolating it into the URI template published a second one. Reproduced on postgresql-k8s 16/edge rev 943 against data-integrator:

uris:           postgresql://relation_id_4:…@postgresql-k8s-primary.<model>.svc.cluster.local:5432/<db>
read-only-uris: postgresql://relation_id_4:…@postgresql-k8s-primary.<model>.svc.cluster.local:5432:5432/<db>

No client can dial that host. VM built the same URI from the port-less ro_hosts and was correct; both substrates now use that form.

Follow-up lib PRs:

Wiring up at:

Checklist

  • I have added or updated any relevant documentation.
  • I have cleaned any remaining cloud resources from my accounts.

@marceloneppel marceloneppel changed the title [DPE-10841] feat(database): publish client endpoints from the manager (3/7) [DPE-10881] feat(database): publish client endpoints from the manager (3/7) Aug 20, 2026
Completes the client-relation manager with the read/write and read-only endpoint
publishing the config flow refreshes today through a charm bridge. VM derives the
addresses from the online Patroni members, filtering out-of-sync ones; K8s reads
the primary and replicas Services, so CharmState grows the replicas endpoint to
match the primary one it already exposed.

The read-only connection URI is now built from the read-only hosts rather than
the read-only endpoints. K8s interpolated a value that already carried the port
and published postgresql://user:pw@host:5432:5432/db, which no client can dial;
VM's port-less form is the one both substrates now use.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
Coercing a missing leader peer address to an empty string changed the
published endpoint from the charm's literal "None:5432" to ":5432".
Both are undialable in that stale-member edge case, but the port kept
the charm's exact output so a cutover diff stays empty.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
@marceloneppel
marceloneppel marked this pull request as ready for review August 20, 2026 20:53
@marceloneppel
marceloneppel requested a review from a team as a code owner August 20, 2026 20:53
@marceloneppel
marceloneppel requested review from carlcsaposs-canonical, dragomirp, juju-charm-bot and taurus-forever and removed request for a team August 20, 2026 20:53
Comment on lines +440 to +447
def _unit_ip(self, unit: Unit) -> str | None:
"""The client-facing address a peer unit published for this relation."""
if not self.state.peer_relation:
return None
try:
return self.state.peer_relation.data[unit].get(f"{self.relation_name}-address")
except KeyError:
return None

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.

This can easily go in the state

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants