You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #788, which wired identity-scoped response caching and per-mutation
`invalidateCachedResponse` / `invalidateCachedResponsesMatching` calls into
individual API domain modules (`api/issues.ts`, `api/projects.ts`,
`api/dashboards.ts`).
That per-site approach works but is spread across many files, easy to forget,
and couples cache concerns with API-call logic. A reviewer noted on #788:
I'd say at this point we should have a centralized mechanism for this.
Maybe another command helper for mutation endpoints where we pass the API
URL(s) we use so it automatically invalidates the caches upon success.
The wrapper calls the `invalidates` callback after `func()` resolves
successfully (and skips it on `--dry-run`), passing the result so prefixes
that depend on newly-created IDs work out of the box. `invalidates` can
also be a static array for the common case.
Pros: explicit, reviewable, colocated with the command definition.
Cons: needs wiring into every mutation command. Requires every mutation
command to use the new builder — easy to miss for `sentry api -X DELETE ...`.
Option B — automatic invalidation at the HTTP layer
Intercept non-GET responses in `sentry-client.ts` `authenticatedFetch` and
invalidate any cached GET responses whose URL `startsWith` the mutation URL.
Add a URL-prefix-mapping table for related list endpoints (e.g., mutating
`/api/0/projects/<org>/<proj>/` also invalidates
`/api/0/organizations/<org>/projects/`).
Pros: zero-touch for command authors. Covers `sentry api -X POST` too.
Cons: mapping table is another source of truth; cross-endpoint
invalidations (project detail → org project list) need explicit rules.
Option C — hybrid (recommended)
Combine A and B:
HTTP-layer default: auto-invalidate the exact URL family of any non-GET.
Covers the common self-URL case and `sentry api` for free.
Command-layer override: `invalidates` callback for cross-endpoint
relationships the HTTP layer can't know (e.g., `POST /projects/` also
invalidates `/organizations//projects/`).
The domain API modules keep no invalidation calls.
Proposed approach
Add a URL-prefix builder module (`src/lib/cache-keys.ts` or similar) that
encodes the canonical list-vs-detail relationships for each entity. This
keeps the identity-scoped prefix construction out of command code.
Implement the HTTP-layer default in `sentry-client.ts`: on successful
non-GET, compute the invalidation URLs via the prefix builder and call
`invalidateCachedResponse` / `invalidateCachedResponsesMatching` with
them. Wrap in try/catch — invalidation failures must never fail a
mutation that already succeeded.
Add `invalidates?: InvalidateSpec` to `buildCommand` for the
cross-endpoint cases. Run after `func()` success, before return-value
rendering, skipped on `--dry-run`.
Follow-up to #788, which wired identity-scoped response caching and per-mutation
`invalidateCachedResponse` / `invalidateCachedResponsesMatching` calls into
individual API domain modules (`api/issues.ts`, `api/projects.ts`,
`api/dashboards.ts`).
That per-site approach works but is spread across many files, easy to forget,
and couples cache concerns with API-call logic. A reviewer noted on #788:
This issue tracks building that helper.
Current state (post-#788)
Mutations that currently invalidate cache entries manually:
Mutations that DO NOT yet invalidate (will need to be covered as we centralize):
Design options
Option A — declarative helper at the command layer (preferred)
Introduce `buildMutateCommand` (sibling of the existing `buildDeleteCommand`)
or extend `buildCommand` with an `invalidates` option:
```ts
buildMutateCommand({
// ...same as buildCommand...
invalidates: (result, ctx) => [
invalidationPrefixForOrgProjects(ctx.orgSlug),
invalidationPrefixForProjectDetail(ctx.orgSlug, ctx.projectSlug),
],
});
```
The wrapper calls the `invalidates` callback after `func()` resolves
successfully (and skips it on `--dry-run`), passing the result so prefixes
that depend on newly-created IDs work out of the box. `invalidates` can
also be a static array for the common case.
Pros: explicit, reviewable, colocated with the command definition.
Cons: needs wiring into every mutation command. Requires every mutation
command to use the new builder — easy to miss for `sentry api -X DELETE ...`.
Option B — automatic invalidation at the HTTP layer
Intercept non-GET responses in `sentry-client.ts` `authenticatedFetch` and
invalidate any cached GET responses whose URL `startsWith` the mutation URL.
Add a URL-prefix-mapping table for related list endpoints (e.g., mutating
`/api/0/projects/<org>/<proj>/` also invalidates
`/api/0/organizations/<org>/projects/`).
Pros: zero-touch for command authors. Covers `sentry api -X POST` too.
Cons: mapping table is another source of truth; cross-endpoint
invalidations (project detail → org project list) need explicit rules.
Option C — hybrid (recommended)
Combine A and B:
Covers the common self-URL case and `sentry api` for free.
relationships the HTTP layer can't know (e.g., `POST /projects/` also
invalidates `/organizations//projects/`).
The domain API modules keep no invalidation calls.
Proposed approach
encodes the canonical list-vs-detail relationships for each entity. This
keeps the identity-scoped prefix construction out of command code.
non-GET, compute the invalidation URLs via the prefix builder and call
`invalidateCachedResponse` / `invalidateCachedResponsesMatching` with
them. Wrap in try/catch — invalidation failures must never fail a
mutation that already succeeded.
cross-endpoint cases. Run after `func()` success, before return-value
rendering, skipped on `--dry-run`.
and the domain-specific `invalidate*Caches` helpers in
`api/issues.ts` / `api/projects.ts`.
verify the tests covering cache freshness after mutations still pass.
Non-goals
Tests
with the HTTP-layer + command-layer hooks exercised in isolation.
commands that verify GET cache staleness bounds.
Related
decentralized approach this issue replaces).