What happens
_pack in api/oss/src/utils/caching.py truncates a project id to its last 12
characters before building the Redis key:
cache:p:{project_id[-12:]}:u:{user_id[-12:]}:{namespace}:{key}
Two projects whose UUIDs share that 12-character suffix therefore share every cache
entry in a namespace that does not opt out. PR #6164 added the full_project_id=True
opt-out and turned it on for the two vault namespaces (list_secrets,
list_secrets_generation), because those cache secret payloads. Everything else still
keys on the truncated id, including the two that decide authorization:
check_permissions (oss/src/apis/fastapi/access/router.py)
check_action_access (oss/src/services/db_manager.py,
oss/src/core/access/permissions/service.py)
queries:retrieve, testsets:retrieve, artifact, and the webhook caches
- the evaluation runtime locks (
oss/src/core/evaluations/runtime/locks.py), which go
through the same packing
Project ids are server-generated UUID4s, so a caller cannot steer a collision, and the
odds of one arising are remote. The consequence if it ever did — one project reading
another's cached permission result — is bad enough that the truncation should not be the
default.
Why it was not fixed in #6164
Flipping the default is a one-line change in _pack, but it is not a one-line change in
effect:
invalidate_cache has no full_project_id parameter and packs its scan pattern with
the truncated id. Flipping the writers without flipping invalidation would leave every
namespace unable to clear its own entries.
- The evaluation runtime locks are packed the same way. Changing the key shape mid
rolling deploy means old and new pods hold different keys for the same logical lock,
so mutual exclusion is lost for the length of the deploy.
- Every namespace goes cold at once on deploy.
That is its own change with its own review, not a rider on a secrets PR.
What a fix needs
full_project_id plumbed through invalidate_cache (both the concrete-key and the
pattern branch), then the default flipped for writers and invalidators together.
- A deploy plan for the lock namespace (drain, or dual-read the old key shape for one
release).
- The tenancy test at
api/oss/tests/pytest/unit/utils/test_cache_key_tenancy.py
updated: it currently pins the truncated collision as a known hazard and points here.
What happens
_packinapi/oss/src/utils/caching.pytruncates a project id to its last 12characters before building the Redis key:
Two projects whose UUIDs share that 12-character suffix therefore share every cache
entry in a namespace that does not opt out. PR #6164 added the
full_project_id=Trueopt-out and turned it on for the two vault namespaces (
list_secrets,list_secrets_generation), because those cache secret payloads. Everything else stillkeys on the truncated id, including the two that decide authorization:
check_permissions(oss/src/apis/fastapi/access/router.py)check_action_access(oss/src/services/db_manager.py,oss/src/core/access/permissions/service.py)queries:retrieve,testsets:retrieve,artifact, and the webhook cachesoss/src/core/evaluations/runtime/locks.py), which gothrough the same packing
Project ids are server-generated UUID4s, so a caller cannot steer a collision, and the
odds of one arising are remote. The consequence if it ever did — one project reading
another's cached permission result — is bad enough that the truncation should not be the
default.
Why it was not fixed in #6164
Flipping the default is a one-line change in
_pack, but it is not a one-line change ineffect:
invalidate_cachehas nofull_project_idparameter and packs its scan pattern withthe truncated id. Flipping the writers without flipping invalidation would leave every
namespace unable to clear its own entries.
rolling deploy means old and new pods hold different keys for the same logical lock,
so mutual exclusion is lost for the length of the deploy.
That is its own change with its own review, not a rider on a secrets PR.
What a fix needs
full_project_idplumbed throughinvalidate_cache(both the concrete-key and thepattern branch), then the default flipped for writers and invalidators together.
release).
api/oss/tests/pytest/unit/utils/test_cache_key_tenancy.pyupdated: it currently pins the truncated collision as a known hazard and points here.