mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-16 17:46:08 +00:00
fix(db): clamp traffic counters below int64 max and repair overflowed rows
A counter pushed past int64 (multi-node setups hit this via historic delta-compounding bugs) makes SQLite silently promote the INTEGER cell to REAL. From then on the column no longer scans into the Go int64 field and every reader of client_traffics fails at once: the inbounds page, xray restarts, and node traffic sync all return "converting driver.Value type float64 to int64". Two-part fix: every unbounded "up = up + ?" add (local traffic, node delta merge, inbound counters, plus the Go-side outbound accumulation) now saturates at TrafficMax, a cap safely below math.MaxInt64 so one more delta cannot overflow; and a startup repair casts REAL-promoted cells back to INTEGER and clamps all traffic counters into [0, TrafficMax] across client_traffics, inbounds, outbound_traffics and node_client_traffics, restoring access to already-corrupted panels without manual sqlite surgery. Closes #5762
This commit is contained in:
@@ -744,10 +744,12 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
||||
if err := tx.Exec(
|
||||
fmt.Sprintf(
|
||||
`UPDATE client_traffics
|
||||
SET up = up + ?, down = down + ?, enable = %s, total = ?,
|
||||
SET up = %s, down = %s, enable = %s, total = ?,
|
||||
expiry_time = CASE WHEN expiry_time > 0 AND CAST(? AS BIGINT) <= 0 THEN expiry_time ELSE CAST(? AS BIGINT) END,
|
||||
reset = ?, last_online = %s
|
||||
WHERE email = ?`,
|
||||
database.ClampedAddExpr("up"),
|
||||
database.ClampedAddExpr("down"),
|
||||
enableExpr,
|
||||
database.GreatestExpr("last_online", "?"),
|
||||
),
|
||||
|
||||
@@ -87,8 +87,8 @@ func (s *InboundService) addInboundTraffic(tx *gorm.DB, traffics []*xray.Traffic
|
||||
if traffic.IsInbound {
|
||||
err = tx.Model(&model.Inbound{}).Where("tag = ? AND node_id IS NULL", traffic.Tag).
|
||||
Updates(map[string]any{
|
||||
"up": gorm.Expr("up + ?", traffic.Up),
|
||||
"down": gorm.Expr("down + ?", traffic.Down),
|
||||
"up": gorm.Expr(database.ClampedAddExpr("up"), traffic.Up),
|
||||
"down": gorm.Expr(database.ClampedAddExpr("down"), traffic.Down),
|
||||
}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -153,7 +153,9 @@ func (s *InboundService) addClientTraffic(tx *gorm.DB, traffics []*xray.ClientTr
|
||||
}
|
||||
if err = tx.Exec(
|
||||
fmt.Sprintf(
|
||||
`UPDATE client_traffics SET up = up + ?, down = down + ?, last_online = %s WHERE email = ?`,
|
||||
`UPDATE client_traffics SET up = %s, down = %s, last_online = %s WHERE email = ?`,
|
||||
database.ClampedAddExpr("up"),
|
||||
database.ClampedAddExpr("down"),
|
||||
database.GreatestExpr("last_online", "?"),
|
||||
),
|
||||
t.Up, t.Down, now, ct.Email,
|
||||
|
||||
@@ -42,6 +42,16 @@ func (s *OutboundService) AddTraffic(traffics []*xray.Traffic, clientTraffics []
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// saturatingAdd caps counters at database.TrafficMax: unlike the SQL paths,
|
||||
// this read-modify-write add happens in Go, where an int64 overflow silently
|
||||
// wraps negative instead of erroring (#5762).
|
||||
func saturatingAdd(a, b int64) int64 {
|
||||
if b > database.TrafficMax-a {
|
||||
return database.TrafficMax
|
||||
}
|
||||
return a + b
|
||||
}
|
||||
|
||||
func (s *OutboundService) addOutboundTraffic(tx *gorm.DB, traffics []*xray.Traffic) error {
|
||||
if len(traffics) == 0 {
|
||||
return nil
|
||||
@@ -61,9 +71,9 @@ func (s *OutboundService) addOutboundTraffic(tx *gorm.DB, traffics []*xray.Traff
|
||||
}
|
||||
|
||||
outbound.Tag = traffic.Tag
|
||||
outbound.Up = outbound.Up + traffic.Up
|
||||
outbound.Down = outbound.Down + traffic.Down
|
||||
outbound.Total = outbound.Up + outbound.Down
|
||||
outbound.Up = saturatingAdd(outbound.Up, traffic.Up)
|
||||
outbound.Down = saturatingAdd(outbound.Down, traffic.Down)
|
||||
outbound.Total = saturatingAdd(outbound.Up, outbound.Down)
|
||||
|
||||
err = tx.Save(&outbound).Error
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user