Cache the dependent/suggester counts in Redis - #1836
Open
Seldaek wants to merge 1 commit into
Open
Conversation
getDependentCount() and getSuggestCount() run a COUNT(*) over the dependent/suggester tables on every package page view, again in the package JSON API, and again on the dependents/suggesters pages. For a widely-required package like psr/log that is a several-hundred-thousand-row index scan, and the package page alone accounts for 4.5M requests per APM period. The existing indexes already cover the queries, so there is nothing left to optimise in the query itself - the fix is to stop running it. Cache both counts for an hour with a random variance to spread out the refresh of the most-requested packages. A count badge being an hour out of date is harmless, so this is TTL-only: dependent rows are written keyed by the required package name while the writer operates on the requiring package, so precise invalidation would mean a key deletion per required name on every update. Keys are lowercased because the packageName columns use a case-insensitive collation and differently-cased requests must not get separate entries. The test env now points the cache client at its own Redis DB, and IntegrationTestCase flushes it per test: the DB is rolled back between tests but Redis is not, so counts keyed by package name would otherwise leak into later tests that reuse a name.
stof
approved these changes
Sep 7, 2026
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.
getDependentCount()andgetSuggestCount()run aCOUNT(*)over thedependent/suggestertables on every package page view (PackageController:685-686), again in the package JSON API (:572-573), and again on the dependents/suggesters pages. For a widely-required package likepsr/logthat is a several-hundred-thousand-row index scan — and the package page alone is ~4.5M requests per APM period, so this is the largest single source of avoidable MySQL load on the site.by_type (packageName, type)andall_suggesters (packageName)already cover these queries, so there is nothing left to optimise in the query itself. The fix is to stop running it.DependentRepository::updateDependentSuggesters()writes rows keyed by the required package name while operating on the requiring package, so precise invalidation would mean a key deletion per required name on every package update. A count badge being an hour out of date is harmless.packageNamecolumns use a case-insensitive collation and differently-cased requests must not get separate entries.Also drops a now-stale
phpstan-baseline.neonentry:getSuggestCount()never appliedmax(0, …)despite declaringint<0, max>, and the shared helper does.Test isolation
The test env now points the cache client at its own Redis DB (
REDIS_CACHE_URL) andIntegrationTestCaseflushes it per test. The DB is rolled back between tests but Redis is not, so counts keyed by package name would otherwise leak into later tests that reuse a name — latent flakiness for this and any future cache.Verification
composer phpstanclean, full suite green (794 tests). New tests cover per-type keying, cache reads, case-insensitivity, and that a zero count is cached too (so unknown names don't re-query on every page view).