Skip to content

feat(billing): resolve prices from the console behind a flag, with the compiled catalog as fallback (#304) - #467

Draft
mahesh-sangawar wants to merge 1 commit into
mainfrom
feat/304-cutover
Draft

feat(billing): resolve prices from the console behind a flag, with the compiled catalog as fallback (#304)#467
mahesh-sangawar wants to merge 1 commit into
mainfrom
feat/304-cutover

Conversation

@mahesh-sangawar

Copy link
Copy Markdown
Contributor

Second half of #304 — the cutover. Deliberately a draft: not to be merged until the parallel run has been clean for several days. One clean check on deploy day is not "durably zero".

Shipped state is off. With CONSOLE_CATALOG_AUTHORITATIVE unset, behaviour is byte-for-byte what it is today and the console read stays a comparison.

Replace the data, keep the API

pricing.Source is a two-method interface installed via pricing.UseSource. LookupPPPOption and DevelopedCurrencyOptions consult it first and fall through to the compiled catalog. No call site changes, no signature changes — the issue is explicit that deleting the package was never proposed.

After #459 the surface is smaller than #392's original inventory: update.go reads only lookup keys now, so money.go is the only runtime reader of amounts.

Declining is the entire safety design

The console Source answers only from a catalog it actually fetched. Cold, or failed with nothing cached, it returns false and pricing serves the compiled catalog. That fallthrough is the baked-snapshot cold start and the fail-open behaviour — there is no second mechanism, and none is wanted.

It must never answer with a zero Amount: money.go reads a zero as a real price, so a fabricated answer is silent mispricing, strictly worse than a slightly stale one. TestSource_ColdAndFailedDeclinesRatherThanAnsweringZero pins that.

Fallthrough is per lookup, not wholesale, so a source carrying most currencies is not abandoned for the one it lacks.

Why a flag rather than a code-only cutover

This is the one code path that decides what a customer is charged. A flag makes reverting one variable and a restart, not a build-and-ship cycle. It also lets this merge safely while the evidence accumulates, so the cutover itself is a config change made deliberately on a day someone chooses.

Two startup decisions worth reviewing

The first refresh is synchronous. Installing the source before it holds a catalog would leave a window where every lookup falls through — harmless in effect, but the first seconds after a deploy would silently behave like the pre-cutover build, which is the kind of thing nobody notices until a price is wrong and the logs say everything was fine.

A failed first refresh is not fatal. The source installs anyway and declines until a refresh succeeds. A console outage must never stop this service starting.

Verification

  • 11 new tests, written first. go build, go vet, full unit suite green
  • go test -race -count=2 clean on pricing and consolecatalog — the source is installed at startup and refreshed by a ticker while requests read it, so a race here would be a data race on the price of a subscription
  • integration tests actually ran against a real database: ./internal/billing/... and ./internal/handlers/platformadmin/ clean
  • TestSource_InstalledOverAnAgreeingCatalogChangesNothing asserts the property the parallel run has been proving — cutting over while the sources agree is a no-op at the point of use

Before merging

  • several days of consolecatalog: parity clean differences=0 in production
  • decide CONSOLE_CATALOG_MODE for the cutover — currently test; live pricing needs live
  • enable the flag as a separate, deliberate config change after this merges, not with it

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant