From 24a2436f81ce07b882dbd18f310cf6eaca6fc58c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 28 Apr 2014 12:10:37 +0300 Subject: [PATCH] faster cache counter and unified caches --- cache2go/cache.go | 86 ++++++++++++++++++++++++------------------ cache2go/cache_test.go | 19 ++++++++-- 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/cache2go/cache.go b/cache2go/cache.go index 5f63c6be7..2dfea50c9 100644 --- a/cache2go/cache.go +++ b/cache2go/cache.go @@ -31,11 +31,17 @@ type timestampedValue struct { value interface{} } +const ( + PREFIX_LEN = 4 +) + var ( - xcache = make(map[string]expiringCacheEntry) - xMux sync.RWMutex - cache = make(map[string]timestampedValue) - mux sync.RWMutex + xcache = make(map[string]expiringCacheEntry) + xMux sync.RWMutex + cache = make(map[string]timestampedValue) + mux sync.RWMutex + cMux sync.Mutex + counters = make(map[string]int64) ) // The main function to cache with expiration @@ -46,6 +52,7 @@ func (xe *XEntry) XCache(key string, expire time.Duration, value expiringCacheEn xe.timestamp = time.Now() xMux.Lock() xcache[key] = value + count(key) xMux.Unlock() go xe.expire() } @@ -61,6 +68,7 @@ func (xe *XEntry) expire() { if !xe.keepAlive { xMux.Lock() delete(xcache, xe.key) + descount(xe.key) xMux.Unlock() } } @@ -99,6 +107,7 @@ func Cache(key string, value interface{}) { mux.Lock() defer mux.Unlock() cache[key] = timestampedValue{time.Now(), value} + count(key) } // The function to extract a value for a key that never expire @@ -117,10 +126,6 @@ func GetKeyAge(key string) (time.Duration, error) { if r, ok := cache[key]; ok { return time.Since(r.timestamp), nil } - return 0, errors.New("not found") -} - -func GetXKeyAge(key string) (time.Duration, error) { xMux.RLock() defer xMux.RUnlock() if r, ok := xcache[key]; ok { @@ -133,9 +138,6 @@ func RemKey(key string) { mux.Lock() defer mux.Unlock() delete(cache, key) -} - -func XRemKey(key string) { xMux.Lock() defer xMux.Unlock() if r, ok := xcache[key]; ok { @@ -144,6 +146,7 @@ func XRemKey(key string) { } } delete(xcache, key) + descount(key) } func RemPrefixKey(prefix string) { @@ -152,11 +155,9 @@ func RemPrefixKey(prefix string) { for key, _ := range cache { if strings.HasPrefix(key, prefix) { delete(cache, key) + descount(key) } } -} - -func XRemPrefixKey(prefix string) { xMux.Lock() defer xMux.Unlock() for key, _ := range xcache { @@ -167,6 +168,7 @@ func XRemPrefixKey(prefix string) { } } delete(xcache, key) + descount(key) } } } @@ -178,11 +180,6 @@ func GetAllEntries(prefix string) map[string]interface{} { result[key] = timestampedValue.value } } - return result -} - -func XGetAllEntries(prefix string) map[string]interface{} { - result := make(map[string]interface{}) for key, value := range xcache { if strings.HasPrefix(key, prefix) { result[key] = value @@ -191,8 +188,8 @@ func XGetAllEntries(prefix string) map[string]interface{} { return result } -// Delete all keys from expiraton cache -func XFlush() { +// Delete all keys from cache +func Flush() { xMux.Lock() defer xMux.Unlock() for _, v := range xcache { @@ -201,29 +198,44 @@ func XFlush() { } } xcache = make(map[string]expiringCacheEntry) -} - -// Delete all keys from cache -func Flush() { mux.Lock() defer mux.Unlock() cache = make(map[string]timestampedValue) + counters = make(map[string]int64) } -func CountEntries(prefix string) (result int) { - for key, _ := range cache { - if strings.HasPrefix(key, prefix) { - result++ - } +func CountEntries(prefix string) (result int64) { + if _, ok := counters[prefix]; ok { + return counters[prefix] } - return + return 0 } -func XCountEntries(prefix string) (result int) { - for key, _ := range xcache { - if strings.HasPrefix(key, prefix) { - result++ - } +// increments the counter for the specified key prefix +func count(key string) { + if len(key) < PREFIX_LEN { + return + } + cMux.Lock() + defer cMux.Unlock() + prefix := key[:PREFIX_LEN] + if _, ok := counters[prefix]; ok { + // increase the value + counters[prefix] += 1 + } else { + counters[prefix] = 1 + } +} + +// decrements the counter for the specified key prefix +func descount(key string) { + if len(key) < PREFIX_LEN { + return + } + cMux.Lock() + defer cMux.Unlock() + prefix := key[:PREFIX_LEN] + if _, ok := counters[prefix]; ok { + counters[prefix] -= 1 } - return } diff --git a/cache2go/cache_test.go b/cache2go/cache_test.go index 9ef148de8..de39a028e 100644 --- a/cache2go/cache_test.go +++ b/cache2go/cache_test.go @@ -57,7 +57,7 @@ func TestFlush(t *testing.T) { a := &myStruct{data: "mama are mere"} a.XCache("mama", 10*time.Second, a) time.Sleep(1000 * time.Millisecond) - XFlush() + Flush() b, err := GetXCached("mama") if err == nil || b != nil { t.Error("Error expiring data") @@ -67,7 +67,7 @@ func TestFlush(t *testing.T) { func TestFlushNoTimout(t *testing.T) { a := &myStruct{data: "mama are mere"} a.XCache("mama", 10*time.Second, a) - XFlush() + Flush() b, err := GetXCached("mama") if err == nil || b != nil { t.Error("Error expiring data") @@ -91,7 +91,7 @@ func TestXRemKey(t *testing.T) { if t1, err := GetXCached("mama"); err != nil || t1 != a { t.Error("Error setting xcache") } - XRemKey("mama") + RemKey("mama") if t1, err := GetXCached("mama"); err == nil || t1 == a { t.Error("Error removing xcached key: ", err, t1) } @@ -134,10 +134,21 @@ func TestXRemPrefixKey(t *testing.T) { a.XCache("x_t1", 10*time.Second, a) a.XCache("y_t1", 10*time.Second, a) - XRemPrefixKey("x_") + RemPrefixKey("x_") _, errX := GetXCached("x_t1") _, errY := GetXCached("y_t1") if errX == nil || errY != nil { t.Error("Error removing prefix: ", errX, errY) } } + +func TestCount(t *testing.T) { + Cache("dst_A1", "1") + Cache("dst_A2", "2") + Cache("rpf_A3", "3") + Cache("dst_A4", "4") + Cache("dst_A5", "5") + if CountEntries("dst_") != 4 { + t.Error("Error countiong entries: ", CountEntries("dst_")) + } +}