fix geocode cache key ignoring the geojson variant - #137
Conversation
geocodePhrase gained an includeGeoJson parameter in FoggedLens#109, but the cache key still only used the query. Whichever endpoint asked first won the cache entry for 24 hours, so /geocode could return a result with no geojson when /geocode/multi had already cached that query.
kawacukennedy
left a comment
There was a problem hiding this comment.
Nice catch — this is a real bug. /geocode (single, via geocodeSingleResult) and /geocode/multi both flow through geocodePhrase, but the single endpoint requests includeGeoJson: true (so the new map can draw the boundary and derive zoom from result.geojson), while multi requests false. Under the old key both variants collided for 24h, so whichever endpoint asked first could starve the other of geojson.
The fix is correct
Keying by variant (geojson vs plain) is exactly the right granularity. I verified it against the route in app.ts/server.ts — both endpoints call geocodePhrase(query, includeGeoJson) with different values, so the two variants are genuinely different responses that must not share a cache entry.
Tests are well-designed
- First test proves the regression: requesting the plain variant then the geojson variant produces two upstream calls (
urls.length === 2), the second carryingpolygon_geojson=1and returninggeojson. - Second test guards against the obvious over-correction: same variant twice still hits the cache (one call), so you haven't just disabled caching.
- Using a unique query per run works around the fixed
/tmp/nominatim-cachedisk path persisting between runs — reasonable given the current no-seam cache setup you flagged.
One small thing, not blocking: the disk cache at /tmp/nominatim-cache is shared across the whole API and survives restarts, so the "unique query" strategy (and the geojson keying) depends on the cache never being empty-cold in an unintended way. If you ever add an injectable cache seam (you mentioned you'd be happy to), that would let these tests control TTLs and make them fully deterministic. Happy to help take that on in a follow-up.
Good, focused fix — this resolves the map landmarks/zoom issue cleanly.
|
thanks for looking it over. the cache seam is still on my list once this lands, happy if you beat me to it. |
/geocodeand/geocode/multiboth go throughNominatimClient.geocodePhrase, and they ask for different things. The single endpoint passesincludeGeoJson: trueso the map can draw the city boundary; the multi endpoint passesfalse. Both then look the result up under the same cache key:So whichever endpoint asks first wins that query for the next 24 hours. If
/geocode/multigets there first, a later/geocodefor the same place is served the cached entry, which has nogeojsonon it, and no Nominatim call is made to go get it.Map.vuereadsresult.geojsonto set the boundary polygon and to work out the zoom level, so the search quietly lands on the place with no boundary drawn.The parameter was added in #109; the cache key is from back when there was only one variant.
The fix puts the variant in the key. I also added a test for the cached path, since there wasn't one.
Verifying
api/services/NominatimClient.test.tsstubsfetchand counts the upstream calls. On master:One upstream call instead of two, because the second request came back from the cache.
With the fix, the whole
apisuite:The second test in the file asks for the same variant twice and still expects a single upstream call, so this isn't just turning the cache off.
One thing worth flagging: the disk store lives at a fixed
/tmp/nominatim-cacheand outlives the test process, so the tests generate a query string they haven't cached before rather than assuming an empty cache. Happy to swap that for an injectable cache if you'd rather have the seam.