Skip to content

Backend: Cache localized GetLanguages/GetRegions responses per locale#9302

Merged
aapeliv merged 2 commits into
developfrom
backend/fix/cache-localized-resource-responses
Jul 23, 2026
Merged

Backend: Cache localized GetLanguages/GetRegions responses per locale#9302
aapeliv merged 2 commits into
developfrom
backend/fix/cache-localized-resource-responses

Conversation

@aapeliv

@aapeliv aapeliv commented Jul 19, 2026

Copy link
Copy Markdown
Member

Since #9173, Resources/GetLanguages localizes all ~300 language names on every request. Each name goes through babel.Locale.parse, and ~120 of our ISO639-3 codes aren't known to babel at all — for those, babel does an uncached filesystem stat and raises UnknownLocaleError each time. That adds up to ~38ms of pure CPU per request (measured warm), on an endpoint that used to be a cached-dict lookup — which is what caused the backend CPU usage spike starting 2026-06-28.

The responses are fully static per locale, so this caches the built response protos in an lru_cache keyed on the babel locale (bounded by the set of supported UI locales). Per-request cost returns to effectively zero; the first request per locale after startup pays the localization cost once. GetRegions gets the same treatment (it was cheap, but it's the same shape).

Sharing one proto message across requests is safe here: nothing mutates responses after the servicer returns them — the tracing interceptor's sensitive-field sanitization deepcopies first — and there's a comment in the servicer noting this caveat.

Also removes the now-unused per-code try_localize_* wrappers from LocalizationContext, since the servicer now calls the module-level functions from couchers/i18n/localize.py directly (those remain and are still tested).

Testing

  • Benchmarked the old path warm: ~38ms CPU per GetLanguages call (~24ms of that in the exception path for the 122 codes babel doesn't know); the new path is an lru_cache hit after the first call per locale.
  • uv run pytest src/tests/test_resources.py src/tests/test_localize.py — 13 passed. test_resources.py already covers localized language/region names for non-English locales, which exercises the cached path.
  • make format and make mypy pass.

Backend checklist

  • Added tests for any new code or added a regression test if fixing a bug (covered by existing localization tests in test_resources.py/test_localize.py)
  • Run the backend locally and it works
  • Added migrations if there are any database changes, rebased onto develop if necessary for linear migration history (no database changes)

For maintainers

  • Maintainers can push commits to my branch
  • Maintainers can merge this PR for me

This PR was created with the Couchers PR skill.

Since #9173, GetLanguages localized all ~300 language names on every
request via babel.Locale.parse, costing ~38ms of CPU per call. The
responses are static per locale, so cache the built protos.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@CouchersBot

CouchersBot commented Jul 19, 2026

Copy link
Copy Markdown
Member

Mobile

Install the Dev Tool (iOS via TestFlight, iOS Simulator, or Android .apk) here.

Scan the QR with your phone camera, or tap Open in Dev Tool on the device, to open this branch in the installed Dev Tool dev client.

    iOS         Android    
    QR to open the iOS build         QR to open the Android build    
    Open in Dev Tool         Open in Dev Tool    
Deep links

iOS

couchers-devtool://expo-development-client/?url=https%3A%2F%2Fc5984238--ota.preview.couchershq.org%2Fios%2Fmanifest

Android

couchers-devtool://expo-development-client/?url=https%3A%2F%2Fc5984238--ota.preview.couchershq.org%2Fandroid%2Fmanifest

Web (Vercel)

View the Vercel web preview for this branch.

Backend

Schema Schema diff Sample emails

Other

Protos Backend coverage Web coverage

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@aapeliv
aapeliv requested a review from tristanlabelle July 19, 2026 23:23
@aapeliv
aapeliv marked this pull request as ready for review July 19, 2026 23:23

@tristanlabelle tristanlabelle left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Comment thread app/backend/src/couchers/servicers/resources.py
@aapeliv
aapeliv merged commit 82dddc5 into develop Jul 23, 2026
7 checks passed
@aapeliv
aapeliv deleted the backend/fix/cache-localized-resource-responses branch July 23, 2026 00:08
@github-actions

Copy link
Copy Markdown
Contributor

📝 Release Notes

This PR does not need to be included in release notes.

Reason: This is a backend performance optimization that fixes CPU usage on resource endpoints, but it does not introduce a user-visible feature or a noticeable end-user change on its own. While important operationally, it is closer to internal infrastructure/performance maintenance than a major platform-facing improvement users would recognize in release notes.

🤖 Bot Debug Information

Model: couchers.openai.gpt-5.4
Decision: exclude
Reasoning: This is a backend performance optimization that fixes CPU usage on resource endpoints, but it does not introduce a user-visible feature or a noticeable end-user change on its own. While important operationally, it is closer to internal infrastructure/performance maintenance than a major platform-facing improvement users would recognize in release notes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants