11package ccache
22
33import (
4+ "strings"
45 "sync"
56 "time"
67)
@@ -26,22 +27,61 @@ func (b *bucket) set(key string, value interface{}, duration time.Duration) (*It
2627 expires := time .Now ().Add (duration ).UnixNano ()
2728 item := newItem (key , value , expires )
2829 b .Lock ()
29- defer b .Unlock ()
3030 existing := b .lookup [key ]
3131 b .lookup [key ] = item
32+ b .Unlock ()
3233 return item , existing
3334}
3435
3536func (b * bucket ) delete (key string ) * Item {
3637 b .Lock ()
37- defer b .Unlock ()
3838 item := b .lookup [key ]
3939 delete (b .lookup , key )
40+ b .Unlock ()
4041 return item
4142}
4243
44+ // This is an expensive operation, so we do what we can to optimize it and limit
45+ // the impact it has on concurrent operations. Specifically, we:
46+ // 1 - Do an initial iteration to collect matches. This allows us to do the
47+ // "expensive" prefix check (on all values) using only a read-lock
48+ // 2 - Do a second iteration, under write lock, for the matched results to do
49+ // the actual deletion
50+
51+ // Also, this is the only place where the Bucket is aware of cache detail: the
52+ // deletables channel. Passing it here lets us avoid iterating over matched items
53+ // again in the cache. Further, we pass item to deletables BEFORE actually removing
54+ // the item from the map. I'm pretty sure this is 100% fine, but it is unique.
55+ // (We do this so that the write to the channel is under the read lock and not the
56+ // write lock)
57+ func (b * bucket ) deletePrefix (prefix string , deletables chan * Item ) int {
58+ lookup := b .lookup
59+ items := make ([]* Item , 0 , len (lookup )/ 10 )
60+
61+ b .RLock ()
62+ for key , item := range lookup {
63+ if strings .HasPrefix (key , prefix ) {
64+ deletables <- item
65+ items = append (items , item )
66+ }
67+ }
68+ b .RUnlock ()
69+
70+ if len (items ) == 0 {
71+ // avoid the write lock if we can
72+ return 0
73+ }
74+
75+ b .Lock ()
76+ for _ , item := range items {
77+ delete (lookup , item .key )
78+ }
79+ b .Unlock ()
80+ return len (items )
81+ }
82+
4383func (b * bucket ) clear () {
4484 b .Lock ()
45- defer b .Unlock ()
4685 b .lookup = make (map [string ]* Item )
86+ b .Unlock ()
4787}
0 commit comments