...

Source file src/github.com/launchdarkly/ccache/layeredcache_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 LayeredCacheTests struct{}
    14  
    15  func Test_LayeredCache(t *testing.T) {
    16  	Expectify(new(LayeredCacheTests), t)
    17  }
    18  
    19  func (_ *LayeredCacheTests) GetsANonExistantValue() {
    20  	cache := newLayered()
    21  	Expect(cache.Get("spice", "flow")).To.Equal(nil)
    22  	Expect(cache.ItemCount()).To.Equal(0)
    23  }
    24  
    25  func (_ *LayeredCacheTests) SetANewValue() {
    26  	cache := newLayered()
    27  	cache.Set("spice", "flow", "a value", time.Minute)
    28  	Expect(cache.Get("spice", "flow").Value()).To.Equal("a value")
    29  	Expect(cache.Get("spice", "stop")).To.Equal(nil)
    30  	Expect(cache.ItemCount()).To.Equal(1)
    31  }
    32  
    33  func (_ *LayeredCacheTests) SetsMultipleValueWithinTheSameLayer() {
    34  	cache := newLayered()
    35  	cache.Set("spice", "flow", "value-a", time.Minute)
    36  	cache.Set("spice", "must", "value-b", time.Minute)
    37  	cache.Set("leto", "sister", "ghanima", time.Minute)
    38  	Expect(cache.Get("spice", "flow").Value()).To.Equal("value-a")
    39  	Expect(cache.Get("spice", "must").Value()).To.Equal("value-b")
    40  	Expect(cache.Get("spice", "worm")).To.Equal(nil)
    41  
    42  	Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
    43  	Expect(cache.Get("leto", "brother")).To.Equal(nil)
    44  	Expect(cache.Get("baron", "friend")).To.Equal(nil)
    45  	Expect(cache.ItemCount()).To.Equal(3)
    46  }
    47  
    48  func (_ *LayeredCacheTests) ReplaceDoesNothingIfKeyDoesNotExist() {
    49  	cache := newLayered()
    50  	Expect(cache.Replace("spice", "flow", "value-a")).To.Equal(false)
    51  	Expect(cache.Get("spice", "flow")).To.Equal(nil)
    52  }
    53  
    54  func (_ *LayeredCacheTests) ReplaceUpdatesTheValue() {
    55  	cache := newLayered()
    56  	cache.Set("spice", "flow", "value-a", time.Minute)
    57  	Expect(cache.Replace("spice", "flow", "value-b")).To.Equal(true)
    58  	Expect(cache.Get("spice", "flow").Value().(string)).To.Equal("value-b")
    59  	Expect(cache.ItemCount()).To.Equal(1)
    60  	//not sure how to test that the TTL hasn't changed sort of a sleep..
    61  }
    62  
    63  func (_ *LayeredCacheTests) DeletesAValue() {
    64  	cache := newLayered()
    65  	cache.Set("spice", "flow", "value-a", time.Minute)
    66  	cache.Set("spice", "must", "value-b", time.Minute)
    67  	cache.Set("leto", "sister", "ghanima", time.Minute)
    68  	cache.Delete("spice", "flow")
    69  	Expect(cache.Get("spice", "flow")).To.Equal(nil)
    70  	Expect(cache.Get("spice", "must").Value()).To.Equal("value-b")
    71  	Expect(cache.Get("spice", "worm")).To.Equal(nil)
    72  	Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
    73  	Expect(cache.ItemCount()).To.Equal(2)
    74  }
    75  
    76  func (_ *LayeredCacheTests) DeletesAPrefix() {
    77  	cache := newLayered()
    78  	Expect(cache.ItemCount()).To.Equal(0)
    79  
    80  	cache.Set("spice", "aaa", "1", time.Minute)
    81  	cache.Set("spice", "aab", "2", time.Minute)
    82  	cache.Set("spice", "aac", "3", time.Minute)
    83  	cache.Set("leto", "aac", "3", time.Minute)
    84  	cache.Set("spice", "ac", "4", time.Minute)
    85  	cache.Set("spice", "z5", "7", time.Minute)
    86  	Expect(cache.ItemCount()).To.Equal(6)
    87  
    88  	Expect(cache.DeletePrefix("spice", "9a")).To.Equal(0)
    89  	Expect(cache.ItemCount()).To.Equal(6)
    90  
    91  	Expect(cache.DeletePrefix("spice", "aa")).To.Equal(3)
    92  	Expect(cache.Get("spice", "aaa")).To.Equal(nil)
    93  	Expect(cache.Get("spice", "aab")).To.Equal(nil)
    94  	Expect(cache.Get("spice", "aac")).To.Equal(nil)
    95  	Expect(cache.Get("spice", "ac").Value()).To.Equal("4")
    96  	Expect(cache.Get("spice", "z5").Value()).To.Equal("7")
    97  	Expect(cache.ItemCount()).To.Equal(3)
    98  }
    99  
   100  func (_ *LayeredCacheTests) DeletesAFunc() {
   101  	cache := newLayered()
   102  	Expect(cache.ItemCount()).To.Equal(0)
   103  
   104  	cache.Set("spice", "a", 1, time.Minute)
   105  	cache.Set("leto", "b", 2, time.Minute)
   106  	cache.Set("spice", "c", 3, time.Minute)
   107  	cache.Set("spice", "d", 4, time.Minute)
   108  	cache.Set("spice", "e", 5, time.Minute)
   109  	cache.Set("spice", "f", 6, time.Minute)
   110  	Expect(cache.ItemCount()).To.Equal(6)
   111  
   112  	Expect(cache.DeleteFunc("spice", func(key string, item *Item) bool {
   113  		return false
   114  	})).To.Equal(0)
   115  	Expect(cache.ItemCount()).To.Equal(6)
   116  
   117  	Expect(cache.DeleteFunc("spice", func(key string, item *Item) bool {
   118  		return item.Value().(int) < 4
   119  	})).To.Equal(2)
   120  	Expect(cache.ItemCount()).To.Equal(4)
   121  
   122  	Expect(cache.DeleteFunc("spice", func(key string, item *Item) bool {
   123  		return key == "d"
   124  	})).To.Equal(1)
   125  	Expect(cache.ItemCount()).To.Equal(3)
   126  
   127  }
   128  
   129  func (_ *LayeredCacheTests) OnDeleteCallbackCalled() {
   130  	onDeleteFnCalled := int32(0)
   131  	onDeleteFn := func(item *Item) {
   132  		if item.group == "spice" && item.key == "flow" {
   133  			atomic.AddInt32(&onDeleteFnCalled, 1)
   134  		}
   135  	}
   136  
   137  	cache := Layered(Configure().OnDelete(onDeleteFn))
   138  	cache.Set("spice", "flow", "value-a", time.Minute)
   139  	cache.Set("spice", "must", "value-b", time.Minute)
   140  	cache.Set("leto", "sister", "ghanima", time.Minute)
   141  
   142  	time.Sleep(time.Millisecond * 10) // Run once to init
   143  	cache.Delete("spice", "flow")
   144  	time.Sleep(time.Millisecond * 10) // Wait for worker to pick up deleted items
   145  
   146  	Expect(cache.Get("spice", "flow")).To.Equal(nil)
   147  	Expect(cache.Get("spice", "must").Value()).To.Equal("value-b")
   148  	Expect(cache.Get("spice", "worm")).To.Equal(nil)
   149  	Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
   150  
   151  	Expect(atomic.LoadInt32(&onDeleteFnCalled)).To.Eql(1)
   152  }
   153  
   154  func (_ *LayeredCacheTests) DeletesALayer() {
   155  	cache := newLayered()
   156  	cache.Set("spice", "flow", "value-a", time.Minute)
   157  	cache.Set("spice", "must", "value-b", time.Minute)
   158  	cache.Set("leto", "sister", "ghanima", time.Minute)
   159  	cache.DeleteAll("spice")
   160  	Expect(cache.Get("spice", "flow")).To.Equal(nil)
   161  	Expect(cache.Get("spice", "must")).To.Equal(nil)
   162  	Expect(cache.Get("spice", "worm")).To.Equal(nil)
   163  	Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
   164  }
   165  
   166  func (_ LayeredCacheTests) GCsTheOldestItems() {
   167  	cache := Layered(Configure().ItemsToPrune(10))
   168  	cache.Set("xx", "a", 23, time.Minute)
   169  	for i := 0; i < 500; i++ {
   170  		cache.Set(strconv.Itoa(i), "a", i, time.Minute)
   171  	}
   172  	cache.Set("xx", "b", 9001, time.Minute)
   173  	//let the items get promoted (and added to our list)
   174  	time.Sleep(time.Millisecond * 10)
   175  	gcLayeredCache(cache)
   176  	Expect(cache.Get("xx", "a")).To.Equal(nil)
   177  	Expect(cache.Get("xx", "b").Value()).To.Equal(9001)
   178  	Expect(cache.Get("8", "a")).To.Equal(nil)
   179  	Expect(cache.Get("9", "a").Value()).To.Equal(9)
   180  	Expect(cache.Get("10", "a").Value()).To.Equal(10)
   181  }
   182  
   183  func (_ LayeredCacheTests) PromotedItemsDontGetPruned() {
   184  	cache := Layered(Configure().ItemsToPrune(10).GetsPerPromote(1))
   185  	for i := 0; i < 500; i++ {
   186  		cache.Set(strconv.Itoa(i), "a", i, time.Minute)
   187  	}
   188  	time.Sleep(time.Millisecond * 10) //run the worker once to init the list
   189  	cache.Get("9", "a")
   190  	time.Sleep(time.Millisecond * 10)
   191  	gcLayeredCache(cache)
   192  	Expect(cache.Get("9", "a").Value()).To.Equal(9)
   193  	Expect(cache.Get("10", "a")).To.Equal(nil)
   194  	Expect(cache.Get("11", "a").Value()).To.Equal(11)
   195  }
   196  
   197  func (_ LayeredCacheTests) TrackerDoesNotCleanupHeldInstance() {
   198  	cache := Layered(Configure().ItemsToPrune(10).Track())
   199  	item0 := cache.TrackingSet("0", "a", 0, time.Minute)
   200  	for i := 1; i < 11; i++ {
   201  		cache.Set(strconv.Itoa(i), "a", i, time.Minute)
   202  	}
   203  	item1 := cache.TrackingGet("1", "a")
   204  	time.Sleep(time.Millisecond * 10)
   205  	gcLayeredCache(cache)
   206  	Expect(cache.Get("0", "a").Value()).To.Equal(0)
   207  	Expect(cache.Get("1", "a").Value()).To.Equal(1)
   208  	item0.Release()
   209  	item1.Release()
   210  	gcLayeredCache(cache)
   211  	Expect(cache.Get("0", "a")).To.Equal(nil)
   212  	Expect(cache.Get("1", "a")).To.Equal(nil)
   213  }
   214  
   215  func (_ LayeredCacheTests) RemovesOldestItemWhenFull() {
   216  	cache := Layered(Configure().MaxSize(5).ItemsToPrune(1))
   217  	cache.Set("xx", "a", 23, time.Minute)
   218  	for i := 0; i < 7; i++ {
   219  		cache.Set(strconv.Itoa(i), "a", i, time.Minute)
   220  	}
   221  	cache.Set("xx", "b", 9001, time.Minute)
   222  	time.Sleep(time.Millisecond * 10)
   223  	Expect(cache.Get("xx", "a")).To.Equal(nil)
   224  	Expect(cache.Get("0", "a")).To.Equal(nil)
   225  	Expect(cache.Get("1", "a")).To.Equal(nil)
   226  	Expect(cache.Get("2", "a")).To.Equal(nil)
   227  	Expect(cache.Get("3", "a").Value()).To.Equal(3)
   228  	Expect(cache.Get("xx", "b").Value()).To.Equal(9001)
   229  	Expect(cache.GetDropped()).To.Equal(4)
   230  	Expect(cache.GetDropped()).To.Equal(0)
   231  }
   232  
   233  func (_ LayeredCacheTests) ResizeOnTheFly() {
   234  	cache := Layered(Configure().MaxSize(9).ItemsToPrune(1))
   235  	for i := 0; i < 5; i++ {
   236  		cache.Set(strconv.Itoa(i), "a", i, time.Minute)
   237  	}
   238  	cache.SetMaxSize(3)
   239  	time.Sleep(time.Millisecond * 10)
   240  	Expect(cache.GetDropped()).To.Equal(2)
   241  	Expect(cache.Get("0", "a")).To.Equal(nil)
   242  	Expect(cache.Get("1", "a")).To.Equal(nil)
   243  	Expect(cache.Get("2", "a").Value()).To.Equal(2)
   244  	Expect(cache.Get("3", "a").Value()).To.Equal(3)
   245  	Expect(cache.Get("4", "a").Value()).To.Equal(4)
   246  
   247  	cache.Set("5", "a", 5, time.Minute)
   248  	time.Sleep(time.Millisecond * 5)
   249  	Expect(cache.GetDropped()).To.Equal(1)
   250  	Expect(cache.Get("2", "a")).To.Equal(nil)
   251  	Expect(cache.Get("3", "a").Value()).To.Equal(3)
   252  	Expect(cache.Get("4", "a").Value()).To.Equal(4)
   253  	Expect(cache.Get("5", "a").Value()).To.Equal(5)
   254  
   255  	cache.SetMaxSize(10)
   256  	cache.Set("6", "a", 6, time.Minute)
   257  	time.Sleep(time.Millisecond * 10)
   258  	Expect(cache.GetDropped()).To.Equal(0)
   259  	Expect(cache.Get("3", "a").Value()).To.Equal(3)
   260  	Expect(cache.Get("4", "a").Value()).To.Equal(4)
   261  	Expect(cache.Get("5", "a").Value()).To.Equal(5)
   262  	Expect(cache.Get("6", "a").Value()).To.Equal(6)
   263  }
   264  
   265  func (_ LayeredCacheTests) RemovesOldestItemWhenFullBySizer() {
   266  	cache := Layered(Configure().MaxSize(9).ItemsToPrune(2))
   267  	for i := 0; i < 7; i++ {
   268  		cache.Set("pri", strconv.Itoa(i), &SizedItem{i, 2}, time.Minute)
   269  	}
   270  	time.Sleep(time.Millisecond * 10)
   271  	Expect(cache.Get("pri", "0")).To.Equal(nil)
   272  	Expect(cache.Get("pri", "1")).To.Equal(nil)
   273  	Expect(cache.Get("pri", "2")).To.Equal(nil)
   274  	Expect(cache.Get("pri", "3")).To.Equal(nil)
   275  	Expect(cache.Get("pri", "4").Value().(*SizedItem).id).To.Equal(4)
   276  }
   277  
   278  func (_ LayeredCacheTests) SetUpdatesSizeOnDelta() {
   279  	cache := Layered(Configure())
   280  	cache.Set("pri", "a", &SizedItem{0, 2}, time.Minute)
   281  	cache.Set("pri", "b", &SizedItem{0, 3}, time.Minute)
   282  	time.Sleep(time.Millisecond * 5)
   283  	checkLayeredSize(cache, 5)
   284  	cache.Set("pri", "b", &SizedItem{0, 3}, time.Minute)
   285  	time.Sleep(time.Millisecond * 5)
   286  	checkLayeredSize(cache, 5)
   287  	cache.Set("pri", "b", &SizedItem{0, 4}, time.Minute)
   288  	time.Sleep(time.Millisecond * 5)
   289  	checkLayeredSize(cache, 6)
   290  	cache.Set("pri", "b", &SizedItem{0, 2}, time.Minute)
   291  	cache.Set("sec", "b", &SizedItem{0, 3}, time.Minute)
   292  	time.Sleep(time.Millisecond * 5)
   293  	checkLayeredSize(cache, 7)
   294  	cache.Delete("pri", "b")
   295  	time.Sleep(time.Millisecond * 10)
   296  	checkLayeredSize(cache, 5)
   297  }
   298  
   299  func (_ LayeredCacheTests) ReplaceDoesNotchangeSizeIfNotSet() {
   300  	cache := Layered(Configure())
   301  	cache.Set("pri", "1", &SizedItem{1, 2}, time.Minute)
   302  	cache.Set("pri", "2", &SizedItem{1, 2}, time.Minute)
   303  	cache.Set("pri", "3", &SizedItem{1, 2}, time.Minute)
   304  	cache.Replace("sec", "3", &SizedItem{1, 2})
   305  	time.Sleep(time.Millisecond * 5)
   306  	checkLayeredSize(cache, 6)
   307  }
   308  
   309  func (_ LayeredCacheTests) ReplaceChangesSize() {
   310  	cache := Layered(Configure())
   311  	cache.Set("pri", "1", &SizedItem{1, 2}, time.Minute)
   312  	cache.Set("pri", "2", &SizedItem{1, 2}, time.Minute)
   313  
   314  	cache.Replace("pri", "2", &SizedItem{1, 2})
   315  	time.Sleep(time.Millisecond * 5)
   316  	checkLayeredSize(cache, 4)
   317  
   318  	cache.Replace("pri", "2", &SizedItem{1, 1})
   319  	time.Sleep(time.Millisecond * 5)
   320  	checkLayeredSize(cache, 3)
   321  
   322  	cache.Replace("pri", "2", &SizedItem{1, 3})
   323  	time.Sleep(time.Millisecond * 5)
   324  	checkLayeredSize(cache, 5)
   325  }
   326  
   327  func (_ LayeredCacheTests) EachFunc() {
   328  	cache := Layered(Configure().MaxSize(3).ItemsToPrune(1))
   329  	Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{})
   330  
   331  	cache.Set("1", "a", 1, time.Minute)
   332  	Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{"a"})
   333  
   334  	cache.Set("1", "b", 2, time.Minute)
   335  	time.Sleep(time.Millisecond * 10)
   336  	Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{"a", "b"})
   337  
   338  	cache.Set("1", "c", 3, time.Minute)
   339  	time.Sleep(time.Millisecond * 10)
   340  	Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{"a", "b", "c"})
   341  
   342  	cache.Set("1", "d", 4, time.Minute)
   343  	time.Sleep(time.Millisecond * 10)
   344  	Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{"b", "c", "d"})
   345  
   346  	// iteration is non-deterministic, all we know for sure is "stop" should not be in there
   347  	cache.Set("1", "stop", 5, time.Minute)
   348  	time.Sleep(time.Millisecond * 10)
   349  	Expect(forEachKeysLayered(cache, "1")).Not.To.Contain("stop")
   350  
   351  	cache.Set("1", "e", 6, time.Minute)
   352  	time.Sleep(time.Millisecond * 10)
   353  	Expect(forEachKeysLayered(cache, "1")).Not.To.Contain("stop")
   354  }
   355  
   356  func newLayered() *LayeredCache {
   357  	c := Layered(Configure())
   358  	c.Clear()
   359  	return c
   360  }
   361  
   362  func checkLayeredSize(cache *LayeredCache, sz int64) {
   363  	cache.Stop()
   364  	Expect(cache.size).To.Equal(sz)
   365  	cache.restart()
   366  }
   367  
   368  func gcLayeredCache(cache *LayeredCache) {
   369  	cache.Stop()
   370  	cache.gc()
   371  	cache.restart()
   372  }
   373  
   374  func forEachKeysLayered(cache *LayeredCache, primary string) []string {
   375  	keys := make([]string, 0, 10)
   376  	cache.ForEachFunc(primary, func(key string, i *Item) bool {
   377  		if key == "stop" {
   378  			return false
   379  		}
   380  		keys = append(keys, key)
   381  		return true
   382  	})
   383  	sort.Strings(keys)
   384  	return keys
   385  }
   386  

View as plain text