github.com/m3db/m3@v1.5.0/src/cluster/kv/util/atomic_test.go (about)

     1  // Copyright (c) 2018 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 util
    22  
    23  import (
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/cluster/generated/proto/commonpb"
    29  	"github.com/m3db/m3/src/cluster/kv/mem"
    30  
    31  	"github.com/fortytw2/leaktest"
    32  	"github.com/stretchr/testify/require"
    33  	"go.uber.org/atomic"
    34  )
    35  
    36  func TestWatchAndAtomicUpdateBool(t *testing.T) {
    37  	testConfig := struct {
    38  		sync.RWMutex
    39  		v *atomic.Bool
    40  	}{
    41  		v: atomic.NewBool(false),
    42  	}
    43  
    44  	valueFn := func() bool {
    45  		testConfig.RLock()
    46  		defer testConfig.RUnlock()
    47  
    48  		return testConfig.v.Load()
    49  	}
    50  
    51  	var (
    52  		store        = mem.NewStore()
    53  		defaultValue = false
    54  	)
    55  
    56  	watch, err := WatchAndUpdateAtomicBool(
    57  		store, "foo", testConfig.v, defaultValue, nil,
    58  	)
    59  	require.NoError(t, err)
    60  
    61  	// Valid update.
    62  	_, err = store.Set("foo", &commonpb.BoolProto{Value: false})
    63  	require.NoError(t, err)
    64  	for {
    65  		if !valueFn() {
    66  			break
    67  		}
    68  	}
    69  
    70  	// Malformed updates should not be applied.
    71  	_, err = store.Set("foo", &commonpb.Float64Proto{Value: 20})
    72  	require.NoError(t, err)
    73  	time.Sleep(100 * time.Millisecond)
    74  	require.False(t, valueFn())
    75  
    76  	_, err = store.Set("foo", &commonpb.BoolProto{Value: true})
    77  	require.NoError(t, err)
    78  	for {
    79  		if valueFn() {
    80  			break
    81  		}
    82  	}
    83  
    84  	// Nil updates should apply the default value.
    85  	_, err = store.Delete("foo")
    86  	require.NoError(t, err)
    87  	for {
    88  		if !valueFn() {
    89  			break
    90  		}
    91  	}
    92  
    93  	_, err = store.Set("foo", &commonpb.BoolProto{Value: true})
    94  	require.NoError(t, err)
    95  	for {
    96  		if valueFn() {
    97  			break
    98  		}
    99  	}
   100  
   101  	// Updates should not be applied after the watch is closed and there should not
   102  	// be any goroutines still running.
   103  	watch.Close()
   104  	time.Sleep(100 * time.Millisecond)
   105  	_, err = store.Set("foo", &commonpb.BoolProto{Value: false})
   106  	require.NoError(t, err)
   107  	time.Sleep(100 * time.Millisecond)
   108  	require.True(t, valueFn())
   109  
   110  	leaktest.Check(t)()
   111  }
   112  
   113  func TestWatchAndUpdateAtomicFloat64(t *testing.T) {
   114  	testConfig := struct {
   115  		sync.RWMutex
   116  		v *atomic.Float64
   117  	}{
   118  		v: atomic.NewFloat64(0),
   119  	}
   120  
   121  	valueFn := func() float64 {
   122  		testConfig.RLock()
   123  		defer testConfig.RUnlock()
   124  
   125  		return testConfig.v.Load()
   126  	}
   127  
   128  	var (
   129  		store        = mem.NewStore()
   130  		defaultValue = 1.35
   131  	)
   132  
   133  	watch, err := WatchAndUpdateAtomicFloat64(
   134  		store, "foo", testConfig.v, defaultValue, nil,
   135  	)
   136  	require.NoError(t, err)
   137  
   138  	_, err = store.Set("foo", &commonpb.Float64Proto{Value: 3.7})
   139  	require.NoError(t, err)
   140  	for {
   141  		if valueFn() == 3.7 {
   142  			break
   143  		}
   144  	}
   145  
   146  	// Malformed updates should not be applied.
   147  	_, err = store.Set("foo", &commonpb.Int64Proto{Value: 1})
   148  	require.NoError(t, err)
   149  	time.Sleep(100 * time.Millisecond)
   150  	require.Equal(t, 3.7, valueFn())
   151  
   152  	_, err = store.Set("foo", &commonpb.Float64Proto{Value: 1.2})
   153  	require.NoError(t, err)
   154  	for {
   155  		if valueFn() == 1.2 {
   156  			break
   157  		}
   158  	}
   159  
   160  	// Nil updates should apply the default value.
   161  	_, err = store.Delete("foo")
   162  	require.NoError(t, err)
   163  	for {
   164  		if valueFn() == defaultValue {
   165  			break
   166  		}
   167  	}
   168  
   169  	_, err = store.Set("foo", &commonpb.Float64Proto{Value: 6.2})
   170  	require.NoError(t, err)
   171  	for {
   172  		if valueFn() == 6.2 {
   173  			break
   174  		}
   175  	}
   176  
   177  	// Updates should not be applied after the watch is closed and there should not
   178  	// be any goroutines still running.
   179  	watch.Close()
   180  	time.Sleep(100 * time.Millisecond)
   181  	_, err = store.Set("foo", &commonpb.Float64Proto{Value: 7.2})
   182  	require.NoError(t, err)
   183  	time.Sleep(100 * time.Millisecond)
   184  	require.Equal(t, 6.2, valueFn())
   185  
   186  	leaktest.Check(t)
   187  }
   188  
   189  func TestWatchAndUpdateAtomicInt64(t *testing.T) {
   190  	testConfig := struct {
   191  		sync.RWMutex
   192  		v *atomic.Int64
   193  	}{
   194  		v: atomic.NewInt64(0),
   195  	}
   196  
   197  	valueFn := func() int64 {
   198  		testConfig.RLock()
   199  		defer testConfig.RUnlock()
   200  
   201  		return testConfig.v.Load()
   202  	}
   203  
   204  	var (
   205  		store              = mem.NewStore()
   206  		defaultValue int64 = 3
   207  	)
   208  
   209  	watch, err := WatchAndUpdateAtomicInt64(
   210  		store, "foo", testConfig.v, defaultValue, nil,
   211  	)
   212  	require.NoError(t, err)
   213  
   214  	_, err = store.Set("foo", &commonpb.Int64Proto{Value: 1})
   215  	require.NoError(t, err)
   216  	for {
   217  		if valueFn() == 1 {
   218  			break
   219  		}
   220  	}
   221  
   222  	// Malformed updates should not be applied.
   223  	_, err = store.Set("foo", &commonpb.Float64Proto{Value: 100})
   224  	require.NoError(t, err)
   225  	time.Sleep(100 * time.Millisecond)
   226  	require.Equal(t, int64(1), valueFn())
   227  
   228  	_, err = store.Set("foo", &commonpb.Int64Proto{Value: 7})
   229  	require.NoError(t, err)
   230  	for {
   231  		if valueFn() == 7 {
   232  			break
   233  		}
   234  	}
   235  
   236  	// Nil updates should apply the default value.
   237  	_, err = store.Delete("foo")
   238  	require.NoError(t, err)
   239  	for {
   240  		if valueFn() == defaultValue {
   241  			break
   242  		}
   243  	}
   244  
   245  	_, err = store.Set("foo", &commonpb.Int64Proto{Value: 21})
   246  	require.NoError(t, err)
   247  	for {
   248  		if valueFn() == 21 {
   249  			break
   250  		}
   251  	}
   252  
   253  	// Updates should not be applied after the watch is closed and there should not
   254  	// be any goroutines still running.
   255  	watch.Close()
   256  	time.Sleep(100 * time.Millisecond)
   257  	_, err = store.Set("foo", &commonpb.Int64Proto{Value: 13})
   258  	require.NoError(t, err)
   259  	time.Sleep(100 * time.Millisecond)
   260  	require.Equal(t, int64(21), valueFn())
   261  
   262  	leaktest.Check(t)
   263  }
   264  
   265  func TestWatchAndUpdateAtomicString(t *testing.T) {
   266  	testConfig := struct {
   267  		sync.RWMutex
   268  		v *atomic.String
   269  	}{
   270  		v: atomic.NewString(""),
   271  	}
   272  
   273  	valueFn := func() string {
   274  		testConfig.RLock()
   275  		defer testConfig.RUnlock()
   276  
   277  		return testConfig.v.Load()
   278  	}
   279  
   280  	var (
   281  		store        = mem.NewStore()
   282  		defaultValue = "abc"
   283  	)
   284  
   285  	watch, err := WatchAndUpdateAtomicString(
   286  		store, "foo", testConfig.v, defaultValue, nil,
   287  	)
   288  	require.NoError(t, err)
   289  
   290  	_, err = store.Set("foo", &commonpb.StringProto{Value: "fizz"})
   291  	require.NoError(t, err)
   292  	for {
   293  		if valueFn() == "fizz" {
   294  			break
   295  		}
   296  	}
   297  
   298  	// Malformed updates should not be applied.
   299  	_, err = store.Set("foo", &commonpb.Float64Proto{Value: 100})
   300  	require.NoError(t, err)
   301  	time.Sleep(100 * time.Millisecond)
   302  	require.Equal(t, "fizz", valueFn())
   303  
   304  	_, err = store.Set("foo", &commonpb.StringProto{Value: "buzz"})
   305  	require.NoError(t, err)
   306  	for {
   307  		if valueFn() == "buzz" {
   308  			break
   309  		}
   310  	}
   311  
   312  	// Nil updates should apply the default value.
   313  	_, err = store.Delete("foo")
   314  	require.NoError(t, err)
   315  	for {
   316  		if valueFn() == defaultValue {
   317  			break
   318  		}
   319  	}
   320  
   321  	_, err = store.Set("foo", &commonpb.StringProto{Value: "lol"})
   322  	require.NoError(t, err)
   323  	for {
   324  		if valueFn() == "lol" {
   325  			break
   326  		}
   327  	}
   328  
   329  	// Updates should not be applied after the watch is closed and there should not
   330  	// be any goroutines still running.
   331  	watch.Close()
   332  	time.Sleep(100 * time.Millisecond)
   333  	_, err = store.Set("foo", &commonpb.StringProto{Value: "abc"})
   334  	require.NoError(t, err)
   335  	time.Sleep(100 * time.Millisecond)
   336  	require.Equal(t, "lol", valueFn())
   337  
   338  	leaktest.Check(t)
   339  }