mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-30 15:07:14 +00:00
fix(sub): resolve subscription clients and stats from normalized tables
A subscription fetch inside a large inbound cost seconds because every layer re-parsed the inbound's full settings JSON: getInboundsBySubId preloaded the whole client_traffics table of each matched inbound, matchingClients parsed all clients to filter by subId, and then every per-protocol generator (raw links, JSON outbounds, Clash proxies) parsed the blob again per link — once to find the client by email and once for inbound-level fields like encryption or method. At 500k clients in one inbound that was 13s per raw fetch and 8.5s per JSON fetch; at 100k, 2.6s/1.7s. After this change both cost ~70ms at 100k. matchingClients now resolves through the indexed clients/client_inbounds tables (ListForInboundBySubId, ordered by clients.id like ListForInbound — the same source the running Xray users are built from), and the per-request SubService carries two caches: clientsByInbound, primed by matchingClients/inboundLinks so clientForLink resolves a client without parsing settings (with the old full-parse as fallback, which also fixes the export-all-links path that re-parsed the blob once per client), and settingsByInbound, a once-per-request shallow decode that skips materializing the clients array entirely. The ClientStats preload is replaced by loading only the subscriber's traffic rows (indexed clients.sub_id); statsForClient's per-email DB fallback (#5567) covers any miss, and the case-insensitive email dedupe keeps the #5134 guarantee for case-differing duplicate rows.
This commit is contained in:
@@ -122,7 +122,7 @@ func TestSubJsonServiceVlessFlattened(t *testing.T) {
|
||||
inbound := &model.Inbound{Listen: "1.2.3.4", Port: 443, Protocol: model.VLESS, Settings: `{"encryption":"none"}`}
|
||||
client := model.Client{ID: "uuid-1", Flow: "xtls-rprx-vision"}
|
||||
|
||||
settings := outboundSettings(t, NewSubJsonService("", "", "", nil).genVless(inbound, nil, client, ""))
|
||||
settings := outboundSettings(t, NewSubJsonService("", "", "", nil).genVless(&SubService{}, inbound, nil, client, ""))
|
||||
if _, ok := settings["vnext"]; ok {
|
||||
t.Fatal("vless outbound must not use vnext")
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func TestSubJsonServiceServerUsesServersArray(t *testing.T) {
|
||||
trojan := &model.Inbound{Listen: "1.2.3.4", Port: 443, Protocol: model.Trojan, Settings: `{}`}
|
||||
client := model.Client{Password: "p4ss"}
|
||||
|
||||
settings := outboundSettings(t, NewSubJsonService("", "", "", nil).genServer(trojan, nil, client, ""))
|
||||
settings := outboundSettings(t, NewSubJsonService("", "", "", nil).genServer(&SubService{}, trojan, nil, client, ""))
|
||||
server := firstServer(settings)
|
||||
if server == nil {
|
||||
t.Fatalf("trojan outbound must use a servers array, got: %#v", settings)
|
||||
@@ -164,7 +164,7 @@ func TestSubJsonServiceServerUsesServersArray(t *testing.T) {
|
||||
}
|
||||
|
||||
ss := &model.Inbound{Listen: "1.2.3.4", Port: 443, Protocol: model.Shadowsocks, Settings: `{"method":"aes-256-gcm"}`}
|
||||
ssSettings := outboundSettings(t, NewSubJsonService("", "", "", nil).genServer(ss, nil, client, ""))
|
||||
ssSettings := outboundSettings(t, NewSubJsonService("", "", "", nil).genServer(&SubService{}, ss, nil, client, ""))
|
||||
ssServer := firstServer(ssSettings)
|
||||
if ssServer == nil {
|
||||
t.Fatalf("shadowsocks outbound must use a servers array, got: %#v", ssSettings)
|
||||
@@ -194,7 +194,7 @@ func TestSubJsonServiceXmuxSuppressesGlobalMux(t *testing.T) {
|
||||
inbound := &model.Inbound{Listen: "1.2.3.4", Port: 443, Protocol: model.VLESS, Settings: `{"encryption":"none"}`}
|
||||
client := model.Client{ID: "uuid-1"}
|
||||
|
||||
raw := svc.genVless(inbound, streamSettings, client, mux)
|
||||
raw := svc.genVless(&SubService{}, inbound, streamSettings, client, mux)
|
||||
var ob map[string]any
|
||||
if err := json.Unmarshal(raw, &ob); err != nil {
|
||||
t.Fatalf("unmarshal outbound: %v", err)
|
||||
@@ -240,7 +240,7 @@ func TestSubJsonServiceGlobalMuxWhenNoXmux(t *testing.T) {
|
||||
inbound := &model.Inbound{Listen: "1.2.3.4", Port: 443, Protocol: model.VLESS, Settings: `{"encryption":"none"}`}
|
||||
client := model.Client{ID: "uuid-1"}
|
||||
|
||||
raw := svc.genVless(inbound, streamSettings, client, mux)
|
||||
raw := svc.genVless(&SubService{}, inbound, streamSettings, client, mux)
|
||||
var ob map[string]any
|
||||
if err := json.Unmarshal(raw, &ob); err != nil {
|
||||
t.Fatalf("unmarshal outbound: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user