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 }