Skip to content

fix(manager): bypass GraphQL N+1 for language cache with custom CMS REST endpoint#646

Merged
tataihono merged 9 commits intomainfrom
fix/manager-language-cache-performance
Apr 3, 2026
Merged

fix(manager): bypass GraphQL N+1 for language cache with custom CMS REST endpoint#646
tataihono merged 9 commits intomainfrom
fix/manager-language-cache-performance

Conversation

@tataihono
Copy link
Copy Markdown
Contributor

Summary

  • Problem: The manager's language cache warm fires 4 parallel GraphQL queries that produce ~20K individual DB queries (Strapi v5 has no DataLoader). This exhausts the 25-connection PostgreSQL pool and blocks all CMS requests — auth checks took 73-167 seconds during a language cache refresh.
  • Fix: Created a custom CMS REST endpoint (/api/language-geo) that returns all language, country, continent, and country-language data in a single SQL query using raw knex, then updated the manager to call it instead of GraphQL.
  • Pattern: Same approach as the video coverage fix (PR fix(manager): optimize video coverage query (22-47s → <1s) #637).

Changes

CMS (apps/cms/)

  • New custom API: src/api/language-geo/ with route, controller, and service
  • Single SQL query joins country_languages, languages, countries, continents through Strapi v5 link tables
  • Filters published_at IS NOT NULL to exclude draft rows
  • JS aggregation in service produces { continents, countries, languages } shape

Manager (apps/manager/)

  • Replaced 4 parallel GraphQL queries + in-memory transform with a single fetch() to /api/language-geo
  • Removed ~175 lines of GraphQL query definitions and aggregation logic
  • SWR cache wrapper, TTL settings, and route handler unchanged

Requirements

  • R1. Language/geo data fetch must not exhaust the CMS connection pool (single query vs ~20K)
  • R2. Language cache refresh must complete in under 5 seconds
  • R3. Auth checks remain responsive during language cache refresh
  • R4. Language picker still shows all ~4,560 languages grouped by continent and country with speaker counts

Testing

  • Both CMS and manager typecheck clean
  • Linting passes (pre-commit hooks)
  • Data contract preserved: same { continents, countries, languages } JSON shape

Post-Deploy Monitoring & Validation

  • What to monitor/search
    • Logs: language-cache label in manager logs, KnexTimeoutError in CMS logs (should disappear)
    • Metrics: CMS PostgreSQL active connections during manager startup
  • Expected healthy behavior
    • Language cache warm completes in <5s (vs 30s+)
    • No KnexTimeoutError during language cache refresh
    • /api/users/me responds in <1s during cache warm
  • Failure signal / rollback trigger
    • Manager returns 502 on /api/languages → CMS endpoint returning errors → rollback CMS deploy
    • Language picker shows empty or missing data → data shape mismatch → rollback manager deploy
  • Validation window & owner
    • Window: First 30 minutes after deploy
    • Owner: tataihono

Compound Engineering v2.51.0
🤖 Generated with Claude Opus 4.6 (1M context) via Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) [email protected]

@railway-app
Copy link
Copy Markdown

railway-app bot commented Apr 3, 2026

🚅 Deployed to the forge-pr-646 environment in forge

Service Status Web Updated (UTC)
@forge/cms ✅ Success (View Logs) Apr 3, 2026 at 7:25 am
@forge/manager ✅ Success (View Logs) Apr 3, 2026 at 7:20 am
1 service not affected by this PR
  • @forge/web

@railway-app railway-app bot temporarily deployed to forge / forge-pr-646 April 3, 2026 07:09 Destroyed
@railway-app railway-app bot temporarily deployed to forge / forge-pr-646 April 3, 2026 07:15 Destroyed
@railway-app railway-app bot temporarily deployed to forge / forge-pr-646 April 3, 2026 07:19 Destroyed
@railway-app railway-app bot temporarily deployed to forge / forge-pr-646 April 3, 2026 07:21 Destroyed
@tataihono tataihono merged commit b91fdd4 into main Apr 3, 2026
28 checks passed
@tataihono tataihono deleted the fix/manager-language-cache-performance branch April 3, 2026 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant