snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
73
internal/config/cache.go
Normal file
73
internal/config/cache.go
Normal file
@@ -0,0 +1,73 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user