2017-11-23 05:37:08 +00:00
package common
import (
"sync"
"sync/atomic"
)
2018-06-17 07:28:18 +00:00
// TopicCache is an interface which spits out topics from a fast cache rather than the database, whether from memory or from an application like Redis. Topics may not be present in the cache but may be in the database
2017-11-23 05:37:08 +00:00
type TopicCache interface {
Get ( id int ) ( * Topic , error )
GetUnsafe ( id int ) ( * Topic , error )
Set ( item * Topic ) error
Add ( item * Topic ) error
AddUnsafe ( item * Topic ) error
Remove ( id int ) error
RemoveUnsafe ( id int ) error
Flush ( )
Length ( ) int
SetCapacity ( capacity int )
GetCapacity ( ) int
}
2018-06-17 07:28:18 +00:00
// MemoryTopicCache stores and pulls topics out of the current process' memory
2017-11-23 05:37:08 +00:00
type MemoryTopicCache struct {
items map [ int ] * Topic
length int64 // sync/atomic only lets us operate on int32s and int64s
capacity int
sync . RWMutex
}
// NewMemoryTopicCache gives you a new instance of MemoryTopicCache
func NewMemoryTopicCache ( capacity int ) * MemoryTopicCache {
return & MemoryTopicCache {
items : make ( map [ int ] * Topic ) ,
capacity : capacity ,
}
}
2018-06-17 07:28:18 +00:00
// Get fetches a topic by ID. Returns ErrNoRows if not present.
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) Get ( id int ) ( * Topic , error ) {
mts . RLock ( )
item , ok := mts . items [ id ]
mts . RUnlock ( )
if ok {
return item , nil
}
return item , ErrNoRows
}
2018-06-17 07:28:18 +00:00
// GetUnsafe fetches a topic by ID. Returns ErrNoRows if not present. THIS METHOD IS NOT THREAD-SAFE.
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) GetUnsafe ( id int ) ( * Topic , error ) {
item , ok := mts . items [ id ]
if ok {
return item , nil
}
return item , ErrNoRows
}
2018-06-17 07:28:18 +00:00
// Set overwrites the value of a topic in the cache, whether it's present or not. May return a capacity overflow error.
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) Set ( item * Topic ) error {
mts . Lock ( )
_ , ok := mts . items [ item . ID ]
if ok {
mts . items [ item . ID ] = item
} else if int ( mts . length ) >= mts . capacity {
mts . Unlock ( )
return ErrStoreCapacityOverflow
} else {
mts . items [ item . ID ] = item
atomic . AddInt64 ( & mts . length , 1 )
}
mts . Unlock ( )
return nil
}
2018-06-17 07:28:18 +00:00
// Add adds a topic to the cache, similar to Set, but it's only intended for new items. This method might be deprecated in the near future, use Set. May return a capacity overflow error.
// ? Is this redundant if we have Set? Are the efficiency wins worth this? Is this even used?
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) Add ( item * Topic ) error {
2018-06-17 07:28:18 +00:00
mts . Lock ( )
2017-11-23 05:37:08 +00:00
if int ( mts . length ) >= mts . capacity {
2018-06-17 07:28:18 +00:00
mts . Unlock ( )
2017-11-23 05:37:08 +00:00
return ErrStoreCapacityOverflow
}
mts . items [ item . ID ] = item
mts . Unlock ( )
atomic . AddInt64 ( & mts . length , 1 )
return nil
}
2018-06-17 07:28:18 +00:00
// AddUnsafe is the unsafe version of Add. May return a capacity overflow error. THIS METHOD IS NOT THREAD-SAFE.
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) AddUnsafe ( item * Topic ) error {
if int ( mts . length ) >= mts . capacity {
return ErrStoreCapacityOverflow
}
mts . items [ item . ID ] = item
2018-06-17 07:28:18 +00:00
mts . length = int64 ( len ( mts . items ) )
2017-11-23 05:37:08 +00:00
return nil
}
2018-06-17 07:28:18 +00:00
// Remove removes a topic from the cache by ID, if they exist. Returns ErrNoRows if no items exist.
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) Remove ( id int ) error {
mts . Lock ( )
2018-06-17 07:28:18 +00:00
_ , ok := mts . items [ id ]
if ! ok {
mts . Unlock ( )
return ErrNoRows
}
2017-11-23 05:37:08 +00:00
delete ( mts . items , id )
mts . Unlock ( )
atomic . AddInt64 ( & mts . length , - 1 )
return nil
}
2018-06-17 07:28:18 +00:00
// RemoveUnsafe is the unsafe version of Remove. THIS METHOD IS NOT THREAD-SAFE.
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) RemoveUnsafe ( id int ) error {
2018-06-17 07:28:18 +00:00
_ , ok := mts . items [ id ]
if ! ok {
return ErrNoRows
}
2017-11-23 05:37:08 +00:00
delete ( mts . items , id )
atomic . AddInt64 ( & mts . length , - 1 )
return nil
}
2018-06-17 07:28:18 +00:00
// Flush removes all the topics from the cache, useful for tests.
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) Flush ( ) {
mts . Lock ( )
mts . items = make ( map [ int ] * Topic )
mts . length = 0
mts . Unlock ( )
}
// ! Is this concurrent?
// Length returns the number of topics in the memory cache
func ( mts * MemoryTopicCache ) Length ( ) int {
return int ( mts . length )
}
2018-06-17 07:28:18 +00:00
// SetCapacity sets the maximum number of topics which this cache can hold
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) SetCapacity ( capacity int ) {
2018-06-17 07:28:18 +00:00
// Ints are moved in a single instruction, so this should be thread-safe
2017-11-23 05:37:08 +00:00
mts . capacity = capacity
}
2018-06-17 07:28:18 +00:00
// GetCapacity returns the maximum number of topics this cache can hold
2017-11-23 05:37:08 +00:00
func ( mts * MemoryTopicCache ) GetCapacity ( ) int {
return mts . capacity
}