Context
The in-web workspace files search typeahead (WorkspaceFileTable + GET /me/workspaces/:name/files/facets) already avoids re-walking metadata on every keystroke:
- Facet keys load lazily on first menu focus, then stay in React state for the mount (
facets is only set once).
- Facet values load per key, once, into
facetValues[key].
- In-flight refs block duplicate concurrent requests.
- Keystrokes only filter the already-loaded lists in
buildSuggestions (capped at 50 keys / 50 values server-side).
- Server queries use
file_metadata_lookup_idx (workspace, meta_key, meta_value) and load values lazily so opening the menu is one grouped query, not N.
No product change is required for correctness or basic performance. This issue tracks optional caching if first-open latency or reload flicker becomes noticeable.
Problem
What is not cached today:
- No browser persistence — full page reload / remount refetches facets from the API.
- No server-side cache — every cold open runs the D1
GROUP BY for keys (and per-key value aggregates on first drill-in).
- Client fetch uses
cache: "no-store"; the facets route has no HTTP/Cache-Control story.
- Session cache can go slightly stale after uploads (acceptable for typeahead; any longer-lived cache needs a short TTL, not a complex invalidation bus).
Proposed improvements (when needed)
1. Client: sessionStorage (or similar) for facet keys
- Mirror the pattern in
apps/web/src/lib/workspace-cache.ts (versioned payload + short TTL).
- Persist facet keys (and optionally recently loaded values) per workspace for the tab session.
- On focus: paint from cache immediately, optionally revalidate in background.
- Win: kills empty/loading flicker after reload; zero server change.
2. Server: short-TTL cache of facetKeys(workspace)
- e.g. KV (or existing cache pattern ~30–120s) keyed by workspace name.
- Return the same
{ keys, truncated } shape from facetKeys in apps/api/src/file-metadata.ts / GET …/files/facets.
- Soft-stale is fine; do not invent per-upload invalidation for autocomplete.
- Win: reduces repeated D1 aggregation on cold opens / multi-tab use.
Explicit non-goals
- Do not re-fetch facets on every keystroke (already avoided).
- Do not precompute all key→values when the menu opens (lazy values stay).
- Do not add a write-path invalidation bus unless D1 cost becomes material.
- Do not materialize counts on write unless aggregates get large enough to profile as a problem.
When to prioritize
| Signal |
Prefer |
| Menu “pops” empty after every reload |
Client sessionStorage |
| Slow first open on large workspaces / rising D1 cost on facets |
Server short-TTL cache |
| Stale suggestions after heavy upload bursts |
Soft refresh on idle/focus or after successful upload — not per keystroke |
References
- Client load/cache:
apps/web/src/components/WorkspaceFileTable.tsx (loadFacets / loadFacetValues)
- Suggestion filtering:
apps/web/src/lib/workspace-search-suggest.ts
- API route:
apps/api/src/routes/me.ts → GET /workspaces/:name/files/facets
- D1 queries:
apps/api/src/file-metadata.ts (facetKeys, facetValues, FACET_*_LIMIT)
- Existing shell snapshot cache (pattern only):
apps/web/src/lib/workspace-cache.ts
Acceptance (if/when implemented)
Context
The in-web workspace files search typeahead (
WorkspaceFileTable+GET /me/workspaces/:name/files/facets) already avoids re-walking metadata on every keystroke:facetsis only set once).facetValues[key].buildSuggestions(capped at 50 keys / 50 values server-side).file_metadata_lookup_idx (workspace, meta_key, meta_value)and load values lazily so opening the menu is one grouped query, not N.No product change is required for correctness or basic performance. This issue tracks optional caching if first-open latency or reload flicker becomes noticeable.
Problem
What is not cached today:
GROUP BYfor keys (and per-key value aggregates on first drill-in).cache: "no-store"; the facets route has no HTTP/Cache-Controlstory.Proposed improvements (when needed)
1. Client:
sessionStorage(or similar) for facet keysapps/web/src/lib/workspace-cache.ts(versioned payload + short TTL).2. Server: short-TTL cache of
facetKeys(workspace){ keys, truncated }shape fromfacetKeysinapps/api/src/file-metadata.ts/GET …/files/facets.Explicit non-goals
When to prioritize
References
apps/web/src/components/WorkspaceFileTable.tsx(loadFacets/loadFacetValues)apps/web/src/lib/workspace-search-suggest.tsapps/api/src/routes/me.ts→GET /workspaces/:name/files/facetsapps/api/src/file-metadata.ts(facetKeys,facetValues,FACET_*_LIMIT)apps/web/src/lib/workspace-cache.tsAcceptance (if/when implemented)