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:
n0ctal
2026-09-26 23:30:22 +05:00
committed by GitHub
parent d86a3def85
commit c0c0136037
6 changed files with 255 additions and 16 deletions
+1 -1
View File
@@ -153,7 +153,7 @@ func TestClientHwidGateRegistersAndBlocks(t *testing.T) {
t.Fatalf("updated HWID metadata missing: %#v", list)
}
if err := svc.setClientLimitHwidByEmail(nil, rec.Email, 1); err != nil {
if err := svc.setClientLimitHwidByEmail(rec.Email, 1); err != nil {
t.Fatalf("lower limit: %v", err)
}
var count int64