perf: multi-worker uvicorn + fix N+1 on the hottest path in the app
mbs-api ran single-worker uvicorn with no --workers flag at all — one
event loop handling every request. Now install.sh (and mbs update, so
existing installs pick it up too) compute a worker count from nproc
(clamped 1-4, matching typical VPS core counts) and bake it into the
systemd unit via sed substitution of a __WORKERS__ placeholder. Safe
to parallelize: verified no api.py module-level mutable state, all
of it already goes through sqlite (payment idempotency and the
xray-config file lock are already correct across separate processes,
not just asyncio tasks within one — confirmed both are OS/db-level,
not in-process). Verified with a mock dry-run of the new systemd-unit
section (fake nproc, real sed substitution) producing the expected
ExecStart line for several core counts.
Also: build_subscription_text() — called on every single hit of
/sub/{token}, the single most frequently called endpoint in the whole
app, since every VPN client re-fetches on every reconnect — was doing
one db.get_node() call per distinct node in a user's subscriptions
instead of fetching once. Same N+1 shape as the admin-endpoint bugs
fixed yesterday, except this one is on the hot path, not just the
admin panel. Fixed to batch-fetch via db.list_nodes() once.
Verified: correct output for a 4-node subscription (each node's
address appears exactly once, de1's 4 transports all present,
nothing silently dropped) and measured ~1.3ms/call average.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
7098e59967
commit
11c75c13d1
4 changed files with 20 additions and 3 deletions
|
|
@ -377,8 +377,13 @@ cat > /usr/local/etc/xray/config.json << XRAYEOF
|
||||||
XRAYEOF
|
XRAYEOF
|
||||||
|
|
||||||
echo "systemd-юниты..."
|
echo "systemd-юниты..."
|
||||||
|
CPU_COUNT=$(nproc 2>/dev/null || echo 1)
|
||||||
|
if [ "$CPU_COUNT" -lt 2 ]; then API_WORKERS=1
|
||||||
|
elif [ "$CPU_COUNT" -gt 4 ]; then API_WORKERS=4
|
||||||
|
else API_WORKERS=$CPU_COUNT
|
||||||
|
fi
|
||||||
cp "$APP_DIR/systemd/mbs-bot.service" /etc/systemd/system/mbs-bot.service
|
cp "$APP_DIR/systemd/mbs-bot.service" /etc/systemd/system/mbs-bot.service
|
||||||
cp "$APP_DIR/systemd/mbs-api.service" /etc/systemd/system/mbs-api.service
|
sed "s/__WORKERS__/$API_WORKERS/" "$APP_DIR/systemd/mbs-api.service" > /etc/systemd/system/mbs-api.service
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
|
|
||||||
echo "ставим CLI mbs..."
|
echo "ставим CLI mbs..."
|
||||||
|
|
|
||||||
3
links.py
3
links.py
|
|
@ -98,9 +98,10 @@ def build_subscription_text(subs: list[dict]) -> str:
|
||||||
if cur is None or s["expires_at"] > cur["expires_at"]:
|
if cur is None or s["expires_at"] > cur["expires_at"]:
|
||||||
best_by_node[s["node"]] = s
|
best_by_node[s["node"]] = s
|
||||||
|
|
||||||
|
nodes_by_code = {n["code"]: n for n in db.list_nodes()}
|
||||||
lines = []
|
lines = []
|
||||||
for node_code, s in best_by_node.items():
|
for node_code, s in best_by_node.items():
|
||||||
node = db.get_node(node_code)
|
node = nodes_by_code.get(node_code)
|
||||||
if not node:
|
if not node:
|
||||||
continue
|
continue
|
||||||
base_name = display_name(node["label"])
|
base_name = display_name(node["label"])
|
||||||
|
|
|
||||||
11
mbs
11
mbs
|
|
@ -98,6 +98,17 @@ cmd_update() {
|
||||||
cp "$APP_DIR/mbs" /usr/local/bin/mbs
|
cp "$APP_DIR/mbs" /usr/local/bin/mbs
|
||||||
chmod +x /usr/local/bin/mbs
|
chmod +x /usr/local/bin/mbs
|
||||||
|
|
||||||
|
echo "обновляю systemd-юниты..."
|
||||||
|
local cpu_count api_workers
|
||||||
|
cpu_count=$(nproc 2>/dev/null || echo 1)
|
||||||
|
if [ "$cpu_count" -lt 2 ]; then api_workers=1
|
||||||
|
elif [ "$cpu_count" -gt 4 ]; then api_workers=4
|
||||||
|
else api_workers=$cpu_count
|
||||||
|
fi
|
||||||
|
cp "$APP_DIR/systemd/mbs-bot.service" /etc/systemd/system/mbs-bot.service
|
||||||
|
sed "s/__WORKERS__/$api_workers/" "$APP_DIR/systemd/mbs-api.service" > /etc/systemd/system/mbs-api.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
echo "перезапускаю..."
|
echo "перезапускаю..."
|
||||||
systemctl restart mbs-bot mbs-api xray
|
systemctl restart mbs-bot mbs-api xray
|
||||||
systemctl reload nginx 2>/dev/null || true
|
systemctl reload nginx 2>/dev/null || true
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ After=network.target
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
WorkingDirectory=/opt/mbs-panel
|
WorkingDirectory=/opt/mbs-panel
|
||||||
ExecStart=/opt/mbs-panel/venv/bin/uvicorn api:app --host 127.0.0.1 --port 8001
|
ExecStart=/opt/mbs-panel/venv/bin/uvicorn api:app --host 127.0.0.1 --port 8001 --workers __WORKERS__
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=3
|
RestartSec=3
|
||||||
User=root
|
User=root
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue