ccache/bucket.go

94 lines
2.2 KiB
Go
Raw Permalink Normal View History

2013-10-19 02:56:28 +02:00
package ccache
import (
2020-01-23 03:27:12 +01:00
"strings"
"sync"
"time"
2013-10-19 02:56:28 +02:00
)
type bucket struct {
sync.RWMutex
lookup map[string]*Item
2013-10-19 02:56:28 +02:00
}
2019-01-26 06:33:50 +01:00
func (b *bucket) itemCount() int {
b.RLock()
defer b.RUnlock()
return len(b.lookup)
}
func (b *bucket) get(key string) *Item {
b.RLock()
defer b.RUnlock()
return b.lookup[key]
2013-10-19 02:56:28 +02:00
}
func (b *bucket) set(key string, value interface{}, duration time.Duration, track bool) (*Item, *Item) {
expires := time.Now().Add(duration).UnixNano()
item := newItem(key, value, expires, track)
2014-11-13 16:20:12 +01:00
b.Lock()
existing := b.lookup[key]
b.lookup[key] = item
2020-01-23 05:55:55 +01:00
b.Unlock()
return item, existing
2014-11-13 16:20:12 +01:00
}
func (b *bucket) delete(key string) *Item {
b.Lock()
item := b.lookup[key]
delete(b.lookup, key)
2020-01-23 05:55:55 +01:00
b.Unlock()
return item
2013-10-19 02:56:28 +02:00
}
2020-01-23 03:27:12 +01:00
// This is an expensive operation, so we do what we can to optimize it and limit
// the impact it has on concurrent operations. Specifically, we:
// 1 - Do an initial iteration to collect matches. This allows us to do the
// "expensive" prefix check (on all values) using only a read-lock
// 2 - Do a second iteration, under write lock, for the matched results to do
// the actual deletion
// Also, this is the only place where the Bucket is aware of cache detail: the
// deletables channel. Passing it here lets us avoid iterating over matched items
// again in the cache. Further, we pass item to deletables BEFORE actually removing
// 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)
2020-08-13 16:10:22 +02:00
func (b *bucket) deleteFunc(matches func(key string, item *Item) bool, deletables chan *Item) int {
2020-01-23 03:27:12 +01:00
lookup := b.lookup
2020-08-13 09:49:28 +02:00
items := make([]*Item, 0)
2020-01-23 03:27:12 +01:00
b.RLock()
for key, item := range lookup {
if matches(key, item) {
2020-01-23 03:27:12 +01:00
deletables <- item
items = append(items, item)
}
}
b.RUnlock()
if len(items) == 0 {
// avoid the write lock if we can
return 0
}
b.Lock()
for _, item := range items {
delete(lookup, item.key)
}
2020-01-23 05:55:55 +01:00
b.Unlock()
2020-01-23 03:27:12 +01:00
return len(items)
}
func (b *bucket) deletePrefix(prefix string, deletables chan *Item) int {
2020-08-13 16:10:22 +02:00
return b.deleteFunc(func(key string, item *Item) bool {
return strings.HasPrefix(key, prefix)
}, deletables)
}
func (b *bucket) clear() {
b.Lock()
b.lookup = make(map[string]*Item)
2020-01-23 05:55:55 +01:00
b.Unlock()
}