Maps: matching a country name loads Babel's territory names for all 85 languages into every process #1109
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#1109
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?
What exists today
src/postulo/jobs/places.py:199-232_countries()(anlru_cache(maxsize=1)) readsgeonames-countryinfo.txtand then_translated_countries()(:234-247), which callsLocale.parse(tag).territoriesfor each of the 85 tags incore.languages.NATIVE_NAMES. Babel keeps every parsed locale in its module cache (babel.localedata._cache) for the life of the process, so all 85 locales (95 cache entries with their parents) stay loaded.It is reached from
resolve()(:382, the call at:402) whenever the typed text has a country part and the city table has a candidate.resolve()is called byplace()(:345, at:358), whichjobs/placing.py:43runs on an address save when mapping is offered and the place key changed, and whichjobs/views.py:375runs; and bysplit_location()(jobs/models.py:50, call at:63), used atjobs/models.py:780and by migration 0033.Measured with
tracemallocaroundplaces._translated_countries(<every CLDR territory code>)underpostulo.config.settings.test(scriptaudit/m_scripts/mem.py m1): 56.6 MB retained, 57.1 MB peak, 5.61 s, Babel cache 95 locales, 22,334 (code, name) pairs. The countryinfo file is not in the local checkout, so the call was measured directly rather than through_countries(); on an instance that ranfetch_geonamesthe path above runs it as written.What changed from the candidate: the memory is mostly the same Babel locale data that
core/language_names._index()loads (#1086). Run after M1 in the same process,_index()added only 7.2 MB. So the two share one cost, and fixing #1086 alone leaves this path loading all 85 locales anyway. The memory only falls once both are fixed.What would change
Build the country-name table once at provisioning, the way #399 moved the cities into
geonames-cities1000.sqlite:manage.py fetch_geonameswrites acountries(key, code)table (folded name to ISO code, from countryinfo plus the CLDR territory names in eachNATIVE_NAMESlanguage) into the lookup database, and_countries()becomes one indexedSELECT ... WHERE key = ?like_candidates(). No process then loads Babel locale data for this. When the table is missing the behaviour stays as today's missing-file case (no country filter). Babel stays the source; no new library.A lighter alternative is to read only the instance's selected languages (
languages.selected(), #347) plus English, but that still holds several MB per worker and changes which country names match; the provisioning table keeps every name and costs nothing at run time.What it saves or protects
large: about 57 MB held per worker for its whole life and a 5.6 s first call (measured), on the first address placed with a country
Accessibility
none
Decisions
fetch_geonamesstamps the CLDR version (language_names.cldr_version()) into the table and the start-up check warns when it differs.Found by the efficiency and reliability audit of 2026-10-30 (finding M1; index postulo/postulo#1108). Checked against
mainatdaee30b15(2026-10-30).