...

Source file src/github.com/launchdarkly/ccache/cache_test.go

Documentation: github.com/launchdarkly/ccache

     1  package ccache
     2  
     3  import (
     4  	"sort"
     5  	"strconv"
     6  	"sync/atomic"
     7  	"testing"
     8  	"time"
     9  
    10  	. "github.com/karlseguin/expect"
    11  )
    12  
    13  type CacheTests struct{}
    14  
    15  func Test_Cache(t *testing.T) {
    16  	Expectify(new(CacheTests), t)
    17  }
    18  
    19  func (_ CacheTests) DeletesAValue() {
    20  	cache := New(Configure())
    21  	Expect(cache.ItemCount()).To.Equal(0)
    22  
    23  	cache.Set("spice", "flow", time.Minute)
    24  	cache.Set("worm", "sand", time.Minute)
    25  	Expect(cache.ItemCount()).To.Equal(2)
    26  
    27  	cache.Delete("spice")
    28  	Expect(cache.Get("spice")).To.Equal(nil)
    29  	Expect(cache.Get("worm").Value()).To.Equal("sand")
    30  	Expect(cache.ItemCount()).To.Equal(1)
    31  }
    32  
    33  func (_ CacheTests) DeletesAPrefix() {
    34  	cache := New(Configure())
    35  	Expect(cache.ItemCount()).To.Equal(0)
    36  
    37  	cache.Set("aaa", "1", time.Minute)
    38  	cache.Set("aab", "2", time.Minute)
    39  	cache.Set("aac", "3", time.Minute)
    40  	cache.Set("ac", "4", time.Minute)
    41  	cache.Set("z5", "7", time.Minute)
    42  	Expect(cache.ItemCount()).To.Equal(5)
    43  
    44  	Expect(cache.DeletePrefix("9a")).To.Equal(0)
    45  	Expect(cache.ItemCount()).To.Equal(5)
    46  
    47  	Expect(cache.DeletePrefix("aa")).To.Equal(3)
    48  	Expect(cache.Get("aaa")).To.Equal(nil)
    49  	Expect(cache.Get("aab")).To.Equal(nil)
    50  	Expect(cache.Get("aac")).To.Equal(nil)
    51  	Expect(cache.Get("ac").Value()).To.Equal("4")
    52  	Expect(cache.Get("z5").Value()).To.Equal("7")
    53  	Expect(cache.ItemCount()).To.Equal(2)
    54  }
    55  
    56  func (_ CacheTests) DeletesAFunc() {
    57  	cache := New(Configure())
    58  	Expect(cache.ItemCount()).To.Equal(0)
    59  
    60  	cache.Set("a", 1, time.Minute)
    61  	cache.Set("b", 2, time.Minute)
    62  	cache.Set("c", 3, time.Minute)
    63  	cache.Set("d", 4, time.Minute)
    64  	cache.Set("e", 5, time.Minute)
    65  	cache.Set("f", 6, time.Minute)
    66  	Expect(cache.ItemCount()).To.Equal(6)
    67  
    68  	Expect(cache.DeleteFunc(func(key string, item *Item) bool {
    69  		return false
    70  	})).To.Equal(0)
    71  	Expect(cache.ItemCount()).To.Equal(6)
    72  
    73  	Expect(cache.DeleteFunc(func(key string, item *Item) bool {
    74  		return item.Value().(int) < 4
    75  	})).To.Equal(3)
    76  	Expect(cache.ItemCount()).To.Equal(3)
    77  
    78  	Expect(cache.DeleteFunc(func(key string, item *Item) bool {
    79  		return key == "d"
    80  	})).To.Equal(1)
    81  	Expect(cache.ItemCount()).To.Equal(2)
    82  
    83  }
    84  
    85  func (_ CacheTests) OnDeleteCallbackCalled() {
    86  	onDeleteFnCalled := int32(0)
    87  	onDeleteFn := func(item *Item) {
    88  		if item.key == "spice" {
    89  			atomic.AddInt32(&onDeleteFnCalled, 1)
    90  		}
    91  	}
    92  
    93  	cache := New(Configure().OnDelete(onDeleteFn))
    94  	cache.Set("spice", "flow", time.Minute)
    95  	cache.Set("worm", "sand", time.Minute)
    96  
    97  	time.Sleep(time.Millisecond * 10) // Run once to init
    98  	cache.Delete("spice")
    99  	time.Sleep(time.Millisecond * 10) // Wait for worker to pick up deleted items
   100  
   101  	Expect(cache.Get("spice")).To.Equal(nil)
   102  	Expect(cache.Get("worm").Value()).To.Equal("sand")
   103  	Expect(atomic.LoadInt32(&onDeleteFnCalled)).To.Eql(1)
   104  }
   105  
   106  func (_ CacheTests) FetchesExpiredItems() {
   107  	cache := New(Configure())
   108  	fn := func() (interface{}, error) { return "moo-moo", nil }
   109  
   110  	cache.Set("beef", "moo", time.Second*-1)
   111  	Expect(cache.Get("beef").Value()).To.Equal("moo")
   112  
   113  	out, _ := cache.Fetch("beef", time.Second, fn)
   114  	Expect(out.Value()).To.Equal("moo-moo")
   115  }
   116  
   117  func (_ CacheTests) GCsTheOldestItems() {
   118  	cache := New(Configure().ItemsToPrune(10))
   119  	for i := 0; i < 500; i++ {
   120  		cache.Set(strconv.Itoa(i), i, time.Minute)
   121  	}
   122  	//let the items get promoted (and added to our list)
   123  	time.Sleep(time.Millisecond * 10)
   124  	gcCache(cache)
   125  	Expect(cache.Get("9")).To.Equal(nil)
   126  	Expect(cache.Get("10").Value()).To.Equal(10)
   127  	Expect(cache.ItemCount()).To.Equal(490)
   128  }
   129  
   130  func (_ CacheTests) PromotedItemsDontGetPruned() {
   131  	cache := New(Configure().ItemsToPrune(10).GetsPerPromote(1))
   132  	for i := 0; i < 500; i++ {
   133  		cache.Set(strconv.Itoa(i), i, time.Minute)
   134  	}
   135  	time.Sleep(time.Millisecond * 10) //run the worker once to init the list
   136  	cache.Get("9")
   137  	time.Sleep(time.Millisecond * 10)
   138  	gcCache(cache)
   139  	Expect(cache.Get("9").Value()).To.Equal(9)
   140  	Expect(cache.Get("10")).To.Equal(nil)
   141  	Expect(cache.Get("11").Value()).To.Equal(11)
   142  }
   143  
   144  func (_ CacheTests) TrackerDoesNotCleanupHeldInstance() {
   145  	cache := New(Configure().ItemsToPrune(11).Track())
   146  	item0 := cache.TrackingSet("0", 0, time.Minute)
   147  	for i := 1; i < 11; i++ {
   148  		cache.Set(strconv.Itoa(i), i, time.Minute)
   149  	}
   150  	item1 := cache.TrackingGet("1")
   151  	time.Sleep(time.Millisecond * 10)
   152  	gcCache(cache)
   153  	Expect(cache.Get("0").Value()).To.Equal(0)
   154  	Expect(cache.Get("1").Value()).To.Equal(1)
   155  	item0.Release()
   156  	item1.Release()
   157  	gcCache(cache)
   158  	Expect(cache.Get("0")).To.Equal(nil)
   159  	Expect(cache.Get("1")).To.Equal(nil)
   160  }
   161  
   162  func (_ CacheTests) RemovesOldestItemWhenFull() {
   163  	onDeleteFnCalled := false
   164  	onDeleteFn := func(item *Item) {
   165  		if item.key == "0" {
   166  			onDeleteFnCalled = true
   167  		}
   168  	}
   169  
   170  	cache := New(Configure().MaxSize(5).ItemsToPrune(1).OnDelete(onDeleteFn))
   171  	for i := 0; i < 7; i++ {
   172  		cache.Set(strconv.Itoa(i), i, time.Minute)
   173  	}
   174  	time.Sleep(time.Millisecond * 10)
   175  	Expect(cache.Get("0")).To.Equal(nil)
   176  	Expect(cache.Get("1")).To.Equal(nil)
   177  	Expect(cache.Get("2").Value()).To.Equal(2)
   178  	Expect(onDeleteFnCalled).To.Equal(true)
   179  	Expect(cache.ItemCount()).To.Equal(5)
   180  }
   181  
   182  func (_ CacheTests) RemovesOldestItemWhenFullBySizer() {
   183  	cache := New(Configure().MaxSize(9).ItemsToPrune(2))
   184  	for i := 0; i < 7; i++ {
   185  		cache.Set(strconv.Itoa(i), &SizedItem{i, 2}, time.Minute)
   186  	}
   187  	time.Sleep(time.Millisecond * 10)
   188  	Expect(cache.Get("0")).To.Equal(nil)
   189  	Expect(cache.Get("1")).To.Equal(nil)
   190  	Expect(cache.Get("2")).To.Equal(nil)
   191  	Expect(cache.Get("3")).To.Equal(nil)
   192  	Expect(cache.Get("4").Value().(*SizedItem).id).To.Equal(4)
   193  	Expect(cache.GetDropped()).To.Equal(4)
   194  	Expect(cache.GetDropped()).To.Equal(0)
   195  }
   196  
   197  func (_ CacheTests) SetUpdatesSizeOnDelta() {
   198  	cache := New(Configure())
   199  	cache.Set("a", &SizedItem{0, 2}, time.Minute)
   200  	cache.Set("b", &SizedItem{0, 3}, time.Minute)
   201  	time.Sleep(time.Millisecond * 5)
   202  	checkSize(cache, 5)
   203  	cache.Set("b", &SizedItem{0, 3}, time.Minute)
   204  	time.Sleep(time.Millisecond * 5)
   205  	checkSize(cache, 5)
   206  	cache.Set("b", &SizedItem{0, 4}, time.Minute)
   207  	time.Sleep(time.Millisecond * 5)
   208  	checkSize(cache, 6)
   209  	cache.Set("b", &SizedItem{0, 2}, time.Minute)
   210  	time.Sleep(time.Millisecond * 5)
   211  	checkSize(cache, 4)
   212  	cache.Delete("b")
   213  	time.Sleep(time.Millisecond * 100)
   214  	checkSize(cache, 2)
   215  }
   216  
   217  func (_ CacheTests) ReplaceDoesNotchangeSizeIfNotSet() {
   218  	cache := New(Configure())
   219  	cache.Set("1", &SizedItem{1, 2}, time.Minute)
   220  	cache.Set("2", &SizedItem{1, 2}, time.Minute)
   221  	cache.Set("3", &SizedItem{1, 2}, time.Minute)
   222  	cache.Replace("4", &SizedItem{1, 2})
   223  	time.Sleep(time.Millisecond * 5)
   224  	checkSize(cache, 6)
   225  }
   226  
   227  func (_ CacheTests) ReplaceChangesSize() {
   228  	cache := New(Configure())
   229  	cache.Set("1", &SizedItem{1, 2}, time.Minute)
   230  	cache.Set("2", &SizedItem{1, 2}, time.Minute)
   231  
   232  	cache.Replace("2", &SizedItem{1, 2})
   233  	time.Sleep(time.Millisecond * 5)
   234  	checkSize(cache, 4)
   235  
   236  	cache.Replace("2", &SizedItem{1, 1})
   237  	time.Sleep(time.Millisecond * 5)
   238  	checkSize(cache, 3)
   239  
   240  	cache.Replace("2", &SizedItem{1, 3})
   241  	time.Sleep(time.Millisecond * 5)
   242  	checkSize(cache, 5)
   243  }
   244  
   245  func (_ CacheTests) ResizeOnTheFly() {
   246  	cache := New(Configure().MaxSize(9).ItemsToPrune(1))
   247  	for i := 0; i < 5; i++ {
   248  		cache.Set(strconv.Itoa(i), i, time.Minute)
   249  	}
   250  	cache.SetMaxSize(3)
   251  	time.Sleep(time.Millisecond * 10)
   252  	Expect(cache.GetDropped()).To.Equal(2)
   253  	Expect(cache.Get("0")).To.Equal(nil)
   254  	Expect(cache.Get("1")).To.Equal(nil)
   255  	Expect(cache.Get("2").Value()).To.Equal(2)
   256  	Expect(cache.Get("3").Value()).To.Equal(3)
   257  	Expect(cache.Get("4").Value()).To.Equal(4)
   258  
   259  	cache.Set("5", 5, time.Minute)
   260  	time.Sleep(time.Millisecond * 5)
   261  	Expect(cache.GetDropped()).To.Equal(1)
   262  	Expect(cache.Get("2")).To.Equal(nil)
   263  	Expect(cache.Get("3").Value()).To.Equal(3)
   264  	Expect(cache.Get("4").Value()).To.Equal(4)
   265  	Expect(cache.Get("5").Value()).To.Equal(5)
   266  
   267  	cache.SetMaxSize(10)
   268  	cache.Set("6", 6, time.Minute)
   269  	time.Sleep(time.Millisecond * 10)
   270  	Expect(cache.GetDropped()).To.Equal(0)
   271  	Expect(cache.Get("3").Value()).To.Equal(3)
   272  	Expect(cache.Get("4").Value()).To.Equal(4)
   273  	Expect(cache.Get("5").Value()).To.Equal(5)
   274  	Expect(cache.Get("6").Value()).To.Equal(6)
   275  }
   276  
   277  func (_ CacheTests) ForEachFunc() {
   278  	cache := New(Configure().MaxSize(3).ItemsToPrune(1))
   279  	Expect(forEachKeys(cache)).To.Equal([]string{})
   280  
   281  	cache.Set("1", 1, time.Minute)
   282  	Expect(forEachKeys(cache)).To.Equal([]string{"1"})
   283  
   284  	cache.Set("2", 2, time.Minute)
   285  	time.Sleep(time.Millisecond * 10)
   286  	Expect(forEachKeys(cache)).To.Equal([]string{"1", "2"})
   287  
   288  	cache.Set("3", 3, time.Minute)
   289  	time.Sleep(time.Millisecond * 10)
   290  	Expect(forEachKeys(cache)).To.Equal([]string{"1", "2", "3"})
   291  
   292  	cache.Set("4", 4, time.Minute)
   293  	time.Sleep(time.Millisecond * 10)
   294  	Expect(forEachKeys(cache)).To.Equal([]string{"2", "3", "4"})
   295  
   296  	cache.Set("stop", 5, time.Minute)
   297  	time.Sleep(time.Millisecond * 10)
   298  	Expect(forEachKeys(cache)).Not.To.Contain("stop")
   299  
   300  	cache.Set("6", 6, time.Minute)
   301  	time.Sleep(time.Millisecond * 10)
   302  	Expect(forEachKeys(cache)).Not.To.Contain("stop")
   303  }
   304  
   305  type SizedItem struct {
   306  	id int
   307  	s  int64
   308  }
   309  
   310  func (s *SizedItem) Size() int64 {
   311  	return s.s
   312  }
   313  
   314  func checkSize(cache *Cache, sz int64) {
   315  	cache.Stop()
   316  	Expect(cache.size).To.Equal(sz)
   317  	cache.restart()
   318  }
   319  
   320  func gcCache(cache *Cache) {
   321  	cache.Stop()
   322  	cache.gc()
   323  	cache.restart()
   324  }
   325  
   326  func forEachKeys(cache *Cache) []string {
   327  	keys := make([]string, 0, 10)
   328  	cache.ForEachFunc(func(key string, i *Item) bool {
   329  		if key == "stop" {
   330  			return false
   331  		}
   332  		keys = append(keys, key)
   333  		return true
   334  	})
   335  	sort.Strings(keys)
   336  	return keys
   337  }
   338  

View as plain text