Skip to content

The request's database is resolved nine times per request, recomputing the HMAC each time #3379

Description

@adamtheturtle

What happens

get_database_matching_server_keys finds the database for a request by computing the expected HMAC authorization header for each candidate database until one matches. It is called once by each of eight validator modules and again by the endpoint handler:

src/mock_vws/_services_validators/auth_validators.py
src/mock_vws/_services_validators/database_id_validators.py
src/mock_vws/_services_validators/name_validators.py
src/mock_vws/_services_validators/project_state_validators.py
src/mock_vws/_services_validators/request_quota_validators.py
src/mock_vws/_services_validators/request_rate_validators.py
src/mock_vws/_services_validators/target_quota_validators.py
src/mock_vws/_services_validators/target_validators.py

Each of those calls re-walks the full database list from the start. Counting calls to vws_auth_tools.authorization_header for a single request:

databases=1: POST /targets ->  7 HMAC computations; GET /summary ->  5
databases=5: POST /targets -> 28 HMAC computations; GET /summary -> 20

So the cost is validators multiplied by databases, and the request's own signature is recomputed from its body every time. For POST /targets the body includes the base64-encoded image, so each of those 28 computations hashes the whole image again.

Why it matters

This is the mock's per-request baseline cost, paid on every endpoint, and it grows with the number of databases a test registers. It is not currently a reported problem, so this is about avoidable work rather than an observed regression.

The structural point is the more interesting one: the database is resolved nine separate times and each caller independently handles the "no database matches" case by catching ValueError. There is one answer per request and it is recomputed nine times.

Suggested resolution

Resolve the database once per request and pass the result to the validators and the handler.

This overlaps with #3371 but is separable, and is the smaller of the two: it does not require restructuring the validator chain, only threading an already-computed value through the signatures which currently take databases and re-derive it.

Note that the resolution has to stay lazy enough to preserve error precedence. validate_access_key_exists and validate_authorization deliberately run before the others and return different result codes for an unknown access key versus a bad signature, so "resolve first, then validate" cannot simply replace them.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions