74 lines
1.8 KiB
Go
74 lines
1.8 KiB
Go
package config
|
|
|
|
import (
|
|
"hash/fnv"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
type cacheItem struct {
|
|
channel string
|
|
value channelOptionsResult
|
|
expires int64
|
|
}
|
|
|
|
type cacheShard struct {
|
|
// index is unsigned on purpose: it only ever grows, and unsigned overflow
|
|
// wraps to zero cleanly. With a signed counter the wrap goes negative and
|
|
// the modulo below yields a negative buffer index, panicking the server
|
|
// after 2^31 Set calls land on the same shard.
|
|
index atomic.Uint32
|
|
size uint32
|
|
buffer []atomic.Value
|
|
}
|
|
|
|
type rollingCache struct {
|
|
shards []*cacheShard
|
|
}
|
|
|
|
func newRollingCache(shardSize int, shardCount int) *rollingCache {
|
|
rc := &rollingCache{
|
|
shards: make([]*cacheShard, shardCount),
|
|
}
|
|
for i := range rc.shards {
|
|
shard := &cacheShard{
|
|
size: uint32(shardSize),
|
|
buffer: make([]atomic.Value, shardSize),
|
|
}
|
|
for j := 0; j < shardSize; j++ {
|
|
shard.buffer[j].Store(&cacheItem{}) // Initialize with zero value.
|
|
}
|
|
rc.shards[i] = shard
|
|
}
|
|
return rc
|
|
}
|
|
|
|
func (c *rollingCache) shardForKey(key string) *cacheShard {
|
|
h := fnv.New64a()
|
|
_, _ = h.Write([]byte(key))
|
|
shardIndex := h.Sum64() % uint64(len(c.shards))
|
|
return c.shards[shardIndex]
|
|
}
|
|
|
|
func (c *rollingCache) Get(channel string) (channelOptionsResult, bool) {
|
|
shard := c.shardForKey(channel)
|
|
for i := 0; i < int(shard.size); i++ {
|
|
item := shard.buffer[i].Load().(*cacheItem)
|
|
if item.channel == channel && time.Now().UnixNano() < item.expires {
|
|
return item.value, true
|
|
}
|
|
}
|
|
return channelOptionsResult{}, false
|
|
}
|
|
|
|
func (c *rollingCache) Set(channel string, value channelOptionsResult, ttl time.Duration) {
|
|
shard := c.shardForKey(channel)
|
|
index := int(shard.index.Add(1) % shard.size)
|
|
item := &cacheItem{
|
|
channel: channel,
|
|
value: value,
|
|
expires: time.Now().Add(ttl).UnixNano(),
|
|
}
|
|
shard.buffer[index].Store(item)
|
|
}
|