Upgrading to 1.0

What 1.0 starts refusing, why the work happens on the transition release before you install it, the schema steps that have an order, the wallet migration that has no rollback, and the four things that do not change at all.

Free core 16 min Advanced

Aurora Meter 1.0 refuses several things that earlier versions allowed, and it introduces a schema and a credit engine that existing installations have to move onto deliberately. None of it is hard, and all of it goes wrong if you do the steps in the wrong order.

This guide explains what changes and why. The package’s own docs/upgrading-to-1.0.md and docs/upgrading-to-lots.md are the normative procedures: every command lives there, and where this page and those pages disagree, believe the package.

#Take the transition release first

The single most useful thing you can do is not upgrade to 1.0. It is to upgrade to the 0.5.x transition release, change nothing, and read your logs for a week.

0.5.0 warns about everything 1.0 will refuse and refuses nothing itself. It adds no schema version, writes nothing new to your database, and rolling back to 0.4.x is as safe as rolling forward. Its whole job is to turn a future failed deploy into a log line you can read today.

{:aurora_meter, "~> 1.0"}

One thing in 0.5.0 changes rather than warns. A custom period_source that returns an interval which cannot be correct now raises at first use, naming your module. The default calendar source and Pro’s subscription aligned source both satisfy the contract, so this only affects you if you wrote your own. Run your suite against 0.5.0 before deploying it.

#What 1.0 starts refusing

Each of these logs once per node in 0.5.x, so you will see them before they matter.

What you will see in 0.5.x What 1.0 does What to do
a config key Aurora Meter does not know, with the nearest one it does refuses to boot fix the spelling, or move the key under the library it belongs to
a feature that is not declared on the tenant’s plan the entitlement functions deny it declare it on the plans that should have it
subscribe/2 given a plan id no plans module declares raises, naming the plans it does know fix the id, or declare the plan
a default_plan that is not declared raises at boot point it at a plan that exists
a feature name that arrived as a binary rather than an atom raises pass the atom
a tenant key that resolved to an empty string raises return a non empty binary from to_key/1

That fifth row is worth a sentence of its own. Aurora Meter never calls String.to_atom/1 on a feature name and never will, because that is how a host turns user input into an unbounded atom table. A binary feature name also creates a second counter key that seeds from and flushes into the same database row as the atom, which is the kind of bug that shows up as a number that is almost right.

durable_features is on the deprecated list rather than the refused list. It keeps working until 2.0. There is nothing to do today.

#The undeclared feature policy, one step at a time

This is the setting whose new default can change what your application answers, so the package documents a staged sequence rather than a flag day. In outline:

  1. Upgrade to 0.5.0 and change nothing. The default is :warn, which behaves exactly like 0.4.x.
  2. Run mix aurora_meter.features to see every reference your configuration makes and every plan gap. The gaps are the ones that change behaviour.
  3. Let it run for a week. The task reads configuration, not source, so a feature named only at a call site is invisible to it. The warning log and the declared: false metadata on track telemetry are what find those.
  4. Make it fail in CI with mix aurora_meter.features --strict.
  5. Set undeclared_feature_policy: :deny in staging, then production, while still on 0.5.x. Then 1.0 changes nothing, because you are already running its default.

If you would rather keep the old behaviour through the upgrade and deal with it later, that is supported and it is one line: undeclared_feature_policy: :allow. It is explicit, it is documented and it will not be removed in 1.x.

What 1.0 refuses is not the permissive behaviour. It is inheriting the permissive behaviour by accident.

During the transition, :raise is useful in your test environment and nowhere else. It turns an undeclared feature into a failing test carrying the feature, the tenant key, the plan id and the entry point.

#The schema steps, in order

1.0 raises the schema version, and two of the steps have conditions that are easy to miss.

  • Generate the migration with mix aurora_meter.gen.migration, passing --from the version you are on. It is a no op when there is nothing to apply.
  • Run the events backfill after the durable event identity version and before the unique index version. A backfill that runs after the index has nothing to work with, and an index built before the backfill has rows it cannot cover.
  • The unique index version builds concurrently, and must live in its own migration file with the DDL transaction and the migration lock both disabled. Sharing a file with anything else raises a named error rather than deadlocking your deploy, which is the library refusing to let you find this out in production.
  • Some versions can lose data on the way down. The down migration for those requires you to say so explicitly. That is not a formality; read what it drops before you type it.

After the schema, AuroraMeter.Plans.register!/0 runs at boot, writes a snapshot of every compiled plan version, and backfills a version label onto subscription rows that predate versioning. It runs in batches, it is idempotent, and it must not be called inside a transaction of your own. Until it has run, scheduling a plan transition answers {:error, {:unavailable, :registration_incomplete}}, which is the boot step not having happened rather than a bug in your call.

#The credit wallets

This one is a data migration, not a schema step, and it has its own page in the package.

The short version: a wallet created after the upgrade is born on the lot engine automatically. A wallet that already existed is not, and it keeps behaving exactly as it did, which means your installation runs two populations until you move the old one.

mix aurora_meter.credits.migrate_lots -r MyApp.Repo

It runs in shadow mode first. It replays each wallet’s ledger, compares the result against the balance row that is already there, and reports. The balance row is the oracle. A wallet whose replay does not reproduce it exactly is blocked and never adjusted to fit.

Read the blocked list before you do anything else. A blocked wallet is telling you something true about your ledger, and the migration is the first thing that has ever checked it.

#What does not change

Worth saying plainly, because upgrade guides tend to imply that everything moves.

  • AuroraMeter.track/4, usage/2, check/2, remaining/2, quota/2 and with_quota/4 keep their names, their arguments and their return shapes.
  • A tenant’s plan does not move. Registering plan versions never reprices anybody; only an explicit transition does.
  • Your plan modules compile unchanged.
  • durable_features keeps working.

#Rollback boundaries

Know these before you start, not during.

Rolling back 0.5.0 to 0.4.x is safe in both directions. There is no schema change and nothing new is written.

Rolling back across a schema version is bounded by that version’s down migration, and several of the 1.0 versions drop columns on the way down. Take a backup you have actually restored once, and treat the schema step as the point of no return for that deploy.

Rolling back the wallet migration is not a thing. A migrated wallet is on the lot engine. This is the reason shadow mode exists and the reason you read its report rather than skimming it.

#The order, in one list

  1. Upgrade to 0.5.x. Change nothing. Read the logs for a week.
  2. Fix what the logs and mix aurora_meter.features found.
  3. Set undeclared_feature_policy: :deny on 0.5.x and let it run.
  4. Upgrade to 1.0. Run the schema migrations in order, with the backfill in its place and the concurrent index in its own file.
  5. Let register!/0 run and confirm the plan versions are stored.
  6. Run the wallet migration in shadow. Read the report. Deal with the blocked wallets.
  7. Run the wallet migration for real.

Steps 1 to 3 are where the work is, and they happen before the version you are upgrading to is even installed. That is the design.

Keep going

The core is free. Pro is the Stripe half.

Everything the free MIT package does is yours with no account and no expiry. Aurora Meter Pro adds Stripe checkout, metered usage reporting, prepaid top-ups, dashboards and alerts, and every plan starts with a free trial.