Skip to content

fix(api): pack cache keys with the full project id outside the vault namespaces #6166

Description

@mmabrouk

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:

  1. 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.
  2. 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.
  3. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugpythonPull requests that update Python code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions