dispatch/storage/channel_index.go

169 lines
3.2 KiB
Go

package storage
import (
"sort"
"strings"
"sync"
"time"
)
type ChannelListItem struct {
Name string
UserCount int
Topic string
}
type ChannelListIndex interface {
Add(item *ChannelListItem)
Finish()
Search(q string) []*ChannelListItem
SearchN(q string, start, n int) []*ChannelListItem
len() int
}
type MapChannelListIndex struct {
channels chanList
m map[string][]*ChannelListItem
}
func NewMapChannelListIndex() *MapChannelListIndex {
return &MapChannelListIndex{
m: map[string][]*ChannelListItem{},
}
}
func (idx *MapChannelListIndex) Add(item *ChannelListItem) {
idx.channels = append(idx.channels, item)
}
func (idx *MapChannelListIndex) Finish() {
sort.Sort(idx.channels)
for _, ch := range idx.channels {
key := strings.TrimLeft(strings.ToLower(ch.Name), "#")
for i := 1; i <= len(key); i++ {
k := key[:i]
if _, ok := idx.m[k]; ok {
idx.m[k] = append(idx.m[k], ch)
} else {
idx.m[k] = chanList{ch}
}
}
}
}
func (idx *MapChannelListIndex) Search(q string) []*ChannelListItem {
if q == "" {
return idx.channels
}
return idx.m[q]
}
func (idx *MapChannelListIndex) SearchN(q string, start, n int) []*ChannelListItem {
if q == "" {
if start >= len(idx.channels) {
return nil
}
return idx.channels[start:min(start+n, len(idx.channels))]
}
res := idx.m[q]
if start >= len(res) {
return nil
}
return res[start:min(start+n, len(res))]
}
func (idx *MapChannelListIndex) len() int {
return len(idx.channels)
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
type chanList []*ChannelListItem
func (c chanList) Len() int {
return len(c)
}
func (c chanList) Less(i, j int) bool {
return c[i].UserCount > c[j].UserCount ||
(c[i].UserCount == c[j].UserCount &&
strings.ToLower(c[i].Name) < strings.ToLower(c[j].Name))
}
func (c chanList) Swap(i, j int) {
ch := c[i]
c[i] = c[j]
c[j] = ch
}
const ChannelListUpdateInterval = time.Hour * 24
const ChannelListUpdateTimeout = time.Minute * 5
type ChannelIndexManager struct {
indexes map[string]*managedChannelIndex
lock sync.Mutex
}
func NewChannelIndexManager() *ChannelIndexManager {
return &ChannelIndexManager{
indexes: map[string]*managedChannelIndex{},
}
}
type managedChannelIndex struct {
index ChannelListIndex
updatedAt time.Time
updating bool
}
func (m *ChannelIndexManager) Get(network string) (ChannelListIndex, bool) {
m.lock.Lock()
defer m.lock.Unlock()
idx, ok := m.indexes[network]
if !ok {
m.indexes[network] = &managedChannelIndex{
updating: true,
}
go m.timeoutUpdate(network)
return nil, true
}
if !idx.updating && time.Since(idx.updatedAt) > ChannelListUpdateInterval {
idx.updating = true
go m.timeoutUpdate(network)
return idx.index, true
}
return idx.index, false
}
func (m *ChannelIndexManager) Set(network string, index ChannelListIndex) {
if index.len() > 0 {
m.lock.Lock()
m.indexes[network] = &managedChannelIndex{
index: index,
updatedAt: time.Now(),
}
m.lock.Unlock()
}
}
func (m *ChannelIndexManager) timeoutUpdate(network string) {
time.Sleep(ChannelListUpdateTimeout)
m.lock.Lock()
if m.indexes[network].updating {
m.indexes[network].updating = false
}
m.lock.Unlock()
}