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 }