fix(x-ui): fix OOM crash in generate_subscription_url_mapping for large deployments#4
Conversation
|
Warning Review limit reached
More reviews will be available in 50 minutes and 50 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughThe pull request optimizes subscription URL mapping generation by pre-loading subscription tokens into a lookup map once per inbound, removing the per-row ChangesSubscription URL Mapping Optimization
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
28fa498 to
f098196
Compare
…ge deployments The script performed a LEFT JOIN between client_traffics and inbounds and called fetchall(), which loaded the full inbound.settings JSON blob (containing all clients, several MB each) once per user row. On a deployment with 10,654 users across 3 inbounds this caused the OOM killer to terminate the process before any output file was written. Fix: pre-load a email→subId dict by reading each inbound's settings JSON once (O(inbounds)), then query client_traffics without the JOIN and do an O(1) dict lookup per user. Memory usage drops from O(users × inbound_json_size) to O(inbounds × inbound_json_size + users). Tested on a real deployment: - 10,654 users, 3 inbounds (~7 MB settings JSON each), 2 GB RAM server - Before: OOM killed after ~5 s, no output - After: completes in ~3 s, 10,652 mappings generated correctly
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@x-ui/migration/generate_subscription_url_mapping.py`:
- Around line 228-254: The email_to_subid map currently keys only by email
causing later inbounds to overwrite earlier subIds; change email_to_subid to use
a composite key (inbound_id, email) when populating it (use ib_row['id'] as
inbound_id alongside client_email and sub_id) and then update the lookup site
(where client_traffics rows are matched — the code that currently looks up by
client_email, referenced as email_to_subid[client_email] around the later
lookup) to use the same (inbound_id, email) tuple key so each inbound's client
entry is preserved and looked up correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ecb32dd6-5974-46d8-9157-ba674bf8e2d1
📒 Files selected for processing (1)
x-ui/migration/generate_subscription_url_mapping.py
f098196 to
ed8c2c5
Compare
Use a dedicated xui_cursor with cursor iteration to avoid holding all client_traffics rows in memory at once. A COUNT(*) query is issued first so total count remains available for progress logging and the result dict. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
PR #4 is ready for review/merge. It includes the original OOM fix plus the follow-up review improvements:
Tested successfully on a large deployment: 10,654 users across 3 inbounds on a 2GB RAM server. |
Problem
On large x-ui deployments (10k+ users),
generate_subscription_url_mapping.pyis killed by the OOM killer (exit code 137 / SIGKILL) and produces no output file. The script appears to start (Fetching users from x-ui...) but then silently dies.Root cause
The query does a
LEFT JOINbetweenclient_trafficsandinbounds:The
inbounds.settingscolumn contains a JSON blob that embeds all clients for that inbound (can be 3,500+ clients, ~7 MB per inbound). Joining this intoclient_trafficsand callingfetchall()duplicates the full blob for every user row.Example: 10,654 users × 3 inbounds × ~7 MB per inbound ≈ ~75 GB resident memory → OOM kill before any output is written.
Fix
Pre-load an
email → subIddict by reading each inbound's settings JSON once (O(inbounds) passes), then queryclient_trafficswithout theLEFT JOINand do an O(1) dict lookup per user.Memory usage drops from O(users × inbound_json_size) to O(inbounds × inbound_json_size + users).
The existing
extract_subscription_token_from_inbound()helper is no longer called in the hot path (it is still used by the per-row branch for deployments that store the token directly on the client_traffics row).Test results
Tested on a real production deployment:
Summary by CodeRabbit