client_cross_inbound_race_test.go 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. package service
  2. import (
  3. "fmt"
  4. "sync"
  5. "testing"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  7. )
  8. // Concurrent creates on two inbounds hold two different lockInbound mutexes,
  9. // so only the serialized writer's in-tx re-check can reject the second claim.
  10. func TestAddInboundClientConcurrentCrossInboundAddressSingleWinner(t *testing.T) {
  11. setupBulkDB(t)
  12. svc := &ClientService{}
  13. inboundSvc := &InboundService{}
  14. ib1 := mkInbound(t, 52210, model.WireGuard, wgServerSettings())
  15. ib2 := mkInbound(t, 52211, model.WireGuard, wgServerSettings())
  16. const rounds = 25
  17. for round := range rounds {
  18. addr := fmt.Sprintf("10.77.%d.7/32", round)
  19. claims := []*model.Inbound{
  20. {Id: ib1.Id, Protocol: model.WireGuard, Settings: clientsSettings(t, []model.Client{
  21. {Email: fmt.Sprintf("race-%d-a@wg", round), Enable: true, AllowedIPs: []string{addr}},
  22. })},
  23. {Id: ib2.Id, Protocol: model.WireGuard, Settings: clientsSettings(t, []model.Client{
  24. {Email: fmt.Sprintf("race-%d-b@wg", round), Enable: true, AllowedIPs: []string{addr}},
  25. })},
  26. }
  27. start := make(chan struct{})
  28. errs := make(chan error, len(claims))
  29. var wg sync.WaitGroup
  30. for _, claim := range claims {
  31. wg.Add(1)
  32. go func(data *model.Inbound) {
  33. defer wg.Done()
  34. <-start
  35. _, err := svc.AddInboundClient(inboundSvc, data)
  36. errs <- err
  37. }(claim)
  38. }
  39. close(start)
  40. wg.Wait()
  41. close(errs)
  42. committed := 0
  43. rejections := make([]string, 0, len(claims))
  44. for err := range errs {
  45. if err == nil {
  46. committed++
  47. continue
  48. }
  49. rejections = append(rejections, err.Error())
  50. }
  51. if committed != 1 {
  52. t.Fatalf("round %d addr %s: concurrent AddInboundClient committed=%d, want exactly 1 (rejections: %v)",
  53. round, addr, committed, rejections)
  54. }
  55. }
  56. }