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  }