OpenHands / OpenHands/enterprise
perf: cache small auth lookup tables instead of querying on every auth check (replaces #14633)
@aivong-openhands is already working on this.
Since Jun 30, 2026.
- Dominant language
- Python
- Stars
- 4
- Forks
- 2
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 101
Description
Why
Follow-up to OpenHands/OpenHands#14633, re-scoped per the analysis in that thread: adding indexes will not reduce load on user_authorizations and blocked_email_domains.
Both tables hold only ~31 rows (a single ~8 KB page). For a table that small the Postgres planner will keep choosing a sequential scan over any index — a 1-page seq scan (≈ seq_page_cost + 31·cpu_tuple_cost) is cheaper than index + heap access, so a new index would simply go unused and the seq_scan counts wouldn't move. The evidence confirms this: seq_tup_read / seq_scan ≈ 31 for both tables (each scan reads all 31 rows in ~1 page).
The cost driver is query volume, not per-scan cost. These tables are read on every authentication/authorization path via UserAuthorizationStore.get_authorization_type — millions of calls — and an index can't reduce the number of calls. The effective fix is to stop hitting the DB on the hot path: cache these small, rarely-changing tables in memory with a short TTL (and/or invalidate on write).
Validation
Hot path to change:
UserAuthorizationStore.get_matching_authorizations/get_authorization_type—enterprise/storage/user_authorization_store.py:52- Callers:
enterprise/server/auth/saas_user_auth.py:740,enterprise/server/auth/user/default_user_authorizer.py:64
Proposed approach:
- Load the full
user_authorizationsrule set (and the blocked-domain list) into process memory behind a short TTL cache (e.g.cachetools.TTLCacheor an@lru_cachewrapped with a TTL) and evaluate the email/provider match in Python instead of issuing a query per request. Invalidate on the create/delete paths in the same store. - TTL keeps a single replica's staleness window small (seconds–minutes); writes are rare so a short TTL is acceptable.
How to verify the fix:
seq_scan/callsfor both tables inpg_stat_user_tablesdrop to roughly(replicas × table_count) / TTLinstead of tracking auth request volume.- Existing auth tests still pass:
enterprise/tests/unit/test_saas_user_auth.py,enterprise/tests/unit/storage/test_user_authorization_store.py. - Add a test asserting a second lookup within the TTL window issues no DB query (mock the session/store) and that a create/delete invalidates the cache.
Related: OpenHands/OpenHands#14633 (closed in favor of this).
This was created by an AI assistant.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.