traffic_poll_scale_test.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package service
  2. import (
  3. "fmt"
  4. "testing"
  5. "time"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database"
  7. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  8. )
  9. func pollReport(ds scaleDataset, k int) ([]*xray.Traffic, []*xray.ClientTraffic) {
  10. traffics := make([]*xray.Traffic, 0, len(ds.tags))
  11. for _, tag := range ds.tags {
  12. traffics = append(traffics, &xray.Traffic{IsInbound: true, Tag: tag, Up: 1 << 20, Down: 2 << 20})
  13. }
  14. clientTraffics := make([]*xray.ClientTraffic, 0, k)
  15. for _, email := range sampleEmails(ds.emails, k) {
  16. clientTraffics = append(clientTraffics, &xray.ClientTraffic{Email: email, Up: 1 << 20, Down: 2 << 20})
  17. }
  18. return traffics, clientTraffics
  19. }
  20. // TestAddTrafficPollScale measures one full traffic-poll cycle (the @every 5s
  21. // job): per-client delta UPDATEs, the auto-renew probe and the depleted-client
  22. // scan, in steady state and with 100 clients depleting / renewing.
  23. func TestAddTrafficPollScale(t *testing.T) {
  24. setupScaleDB(t)
  25. svc := &InboundService{}
  26. shapes := []struct {
  27. name string
  28. inbounds int
  29. }{{"single", 1}, {"spread50", 50}}
  30. for _, n := range scaleSizes(t, 10000, 100000) {
  31. for _, shape := range shapes {
  32. t.Run(fmt.Sprintf("N=%d_%s", n, shape.name), func(t *testing.T) {
  33. db := database.GetDB()
  34. ds := seedScaleDataset(t, n, shape.inbounds)
  35. for _, k := range []int{1000, 10000} {
  36. if k > n {
  37. continue
  38. }
  39. traffics, clientTraffics := pollReport(ds, k)
  40. const reps = 3
  41. start := time.Now()
  42. for range reps {
  43. if _, _, err := svc.AddTraffic(traffics, clientTraffics); err != nil {
  44. t.Fatalf("AddTraffic steady: %v", err)
  45. }
  46. }
  47. perPoll := time.Since(start) / reps
  48. t.Logf("N=%-7d shape=%-8s K=%-6d steady=%v/poll", n, shape.name, k, perPoll.Round(time.Millisecond))
  49. var probe xray.ClientTraffic
  50. if err := db.Where("email = ?", clientTraffics[0].Email).First(&probe).Error; err != nil {
  51. t.Fatalf("load probe row: %v", err)
  52. }
  53. if probe.Up == 0 || probe.Down == 0 {
  54. t.Fatalf("steady polls did not accumulate traffic: up=%d down=%d", probe.Up, probe.Down)
  55. }
  56. }
  57. depleted := ds.perInbound[0][:100]
  58. if err := db.Model(&xray.ClientTraffic{}).
  59. Where("email IN ?", emailsOf(depleted)).
  60. Updates(map[string]any{"up": int64(100 << 30), "down": int64(0)}).Error; err != nil {
  61. t.Fatalf("mark depleted: %v", err)
  62. }
  63. start := time.Now()
  64. if _, _, err := svc.AddTraffic(nil, nil); err != nil {
  65. t.Fatalf("AddTraffic disable: %v", err)
  66. }
  67. t.Logf("N=%-7d shape=%-8s disable100=%v", n, shape.name, time.Since(start).Round(time.Millisecond))
  68. var disabledCount int64
  69. if err := db.Model(&xray.ClientTraffic{}).Where("enable = ?", false).Count(&disabledCount).Error; err != nil {
  70. t.Fatalf("count disabled: %v", err)
  71. }
  72. if disabledCount != 100 {
  73. t.Fatalf("disable100: got %d disabled rows, want 100", disabledCount)
  74. }
  75. renew := ds.perInbound[0][100:200]
  76. past := time.Now().Add(-time.Hour).UnixMilli()
  77. if err := db.Model(&xray.ClientTraffic{}).
  78. Where("email IN ?", emailsOf(renew)).
  79. Updates(map[string]any{"reset": 30, "expiry_time": past, "up": int64(1 << 30)}).Error; err != nil {
  80. t.Fatalf("mark renewable: %v", err)
  81. }
  82. start = time.Now()
  83. if _, _, err := svc.AddTraffic(nil, nil); err != nil {
  84. t.Fatalf("AddTraffic renew: %v", err)
  85. }
  86. t.Logf("N=%-7d shape=%-8s renew100=%v", n, shape.name, time.Since(start).Round(time.Millisecond))
  87. var renewed xray.ClientTraffic
  88. if err := db.Where("email = ?", renew[0].Email).First(&renewed).Error; err != nil {
  89. t.Fatalf("load renewed row: %v", err)
  90. }
  91. if renewed.ExpiryTime <= past || renewed.Up != 0 {
  92. t.Fatalf("renew100 did not renew: expiry=%d up=%d", renewed.ExpiryTime, renewed.Up)
  93. }
  94. })
  95. }
  96. }
  97. }