Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/economy-cache-decile-fractions.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Raised the economy result cache schema version from 3 to 4, so economy results cached while the simulation API returned decile and UK wealth decile relative changes as percentages (x100) instead of fractions are no longer served, and are recomputed on the next request. Budget-window results share this cache version, so they are recomputed too.
8 changes: 7 additions & 1 deletion policyengine_api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,13 @@ def get_bundle_default_dataset_option(country_id: str) -> dict:
) or _resolve_distribution_version(_dist_versions, "policyengine-core", "policyengine")

RUNTIME_CACHE_SCHEMA_VERSIONS = {
"economy_impact": 3,
# 4: economy results cached under 3 can hold decile.relative and the UK
# wealth_decile.relative as percentages (x100) instead of fractions, from
# the simulation API (PolicyEngine/policyengine-sim-api#827). Economy cache
# keys do not change when only the simulation API's code changes, so this
# version was raised to stop serving those results. The budget-window
# cache key uses the same version.
"economy_impact": 4,
"report_output": 3,
}
# Runtime cache versions are stored in legacy VARCHAR(10) columns, and keep at
Expand Down
57 changes: 57 additions & 0 deletions tests/unit/services/test_reform_impacts_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import pytest

from policyengine_api import constants
from policyengine_api.constants import get_economy_impact_cache_version
from policyengine_api.runtime_cache.core import CacheNamespace
from policyengine_api.runtime_cache.fake import InMemoryCacheBackend
from policyengine_api.runtime_cache.reform_impacts import (
Expand Down Expand Up @@ -197,3 +199,58 @@ def test_reform_impact_service_deletes_only_matching_computing_rows(

def test_reform_impact_transitions_return_none_for_missing_execution(service):
assert service.set_error_reform_impact("missing-job", message="missing") is None


def test_impact_cached_under_previous_economy_schema_version_is_not_matched(
service,
):
# Economy results cached under schema version 3 can hold decile relative
# changes as percentages (x100) from the simulation API
# (PolicyEngine/policyengine-sim-api#827). A lookup built from the
# current cache version must not find them.
current_cache_version = get_economy_impact_cache_version(SCOPE.country_id)
with pytest.MonkeyPatch.context() as schema_version_patch:
schema_version_patch.setitem(
constants.RUNTIME_CACHE_SCHEMA_VERSIONS, "economy_impact", 3
)
percentage_decile_cache_version = get_economy_impact_cache_version(
SCOPE.country_id
)

options_hash = "decile-hash"
stale_identity = ReformImpactIdentity(
scope=SCOPE,
api_version=percentage_decile_cache_version,
options_hash=options_hash,
)
service.set_reform_impact(
stale_identity,
ReformImpactExecution(
execution_id="percentage-decile-job",
start_time=datetime(2026, 7, 1),
),
options={"hash": options_hash},
status="computing",
reform_impact_json={},
)
service.set_complete_reform_impact(
"percentage-decile-job",
reform_impact_json={"decile": {"relative": {"1": 1.5}}},
)

current_identity = ReformImpactIdentity(
scope=SCOPE,
api_version=current_cache_version,
options_hash=options_hash,
)

assert [
impact.execution_id for impact in service.get_all_reform_impacts(stale_identity)
] == ["percentage-decile-job"]
assert service.get_all_reform_impacts(current_identity) == []
assert (
service.get_all_reform_impacts_by_options_hash_prefix(
current_identity, f"{options_hash}%"
)
== []
)
21 changes: 21 additions & 0 deletions tests/unit/test_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
POLICYENGINE_CORE_VERSION,
POLICYENGINE_VERSION,
REGION_PREFIXES,
RUNTIME_CACHE_SCHEMA_VERSIONS,
RUNTIME_CACHE_VERSION_LENGTH,
UK_REGION_TYPES,
US_REGION_TYPES,
_bundle_dataset_display_label,
_load_policyengine_bundle,
_normalize_distribution_name,
_resolve_distribution_version,
get_economy_impact_cache_version,
get_py_manifest,
)

Expand Down Expand Up @@ -111,6 +114,24 @@ def test__has_exactly_three_prefixes(self):
assert len(REGION_PREFIXES["us"]) == 3


class TestEconomyImpactCacheVersion:
# Since its move to .py 4.x, the simulation API returned decile.relative
# and the UK wealth_decile.relative as percentages (x100) instead of
# fractions (PolicyEngine/policyengine-sim-api#827). Economy results
# cached under schema version 3 can hold those values, and the cache key
# does not change when the simulation API's code does, so version 4 makes
# every economy cache lookup miss them and recompute.
def test__economy_impact_schema_version_is_4(self):
assert RUNTIME_CACHE_SCHEMA_VERSIONS["economy_impact"] == 4

@pytest.mark.parametrize("country_id", ["us", "uk"])
def test__economy_impact_cache_version_carries_schema_version_4(self, country_id):
cache_version = get_economy_impact_cache_version(country_id)

assert cache_version.startswith("e4")
assert len(cache_version) == RUNTIME_CACHE_VERSION_LENGTH


class TestDistributionVersionHelpers:
def test__normalize_distribution_name(self):
assert _normalize_distribution_name("policyengine_core") == (
Expand Down
Loading