github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/remotestorage/hedge_test.go (about) 1 // Copyright 2020 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package remotestorage 16 17 import ( 18 "context" 19 "sync/atomic" 20 "testing" 21 "time" 22 23 "github.com/stretchr/testify/assert" 24 ) 25 26 func init() { 27 MaxHedgesPerRequest = 1024 28 } 29 30 func TestPercentileStrategy(t *testing.T) { 31 s := NewPercentileStrategy(0, 1*time.Hour, 95.0) 32 for i := 0; i < 90; i++ { 33 s.Observe(1, 1, 1*time.Millisecond, nil) 34 } 35 for i := 0; i < 10; i++ { 36 s.Observe(10, 1, 100*time.Millisecond, nil) 37 } 38 d := s.Duration(10) 39 assert.True(t, d > 90*time.Millisecond, "p95 is greater than 90 milliseconds, is %d", d) 40 } 41 42 func TestMinStrategy(t *testing.T) { 43 u := NewPercentileStrategy(0, 1*time.Hour, 95.0) 44 s := NewMinStrategy(1*time.Second, u) 45 d := s.Duration(10) 46 assert.Equal(t, d, 1*time.Second) 47 for i := 0; i < 100; i++ { 48 s.Observe(10, 1, 15*time.Second, nil) 49 } 50 d = s.Duration(10) 51 assert.NotEqual(t, d, 1*time.Second) 52 } 53 54 func TestHedgerRunsWork(t *testing.T) { 55 h := NewHedger(1, NewMinStrategy(1*time.Second, nil)) 56 ran := false 57 i, err := h.Do(context.Background(), Work{ 58 Work: func(ctx context.Context, n int) (interface{}, error) { 59 ran = true 60 return true, nil 61 }, 62 }) 63 assert.NoError(t, err) 64 assert.True(t, ran) 65 assert.True(t, i.(bool)) 66 } 67 68 func TestHedgerHedgesWork(t *testing.T) { 69 h := NewHedger(1, NewMinStrategy(10*time.Millisecond, nil)) 70 ch := make(chan int, 2) 71 ch <- 1 72 ch <- 2 73 i, err := h.Do(context.Background(), Work{ 74 Work: func(ctx context.Context, n int) (interface{}, error) { 75 i := <-ch 76 if i == 1 { 77 <-ctx.Done() 78 close(ch) 79 return 1, nil 80 } else if i == 2 { 81 return 2, nil 82 } 83 panic("unexpected value in ch") 84 }, 85 }) 86 assert.NoError(t, err) 87 assert.Equal(t, 2, i.(int)) 88 assert.Equal(t, 0, <-ch) 89 } 90 91 func TestHedgerObeysMaxHedges(t *testing.T) { 92 try := func(max int) { 93 h := NewHedger(int64(max), NewMinStrategy(1*time.Millisecond, nil)) 94 cnt := int32(0) 95 i, err := h.Do(context.Background(), Work{ 96 Work: func(ctx context.Context, n int) (interface{}, error) { 97 cur := atomic.AddInt32(&cnt, 1) 98 if cur == int32(max) { 99 time.Sleep(100 * time.Millisecond) 100 return 1, nil 101 } else { 102 <-ctx.Done() 103 return 2, nil 104 } 105 }, 106 }) 107 assert.NoError(t, err) 108 assert.Equal(t, 1, i.(int)) 109 assert.Equal(t, int32(max)+1, atomic.LoadInt32(&cnt)) 110 } 111 try(1) 112 try(2) 113 try(3) 114 } 115 116 func TestMaxHedgesPerRequestObeyed(t *testing.T) { 117 before := MaxHedgesPerRequest 118 defer func() { 119 MaxHedgesPerRequest = before 120 }() 121 122 MaxHedgesPerRequest = 0 123 h := NewHedger(int64(32), NewMinStrategy(1*time.Millisecond, nil)) 124 cnt := int32(0) 125 i, err := h.Do(context.Background(), Work{ 126 Work: func(ctx context.Context, n int) (interface{}, error) { 127 cur := atomic.AddInt32(&cnt, 1) 128 if cur == int32(1) { 129 time.Sleep(500 * time.Millisecond) 130 return 1, nil 131 } else { 132 return 2, nil 133 } 134 }, 135 }) 136 assert.NoError(t, err) 137 assert.Equal(t, 1, i.(int)) 138 139 MaxHedgesPerRequest = 1 140 cnt = int32(0) 141 i, err = h.Do(context.Background(), Work{ 142 Work: func(ctx context.Context, n int) (interface{}, error) { 143 cur := atomic.AddInt32(&cnt, 1) 144 if cur == int32(1) { 145 time.Sleep(30 * time.Second) 146 return 1, nil 147 } else if cur == int32(2) { 148 time.Sleep(500 * time.Millisecond) 149 return 2, nil 150 } else { 151 return 3, nil 152 } 153 }, 154 }) 155 assert.NoError(t, err) 156 assert.Equal(t, 2, i.(int)) 157 } 158 159 func TestHedgerContextCancelObeyed(t *testing.T) { 160 h := NewHedger(int64(3), NewMinStrategy(1*time.Millisecond, nil)) 161 resCh := make(chan struct{}) 162 canCh := make(chan struct{}) 163 ctx, cancel := context.WithCancel(context.Background()) 164 go func() { 165 <-canCh 166 <-canCh 167 <-canCh 168 <-canCh 169 cancel() 170 }() 171 _, err := h.Do(ctx, Work{ 172 Work: func(ctx context.Context, n int) (interface{}, error) { 173 canCh <- struct{}{} 174 <-ctx.Done() 175 resCh <- struct{}{} 176 return nil, nil 177 }, 178 }) 179 assert.Error(t, err, context.Canceled) 180 <-resCh 181 <-resCh 182 <-resCh 183 <-resCh 184 }