mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-10 23:25:02 +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:
@@ -48,6 +48,8 @@ const (
|
||||
hwidFingerprintLength = 12
|
||||
)
|
||||
|
||||
var errClientHwidWriteNotSerialized = errors.New("client HWID write requires the serialized transaction")
|
||||
|
||||
type ClientHwidInfo struct {
|
||||
Id int `json:"id"`
|
||||
FirstSeen int64 `json:"firstSeen"`
|
||||
@@ -300,9 +302,16 @@ func (s *ClientService) DeleteClientHwid(email string, id int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ClientService) setClientLimitHwidByEmail(tx *gorm.DB, email string, limit int) error {
|
||||
if tx == nil {
|
||||
tx = database.GetDB()
|
||||
// Serialize the limit write and trim with SyncInbound and client deletion.
|
||||
func (s *ClientService) setClientLimitHwidByEmail(email string, limit int) error {
|
||||
return runSerializedTx(func(tx *gorm.DB) error {
|
||||
return s.setClientLimitHwidByEmailTx(tx, email, limit)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ClientService) setClientLimitHwidByEmailTx(tx *gorm.DB, email string, limit int) error {
|
||||
if !isSerializedTx(tx) {
|
||||
return errClientHwidWriteNotSerialized
|
||||
}
|
||||
if limit < 0 {
|
||||
limit = 0
|
||||
@@ -346,8 +355,8 @@ func trimClientHwidsForSubID(tx *gorm.DB, subID string, limit int) error {
|
||||
}
|
||||
|
||||
func clearClientHwidsBySubIDTx(tx *gorm.DB, subIDs ...string) error {
|
||||
if tx == nil {
|
||||
tx = database.GetDB()
|
||||
if !isSerializedTx(tx) {
|
||||
return errClientHwidWriteNotSerialized
|
||||
}
|
||||
clean := make([]string, 0, len(subIDs))
|
||||
seen := map[string]struct{}{}
|
||||
|
||||
Reference in New Issue
Block a user