github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/forward_index_dice_test.go (about)

     1  // Copyright (c) 2019 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package storage
    22  
    23  import (
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/dbnode/retention"
    28  	xtime "github.com/m3db/m3/src/x/time"
    29  
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func optionsWithIndexValues(
    34  	indexProb float64,
    35  	indexThreshold float64,
    36  	retOpts retention.Options,
    37  ) Options {
    38  	opts := DefaultTestOptions()
    39  
    40  	idxOpts := opts.IndexOptions()
    41  	idxOpts = idxOpts.
    42  		SetForwardIndexProbability(indexProb).
    43  		SetForwardIndexThreshold(indexThreshold)
    44  
    45  	opts = DefaultTestOptions().
    46  		SetIndexOptions(idxOpts)
    47  
    48  	if retOpts != nil {
    49  		seriesOpts := opts.SeriesOptions().
    50  			SetRetentionOptions(retOpts)
    51  		return opts.SetSeriesOptions(seriesOpts)
    52  	}
    53  
    54  	return opts
    55  }
    56  
    57  func TestDisabledForwardIndexDice(t *testing.T) {
    58  	opts := optionsWithIndexValues(0, 0, nil)
    59  	dice, err := newForwardIndexDice(opts)
    60  	require.NoError(t, err)
    61  	require.False(t, dice.enabled)
    62  
    63  	start := xtime.Now().Truncate(time.Hour)
    64  	end := start.Add(time.Hour)
    65  
    66  	for ts := start; ts.Before(end); ts = ts.Add(time.Second) {
    67  		require.False(t, dice.roll(ts))
    68  	}
    69  }
    70  
    71  func TestInvalidForwardIndexDice(t *testing.T) {
    72  	// Index probability < 0 and > 1 cases.
    73  	invalidIndexProbabilities := []float64{-10, 10}
    74  	for _, prob := range invalidIndexProbabilities {
    75  		opts := optionsWithIndexValues(prob, 0, nil)
    76  		_, err := newForwardIndexDice(opts)
    77  		require.Error(t, err)
    78  	}
    79  
    80  	// Index threshold < 0 and > 1 cases.
    81  	invalidIndexThresholds := []float64{-10, 10}
    82  	for _, threshold := range invalidIndexThresholds {
    83  		opts := optionsWithIndexValues(0.5, threshold, nil)
    84  		_, err := newForwardIndexDice(opts)
    85  		require.Error(t, err)
    86  	}
    87  }
    88  
    89  func TestAlwaysOnForwardIndexDice(t *testing.T) {
    90  	retOpts := retention.NewOptions().
    91  		SetBlockSize(time.Hour).
    92  		SetBufferFuture(time.Minute * 10)
    93  	opts := optionsWithIndexValues(1, 0.9, retOpts)
    94  
    95  	dice, err := newForwardIndexDice(opts)
    96  	require.NoError(t, err)
    97  	require.True(t, dice.enabled)
    98  
    99  	var (
   100  		start     = xtime.Now().Truncate(time.Hour)
   101  		threshold = start.Add(time.Minute * 51)
   102  		end       = start.Add(time.Hour)
   103  	)
   104  
   105  	for ts := start; ts.Before(end); ts = ts.Add(time.Second) {
   106  		indexing := dice.roll(ts)
   107  		if ts.Before(threshold) {
   108  			require.False(t, indexing)
   109  		} else {
   110  			require.True(t, indexing)
   111  		}
   112  	}
   113  }
   114  
   115  type trackingDice struct {
   116  	rolled  int
   117  	success int
   118  }
   119  
   120  func (d *trackingDice) Rate() float64 { return 0 }
   121  func (d *trackingDice) Roll() bool {
   122  	d.rolled++
   123  	return d.rolled%d.success == 0
   124  }
   125  
   126  func TestCustomDice(t *testing.T) {
   127  	d := &trackingDice{
   128  		success: 10,
   129  	}
   130  
   131  	dice := forwardIndexDice{
   132  		enabled:   true,
   133  		blockSize: time.Hour,
   134  
   135  		forwardIndexThreshold: time.Minute * 50,
   136  		forwardIndexDice:      d,
   137  	}
   138  
   139  	var (
   140  		start     = xtime.Now().Truncate(time.Hour)
   141  		threshold = start.Add(time.Minute * 50)
   142  		end       = start.Add(time.Hour)
   143  	)
   144  
   145  	sample := 0
   146  	for ts := start; ts.Before(end); ts = ts.Add(time.Second) {
   147  		indexing := dice.roll(ts)
   148  
   149  		if ts.Before(threshold) {
   150  			require.False(t, indexing)
   151  		} else {
   152  			sample++
   153  			require.Equal(t, sample%10 == 0, indexing)
   154  		}
   155  	}
   156  
   157  	require.Equal(t, 600, d.rolled)
   158  }