Upgrading Aurora Meter Pro to 1.0

Core first, and the requirement will make you

Pro 1.0 declares aurora_meter as >= 1.0.0-rc.1 and < 2.0.0. A host that upgrades Pro while its core is on 0.4.x or 0.5.x does not resolve, and that is deliberate.

The previous requirement was ~> 0.4 or ~> 0.5. ~> 0.4 admits every version below 1.0.0, so Hex resolved the newest published core, which is 0.4.0, and Pro does not compile against 0.4.0: it references AuroraMeter.Checkpoints, AuroraMeter.Event, AuroraMeter.Exporter.Item and AuroraMeter.Schema.PlanTransition, none of which exist before core 0.5.0. No build inside the development workspace could see that, because a sibling checkout of core is always preferred when one is on disk. A clean room found it in a single resolution.

So: upgrade aurora_meter to 1.0 and run its migrations, all of them, following aurora_meter‘s docs/upgrading-to-1.0.md. Then come back here.

The schema route

AuroraMeter.Pro.Migration.latest_version() is 11. Pro 0.3.0 shipped 9.

mix aurora_meter_pro.gen.migration --upgrade -r MyApp.Repo
Order Step What it does Reversible
1 core to schema 10 Pro version 10 reads tables core version 9 creates see core’s guide
2 Pro version 10 Turns every in flight usage_reports.pending_entry into an outbox item Yes, see below
3 Pro version 11 Provider mapping and attribution for scheduled plan transitions Yes

Stop every 0.3.0 reporter before step 2. Version 10 moves staged usage into the outbox. A 0.3.0 reporter still writing pending_entry while that runs stages work the outbox has already taken, and the provider is told twice.

The pending_* columns are deprecated and deliberately not dropped. aurora_meter_usage_reports.pending_entry, pending_usage and pending_since keep their values after version 10, so a rollback to 0.3.0 still has something to resend from. Nothing in 1.0 writes them. They are removed in 2.0.

Two behaviour changes to read before you deploy

  • AuroraMeter.Pro.Live.Dashboard no longer accepts params["tenant"]. It reads session["tenant"] and ignores params entirely; a session with no "tenant" key raises at mount. If your router mounted the dashboard on a route with the tenant in the path, move it into the session in an on_mount hook. A tenant taken from the query string is an authorization decision made by the URL.

  • AuroraMeter.Pro.UsageReporter.report_all/0 stages rather than sends. It flushes and then writes outbox items; the outbox delivers, with retry, idempotency and a quarantine. Code that called report_all/0 and then assumed the provider had been told needs to wait on the outbox instead. See docs/outbox.md.

Erlang/OTP 26 is the floor and Hex cannot express it

A Hex package declares an elixir requirement and there is no field for an OTP floor. A host on OTP 25 will therefore resolve this package and then fail while compiling the Stripe client’s HTTP stack, whose quic dependency declares {minimum_otp_vsn, "26"} in its rebar.config. The floor is in the package description, in README.md and here, and CI runs the suite at it.

Before step 2: the check that says whether any 0.3.0 reporter is still live

Two queries, and version 10 should not run while either has anything to say.

-- staged usage a 0.3.0 reporter would resend
SELECT tenant_key, feature, period_start, pending_usage
FROM aurora_meter_usage_reports
WHERE pending_entry IS NOT NULL
ORDER BY tenant_key, feature;

After version 10 the same query gains AND staged_item_id IS NULL, which is then the set of rows the conversion did not take, and it should be empty.

The wallet cutover in core’s guide has its own check, and it is worth reading its limitation: every node’s "flush:<node>" heartbeat row carries the package version that node is running, so a node too old can be named, but core 0.4.0 has no heartbeat at all and leaves no trace. An empty result there means “no node new enough to be seen is wrong”, not “no node is wrong”.

Rolling back

down is never a rollback. AuroraMeter.Pro.Migration.down/1 refuses six versions (1, 2, 8, 9, 10 and 11) unless you pass confirm_data_loss: true, and names what each destroys: version 10 drops the export outbox, the webhook dedupe table, the source cutovers, the reconciliation items and the recovery actions, which between them are every record of what has already been exported and which inbound provider events have been applied.

After Previous image Why
Pro version 10’s data step not safe. Forward fix only a 0.3.0 reporter resends from pending_entry, which version 10 left in place precisely so a partly upgraded fleet is diagnosable
Pro version 11 safe additive columns only

The supported recovery from a bad upgrade is the backup taken before it, and it has been tested: a pg_dump -Fc taken before core version 7, restored into a fresh database and upgraded again, produced an identical reconciliation document and identical per-table checksums on all 25 tables, including aurora_meter_usage_reports and aurora_meter_outbox_items.

Schemas and prefixes

Pro adds no prefix handling of its own and has no repository setting: every query it issues goes through AuroraMeter.Config.repo/0, so core’s boot-time check covers both packages. AuroraMeter.Pro.Migration.up(prefix: ...) raises ArgumentError, and a migration run under mix ecto.migrate --prefix raises AuroraMeter.Config.PrefixError before any version runs. Non-default Postgres schemas and schema-per-tenant prefixes are not supported in 1.x.

What was rehearsed, and what was not

The published pairing a customer is actually in (core 0.4.0 with Pro 0.3.0, schema core 6 and Pro 9) was upgraded end to end with money in the ledger written by those published releases, not by hand. So was the older pairing core 0.3.2 with Pro 0.2.2.

Timings: the steps that take time are core’s, and its guide carries the measurements. Pro versions 10 and 11 are ordinary transactional DDL plus a data step bounded by the number of reports in flight, which is small: three rows in the rehearsal fixture, converted in under a second.

Version 10 applied cleanly with a node on the published pairing (core 0.4.0, Pro 0.3.0) running against the database, and converted all three staged reports (one pending, two uncertain because they were older than the horizon), leaving none behind. What was not demonstrated is the hazard this section warns about: the rehearsal fixture’s staged rows could not drive a 0.3.0 reporter, so “a live 0.3.0 reporter resends” rests on reading that code rather than on watching it. Stop your reporters anyway; the code that would resend is AuroraMeter.Pro.UsageReporter at 0.3.0 and it is plain enough to read.

Rehearse on a copy of your own data.