Skip to content

[DPE-10882] feat(database): own the client relation events, drop the endpoint bridge (4/7) - #219

Open
marceloneppel wants to merge 3 commits into
feat/02-db-endpointsfrom
feat/02-db-events
Open

[DPE-10882] feat(database): own the client relation events, drop the endpoint bridge (4/7)#219
marceloneppel wants to merge 3 commits into
feat/02-db-endpointsfrom
feat/02-db-events

Conversation

@marceloneppel

@marceloneppel marceloneppel commented Aug 17, 2026

Copy link
Copy Markdown
Member

Issue

The manager from PRs 1–3 has no observers, and ConfigManager still reaches back into the charm to refresh endpoints and is still handed a user hash the charm derives from provider state.

Solution

Adds events/database.py, which owns DatabaseProvides, observes database-requested, relation-departed and relation-broken, and constructor-injects the interface into DatabaseManager — the same shape as the TLS handler and its certificate requirers.

Each handler keeps its readiness guard and its work in one observer. defer() is per-observer, so splitting a guard from the action it protects lets a deferred action retry alone against state its guard never re-checked. That is the same failure mode as the TLS reload race in canonical/postgresql-k8s-operator#1618.

The two substrates' guards differ and both are kept. A request waits on the cluster being initialised plus, on VM, a started member and a reachable primary; on K8s, primary_endpoint_ready. A removal waits on the same predicate on VM, but on K8s settles for a started member. The K8s handler also wrote the primary Service endpoint, URI and TLS fields inline before update_endpoints ran; that write is preserved so databag write ordering is unchanged, though it may be redundant (need to validate in https://warthogs.atlassian.net/browse/DPE-10890) — the provider library emits database_requested on the leader only, and update_endpoints publishes the same values immediately after. Each substrate also keeps its own defer log wording.

One smaller divergence is deliberate: the endpoint refresh after a request narrows to the requesting relation on VM, while K8s keeps its whole-relation refresh — each exactly as its charm had it.

One parity gap is fixed up-stack rather than here: the requested_entity_secret_content read needs the ModelError guard the charms wrapped their whole _get_credentials in, because the vendored property raises while a cross-model grant is still pending.

Seam changes:

  • ConfigManager takes the DatabaseManager and calls update_endpoints() itself. The refresh_endpoints charm bridge is deleted.
  • update_config's user_hash parameter is gone; the manager derives it from the same relation data the user-databases map comes from. relations_user_databases_map stays injected — its computation lives in charm.py and diverges between substrates, so it belongs to the core event-loop phase.
  • AbstractPostgreSQLCharm gains set_unit_status, update_config and primary_endpoint. The first routes status writes through the charm_refresh priority gate and stays until the refresh logic itself migrates into the library; the other two are reclaimed by later phases.

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): own the client relation events, drop the endpoint bridge (4/7) [DPE-10882] feat(database): own the client relation events, drop the endpoint bridge (4/7) Aug 20, 2026
…bridge (4/6)

Wires the client relation into the library: the handler owns DatabaseProvides,
observes database-requested, relation-departed and relation-broken, and
constructor-injects the interface into the manager the way the TLS handler
already injects its certificate requirers.

Each handler keeps its readiness guard and its work in the same observer. defer()
is per-observer, so a guard split across observers would let a deferred action
retry alone against state its guard never re-checked.

With endpoint publishing lib-side, the config manager calls the database manager
directly and the refresh_endpoints charm bridge goes away, as does the injected
user hash, which is now derived from the same relation data the map is. The
charms gain bridges for the refresh-aware status write, the config re-render and
the substrate's primary lookup, all of which their own migration phases reclaim.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
K8s 16/edge called update_endpoints() with no event, republishing
endpoints to every relation from the request handler; scoping the call
to the requesting relation matched VM only and left sibling relations
unrefreshed whenever update_config early-exits. Keep the per-substrate
call shape, and give each substrate back its own defer log wording.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The abstract charm claimed set_unit_status is permanent because
charm_refresh is not a migration target, contradicting the manager's
comment on the same seam. The refresh logic does migrate into the
library in its own phase (as the Mongo and OpenSearch kernels already
did), so the seam is interim and both comments now say so.

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
self.relation_name = relation_name

self.database_provides = DatabaseProvides(charm, relation_name=relation_name)
self.manager = DatabaseManager(

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.

Why it is initialized here? shouldn't be in the Charm ?

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.

4 participants