From 97588dd0b9b518a27806e962273c4142b217cd5d Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Thu, 2 Jul 2026 16:24:18 +0200 Subject: [PATCH] fix(traffic): disable depleted clients by id instead of a second full scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit disableInvalidClients evaluated the depleted predicate twice per poll: once to SELECT the rows (for xray removal and settings sync) and again in the UPDATE that flips enable off — each a full client_traffics scan, the second also re-running the cross-panel EXISTS subquery when global rows exist. The UPDATE now flips the already-collected rows by primary key in sqlInChunk batches, sorted for stable lock order. Same rows, same RowsAffected, half the scan cost; id-based matching also stays correct for rows with empty emails. --- internal/web/service/inbound_disable.go | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/internal/web/service/inbound_disable.go b/internal/web/service/inbound_disable.go index ccdb87c17..d2bfe060b 100644 --- a/internal/web/service/inbound_disable.go +++ b/internal/web/service/inbound_disable.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "slices" "strings" "time" @@ -162,13 +163,23 @@ func (s *InboundService) disableInvalidClients(tx *gorm.DB) (bool, int64, error) } } - result := tx.Model(xray.ClientTraffic{}). - Where(cond+" AND enable = ?", now, true). - Update("enable", false) - err = result.Error - count := result.RowsAffected - if err != nil { - return needRestart, count, err + // Flip the rows already collected above by primary key instead of + // re-evaluating the depleted predicate, which was a second full scan of + // client_traffics on every poll. Sorted ids keep the lock order stable. + ids := make([]int, 0, len(depletedRows)) + for i := range depletedRows { + ids = append(ids, depletedRows[i].Id) + } + slices.Sort(ids) + var count int64 + for _, batch := range chunkInts(ids, sqlInChunk) { + result := tx.Model(xray.ClientTraffic{}). + Where("id IN ? AND enable = ?", batch, true). + Update("enable", false) + if result.Error != nil { + return needRestart, count, result.Error + } + count += result.RowsAffected } if len(depletedEmails) > 0 {