Skip to content

[DPE-11482] refactor(refresh): consume the pg_cron-gate lib fix on 16/edge - #2036

Draft
marceloneppel wants to merge 21 commits into
16/edgefrom
pg-cron-gate-consume-16-edge
Draft

marceloneppel wants to merge 21 commits into
16/edgefrom
pg-cron-gate-consume-16-edge

Conversation

@marceloneppel

@marceloneppel marceloneppel commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Issue

Part of the refresh module migration into the single kernel library (canonical/postgresql-single-kernel-library).

The upgrade-family integration tests were deadlocking in a charm crash loop on refresh, and the terraform local-charm refresh tests raced the incompatible-refresh block. Both are fixed on the stacked branch (#1942 + #2031), but that stack sits on add-pg-cron (#2028). This PR brings the same content to 16/edge directly, so it can merge independently of #2028 — #2028's pg_cron work is intentionally not included (the single exception, see Solution, is one config option the library itself requires).

Solution

Combines VM PRs #1942 (refresh-module migration) and #2031 (terraform local-charm race fix), replayed commit-for-commit onto 16/edge (18/18 kept, all re-signed):

  • The VM charm consumes the library's RefreshManager instead of owning its own refresh plumbing: charm-specific refresh helpers deleted (now in the library), RefreshManager.on_init() replaces the post-init resume block, set_unit_status delegates to the manager, and not-yet-migrated side effects move behind duck-typed bridge methods.
  • The library is pinned to the released 16.3.15 (PyPI) — the pg_cron-gate fix from PSKL #354, which gates shared_preload_libraries on what the installed snap ships and unblocks the crash loop. poetry.lock regenerated with the repo's Poetry 2.5.1; the diff is scoped to the library entry.
  • HA upgrade tests: force-refresh-start / resume-refresh wait budgets raised to 30 minutes (the refresh action can take ~18 min end-to-end on CI runners).
  • tests/integration/terraform/test_terraform_deploy.py: the post-refresh wait requires the Refresh incompatible block specifically (unconditional force-refresh-start with a 30-minute budget); the workload-message snap-revision assertion is dropped (the message format varies by charm version).
  • config.yaml: exposes plugin-pg-cron-enable (boolean, default false) — the library's CharmConfig contract requires it at 16.3.15. This is the minimal subset of [DPE-11290] Add pg_cron configuration and tests #2028 the library needs (exposure only, no handler code, no tests); every other option already exists on 16/edge.
  • refresh_versions.toml: snap pins bumped 416/415 → 428/427 (the bump rides in [DPE-11290] Add pg_cron configuration and tests #2028's single commit). Without a snap-revision delta the local-charm refresh is charm-code-only and charm_refresh skips the compatibility check, so the terraform local-charm refresh test would never see the Refresh incompatible block it waits for. The 16/edge snap channel already serves 428.

Unit suite: 150/150 green with the pin; lint (ruff check, ruff format --check) clean.

Checklist

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

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
Adapt the charm to the pinned single-kernel lib tip (abe9a57):
- CharmState now requires s3_requirer: construct one lib-vendored
  S3Requirer(self, s3-parameters) shared by CharmState and PostgreSQLBackups
  (ops permits only one S3Requirer per charm); patch targets in
  test_backups.py follow the vendored module path.
- Declare the status-peers peer relation: CharmState owns StatusesState on
  it at this pin.
- The lib RefreshManager types its charm as AbstractPostgreSQLCharm: pass
  cast(self) with the dispatch surface the refresh manager drives already
  in place. ruff format rides on the pick test_tls patch line.
RefreshManager.reconcile() calls both charm.update_pebble_layers() and
charm.ensure_pgdata_dirs_and_symlinks() on every substrate; the VM charm
mirrors the library VM charm no-op implementations.
… construction

RefreshManager calls the charm-injected set_default_status callable during its
construction (reconcile_refresh_status); _set_primary_status_message reaches
this seam before the refresh_manager attribute is assigned, raising
AttributeError on every charm start. CI surfaced it as hook failures on
refresh-v-three-relation-changed across all upgrade-family integration suites.
Fall back to a direct ops status set until construction completes, mirroring
the direct sets the library itself performs while reconciling.
…resh-manager early publish

The integration branch now carries the /6 stack fix publishing
charm.refresh_manager before charm_refresh construction, so charm_refresh
resume callbacks can dispatch through the manager while its constructor is
still running (unblocks post-snap-refresh resumes on machines).
The refresh-resume path installs the snap through the library's
VMWorkload.install_snap_package, which has no vitality-hint logic, so the
snap name was never appended to resilience.vitality-hint on resume
(test_snap_refresh's post-refresh hint assertion failed deterministically).
Route all install paths through an OOMProtectedVMWorkload subclass that runs
ensure_snap_oom_protection before the snap ensure, delete the duplicated
_install_snap_package helper, and retarget the install unit tests to the
unified seam.
The library's VMWorkload now configures the snap resilience
vitality-hint on every snap-install path, so the charm-side
OOMProtectedVMWorkload subclass, the oom module, its constants, and
their tests move to the library (single-kernel PR #273).
Consume the capability-gated shared_preload_libraries fix from the
lib's draft PR head (canonical/postgresql-single-kernel-library#354,
ce1a35ca) via the commit archive URL instead of the released 16.3.14.
Resolves to 16.3.15 (unreleased). Reverts to a released pin once
the lib PR lands and publishes.
On CI runners force-refresh-start can take ~18 minutes end-to-end
(~7.5 min snap fetch from the store on a cold runner plus ~10 min
post-refresh replication health settle). The charm flow itself
completes; the 600s/900s action budgets just expire mid-settle and
fail the test. Bump force-refresh-start and resume-refresh waits to
30 minutes in the upgrade test files.
…f racing it

The post-refresh wait accepted is_active OR is_blocked, so it exited
~2s after the origin flip - while the charm was still active and before
charm_refresh's compatibility check ran. The test then read
is_blocked == False and skipped force-refresh-start entirely; the
"Refresh incompatible" block landed seconds later and the final
active+idle wait could never succeed, burning its full 1200s budget
(observed on every local-charm run of this branch: zero
force-refresh-start calls in the captured CLI log, app blocked for the
entire window).

Every CI-packed charm is unreleased (...postN.dev0+gSHA), so the block
always arrives - wait for it specifically, then force with
check-compatibility False. Give the forced flow a 30-minute budget
(snap fetch ~7.5 min + replication settle ~10 min on cold runners; the
previous 1200s expired mid-settle). Assert the snap revision actually
advanced, so the test can no longer pass vacuously while the refresh
never progressed (as it did on the base branch).
units is dict[str, UnitStatus]: iterating it yields the unit-name keys,
so the pre-revision capture crashed with AttributeError ('str' object
has no attribute 'workload_status') at test start - before any refresh
step - on both arches in CI. Take the UnitStatus from units.values()
(matching the post-refresh capture) and keep the name key for the
juju.run unit argument.
The terraform module deploys from 16/stable by default, and the
16/stable charm's workload message does not advertise the snap
revision (e.g. just "Primary"), so the strict pre-capture regex
missed and raised AttributeError at test start. Parse both revisions
optionally: compare the progression when the deployed charm reports
one, and rely on the forced refresh completing (which only returns
once the pinned snap is installed and replication is healthy) plus
the refreshed unit's own revision report otherwise.
The workload message content varies by charm version (16/stable
reports just "Primary", the refreshed charm reported an empty
message in the VM run), so parsing it is fragile: the strict
post-capture raised AttributeError on an empty message after the
forced refresh had already succeeded. The flow's progression is
guaranteed without it: the block-wait only succeeds once charm_refresh
blocked as incompatible, force-refresh-start only returns after the
pinned snap is installed and replication is healthy, and a refresh
that never progressed stays blocked and fails the waits.
The pg_cron-gate fix (PSKL #354) is merged and published on PyPI as
16.3.15, so the plain released pin replaces the ce1a35ca archive URL
(superset convention: 16.3.15 > 16.3.12, contains the refresh module,
early-publish fix, OOM protection and the capability-gated
shared_preload_libraries fix).
Lib 16.3.15's CharmConfig declares plugin_pg_cron_enable as required;
the option rides the generic plugin-option path and ships disabled by
default. (§4 ruling of the handoff: minimal #2028 subset, exposure
only, no handler code or tests.)
@marceloneppel marceloneppel added the not bug or enhancement PR is not 'bug' or 'enhancement'. For release notes label Oct 12, 2026
@github-actions github-actions Bot added the Libraries: Out of sync The charm libs used are out-of-sync label Oct 12, 2026
Without a snap-revision delta the local-charm refresh is charm-code-only,
so charm_refresh skips the compatibility check and never raises the
Refresh incompatible block the terraform refresh test waits for (the
deployed 16/stable charm pins the same snap revision this charm would).

This branch has not been deployed

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

Labels

Libraries: Out of sync The charm libs used are out-of-sync not bug or enhancement PR is not 'bug' or 'enhancement'. For release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant