this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

identity: switch to LRU for cache, and allow purging

+30 -40
+27 -38
atproto/identity/cache_directory.go
··· 3 3 import ( 4 4 "context" 5 5 "fmt" 6 - "sync" 7 6 "time" 8 7 9 8 "github.com/bluesky-social/indigo/atproto/syntax" 9 + 10 + "github.com/hashicorp/golang-lru/v2/expirable" 10 11 ) 11 12 12 13 type CacheDirectory struct { 13 - HitTTL time.Duration 14 + Inner Directory 14 15 ErrTTL time.Duration 15 - Inner Directory 16 - mutex sync.RWMutex 17 - handleCache map[syntax.Handle]HandleEntry 18 - identityCache map[syntax.DID]IdentityEntry 16 + handleCache *expirable.LRU[syntax.Handle, HandleEntry] 17 + identityCache *expirable.LRU[syntax.DID, IdentityEntry] 19 18 } 20 19 21 20 type HandleEntry struct { ··· 32 31 33 32 var _ Directory = (*CacheDirectory)(nil) 34 33 35 - func NewCacheDirectory(inner Directory) CacheDirectory { 36 - // NOTE: these are kind of arbitrary default values... 37 - hitTTL := time.Hour * 1 38 - errTTL := time.Minute * 2 34 + // Capacity of zero means unlimited size. Similarly, ttl of zero means unlimited duration. 35 + func NewCacheDirectory(inner Directory, capacity int, hitTTL, errTTL time.Duration) CacheDirectory { 39 36 return CacheDirectory{ 40 - HitTTL: hitTTL, 41 37 ErrTTL: errTTL, 42 38 Inner: inner, 43 - handleCache: make(map[syntax.Handle]HandleEntry, 10), 44 - identityCache: make(map[syntax.DID]IdentityEntry, 10), 39 + handleCache: expirable.NewLRU[syntax.Handle, HandleEntry](capacity, nil, hitTTL), 40 + identityCache: expirable.NewLRU[syntax.DID, IdentityEntry](capacity, nil, hitTTL), 45 41 } 46 42 } 47 43 48 44 func (d *CacheDirectory) IsHandleStale(e *HandleEntry) bool { 49 - if nil == e.Err && time.Since(e.Updated) > d.HitTTL { 50 - return true 51 - } 52 45 if e.Err != nil && time.Since(e.Updated) > d.ErrTTL { 53 46 return true 54 47 } ··· 56 49 } 57 50 58 51 func (d *CacheDirectory) IsIdentityStale(e *IdentityEntry) bool { 59 - if nil == e.Err && time.Since(e.Updated) > d.HitTTL { 60 - return true 61 - } 62 52 if e.Err != nil && time.Since(e.Updated) > d.ErrTTL { 63 53 return true 64 54 } ··· 73 63 DID: "", 74 64 Err: err, 75 65 } 76 - d.mutex.Lock() 77 - d.handleCache[h] = he 78 - d.mutex.Unlock() 66 + d.handleCache.Add(h, he) 79 67 return &he, nil 80 68 } 81 69 ··· 90 78 Err: nil, 91 79 } 92 80 93 - d.mutex.Lock() 94 - d.identityCache[ident.DID] = entry 95 - d.handleCache[ident.Handle] = he 96 - d.mutex.Unlock() 81 + d.identityCache.Add(ident.DID, entry) 82 + d.handleCache.Add(ident.Handle, he) 97 83 return &he, nil 98 84 } 99 85 100 86 func (d *CacheDirectory) ResolveHandle(ctx context.Context, h syntax.Handle) (syntax.DID, error) { 101 87 var err error 102 88 var entry *HandleEntry 103 - d.mutex.RLock() 104 - maybeEntry, ok := d.handleCache[h] 105 - d.mutex.RUnlock() 89 + maybeEntry, ok := d.handleCache.Get(h) 106 90 107 91 if !ok { 108 92 entry, err = d.updateHandle(ctx, h) ··· 139 123 } 140 124 } 141 125 142 - d.mutex.Lock() 143 - d.identityCache[did] = entry 126 + d.identityCache.Add(did, entry) 144 127 if he != nil { 145 - d.handleCache[ident.Handle] = *he 128 + d.handleCache.Add(ident.Handle, *he) 146 129 } 147 - d.mutex.Unlock() 148 130 return &entry, nil 149 131 } 150 132 151 133 func (d *CacheDirectory) LookupDID(ctx context.Context, did syntax.DID) (*Identity, error) { 152 134 var err error 153 135 var entry *IdentityEntry 154 - d.mutex.RLock() 155 - maybeEntry, ok := d.identityCache[did] 156 - d.mutex.RUnlock() 136 + maybeEntry, ok := d.identityCache.Get(did) 157 137 158 138 if !ok { 159 139 entry, err = d.updateDID(ctx, did) ··· 204 184 return nil, fmt.Errorf("at-identifier neither a Handle nor a DID") 205 185 } 206 186 207 - // XXX: 208 187 func (d *CacheDirectory) Purge(ctx context.Context, a syntax.AtIdentifier) error { 209 - return nil 188 + handle, err := a.AsHandle() 189 + if nil == err { // if not an error, is a handle 190 + d.handleCache.Remove(handle) 191 + return nil 192 + } 193 + did, err := a.AsDID() 194 + if nil == err { // if not an error, is a DID 195 + d.identityCache.Remove(did) 196 + return nil 197 + } 198 + return fmt.Errorf("at-identifier neither a Handle nor a DID") 210 199 }
+1 -1
atproto/identity/identity.go
··· 61 61 }, 62 62 }, 63 63 } 64 - cached := NewCacheDirectory(&base) 64 + cached := NewCacheDirectory(&base, 10000, time.Hour*24, time.Minute*2) 65 65 return &cached 66 66 } 67 67
+2 -1
atproto/identity/live_test.go
··· 3 3 import ( 4 4 "context" 5 5 "testing" 6 + "time" 6 7 7 8 "github.com/bluesky-social/indigo/atproto/syntax" 8 9 ··· 58 59 func TestCacheDirectory(t *testing.T) { 59 60 // XXX: t.Skip("skipping live network test") 60 61 inner := BaseDirectory{} 61 - d := NewCacheDirectory(&inner) 62 + d := NewCacheDirectory(&inner, 1000, time.Hour*1, time.Hour*1) 62 63 for i := 0; i < 3; i = i + 1 { 63 64 testDirectoryLive(t, &d) 64 65 }