Repository navigation
perf(api): rank related plots against one cached catalogue index - #12214
Merged
Merged
Conversation
/insights/related cached its response per spec, library, mode and limit. Those thousands of keys evicted each other from the shared 1,000-entry cache, so nearly every call reloaded all specs (p50 755 ms, p99 1.6 s over the weekend of 2026-10-18/19). The catalogue is now cached once as a plain-value index under one key and each request ranks against it in memory (about 0.6 ms). Outputs are identical on the live catalogue for 1,266 spec/library/mode/limit combinations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The focused refactor preserves ranking and cache invalidation behavior, with relevant regression tests and no identified blocking issues.
No open issues 🎉
What changed in this PR
Replaces per-request related-plots caching with one shared catalogue index, reducing database reads while preserving recommendation behavior.
Changes:
- Caches plain-value catalogue entries and ranks results in memory.
- Adds tests for tag selection, preview selection, and cache reuse.
- Documents caching behavior and corrects the supported limit range.
| File | Description |
|---|---|
| tests/unit/api/test_routers.py | Verifies different requests share one catalogue load. |
| tests/unit/api/test_insights_helpers.py | Tests index tag sets and preview selection. |
| docs/reference/api.md | Documents caching and the 1–24 limit range. |
| changelog.d/related-shared-index.md | Records the performance improvement. |
| api/routers/insights.py | Introduces the shared index and in-memory ranking. |
Review effort: Balanced
…rary Second-opinion follow-ups (Fable, Gemini 4 Argon): - refresh_after/refresh_factory on the index key like the other whole-catalogue caches, so it never goes stale past an hour and a TTL expiry no longer costs a live request the full DB load; - impl tags are unioned per library (the unique key is spec, language, library), where the dict comprehension kept only the last impl; - eq=False on the frozen dataclass so the dict field never meets a hash; - the router test also pins that clear_cache() forces a reload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Owner
Author
Second opinion: Fable and Gemini 4 ArgonBoth returned sound with changes. Both confirmed:
Adopted in
Not adopted:
Rerun on the live catalogue after the changes: still 0 differences in 1,266 cases, ranking 0.73 ms per call. |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/insights/related(the related-plots block on every spec and implementation page) cached its response per spec, library, mode and limit. Those thousands of keys competed for the shared 1,000-entryTTLCachewith/plots/filter,/og/*andseo:*, so nearly every call reloaded all 325 specs and 4,863 impls (SpecRepository.get_all). Production over the LinkedIn weekend of 2026-10-18/19: p50 755 ms, p99 1.6 s over 3,134 calls, while every other endpoint had a p50 of 8–120 ms._RelatedEntry: tags per mode and library plus the best preview, no ORM objects) under the single keyinsights:related_index. Each request ranks against that index in memory with the unchanged algorithm (same Jaccard, stable sort, seeded shuffle and jitter).docs/reference/api.mdgets a caching note and the correctlimitrange (1–24, which the docs gave as 1–12).Measured on the live catalogue (read-only):
get_all≈ 1.1 s (local, almost every call)Plan
N/A. This is the Todoist finding from the traffic check on 2026-10-19. Second opinion: Fable and Gemini 4 Argon (results are summarized in a comment).
Test plan
uv run pytest tests/unit tests/integration(CI extrastestandagents): 12,227 passed._related_entry. These replace the tests of the removed_collect_impl_tags.get_all.ruff check,ruff format --check,mypy api core, andtools.changelog checkpass./insights/relatedp50 in the request logs drops from about 750 ms to the level of the other endpoints.🤖 Generated with Claude Code
https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M