diff options
Diffstat (limited to 'internal/cache/status.go')
-rw-r--r-- | internal/cache/status.go | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/internal/cache/status.go b/internal/cache/status.go new file mode 100644 index 000000000..895a5692c --- /dev/null +++ b/internal/cache/status.go @@ -0,0 +1,106 @@ +package cache + +import ( + "sync" + + "github.com/ReneKroon/ttlcache" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +) + +// statusCache is a wrapper around ttlcache.Cache to provide URL and URI lookups for gtsmodel.Status +type StatusCache struct { + cache *ttlcache.Cache // map of IDs -> cached statuses + urls map[string]string // map of status URLs -> IDs + uris map[string]string // map of status URIs -> IDs + mutex sync.Mutex +} + +// newStatusCache returns a new instantiated statusCache object +func NewStatusCache() *StatusCache { + c := StatusCache{ + cache: ttlcache.NewCache(), + urls: make(map[string]string, 100), + uris: make(map[string]string, 100), + mutex: sync.Mutex{}, + } + + // Set callback to purge lookup maps on expiration + c.cache.SetExpirationCallback(func(key string, value interface{}) { + status := value.(*gtsmodel.Status) + + c.mutex.Lock() + delete(c.urls, status.URL) + delete(c.uris, status.URI) + c.mutex.Unlock() + }) + + return &c +} + +// GetByID attempts to fetch a status from the cache by its ID +func (c *StatusCache) GetByID(id string) (*gtsmodel.Status, bool) { + c.mutex.Lock() + status, ok := c.getByID(id) + c.mutex.Unlock() + return status, ok +} + +// GetByURL attempts to fetch a status from the cache by its URL +func (c *StatusCache) GetByURL(url string) (*gtsmodel.Status, bool) { + // Perform safe ID lookup + c.mutex.Lock() + id, ok := c.urls[url] + + // Not found, unlock early + if !ok { + c.mutex.Unlock() + return nil, false + } + + // Attempt status lookup + status, ok := c.getByID(id) + c.mutex.Unlock() + return status, ok +} + +// GetByURI attempts to fetch a status from the cache by its URI +func (c *StatusCache) GetByURI(uri string) (*gtsmodel.Status, bool) { + // Perform safe ID lookup + c.mutex.Lock() + id, ok := c.uris[uri] + + // Not found, unlock early + if !ok { + c.mutex.Unlock() + return nil, false + } + + // Attempt status lookup + status, ok := c.getByID(id) + c.mutex.Unlock() + return status, ok +} + +// getByID performs an unsafe (no mutex locks) lookup of status by ID +func (c *StatusCache) getByID(id string) (*gtsmodel.Status, bool) { + v, ok := c.cache.Get(id) + if !ok { + return nil, false + } + return v.(*gtsmodel.Status), true +} + +// Put places a status in the cache +func (c *StatusCache) Put(status *gtsmodel.Status) { + if status == nil || status.ID == "" || + status.URL == "" || + status.URI == "" { + panic("invalid status") + } + + c.mutex.Lock() + c.cache.Set(status.ID, status) + c.urls[status.URL] = status.ID + c.uris[status.URI] = status.ID + c.mutex.Unlock() +} |