Skip to content

Raise the economy cache schema version so ×100 decile results stop being served - #4026

Merged
anth-volk merged 1 commit into
masterfrom
economy-cache-decile-fractions
Oct 23, 2026
Merged

anth-volk merged 1 commit into
masterfrom
economy-cache-decile-fractions

Conversation

@anth-volk

Copy link
Copy Markdown
Collaborator

Fixes #4025

What and why

Since its move to .py 4.x, the simulation API has returned decile.relative and the UK wealth_decile.relative as percentages (multiplied by 100) instead of fractions. Production economy results have carried these values since 2026-06-29, when the Public API started requesting .py 4.18.3. PolicyEngine/policyengine-sim-api#827 tracks the bug, and PolicyEngine/policyengine-sim-api#904 fixes it by dividing by 100 in build_decile_output.

The Public API caches economy results in Redis for 30 days (REFORM_IMPACT_TTL_SECONDS, with jitter). A lookup matches on the scope (country, policy pair, region, dataset and period), the cache version (ReformImpactIdentity.api_version, from get_economy_impact_cache_version) and the options hash. The cache version is a digest of the country package, .py and policyengine-core versions plus RUNTIME_CACHE_SCHEMA_VERSIONS["economy_impact"]. The options hash covers the request options, the dataset, the model and .py versions, and the simulation app name. None of these change when only the simulation API's code changes, so after #904 is deployed, results already cached for .py 6.7.5 would keep being served with multiplied-by-100 values for up to 30 days.

This PR raises RUNTIME_CACHE_SCHEMA_VERSIONS["economy_impact"] from 3 to 4. The cache version changes from e3… to e4…, so lookups read a different Redis scope index and don't find results stored under version 3. The next request for each economy result recomputes it.

Budget-window cache

BudgetWindowCache.build_key takes its api_version from the same get_economy_impact_cache_version call (through _build_economic_impact_setup_options), so this change also gives every budget-window result a new key, and budget-window results are recomputed once. Budget-window results hold only yearly budget totals (tax revenue, benefit spending and budgetary impact), not decile output, so BUDGET_WINDOW_CACHE_SCHEMA_VERSION doesn't need its own change.

When to merge

Only after PolicyEngine/policyengine-sim-api#904 is merged and deployed to the simulation app the Public API uses, which is policyengine-simulation-py6-7-5 today. Merging earlier would recompute results that are still multiplied by 100 and cache them under the new version for another 30 days.

Interaction with the .py 6.10.0 update (#4024)

#4024 updates .py to 6.10.0, which changes both the cache version digest and the options hash, so results cached for 6.7.5 stop being reused once it ships, with or without this PR. That only helps if the simulation app for .py 6.10.0 also includes #904's fix. Without it, that app computes and caches new multiplied-by-100 results under the new key.

What this doesn't fix

Report outputs already saved to v1 report records keep the multiplied-by-100 values, because they are stored in the database and are not read back from the economy cache. Correcting them is a follow-up. RUNTIME_CACHE_SCHEMA_VERSIONS["report_output"] is unchanged here.

Tests

  • tests/unit/test_constants.py::TestEconomyImpactCacheVersion pins RUNTIME_CACHE_SCHEMA_VERSIONS["economy_impact"] at 4, and checks that the US and UK cache versions start with e4 and still fit the 10-character api_version column.
  • tests/unit/services/test_reform_impacts_service.py::test_impact_cached_under_previous_economy_schema_version_is_not_matched stores a completed impact under the version-3 cache version in the in-memory cache, then checks that the exact and options-hash-prefix lookups with the current cache version return nothing, while a lookup with the old version still finds it.

With the version still at 3, all four new tests failed (assert 3 == 4, 'e30e9091da'.startswith('e4'), and the version-3 impact returned by the current-version lookup). They pass at 4.

Local checks:

  • make format, ruff check . and ruff format --check .: clean
  • uv run --frozen --extra dev mypy: no issues
  • focused tests (constants, runtime cache, reform impacts, budget-window cache, economy service, report output service): passed
  • make test: 3285 passed
  • python scripts/run_quality_guards.py: passed

Environment variables

None change. This PR changes one Python constant, its tests and a changelog fragment.

🤖 Generated with Claude Code

Since its move to .py 4.x, the simulation API has returned
decile.relative and the UK wealth_decile.relative as percentages (x100)
instead of fractions (PolicyEngine/policyengine-sim-api#827). The Public
API keeps economy results in Redis for 30 days, and their cache key does
not change when only the simulation API's code changes, so results
already cached would keep serving the x100 values after the simulation
API fix (PolicyEngine/policyengine-sim-api#904) is deployed.

Raising RUNTIME_CACHE_SCHEMA_VERSIONS["economy_impact"] from 3 to 4
changes the cache version in every economy and budget-window cache key,
so results stored under 3 are no longer found and are recomputed.

Tests pin the new version and show that an impact stored under the
version-3 cache version is not matched by a lookup with the current one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.72%. Comparing base (881a009) to head (beadab0).

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #4026       +/-   ##
===========================================
+ Coverage   51.11%   87.72%   +36.60%     
===========================================
  Files         242      258       +16     
  Lines       14441    15864     +1423     
  Branches     2307     2594      +287     
===========================================
+ Hits         7382    13917     +6535     
+ Misses       6436     1280     -5156     
- Partials      623      667       +44     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@anth-volk
anth-volk marked this pull request as ready for review October 22, 2026 21:13

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Anthony, this looks right. I approve.

I confirmed the precondition: the prod deploy of #904 (sim-api run 39879445060, commit 1fe19571) redeployed policyengine-simulation-py6-7-5, so new economy results from the Public API now carry fractions. It's safe to merge this now.

The change and the tests look good: version 4 makes the exact and the prefix lookups miss the version-3 entries, and the new tests fail at 3. The one-time recompute of budget-window results is fine, since they hold no decile output.

Agreed that the ×100 values in saved v1 report records need a follow-up. Is there an issue for it yet?

@anth-volk
anth-volk merged commit a4bd9d3 into master Oct 23, 2026
15 checks passed
anth-volk added a commit that referenced this pull request Oct 23, 2026
Brings in #4026 (raise the economy cache schema version from 3 to 4)
and the 5.7.0 "Update PolicyEngine API" release commit.

Conflicts:

- tests/unit/services/test_reform_impacts_service.py: both sides added
  a new test at the end of the file. This branch added
  test_a_briefly_kept_error_expires_and_leaves_other_records; master
  added test_impact_cached_under_previous_economy_schema_version_is_not_matched.
  Kept both, this branch's test first. The imports master added for its
  test (constants, get_economy_impact_cache_version) merged without
  conflict.

Merged without conflict, checked by hand:

- policyengine_api/constants.py: RUNTIME_CACHE_SCHEMA_VERSIONS
  ["economy_impact"] is 4 (master), and UK_COUNTRY_REGIONS,
  UK_REGION_REGISTRY_CODES and LOCAL_AREA_BREAKDOWN_REGIONS are kept
  (this branch).
- pyproject.toml: package version 5.7.0 (master) and the
  policyengine[models]==6.10.0 pin (this branch).
- uv.lock: policyengine-api 5.7.0 (master) with this branch's .py
  6.10.0 dependency set (policyengine 6.10.0, policyengine-core 3.33.3,
  policyengine-uk 2.132.6). `uv lock --check` passes.
- tests/unit/test_constants.py: both sides' additions kept.
- CHANGELOG.md and changelog.d/: master's 5.7.0 entry and its removal
  of the released remove-economy-comparison fragment, plus master's
  economy-cache-decile-fractions fragment; this branch's fragments are
  unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
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.

Stop serving economy results cached with ×100 decile relative changes

2 participants