Feat: room auto close to free mem

pull/24/head
zijiren233 3 years ago
parent b5f7a440a6
commit 87d083bc47

@ -2,11 +2,17 @@ package bootstrap
import ( import (
"context" "context"
"time"
"github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/internal/op" "github.com/synctv-org/synctv/internal/op"
) )
func InitOp(ctx context.Context) error { func InitOp(ctx context.Context) error {
op.Init(4096) d, err := time.ParseDuration(conf.Conf.Room.TTL)
if err != nil {
return err
}
op.Init(4096, d)
return nil return nil
} }

@ -1,11 +1,13 @@
package conf package conf
type RoomConfig struct { type RoomConfig struct {
MustPassword bool `yaml:"must_password" hc:"must input password to create room" env:"ROOM_MUST_PASSWORD"` MustPassword bool `yaml:"must_password" hc:"must input password to create room" env:"ROOM_MUST_PASSWORD"`
TTL string `yaml:"ttl" hc:"set how long the room will be inactive before the memory will be reclaimed"`
} }
func DefaultRoomConfig() RoomConfig { func DefaultRoomConfig() RoomConfig {
return RoomConfig{ return RoomConfig{
MustPassword: false, MustPassword: false,
TTL: "48h",
} }
} }

@ -1,10 +1,17 @@
package op package op
import ( import (
"time"
"github.com/bluele/gcache" "github.com/bluele/gcache"
synccache "github.com/synctv-org/synctv/utils/syncCache"
) )
func Init(size int) error { func Init(size int, ttl time.Duration) error {
roomTTL = ttl
roomCache = synccache.NewSyncCache[uint, *Room](time.Minute*5, synccache.WithDeletedCallback[uint, *Room](func(v *Room) {
v.close()
}))
userCache = gcache.New(size). userCache = gcache.New(size).
LRU(). LRU().
Build() Build()

@ -3,13 +3,15 @@ package op
import ( import (
"errors" "errors"
"hash/crc32" "hash/crc32"
"time"
"github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/model"
"github.com/zijiren233/gencontainer/rwmap" synccache "github.com/synctv-org/synctv/utils/syncCache"
) )
var roomCache rwmap.RWMap[uint, *Room] var roomTTL = time.Hour * 24 * 2
var roomCache *synccache.SyncCache[uint, *Room]
func CreateRoom(name, password string, conf ...db.CreateRoomConfig) (*Room, error) { func CreateRoom(name, password string, conf ...db.CreateRoomConfig) (*Room, error) {
r, err := db.CreateRoom(name, password, conf...) r, err := db.CreateRoom(name, password, conf...)
@ -28,22 +30,26 @@ func InitRoom(room *model.Room) (*Room, error) {
roomID: room.ID, roomID: room.ID,
}, },
} }
r, loaded := roomCache.LoadOrStore(room.ID, r) i, loaded := roomCache.LoadOrStore(room.ID, r, roomTTL)
if loaded { if loaded {
return r, errors.New("room already init") return r, errors.New("room already init")
} }
return r, nil return i.Value(), nil
} }
func LoadOrInitRoom(room *model.Room) (*Room, bool) { func LoadOrInitRoom(room *model.Room) (*Room, bool) {
return roomCache.LoadOrStore(room.ID, &Room{ i, loaded := roomCache.LoadOrStore(room.ID, &Room{
Room: *room, Room: *room,
version: crc32.ChecksumIEEE(room.HashedPassword), version: crc32.ChecksumIEEE(room.HashedPassword),
current: newCurrent(), current: newCurrent(),
movies: movies{ movies: movies{
roomID: room.ID, roomID: room.ID,
}, },
}) }, roomTTL)
if loaded {
i.SetExpiration(time.Now().Add(roomTTL))
}
return i.Value(), loaded
} }
func DeleteRoom(roomID uint) error { func DeleteRoom(roomID uint) error {
@ -57,36 +63,38 @@ func DeleteRoom(roomID uint) error {
func CloseRoom(roomID uint) error { func CloseRoom(roomID uint) error {
r, loaded := roomCache.LoadAndDelete(roomID) r, loaded := roomCache.LoadAndDelete(roomID)
if loaded { if loaded {
r.close() r.Value().close()
} }
return nil return errors.New("room not found in cache")
} }
func LoadRoomByID(id uint) (*Room, error) { func LoadRoomByID(id uint) (*Room, error) {
r2, ok := roomCache.Load(id) r2, loaded := roomCache.Load(id)
if ok { if loaded {
return r2, nil r2.SetExpiration(time.Now().Add(roomTTL))
return r2.Value(), nil
} }
return nil, errors.New("room not found") return nil, errors.New("room not found")
} }
func LoadOrInitRoomByID(id uint) (*Room, error) { func LoadOrInitRoomByID(id uint) (*Room, error) {
r, ok := roomCache.Load(id) i, loaded := roomCache.Load(id)
if ok { if loaded {
return r, nil i.SetExpiration(time.Now().Add(roomTTL))
return i.Value(), nil
} }
room, err := db.GetRoomByID(id) room, err := db.GetRoomByID(id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r, _ = LoadOrInitRoom(room) r, _ := LoadOrInitRoom(room)
return r, nil return r, nil
} }
func ClientNum(roomID uint) int64 { func ClientNum(roomID uint) int64 {
r, ok := roomCache.Load(roomID) r, loaded := roomCache.Load(roomID)
if ok { if loaded {
return r.ClientNum() return r.Value().ClientNum()
} }
return 0 return 0
} }
@ -119,20 +127,12 @@ func SetRoomPassword(roomID uint, password string) error {
return r.SetPassword(password) return r.SetPassword(password)
} }
func GetAllRoomsInCache() []*Room {
rooms := make([]*Room, roomCache.Len())
roomCache.Range(func(key uint, value *Room) bool {
rooms = append(rooms, value)
return true
})
return rooms
}
func GetAllRoomsInCacheWithNoNeedPassword() []*Room { func GetAllRoomsInCacheWithNoNeedPassword() []*Room {
rooms := make([]*Room, roomCache.Len()) rooms := make([]*Room, 0)
roomCache.Range(func(key uint, value *Room) bool { roomCache.Range(func(key uint, value *synccache.Entry[*Room]) bool {
if !value.NeedPassword() { v := value.Value()
rooms = append(rooms, value) if !v.NeedPassword() {
rooms = append(rooms, v)
} }
return true return true
}) })
@ -140,10 +140,11 @@ func GetAllRoomsInCacheWithNoNeedPassword() []*Room {
} }
func GetAllRoomsInCacheWithoutHidden() []*Room { func GetAllRoomsInCacheWithoutHidden() []*Room {
rooms := make([]*Room, 0, roomCache.Len()) rooms := make([]*Room, 0)
roomCache.Range(func(key uint, value *Room) bool { roomCache.Range(func(key uint, value *synccache.Entry[*Room]) bool {
if !value.Settings.Hidden { v := value.Value()
rooms = append(rooms, value) if !v.Settings.Hidden {
rooms = append(rooms, v)
} }
return true return true
}) })

@ -8,6 +8,7 @@ import (
"github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/provider" "github.com/synctv-org/synctv/internal/provider"
synccache "github.com/synctv-org/synctv/utils/syncCache"
) )
var userCache gcache.Cache var userCache gcache.Cache
@ -78,10 +79,11 @@ func DeleteUserByID(userID uint) error {
} }
userCache.Remove(userID) userCache.Remove(userID)
roomCache.Range(func(key uint, value *Room) bool { roomCache.Range(func(key uint, value *synccache.Entry[*Room]) bool {
if value.CreatorID == userID { v := value.Value()
if v.CreatorID == userID {
roomCache.Delete(key) roomCache.Delete(key)
value.close() v.close()
} }
return true return true
}) })

@ -7,7 +7,7 @@ import (
) )
type SyncCache[K comparable, V any] struct { type SyncCache[K comparable, V any] struct {
cache rwmap.RWMap[K, *entry[V]] cache rwmap.RWMap[K, *Entry[V]]
deletedCallback func(v V) deletedCallback func(v V)
ticker *time.Ticker ticker *time.Ticker
} }
@ -20,10 +20,13 @@ func WithDeletedCallback[K comparable, V any](callback func(v V)) SyncCacheConfi
} }
} }
func NewSyncCache[K comparable, V any](trimTime time.Duration) *SyncCache[K, V] { func NewSyncCache[K comparable, V any](trimTime time.Duration, conf ...SyncCacheConfig[K, V]) *SyncCache[K, V] {
sc := &SyncCache[K, V]{ sc := &SyncCache[K, V]{
ticker: time.NewTicker(trimTime), ticker: time.NewTicker(trimTime),
} }
for _, c := range conf {
c(sc)
}
go func() { go func() {
for range sc.ticker.C { for range sc.ticker.C {
sc.trim() sc.trim()
@ -38,7 +41,7 @@ func (sc *SyncCache[K, V]) Releases() {
} }
func (sc *SyncCache[K, V]) trim() { func (sc *SyncCache[K, V]) trim() {
sc.cache.Range(func(key K, value *entry[V]) bool { sc.cache.Range(func(key K, value *Entry[V]) bool {
if value.IsExpired() { if value.IsExpired() {
e, loaded := sc.cache.LoadAndDelete(key) e, loaded := sc.cache.LoadAndDelete(key)
if loaded && sc.deletedCallback != nil { if loaded && sc.deletedCallback != nil {
@ -53,21 +56,22 @@ func (sc *SyncCache[K, V]) Store(key K, value V, expire time.Duration) {
sc.LoadOrStore(key, value, expire) sc.LoadOrStore(key, value, expire)
} }
func (sc *SyncCache[K, V]) Load(key K) (value V, loaded bool) { func (sc *SyncCache[K, V]) Load(key K) (value *Entry[V], loaded bool) {
e, ok := sc.cache.Load(key) e, ok := sc.cache.Load(key)
if ok && !e.IsExpired() { if ok && !e.IsExpired() {
return e.value, ok return e, ok
} }
return return
} }
func (sc *SyncCache[K, V]) LoadOrStore(key K, value V, expire time.Duration) (actual V, loaded bool) { func (sc *SyncCache[K, V]) LoadOrStore(key K, value V, expire time.Duration) (actual *Entry[V], loaded bool) {
e, loaded := sc.cache.LoadOrStore(key, NewEntry[V](value, expire)) e, loaded := sc.cache.LoadOrStore(key, NewEntry[V](value, expire))
if e.IsExpired() { if e.IsExpired() {
sc.cache.Store(key, NewEntry[V](value, expire)) e = NewEntry[V](value, expire)
return value, false sc.cache.Store(key, e)
return e, false
} }
return e.value, loaded return e, loaded
} }
func (sc *SyncCache[K, V]) AddExpiration(key K, d time.Duration) { func (sc *SyncCache[K, V]) AddExpiration(key K, d time.Duration) {
@ -88,10 +92,10 @@ func (sc *SyncCache[K, V]) Delete(key K) {
sc.LoadAndDelete(key) sc.LoadAndDelete(key)
} }
func (sc *SyncCache[K, V]) LoadAndDelete(key K) (value V, loaded bool) { func (sc *SyncCache[K, V]) LoadAndDelete(key K) (value *Entry[V], loaded bool) {
e, loaded := sc.cache.LoadAndDelete(key) e, loaded := sc.cache.LoadAndDelete(key)
if loaded && !e.IsExpired() { if loaded && !e.IsExpired() {
return e.value, loaded return e, loaded
} }
return return
} }
@ -99,3 +103,12 @@ func (sc *SyncCache[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
func (sc *SyncCache[K, V]) Clear() { func (sc *SyncCache[K, V]) Clear() {
sc.cache.Clear() sc.cache.Clear()
} }
func (sc *SyncCache[K, V]) Range(f func(key K, value *Entry[V]) bool) {
sc.cache.Range(func(key K, value *Entry[V]) bool {
if !value.IsExpired() {
return f(key, value)
}
return true
})
}

@ -5,26 +5,30 @@ import (
"time" "time"
) )
type entry[V any] struct { type Entry[V any] struct {
expiration int64 expiration int64
value V value V
} }
func NewEntry[V any](value V, expire time.Duration) *entry[V] { func NewEntry[V any](value V, expire time.Duration) *Entry[V] {
return &entry[V]{ return &Entry[V]{
expiration: time.Now().Add(expire).UnixMilli(), expiration: time.Now().Add(expire).UnixMilli(),
value: value, value: value,
} }
} }
func (e *entry[V]) IsExpired() bool { func (e *Entry[V]) Value() V {
return e.value
}
func (e *Entry[V]) IsExpired() bool {
return time.Now().After(time.UnixMilli(atomic.LoadInt64(&e.expiration))) return time.Now().After(time.UnixMilli(atomic.LoadInt64(&e.expiration)))
} }
func (e *entry[V]) AddExpiration(d time.Duration) { func (e *Entry[V]) AddExpiration(d time.Duration) {
atomic.AddInt64(&e.expiration, int64(d)) atomic.AddInt64(&e.expiration, int64(d))
} }
func (e *entry[V]) SetExpiration(t time.Time) { func (e *Entry[V]) SetExpiration(t time.Time) {
atomic.StoreInt64(&e.expiration, t.UnixMilli()) atomic.StoreInt64(&e.expiration, t.UnixMilli())
} }

Loading…
Cancel
Save