fix(pricing): exempt /v1/pricing from the rate-limit floor and cache it in the BFF (BUNYIP-518) #517
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/BUNYIP-518-pricing-ratelimit-floor-exempt"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Root cause (candidate 1, confirmed from code)
With the switch on and every tier resolving,
/pricing404'd and its nav/footer links stayed hidden./v1/pricingwas under the unauthenticated rate-limit floor (RateLimitConfig::API_UNAUTH= 20 req/60s per IP) and was not inrate_limit_floor::EXEMPT_PATHS(a test even asserted it was NOT exempt).bunyip-web::public_ctxfetches/v1/pricingon EVERY public page render to decide whether the/pricinglinks are shown, so a few page loads from one browser exhausted that visitor's per-IP bucket. The 429 was swallowed bycalls::pricing(..).unwrap_or_else(_ => PricingResponse::default())into an unpublished payload, which 404s the route and hides its links, while the admin Pricing tiers page looked healthy and nothing hit the logs. That "looks like it never works, nothing in the logs" is exactly why the eliminated candidates (switch, unmappedstandard_price_id, staleArc<RwLock<TierConfig>>) all read fine. Recorded on the issue.Fix
/v1/pricingexempt from the floor (EXEMPT_PATHS, with a comment): it is public, read-only, already TTL-cached server-side (PricingCache), and fetched on every BFF render, so the per-IP floor only ever hurt real browsing.pricing_cache::PricingCache, 30s, inAppState): several public renders coalesce into at most one upstream call instead of one per render, removing the amplification permanently./pricingpage body read the same cached payload, so they stay consistent (no link to a 404).update_stripe_configalready invalidates the server-sidePricingCache(BUNYIP-515).Also: repairs a main build-break
origin/maindoes not currently compile its tests: BUNYIP-517's (#516) tier-derivationprice()fixture merged after BUNYIP-513 (#515) bumpeddunite-stripe, which added a requiredrecurring_interval_countfield; on the combined tree the fixture is missing it. This PR adds the field (the still-open #514 carries the identical one-liner, so they merge cleanly). Flagging because main CI is red until one of them lands.Tests
renders_within_ttl_coalesce_to_one_fetch: N renders inside the TTL make one upstream call (the render that used to trip the floor never fetches).a_429_does_not_unpublish_a_previously_published_page: an expired-then-429 render serves the last published payload (BUNYIP-518 AC: a 429 must not silently unpublish).a_cold_failure_falls_back_to_unpublished: nothing cached + failure -> unpublished default, logged, no panic.probes_and_the_stripe_webhook_are_exempt/ordinary_endpoints_are_cappedassert the floor exemption both ways.just check-containergreen.🤖 Generated with Claude Code
https://claude.ai/code/session_01GkNbvEq6awuMRULFCiYKe3
c84cdcf8d5026ad66949