p2c_test.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package p2c
  2. import (
  3. "context"
  4. "fmt"
  5. "runtime"
  6. "strconv"
  7. "sync"
  8. "testing"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/wuntsong-org/go-zero-plus/core/logx"
  11. "github.com/wuntsong-org/go-zero-plus/core/mathx"
  12. "github.com/wuntsong-org/go-zero-plus/core/stringx"
  13. "google.golang.org/grpc/balancer"
  14. "google.golang.org/grpc/balancer/base"
  15. "google.golang.org/grpc/codes"
  16. "google.golang.org/grpc/resolver"
  17. "google.golang.org/grpc/status"
  18. )
  19. func init() {
  20. logx.Disable()
  21. }
  22. func TestP2cPicker_PickNil(t *testing.T) {
  23. builder := new(p2cPickerBuilder)
  24. picker := builder.Build(base.PickerBuildInfo{})
  25. _, err := picker.Pick(balancer.PickInfo{
  26. FullMethodName: "/",
  27. Ctx: context.Background(),
  28. })
  29. assert.NotNil(t, err)
  30. }
  31. func TestP2cPicker_Pick(t *testing.T) {
  32. tests := []struct {
  33. name string
  34. candidates int
  35. err error
  36. threshold float64
  37. }{
  38. {
  39. name: "empty",
  40. candidates: 0,
  41. err: balancer.ErrNoSubConnAvailable,
  42. },
  43. {
  44. name: "single",
  45. candidates: 1,
  46. threshold: 0.9,
  47. },
  48. {
  49. name: "two",
  50. candidates: 2,
  51. threshold: 0.5,
  52. },
  53. {
  54. name: "multiple",
  55. candidates: 100,
  56. threshold: 0.95,
  57. },
  58. }
  59. for _, test := range tests {
  60. test := test
  61. t.Run(test.name, func(t *testing.T) {
  62. t.Parallel()
  63. const total = 10000
  64. builder := new(p2cPickerBuilder)
  65. ready := make(map[balancer.SubConn]base.SubConnInfo)
  66. for i := 0; i < test.candidates; i++ {
  67. ready[mockClientConn{
  68. id: stringx.Rand(),
  69. }] = base.SubConnInfo{
  70. Address: resolver.Address{
  71. Addr: strconv.Itoa(i),
  72. },
  73. }
  74. }
  75. picker := builder.Build(base.PickerBuildInfo{
  76. ReadySCs: ready,
  77. })
  78. var wg sync.WaitGroup
  79. wg.Add(total)
  80. for i := 0; i < total; i++ {
  81. result, err := picker.Pick(balancer.PickInfo{
  82. FullMethodName: "/",
  83. Ctx: context.Background(),
  84. })
  85. assert.Equal(t, test.err, err)
  86. if test.err != nil {
  87. return
  88. }
  89. if i%100 == 0 {
  90. err = status.Error(codes.DeadlineExceeded, "deadline")
  91. }
  92. go func() {
  93. runtime.Gosched()
  94. result.Done(balancer.DoneInfo{
  95. Err: err,
  96. })
  97. wg.Done()
  98. }()
  99. }
  100. wg.Wait()
  101. dist := make(map[any]int)
  102. conns := picker.(*p2cPicker).conns
  103. for _, conn := range conns {
  104. dist[conn.addr.Addr] = int(conn.requests)
  105. }
  106. entropy := mathx.CalcEntropy(dist)
  107. assert.True(t, entropy > test.threshold, fmt.Sprintf("entropy is %f, less than %f",
  108. entropy, test.threshold))
  109. })
  110. }
  111. }
  112. func TestPickerWithEmptyConns(t *testing.T) {
  113. var picker p2cPicker
  114. _, err := picker.Pick(balancer.PickInfo{
  115. FullMethodName: "/",
  116. Ctx: context.Background(),
  117. })
  118. assert.ErrorIs(t, err, balancer.ErrNoSubConnAvailable)
  119. }
  120. type mockClientConn struct {
  121. // add random string member to avoid map key equality.
  122. id string
  123. }
  124. func (m mockClientConn) GetOrBuildProducer(builder balancer.ProducerBuilder) (
  125. p balancer.Producer, close func()) {
  126. return builder.Build(m)
  127. }
  128. func (m mockClientConn) UpdateAddresses(addresses []resolver.Address) {
  129. }
  130. func (m mockClientConn) Connect() {
  131. }
  132. func (m mockClientConn) Shutdown() {
  133. }