Cache anagram results, keyed on the dictionary contents #5
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Every search scans the full dictionary: 466,550 words, 4.6MB (
core/assets/words.txt),read line by line through
Matcher::getWordList()and filtered four times per word. The sameanagram produces the same result every time, so repeat searches redo all of it.
Since results are deterministic given (anagram, minLength, dictionary), they cache cleanly.
Cache key
The requirement is that entries survive indefinitely but invalidate the moment the dictionary
changes. Three candidate fingerprints:
filemtime()hash_file('xxh128', ...)anagram-finder/coreversionProposed key:
dictHash—hash_file('xxh128', ...)on the dictionary, itself cached (see below)normalisedAnagram— lowercased and letter-sorted, soeamstoxilandlatoximseshare one entry. Worth doing: the same bag of letters in any order is the same query.
Hashing 4.6MB costs a few ms, which would undo the saving if done per request. Cache the hash
under a key derived from
filemtime+filesize— cheap stats that change whenever the fileis replaced, and a false hit there is impossible without an identical-size, identical-mtime
substitution.
Storage
CACHE_STOREis currentlydatabase(config/cache.php:18). That is fine and needs no newservice. Store entries
forever()— the dictionary hash in the key is the invalidationmechanism, so a TTL would only cause needless recomputation. Old entries from a superseded
dictionary become unreachable rather than stale; a
cache:prune-stale-tags-style sweep or aplain
cache:clearon deploy handles the accumulation if it ever matters.Scope
Matcher::findWords()call in the controller, not insidecore— caching is anapplication concern, and
coreshould stay dependency-free (it currently requires onlyilluminate/collections)Matcherif needed — the constantis currently
private const DICTIONARYAcceptance
eamstoxilandlatoximsehit the same cache entrywords.txtinvalidates all entries without a manual flushNote
Related: the
[FIND WORDS]button is already disabled client-side when the input matches thecurrent result (
resources/views/index.blade.php), but that is a UI guard only — a direct POSTstill triggers a full scan. This ticket addresses the actual cost.
Done —
b11bcc3Measured: 386ms cold → 1ms warm on
retinas(282 matches), identical results.Cache key
anagram:{dictHash}:{minLength}:{sortedLetters}— e.g.anagram:bf647cb5910ec9ea8d85efbf053daa2b:3:aeilmostxhash_file('xxh128')on the dictionary rather thanfilemtime, as planned: the file shipsinside the image, so its mtime changes on every rebuild while the words do not.
filemtime:filesize, so 4.6MB is not rehashed per request.eamstoxil,latoximseandLATOXIMSEproduce an identicalkey and share one entry.
forever; a replaced dictionary makes old keys unreachable rather than stale.Caching lives in
app/Services/AnagramSearch.php, not incore—corestays dependency-freeso
tuican keep consuming it.Validation, included per the scope decision
Two pre-existing bugs surfaced while testing, both fixed:
/find/EAMSTOXILreturned 0 matches — the dictionary is lowercase andMatcher::checkcompares raw characters
/find/eam5t0xilaccepted digits and scanned the dictionary anywayBoth routes now share one rule set (
alpha:ascii,min:2,max:24), input is lowercased inprepareForValidation, and invalid input redirects home with the message rendered in theform's existing
@errorblock, input repopulated. Verified: digits, single characters,over-length strings and spaces all redirect rather than reaching the matcher.
Bug found in my own first attempt
Caching the
Collectiondirectly returned__PHP_Incomplete_Classfrom the database store and500'd. Now caching plain arrays and rebuilding the Collection on the way out.
Not done
No tests.
AnagramSearchis structured to be testable — key derivation and normalisation areseparable — but the ticket did not ask and
webcurrently carries only starter-kit tests.Worth its own ticket.