Files
3x-ui-agent/internal/web/service/client_cross_inbound_race_test.go
T
Sanaei f727d04f65
Release 3X-UI for Docker / build (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
v3.7.0
2026-08-24 15:07:15 +02:00

64 lines
1.7 KiB
Go

package service
import (
"fmt"
"sync"
"testing"
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
)
// Concurrent creates on two inbounds hold two different lockInbound mutexes,
// so only the serialized writer's in-tx re-check can reject the second claim.
func TestAddInboundClientConcurrentCrossInboundAddressSingleWinner(t *testing.T) {
setupBulkDB(t)
svc := &ClientService{}
inboundSvc := &InboundService{}
ib1 := mkInbound(t, 52210, model.WireGuard, wgServerSettings())
ib2 := mkInbound(t, 52211, model.WireGuard, wgServerSettings())
const rounds = 25
for round := range rounds {
addr := fmt.Sprintf("10.77.%d.7/32", round)
claims := []*model.Inbound{
{Id: ib1.Id, Protocol: model.WireGuard, Settings: clientsSettings(t, []model.Client{
{Email: fmt.Sprintf("race-%d-a@wg", round), Enable: true, AllowedIPs: []string{addr}},
})},
{Id: ib2.Id, Protocol: model.WireGuard, Settings: clientsSettings(t, []model.Client{
{Email: fmt.Sprintf("race-%d-b@wg", round), Enable: true, AllowedIPs: []string{addr}},
})},
}
start := make(chan struct{})
errs := make(chan error, len(claims))
var wg sync.WaitGroup
for _, claim := range claims {
wg.Add(1)
go func(data *model.Inbound) {
defer wg.Done()
<-start
_, err := svc.AddInboundClient(inboundSvc, data)
errs <- err
}(claim)
}
close(start)
wg.Wait()
close(errs)
committed := 0
rejections := make([]string, 0, len(claims))
for err := range errs {
if err == nil {
committed++
continue
}
rejections = append(rejections, err.Error())
}
if committed != 1 {
t.Fatalf("round %d addr %s: concurrent AddInboundClient committed=%d, want exactly 1 (rejections: %v)",
round, addr, committed, rejections)
}
}
}