github.com/bytedance/gopkg@v0.0.0-20240514070511-01b2cbcf35e1/cache/asynccache/asynccache_test.go (about)

     1  // Copyright 2021 ByteDance 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 asynccache
    16  
    17  import (
    18  	"errors"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  )
    24  
    25  func TestGetOK(t *testing.T) {
    26  	var key, ret = "key", "ret"
    27  	op := Options{
    28  		RefreshDuration: time.Second,
    29  		IsSame: func(key string, oldData, newData interface{}) bool {
    30  			return false
    31  		},
    32  		Fetcher: func(key string) (interface{}, error) {
    33  			return ret, nil
    34  		},
    35  	}
    36  	c := NewAsyncCache(op)
    37  
    38  	v, err := c.Get(key)
    39  	assert.NoError(t, err)
    40  	assert.Equal(t, v.(string), ret)
    41  
    42  	time.Sleep(time.Second / 2)
    43  	ret = "change"
    44  	v, err = c.Get(key)
    45  	assert.NoError(t, err)
    46  	assert.NotEqual(t, v.(string), ret)
    47  
    48  	time.Sleep(time.Second)
    49  	v, err = c.Get(key)
    50  	assert.NoError(t, err)
    51  	assert.Equal(t, v.(string), ret)
    52  }
    53  
    54  func TestGetErr(t *testing.T) {
    55  	var key, ret = "key", "ret"
    56  	var first = true
    57  	op := Options{
    58  		RefreshDuration: time.Second + 100*time.Millisecond,
    59  		IsSame: func(key string, oldData, newData interface{}) bool {
    60  			return false
    61  		},
    62  		Fetcher: func(key string) (interface{}, error) {
    63  			if first {
    64  				first = false
    65  				return nil, errors.New("error")
    66  			}
    67  			return ret, nil
    68  		},
    69  	}
    70  	c := NewAsyncCache(op)
    71  
    72  	v, err := c.Get(key)
    73  	assert.Error(t, err)
    74  	assert.Nil(t, v)
    75  
    76  	time.Sleep(time.Second / 2)
    77  	_, err2 := c.Get(key)
    78  	assert.Equal(t, err, err2)
    79  
    80  	time.Sleep(time.Second + 10*time.Millisecond)
    81  	v, err = c.Get(key)
    82  	assert.NoError(t, err)
    83  	assert.Equal(t, v.(string), ret)
    84  }
    85  
    86  func TestGetOrSetOK(t *testing.T) {
    87  	var key, ret, def = "key", "ret", "def"
    88  	op := Options{
    89  		RefreshDuration: time.Second,
    90  		IsSame: func(key string, oldData, newData interface{}) bool {
    91  			return false
    92  		},
    93  		Fetcher: func(key string) (interface{}, error) {
    94  			return ret, nil
    95  		},
    96  	}
    97  	c := NewAsyncCache(op)
    98  
    99  	v := c.GetOrSet(key, def)
   100  	assert.Equal(t, v.(string), ret)
   101  
   102  	time.Sleep(time.Second / 2)
   103  	ret = "change"
   104  	v = c.GetOrSet(key, def)
   105  	assert.NotEqual(t, v.(string), ret)
   106  
   107  	time.Sleep(time.Second)
   108  	v = c.GetOrSet(key, def)
   109  	assert.Equal(t, v.(string), ret)
   110  }
   111  
   112  func TestGetOrSetErr(t *testing.T) {
   113  	var key, ret, def = "key", "ret", "def"
   114  	var first = true
   115  	op := Options{
   116  		RefreshDuration: time.Second + 500*time.Millisecond,
   117  		IsSame: func(key string, oldData, newData interface{}) bool {
   118  			return false
   119  		},
   120  		Fetcher: func(key string) (interface{}, error) {
   121  			if first {
   122  				first = false
   123  				return nil, errors.New("error")
   124  			}
   125  			return ret, nil
   126  		},
   127  	}
   128  	c := NewAsyncCache(op)
   129  
   130  	v := c.GetOrSet(key, def)
   131  	assert.Equal(t, v.(string), def)
   132  
   133  	time.Sleep(time.Second / 2)
   134  	v = c.GetOrSet(key, ret)
   135  	assert.NotEqual(t, v.(string), ret)
   136  	assert.Equal(t, v.(string), def)
   137  
   138  	time.Sleep(time.Second + 500*time.Millisecond)
   139  	v = c.GetOrSet(key, def)
   140  	assert.Equal(t, v.(string), ret)
   141  }
   142  
   143  func TestSetDefault(t *testing.T) {
   144  	op := Options{
   145  		RefreshDuration: time.Second,
   146  		IsSame: func(key string, oldData, newData interface{}) bool {
   147  			return false
   148  		},
   149  		Fetcher: func(key string) (interface{}, error) {
   150  			return nil, errors.New("error")
   151  		},
   152  	}
   153  	c := NewAsyncCache(op)
   154  
   155  	v := c.GetOrSet("key1", "def1")
   156  	assert.Equal(t, v.(string), "def1")
   157  
   158  	exist := c.SetDefault("key2", "val2")
   159  	assert.False(t, exist)
   160  	v = c.GetOrSet("key2", "def2")
   161  	assert.Equal(t, v.(string), "val2")
   162  
   163  	exist = c.SetDefault("key2", "val3")
   164  	assert.True(t, exist)
   165  	v = c.GetOrSet("key2", "def2")
   166  	assert.Equal(t, v.(string), "val2")
   167  }
   168  
   169  func TestDeleteIf(t *testing.T) {
   170  	op := Options{
   171  		RefreshDuration: time.Second,
   172  		IsSame: func(key string, oldData, newData interface{}) bool {
   173  			return false
   174  		},
   175  		Fetcher: func(key string) (interface{}, error) {
   176  			return nil, errors.New("error")
   177  		},
   178  	}
   179  	c := NewAsyncCache(op)
   180  
   181  	c.SetDefault("key", "val")
   182  	v := c.GetOrSet("key", "def")
   183  	assert.Equal(t, v.(string), "val")
   184  
   185  	d, _ := c.(interface{ DeleteIf(func(key string) bool) })
   186  	d.DeleteIf(func(string) bool { return true })
   187  
   188  	v = c.GetOrSet("key", "def")
   189  	assert.Equal(t, v.(string), "def")
   190  }
   191  
   192  func TestClose(t *testing.T) {
   193  	var dur = time.Second / 10
   194  	var cnt int
   195  	op := Options{
   196  		RefreshDuration: dur - 10*time.Millisecond,
   197  		IsSame: func(key string, oldData, newData interface{}) bool {
   198  			return false
   199  		},
   200  		Fetcher: func(key string) (interface{}, error) {
   201  			cnt++
   202  			return cnt, nil
   203  		},
   204  		EnableExpire:   true,
   205  		ExpireDuration: time.Second,
   206  	}
   207  	c := NewAsyncCache(op)
   208  
   209  	v := c.GetOrSet("key", 10)
   210  	assert.Equal(t, v.(int), 1)
   211  
   212  	time.Sleep(dur)
   213  	v = c.GetOrSet("key", 10)
   214  	assert.Equal(t, v.(int), 2)
   215  
   216  	time.Sleep(dur)
   217  	v = c.GetOrSet("key", 10)
   218  	assert.Equal(t, v.(int), 3)
   219  
   220  	c.Close()
   221  
   222  	time.Sleep(dur)
   223  	v = c.GetOrSet("key", 10)
   224  	assert.Equal(t, v.(int), 3)
   225  }
   226  
   227  func TestExpire(t *testing.T) {
   228  	// trigger is used to mark whether fetcher is called
   229  	trigger := false
   230  	op := Options{
   231  		EnableExpire:    true,
   232  		ExpireDuration:  3 * time.Minute,
   233  		RefreshDuration: time.Minute,
   234  		IsSame: func(key string, oldData, newData interface{}) bool {
   235  			return true
   236  		},
   237  		Fetcher: func(key string) (interface{}, error) {
   238  			trigger = true
   239  			return "", nil
   240  		},
   241  	}
   242  	c := NewAsyncCache(op).(*asyncCache)
   243  
   244  	// GetOrSet cannot trigger fetcher when SetDefault before
   245  	c.SetDefault("key-default", "")
   246  	c.SetDefault("key-alive", "")
   247  	c.GetOrSet("key-alive", "")
   248  	assert.False(t, trigger)
   249  
   250  	c.Get("key-expire")
   251  	assert.True(t, trigger)
   252  
   253  	// first expire set tag
   254  	c.expire()
   255  
   256  	trigger = false
   257  	c.Get("key-alive")
   258  	assert.False(t, trigger)
   259  	// second expire, both key-default & key-expire have been removed
   260  	c.expire()
   261  	c.refresh() // prove refresh does not affect expire
   262  
   263  	trigger = false
   264  	c.Get("key-alive")
   265  	assert.False(t, trigger)
   266  	trigger = false
   267  	c.Get("key-default")
   268  	assert.True(t, trigger)
   269  	trigger = false
   270  	c.Get("key-expire")
   271  	assert.True(t, trigger)
   272  }
   273  
   274  func BenchmarkGet(b *testing.B) {
   275  	var key = "key"
   276  	op := Options{
   277  		RefreshDuration: time.Second,
   278  		IsSame: func(key string, oldData, newData interface{}) bool {
   279  			return false
   280  		},
   281  		Fetcher: func(key string) (interface{}, error) {
   282  			return "", nil
   283  		},
   284  	}
   285  	c := NewAsyncCache(op)
   286  
   287  	b.ReportAllocs()
   288  	b.ResetTimer()
   289  	for i := 0; i < b.N; i++ {
   290  		_, _ = c.Get(key)
   291  	}
   292  }
   293  
   294  func BenchmarkGetParallel(b *testing.B) {
   295  	var key = "key"
   296  	op := Options{
   297  		RefreshDuration: time.Second,
   298  		IsSame: func(key string, oldData, newData interface{}) bool {
   299  			return false
   300  		},
   301  		Fetcher: func(key string) (interface{}, error) {
   302  			return "", nil
   303  		},
   304  	}
   305  	c := NewAsyncCache(op)
   306  
   307  	b.ReportAllocs()
   308  	b.ResetTimer()
   309  	b.RunParallel(func(pb *testing.PB) {
   310  		for pb.Next() {
   311  			_, _ = c.Get(key)
   312  		}
   313  	})
   314  }
   315  
   316  func BenchmarkGetOrSet(b *testing.B) {
   317  	var key, def = "key", "def"
   318  	op := Options{
   319  		RefreshDuration: time.Second,
   320  		IsSame: func(key string, oldData, newData interface{}) bool {
   321  			return false
   322  		},
   323  		Fetcher: func(key string) (interface{}, error) {
   324  			return "", nil
   325  		},
   326  	}
   327  	c := NewAsyncCache(op)
   328  
   329  	b.ReportAllocs()
   330  	b.ResetTimer()
   331  	for i := 0; i < b.N; i++ {
   332  		_ = c.GetOrSet(key, def)
   333  	}
   334  }
   335  
   336  func BenchmarkGetOrSetParallel(b *testing.B) {
   337  	var key, def = "key", "def"
   338  	op := Options{
   339  		RefreshDuration: time.Second,
   340  		IsSame: func(key string, oldData, newData interface{}) bool {
   341  			return false
   342  		},
   343  		Fetcher: func(key string) (interface{}, error) {
   344  			return "", nil
   345  		},
   346  	}
   347  	c := NewAsyncCache(op)
   348  
   349  	b.ReportAllocs()
   350  	b.ResetTimer()
   351  	b.RunParallel(func(pb *testing.PB) {
   352  		for pb.Next() {
   353  			_ = c.GetOrSet(key, def)
   354  		}
   355  	})
   356  }
   357  
   358  func BenchmarkRefresh(b *testing.B) {
   359  	var key, def = "key", "def"
   360  	op := Options{
   361  		RefreshDuration: time.Second,
   362  		IsSame: func(key string, oldData, newData interface{}) bool {
   363  			return false
   364  		},
   365  		Fetcher: func(key string) (interface{}, error) {
   366  			return "", nil
   367  		},
   368  	}
   369  	c := NewAsyncCache(op).(*asyncCache)
   370  	c.SetDefault(key, def)
   371  
   372  	b.ReportAllocs()
   373  	b.ResetTimer()
   374  	for i := 0; i < b.N; i++ {
   375  		c.refresh()
   376  	}
   377  }
   378  
   379  func BenchmarkRefreshParallel(b *testing.B) {
   380  	var key, def = "key", "def"
   381  	op := Options{
   382  		RefreshDuration: time.Second,
   383  		IsSame: func(key string, oldData, newData interface{}) bool {
   384  			return false
   385  		},
   386  		Fetcher: func(key string) (interface{}, error) {
   387  			return "", nil
   388  		},
   389  	}
   390  	c := NewAsyncCache(op).(*asyncCache)
   391  	c.SetDefault(key, def)
   392  
   393  	b.ReportAllocs()
   394  	b.ResetTimer()
   395  	b.RunParallel(func(pb *testing.PB) {
   396  		for pb.Next() {
   397  			c.refresh()
   398  		}
   399  	})
   400  }