Repository navigation
serviceParser: ~70% of SELECTs in one parse repeat an earlier query with the same values (845 of 1188 on the appwrite template)
#12201
alex-frolov
started this conversation in
Improvement Requests
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
What happens
One
Service::parse()of the stocktemplates/compose/appwrite.yaml(14 compose services) issues 1,750 queries, 1,188 of them SELECTs. 845 of those SELECTs repeat a query that already ran in the same parse, with the same SQL and the same bound values. The second parse (the oneStartServicedoes afterCreate) has fewer writes but the same 845 repeats: nothing is left to create, and the lookups still run.appwriteappflowyhoppscotchWhere the repeats come from (appwrite, first parse):
bootstrap/helpers/parsers.php:3179environment_variables()->firstOrCreate(['key' => $varName, …])for${VAR:-default}bootstrap/helpers/parsers.php:3114firstOrCreatefor each reference found in a valuebootstrap/helpers/parsers.php:3148firstOrCreatefor a plain${VAR}bootstrap/helpers/parsers.php:2758firstOrCreatefor generatedSERVICE_*valuesbootstrap/helpers/parsers.php:3130updateOrCreateforSERVICE_*valuesThe cause is the same at every site. The environment variables belong to the
Service(resourceable_type = App\Models\Service). But the loop runs once per compose service, and appwrite's services share most of their variables. So_APP_ENVis looked up 14 times,_APP_DB_HOST12 times, and so on. One variable can also be looked up twice in the same iteration: once as a reference (:3114), then again as the primary variable (:3148/:3179).A smaller instance of the same pattern:
findServiceApplicationForEnvName()(bootstrap/helpers/domains.php:584) runs$resource->applications()->pluck('name')for everySERVICE_FQDN_*/SERVICE_URL_*variable it resolves. That is 8 identical queries per appwrite parse.parse()runs on create, on every start/deploy and on every domain save (updateCompose). The measurement uses sqlite in memory, so it shows no network time. A real instance pays one round-trip to Postgres for every one of those queries.Expected
Each environment variable row of the service is read at most once per parse. For example, load
$resource->environment_variables()->get()->keyBy('key')once before the loop, then look keys up in that collection, and create only the missing ones. InfindServiceApplicationForEnvName(), read the application names once per parse. The output of the parse stays the same. The golden test (ServiceParserReferenceTest) already pins it, so the change can be checked against that test.How to reproduce
Drop this file into
tests/Feature/and runphp artisan test tests/Feature/ServiceParseQueryCountReproTest.php. It uses onlyDB::listen:Output at
8ed31bb(the parser files are unchanged onmain0eeb630):How this was found
Found with query-guard, a PHPUnit extension that traces the SQL of an ordinary test run. It groups queries by call site and shape, and flags a call site when the same query with the same bound values runs 5+ times inside one test. It was run over Coolify's own suite, unchanged.
ServiceParserReferenceTestwas the loudest test.You do not need that tool to check any of this. The reproduction above uses only
DB::listen, and every number quoted comes from it.DB::enableQueryLog()around$service->parse()in Tinker gives the same counts.All reactions