Repository navigation
fix(config): propagate only changed global defaults, in the same tx - #500
Conversation
A PUT /api/config carrying any default_* key copied all four global defaults (term, payment, coverage, ramp) onto every service config and only logged save failures. A coverage-only PUT reset a service set to 3-year all-upfront back to the global term and payment, and the recommendations lookback control, which round-trips the whole config, did the same on every change. UpdateGlobalConfigAtomic now compares the defaults before and after the apply closure and, in the same advisory-locked transaction, overwrites only the service-config columns whose default changed. A failed propagation rolls back the global write and surfaces as an error instead of a 200 "updated". The handler-side best-effort loop is gone. Closes #225
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (1)
Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 2 reviews per hour. 📝 WalkthroughWalkthroughGlobal default changes now propagate to service configurations within the atomic database update. Only changed defaults update corresponding service-config fields. Propagation errors abort the transaction. The API handler no longer writes service configurations separately. ChangesGlobal Default Propagation
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to No merge-blocking issue was found in the reviewed changes; the defaults update preserves unrelated service settings and fails atomically if propagation fails. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
…nsaction Assert the global_config row and the propagated service_configs rows carry the same xmin, so running the propagation on the pool instead of the locked transaction fails the integration test.
What
PUT /api/configno longer overwrites per-service term, payment, coverage and ramp settings with the global defaults unless that default actually changed. When propagation fails, the request now returns an error and the global write is rolled back.Why
propagateGlobalDefaultsran whenever the body contained anydefault_*key. It then copied all four defaults onto every service config and only logged save failures. Two real paths hit this:aws/ec2from 3-year all-upfront to the global term and payment, so the next scheduled purchase would buy the wrong commitment. This is the scenario in the issue.frontend/src/recommendations.tsupdateConfig(payload)) sends the whole cached config back. Every change to the lookback therefore reset term, payment, coverage and ramp on every service, and no per-service PUT followed to restore them.A present-key overlay, which the issue suggested, does not fix the second path because every key is present in that body. So this PR propagates a default only when its value differs from the stored one. That matches what the Settings UI already does: it propagates a default to services only after the user changes it and confirms the diff preview.
How
PostgresStore.UpdateGlobalConfigAtomicsnapshots the config it read under the advisory lock. After the upsert, in the same transaction, it runs oneUPDATE service_configsthat sets only the columns whose default changed (a static SQL statement usingCASE WHEN $changed THEN $value ELSE col END). If that statement fails, the global change rolls back too, so a retry is not stuck in a half-propagated state.propagateGlobalDefaults,anyKeyPresent) is removed. A store error now reaches the caller instead of a 200updated.Verification
Env:
GOTOOLCHAIN=go1.26.6 GOWORK=off GOFLAGS='-p=2 -count=1' AWS_EC2_METADATA_DISABLED=trueTestUpdateConfigPropagatesOnlyChangedDefaults(-tags integration), which drivesupdateConfigagainst a real store:{"default_coverage": 70}keeps each service's term, payment and ramp and propagates coverage 70.service_configsupdates,{"default_term": 3}returnsfailed to propagate global defaults...and the globaldefault_termstays 1.expected: {3, "all-upfront", 70, "weekly-25pct"} actual: {1, "no-upfront", 70, "immediate"}. The new pgxmock and handler tests also fail on main.termChanged := truefails the integration test and two pgxmock tests.go build ./...,go vet(with and without-tags integration) on api/config/server, andgo test ./internal/api/ ./internal/config/ ./internal/server/all pass (3629 tests).TestConfigInfrastructureKeyHTTP(integration) passes.golangci-lint run --build-tags integrationon api and config reports no issues.For the reviewer to challenge
default_coveragechanges, which is unchanged behaviour.Closes #225
Summary by CodeRabbit