Merge pull request #45 from bep/DeleteFunc

Add DeleteFunc
This commit is contained in:
Karl Seguin 2020-08-13 10:37:20 +08:00 committed by GitHub
commit f63031fa40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 90 additions and 2 deletions

View file

@ -54,13 +54,13 @@ func (b *bucket) delete(key string) *Item {
// the item from the map. I'm pretty sure this is 100% fine, but it is unique.
// (We do this so that the write to the channel is under the read lock and not the
// write lock)
func (b *bucket) deletePrefix(prefix string, deletables chan *Item) int {
func (b *bucket) deleteFunc(matches func(key string, item interface{}) bool, deletables chan *Item) int {
lookup := b.lookup
items := make([]*Item, 0, len(lookup)/10)
b.RLock()
for key, item := range lookup {
if strings.HasPrefix(key, prefix) {
if matches(key, item) {
deletables <- item
items = append(items, item)
}
@ -80,6 +80,12 @@ func (b *bucket) deletePrefix(prefix string, deletables chan *Item) int {
return len(items)
}
func (b *bucket) deletePrefix(prefix string, deletables chan *Item) int {
return b.deleteFunc(func(key string, item interface{}) bool {
return strings.HasPrefix(key, prefix)
}, deletables)
}
func (b *bucket) clear() {
b.Lock()
b.lookup = make(map[string]*Item)

View file

@ -63,6 +63,15 @@ func (c *Cache) DeletePrefix(prefix string) int {
return count
}
// Deletes all items that the matches func evaluates to true.
func (c *Cache) DeleteFunc(matches func(key string, item interface{}) bool) int {
count := 0
for _, b := range c.buckets {
count += b.deleteFunc(matches, c.deletables)
}
return count
}
// Get an item from the cache. Returns nil if the item wasn't found.
// This can return an expired item. Use item.Expired() to see if the item
// is expired and item.TTL() to see how long until the item expires (which

View file

@ -51,6 +51,35 @@ func (_ CacheTests) DeletesAPrefix() {
Expect(cache.ItemCount()).To.Equal(2)
}
func (_ CacheTests) DeletesAFunc() {
cache := New(Configure())
Expect(cache.ItemCount()).To.Equal(0)
cache.Set("a", 1, time.Minute)
cache.Set("b", 2, time.Minute)
cache.Set("c", 3, time.Minute)
cache.Set("d", 4, time.Minute)
cache.Set("e", 5, time.Minute)
cache.Set("f", 6, time.Minute)
Expect(cache.ItemCount()).To.Equal(6)
Expect(cache.DeleteFunc(func(key string, item interface{}) bool {
return false
})).To.Equal(0)
Expect(cache.ItemCount()).To.Equal(6)
Expect(cache.DeleteFunc(func(key string, item interface{}) bool {
return item.(*Item).Value().(int) < 4
})).To.Equal(3)
Expect(cache.ItemCount()).To.Equal(3)
Expect(cache.DeleteFunc(func(key string, item interface{}) bool {
return key == "d"
})).To.Equal(1)
Expect(cache.ItemCount()).To.Equal(2)
}
func (_ CacheTests) OnDeleteCallbackCalled() {
onDeleteFnCalled := false
onDeleteFn := func(item *Item) {

View file

@ -71,6 +71,16 @@ func (b *layeredBucket) deletePrefix(primary, prefix string, deletables chan *It
return bucket.deletePrefix(prefix, deletables)
}
func (b *layeredBucket) deleteFunc(primary string, matches func(key string, item interface{}) bool, deletables chan *Item) int {
b.RLock()
bucket, exists := b.buckets[primary]
b.RUnlock()
if exists == false {
return 0
}
return bucket.deleteFunc(matches, deletables)
}
func (b *layeredBucket) deleteAll(primary string, deletables chan *Item) bool {
b.RLock()
bucket, exists := b.buckets[primary]

View file

@ -154,6 +154,11 @@ func (c *LayeredCache) DeletePrefix(primary, prefix string) int {
return c.bucket(primary).deletePrefix(primary, prefix, c.deletables)
}
// Deletes all items that share the same primary key and where the matches func evaluates to true.
func (c *LayeredCache) DeleteFunc(primary string, matches func(key string, item interface{}) bool) int {
return c.bucket(primary).deleteFunc(primary, matches, c.deletables)
}
//this isn't thread safe. It's meant to be called from non-concurrent tests
func (c *LayeredCache) Clear() {
for _, bucket := range c.buckets {

View file

@ -95,6 +95,35 @@ func (_ *LayeredCacheTests) DeletesAPrefix() {
Expect(cache.ItemCount()).To.Equal(3)
}
func (_ *LayeredCacheTests) DeletesAFunc() {
cache := newLayered()
Expect(cache.ItemCount()).To.Equal(0)
cache.Set("spice", "a", 1, time.Minute)
cache.Set("leto", "b", 2, time.Minute)
cache.Set("spice", "c", 3, time.Minute)
cache.Set("spice", "d", 4, time.Minute)
cache.Set("spice", "e", 5, time.Minute)
cache.Set("spice", "f", 6, time.Minute)
Expect(cache.ItemCount()).To.Equal(6)
Expect(cache.DeleteFunc("spice", func(key string, item interface{}) bool {
return false
})).To.Equal(0)
Expect(cache.ItemCount()).To.Equal(6)
Expect(cache.DeleteFunc("spice", func(key string, item interface{}) bool {
return item.(*Item).Value().(int) < 4
})).To.Equal(2)
Expect(cache.ItemCount()).To.Equal(4)
Expect(cache.DeleteFunc("spice", func(key string, item interface{}) bool {
return key == "d"
})).To.Equal(1)
Expect(cache.ItemCount()).To.Equal(3)
}
func (_ *LayeredCacheTests) OnDeleteCallbackCalled() {
onDeleteFnCalled := false