Skip to content

Commit 1980800

Browse files
fix(features): fix cache isolation for server-only features (#6092)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 9b2f5e6 commit 1980800

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

api/features/views.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,9 @@ def _get_flags_from_cache(
859859
from_replica: bool = False,
860860
) -> list[typing.Any]:
861861
data: list[typing.Any]
862-
data = flags_cache.get(environment.api_key)
862+
# Include request origin in cache key to isolate client vs server requests
863+
cache_key = f"{environment.api_key}:{self.request.originated_from.value}"
864+
data = flags_cache.get(cache_key)
863865
if not data:
864866
data = self.get_serializer(
865867
get_environment_flags_list(
@@ -869,7 +871,7 @@ def _get_flags_from_cache(
869871
),
870872
many=True,
871873
).data
872-
flags_cache.set(environment.api_key, data, settings.CACHE_FLAGS_SECONDS)
874+
flags_cache.set(cache_key, data, settings.CACHE_FLAGS_SECONDS)
873875

874876
return data
875877

api/tests/unit/features/test_unit_features_views.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,47 @@ def test_get_flags__server_key_only_feature__server_key_auth__return_expected(
10761076
assert response.json()
10771077

10781078

1079+
def test_get_flags__server_key_only_feature__cache_isolation_between_client_and_server_keys(
1080+
environment: Environment,
1081+
environment_api_key: EnvironmentAPIKey,
1082+
feature: Feature,
1083+
settings: SettingsWrapper,
1084+
use_local_mem_cache_for_cache_middleware: None,
1085+
) -> None:
1086+
# Given
1087+
feature.is_server_key_only = True
1088+
feature.save()
1089+
1090+
# Enable caching
1091+
settings.CACHE_FLAGS_SECONDS = 30
1092+
1093+
url = reverse("api-v1:flags")
1094+
1095+
# Create clients for server and client keys
1096+
server_client = APIClient(
1097+
headers={SDK_ENVIRONMENT_KEY_HEADER: environment_api_key.key}
1098+
)
1099+
client_client = APIClient(headers={SDK_ENVIRONMENT_KEY_HEADER: environment.api_key})
1100+
1101+
# When - First request with SERVER key (populates cache)
1102+
server_response = server_client.get(url)
1103+
1104+
# Then - Server should see the server-only feature
1105+
assert server_response.status_code == status.HTTP_200_OK
1106+
server_flags = server_response.json()
1107+
server_feature_names = [flag["feature"]["name"] for flag in server_flags]
1108+
assert feature.name in server_feature_names
1109+
1110+
# When - Second request with CLIENT key (should not get server-only from cache)
1111+
client_response = client_client.get(url)
1112+
1113+
# Then - Client should NOT see the server-only feature
1114+
assert client_response.status_code == status.HTTP_200_OK
1115+
client_flags = client_response.json()
1116+
client_feature_names = [flag["feature"]["name"] for flag in client_flags]
1117+
assert feature.name not in client_feature_names
1118+
1119+
10791120
def test_get_feature_states_by_uuid(
10801121
admin_client_new: APIClient,
10811122
environment: Environment,

0 commit comments

Comments
 (0)