github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/common/poll_cache.go (about)

     1  package common
     2  
     3  import (
     4  	"sync"
     5  	"sync/atomic"
     6  )
     7  
     8  // PollCache is an interface which spits out polls from a fast cache rather than the database, whether from memory or from an application like Redis. Polls may not be present in the cache but may be in the database
     9  type PollCache interface {
    10  	Get(id int) (*Poll, error)
    11  	GetUnsafe(id int) (*Poll, error)
    12  	BulkGet(ids []int) (list []*Poll)
    13  	Set(item *Poll) error
    14  	Add(item *Poll) error
    15  	AddUnsafe(item *Poll) error
    16  	Remove(id int) error
    17  	RemoveUnsafe(id int) error
    18  	Flush()
    19  	Length() int
    20  	SetCapacity(capacity int)
    21  	GetCapacity() int
    22  }
    23  
    24  // MemoryPollCache stores and pulls polls out of the current process' memory
    25  type MemoryPollCache struct {
    26  	items    map[int]*Poll
    27  	length   int64
    28  	capacity int
    29  
    30  	sync.RWMutex
    31  }
    32  
    33  // NewMemoryPollCache gives you a new instance of MemoryPollCache
    34  func NewMemoryPollCache(capacity int) *MemoryPollCache {
    35  	return &MemoryPollCache{
    36  		items:    make(map[int]*Poll),
    37  		capacity: capacity,
    38  	}
    39  }
    40  
    41  // Get fetches a poll by ID. Returns ErrNoRows if not present.
    42  func (s *MemoryPollCache) Get(id int) (*Poll, error) {
    43  	s.RLock()
    44  	item, ok := s.items[id]
    45  	s.RUnlock()
    46  	if ok {
    47  		return item, nil
    48  	}
    49  	return item, ErrNoRows
    50  }
    51  
    52  // BulkGet fetches multiple polls by their IDs. Indices without polls will be set to nil, so make sure you check for those, we might want to change this behaviour to make it less confusing.
    53  func (s *MemoryPollCache) BulkGet(ids []int) (list []*Poll) {
    54  	list = make([]*Poll, len(ids))
    55  	s.RLock()
    56  	for i, id := range ids {
    57  		list[i] = s.items[id]
    58  	}
    59  	s.RUnlock()
    60  	return list
    61  }
    62  
    63  // GetUnsafe fetches a poll by ID. Returns ErrNoRows if not present. THIS METHOD IS NOT THREAD-SAFE.
    64  func (s *MemoryPollCache) GetUnsafe(id int) (*Poll, error) {
    65  	item, ok := s.items[id]
    66  	if ok {
    67  		return item, nil
    68  	}
    69  	return item, ErrNoRows
    70  }
    71  
    72  // Set overwrites the value of a poll in the cache, whether it's present or not. May return a capacity overflow error.
    73  func (s *MemoryPollCache) Set(item *Poll) error {
    74  	s.Lock()
    75  	user, ok := s.items[item.ID]
    76  	if ok {
    77  		s.Unlock()
    78  		*user = *item
    79  	} else if int(s.length) >= s.capacity {
    80  		s.Unlock()
    81  		return ErrStoreCapacityOverflow
    82  	} else {
    83  		s.items[item.ID] = item
    84  		s.Unlock()
    85  		atomic.AddInt64(&s.length, 1)
    86  	}
    87  	return nil
    88  }
    89  
    90  // Add adds a poll 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.
    91  // ? Is this redundant if we have Set? Are the efficiency wins worth this? Is this even used?
    92  func (s *MemoryPollCache) Add(item *Poll) error {
    93  	s.Lock()
    94  	if int(s.length) >= s.capacity {
    95  		s.Unlock()
    96  		return ErrStoreCapacityOverflow
    97  	}
    98  	s.items[item.ID] = item
    99  	s.length = int64(len(s.items))
   100  	s.Unlock()
   101  	return nil
   102  }
   103  
   104  // AddUnsafe is the unsafe version of Add. May return a capacity overflow error. THIS METHOD IS NOT THREAD-SAFE.
   105  func (s *MemoryPollCache) AddUnsafe(item *Poll) error {
   106  	if int(s.length) >= s.capacity {
   107  		return ErrStoreCapacityOverflow
   108  	}
   109  	s.items[item.ID] = item
   110  	s.length = int64(len(s.items))
   111  	return nil
   112  }
   113  
   114  // Remove removes a poll from the cache by ID, if they exist. Returns ErrNoRows if no items exist.
   115  func (s *MemoryPollCache) Remove(id int) error {
   116  	s.Lock()
   117  	_, ok := s.items[id]
   118  	if !ok {
   119  		s.Unlock()
   120  		return ErrNoRows
   121  	}
   122  	delete(s.items, id)
   123  	s.Unlock()
   124  	atomic.AddInt64(&s.length, -1)
   125  	return nil
   126  }
   127  
   128  // RemoveUnsafe is the unsafe version of Remove. THIS METHOD IS NOT THREAD-SAFE.
   129  func (s *MemoryPollCache) RemoveUnsafe(id int) error {
   130  	_, ok := s.items[id]
   131  	if !ok {
   132  		return ErrNoRows
   133  	}
   134  	delete(s.items, id)
   135  	atomic.AddInt64(&s.length, -1)
   136  	return nil
   137  }
   138  
   139  // Flush removes all the polls from the cache, useful for tests.
   140  func (s *MemoryPollCache) Flush() {
   141  	m := make(map[int]*Poll)
   142  	s.Lock()
   143  	s.items = m
   144  	s.length = 0
   145  	s.Unlock()
   146  }
   147  
   148  // ! Is this concurrent?
   149  // Length returns the number of polls in the memory cache
   150  func (s *MemoryPollCache) Length() int {
   151  	return int(s.length)
   152  }
   153  
   154  // SetCapacity sets the maximum number of polls which this cache can hold
   155  func (s *MemoryPollCache) SetCapacity(capacity int) {
   156  	// Ints are moved in a single instruction, so this should be thread-safe
   157  	s.capacity = capacity
   158  }
   159  
   160  // GetCapacity returns the maximum number of polls this cache can hold
   161  func (s *MemoryPollCache) GetCapacity() int {
   162  	return s.capacity
   163  }
   164  
   165  // NullPollCache is a poll cache to be used when you don't want a cache and just want queries to passthrough to the database
   166  type NullPollCache struct {
   167  }
   168  
   169  // NewNullPollCache gives you a new instance of NullPollCache
   170  func NewNullPollCache() *NullPollCache {
   171  	return &NullPollCache{}
   172  }
   173  
   174  // nolint
   175  func (s *NullPollCache) Get(id int) (*Poll, error) {
   176  	return nil, ErrNoRows
   177  }
   178  func (s *NullPollCache) BulkGet(ids []int) (list []*Poll) {
   179  	return make([]*Poll, len(ids))
   180  }
   181  func (s *NullPollCache) GetUnsafe(id int) (*Poll, error) {
   182  	return nil, ErrNoRows
   183  }
   184  func (s *NullPollCache) Set(_ *Poll) error {
   185  	return nil
   186  }
   187  func (s *NullPollCache) Add(_ *Poll) error {
   188  	return nil
   189  }
   190  func (s *NullPollCache) AddUnsafe(_ *Poll) error {
   191  	return nil
   192  }
   193  func (s *NullPollCache) Remove(id int) error {
   194  	return nil
   195  }
   196  func (s *NullPollCache) RemoveUnsafe(id int) error {
   197  	return nil
   198  }
   199  func (s *NullPollCache) Flush() {
   200  }
   201  func (s *NullPollCache) Length() int {
   202  	return 0
   203  }
   204  func (s *NullPollCache) SetCapacity(_ int) {
   205  }
   206  func (s *NullPollCache) GetCapacity() int {
   207  	return 0
   208  }