mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-11 15:45:00 +03:00
fix(hwid): serialize the device-limit write with its trim (#6591)
setClientLimitHwidByEmail wrote clients.limit_hwid and then trimmed client_hwids as two independent statements. A traffic-cycle Save that read the record before the limit changed could write the stale value back after it, and a failed trim committed the new limit anyway. Both halves now run inside runSerializedTx, the transaction the traffic writer already owns. setClientLimitHwidByEmailTx and clearClientHwidsBySubIDTx refuse a handle that is not that transaction (errClientHwidWriteNotSerialized) instead of falling back to the shared handle. Client delete moves onto the same writer, and BulkCreate withdraws a re-created client's tombstone before applying its optional HWID limit. TestSetClientLimitHwidIsSerializedWithSyncInbound holds a stale traffic-cycle Save open across the limit change and fails without the serialization (limit_hwid = 5, want 1).
This commit is contained in:
@@ -553,7 +553,7 @@ func (s *ClientService) BulkAdjust(inboundSvc *InboundService, emails []string,
|
||||
}
|
||||
}
|
||||
if adjustHwid {
|
||||
if err := s.setClientLimitHwidByEmail(db, email, *limitHwid); err != nil {
|
||||
if err := s.setClientLimitHwidByEmail(email, *limitHwid); err != nil {
|
||||
if _, already := skippedReasons[email]; !already {
|
||||
skippedReasons[email] = err.Error()
|
||||
}
|
||||
@@ -1487,22 +1487,22 @@ func (s *ClientService) BulkCreate(inboundSvc *InboundService, payloads []Client
|
||||
}
|
||||
}
|
||||
|
||||
createdEmails := make([]string, 0, len(prep))
|
||||
for idx := range prep {
|
||||
if failed[idx] {
|
||||
skip(prep[idx].client.Email, reason[idx])
|
||||
continue
|
||||
}
|
||||
if err := s.setClientLimitHwidByEmail(nil, prep[idx].client.Email, prep[idx].limitHwid); err != nil {
|
||||
// The client is already live after fanout; never leave a stale delete
|
||||
// tombstone merely because applying its optional HWID limit failed.
|
||||
withdrawClientTombstones(prep[idx].client.Email)
|
||||
if err := s.setClientLimitHwidByEmail(prep[idx].client.Email, prep[idx].limitHwid); err != nil {
|
||||
skip(prep[idx].client.Email, err.Error())
|
||||
continue
|
||||
}
|
||||
createdEmails = append(createdEmails, prep[idx].client.Email)
|
||||
result.Created++
|
||||
}
|
||||
// A re-created email is a live identity again: a delete tombstone left
|
||||
// standing makes the next node merge prune the new client's inbound links.
|
||||
withdrawClientTombstones(createdEmails...)
|
||||
return result, needRestart, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user