chore: cache the Lagoon token and aggregate the pre-warm pool check#229
Merged
Conversation
Implements plans 007 and 008 from the advisory audit:
- LagoonClientService minted a token over SSH (process spawn, 30s
timeout) on every call unless a fetcher was bound — the deployment
service pays that per redeploy trigger and per poll, so rollout bursts
serialized workers behind repeated SSH auth. Successful tokens are now
cached for 110 seconds (under the FTLagoon provider stack's 2-minute
max token age), keyed on user/server/port/keyfile via a public static
tokenCacheKey() so tests and implementation cannot drift. Failures ('')
are never cached — one SSH blip must not poison callers for the TTL —
and a bound token_fetcher still short-circuits everything.
- The pre-warm poller evaluated needs_unallocated_maintenance per store
app every 5 seconds — up to ~3N COUNT/EXISTS queries per tick, scaling
with catalog size regardless of change. The tick now eager-loads the
pool count in one grouped withCount (the accessor short-circuits on the
preloaded attribute, unchanged) and checks the query-free deficit
condition first, so idle ticks skip the EXISTS probes entirely. The
per-tick body moved to checkOnce() so tests don't fight the sleep loop;
the truth table (deficit dispatches / satisfied pool doesn't) is pinned.
dan2k3k4
force-pushed
the
chore/scheduler-queue-perf
branch
from
July 17, 2026 15:35
e7fd08f to
a5f8982
Compare
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.
What
PR 4 of 6 executing the advisory plan set (
plans/, committed in #226). Implements plans 007 + 008 — the scheduler/queue throughput pair.Changes
getLagoonToken()spawned ansshsubprocess per call (the onlytoken_fetcherbinding lives in one console command), so every redeploy trigger and every deployment poll paid a full SSH round-trip. Now: successful tokens cached 110s (deliberately under the FTLagoon provider stack's 2-min max token age — grilling decision), cache key derived via a public statictokenCacheKey()so the tests can't drift from the implementation. Two safety properties test-pinned: failures are never cached (one SSH blip can't poison the TTL window) and a boundtoken_fetcherstill bypasses everything (RunLagoonCommandOnAppInstances depends on it).withCount(the accessor's preloaded-attribute short-circuit makes this free — zero accessor changes) and checks the query-free deficit condition first so idle ticks skip the EXISTS probes. Per-tick body extracted tocheckOnce()for testability; dispatch truth table pinned (deficit → job onunallocated-instance-creation; satisfied pool → nothing).Verified equivalence
The
withCountfilter block is byte-equivalent togetUnallocatedInstancesCountAttribute()'s own query (and to the existing withCount inPolydockStoreAppResource::getEloquentQuery()) — checked before landing, per the plan's STOP condition.Testing
php artisan test— 438 passed (5 new)./vendor/bin/phpstan analyse— no errors; Pint cleanGreptile Summary
This PR implements two query-reduction optimizations: a 110-second token cache inside
getLagoonToken()that avoids an SSH subprocess on every redeploy/poll call, and an aggregatedwithCounteager-load in the pre-warm poller that replaces per-app COUNT queries with a single grouped query per tick.fetchLagoonTokenOverSsh()with aCache::get/putlayer keyed on SSH credentials and endpoint; failures are never cached to avoid a transient SSH error poisoning the TTL window; a boundtoken_fetchershort-circuits the whole path unchanged.checkOnce()from the polling loop, addswithCount(['instances as unallocated_instances_count' => ...])so the model accessor's preloaded-attribute short-circuit eliminates the per-app COUNT queries on deficit ticks, and the EXISTS probes only fire for over-target or refresh-configured pools.Confidence Score: 5/5
Safe to merge — both optimizations are additive and preserve all existing caller contracts.
The token cache correctly avoids storing failures, the bound-fetcher bypass path is unchanged, and the withCount alias exactly matches the model accessor fallback query so the short-circuit is transparent. No contracts are broken, no data is mutated differently, and the five new tests cover the key invariants.
No files require special attention.
Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Caller participant LCS as LagoonClientService participant Cache participant SSH as fetchLagoonTokenOverSsh Caller->>LCS: getLagoonToken(config) alt bound token_fetcher registered LCS-->>Caller: app('polydock.lagoon.token_fetcher')(config) else no fetcher bound LCS->>Cache: get(tokenCacheKey(config)) alt cache hit (non-empty string) Cache-->>LCS: cached token LCS-->>Caller: cached token else cache miss or empty Cache-->>LCS: null / '' LCS->>SSH: fetchLagoonTokenOverSsh(config) SSH-->>LCS: token or '' alt "token !== ''" LCS->>Cache: put(key, token, +110s) end LCS-->>Caller: token (may be '') end end%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Caller participant LCS as LagoonClientService participant Cache participant SSH as fetchLagoonTokenOverSsh Caller->>LCS: getLagoonToken(config) alt bound token_fetcher registered LCS-->>Caller: app('polydock.lagoon.token_fetcher')(config) else no fetcher bound LCS->>Cache: get(tokenCacheKey(config)) alt cache hit (non-empty string) Cache-->>LCS: cached token LCS-->>Caller: cached token else cache miss or empty Cache-->>LCS: null / '' LCS->>SSH: fetchLagoonTokenOverSsh(config) SSH-->>LCS: token or '' alt "token !== ''" LCS->>Cache: put(key, token, +110s) end LCS-->>Caller: token (may be '') end endReviews (2): Last reviewed commit: "chore: cache the Lagoon token and aggreg..." | Re-trigger Greptile