Commit 0265df63 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Expire idle groups from memory.

Also get rid of dead groups, they're not useful.
parent dc3256ca
...@@ -97,11 +97,10 @@ type Group struct { ...@@ -97,11 +97,10 @@ type Group struct {
mu sync.Mutex mu sync.Mutex
description *description description *description
// indicates that the group no longer exists, but it still has clients locked *string
dead bool clients map[string]Client
locked *string history []ChatHistoryEntry
clients map[string]Client timestamp time.Time
history []ChatHistoryEntry
} }
func (g *Group) Name() string { func (g *Group) Name() string {
...@@ -201,6 +200,7 @@ func Add(name string, desc *description) (*Group, error) { ...@@ -201,6 +200,7 @@ func Add(name string, desc *description) (*Group, error) {
name: name, name: name,
description: desc, description: desc,
clients: make(map[string]Client), clients: make(map[string]Client),
timestamp: time.Now(),
} }
groups.groups[name] = g groups.groups[name] = g
return g, nil return g, nil
...@@ -211,11 +211,10 @@ func Add(name string, desc *description) (*Group, error) { ...@@ -211,11 +211,10 @@ func Add(name string, desc *description) (*Group, error) {
if desc != nil { if desc != nil {
g.description = desc g.description = desc
g.dead = false
return g, nil return g, nil
} }
if g.dead || time.Since(g.description.loadTime) > 5*time.Second { if time.Since(g.description.loadTime) > 5*time.Second {
if descriptionChanged(name, g.description) { if descriptionChanged(name, g.description) {
desc, err := GetDescription(name) desc, err := GetDescription(name)
if err != nil { if err != nil {
...@@ -223,11 +222,9 @@ func Add(name string, desc *description) (*Group, error) { ...@@ -223,11 +222,9 @@ func Add(name string, desc *description) (*Group, error) {
log.Printf("Reading group %v: %v", log.Printf("Reading group %v: %v",
name, err) name, err)
} }
g.dead = true deleteUnlocked(g)
delGroupUnlocked(name)
return nil, err return nil, err
} }
g.dead = false
g.description = desc g.description = desc
} else { } else {
g.description.loadTime = time.Now() g.description.loadTime = time.Now()
...@@ -266,20 +263,57 @@ func Get(name string) *Group { ...@@ -266,20 +263,57 @@ func Get(name string) *Group {
return groups.groups[name] return groups.groups[name]
} }
func delGroupUnlocked(name string) bool { func Delete(name string) bool {
groups.mu.Lock()
defer groups.mu.Unlock()
g := groups.groups[name] g := groups.groups[name]
if g == nil { if g == nil {
return true return false;
} }
g.mu.Lock()
defer g.mu.Unlock()
return deleteUnlocked(g)
}
// Called with both groups.mu and g.mu taken.
func deleteUnlocked(g *Group) bool {
if len(g.clients) != 0 { if len(g.clients) != 0 {
return false return false
} }
delete(groups.groups, name) delete(groups.groups, g.name)
return true return true
} }
func Expire() {
names := GetNames()
now := time.Now()
for _, name := range names {
g := Get(name)
if g == nil {
continue
}
old := false
g.mu.Lock()
empty := len(g.clients) == 0
if empty && !g.description.Public {
age := now.Sub(g.timestamp)
old = age > maxHistoryAge(g.description)
}
// We cannot take groups.mu at this point without a deadlock.
g.mu.Unlock()
if empty && old {
// Delete will check if the group is still empty
Delete(name)
}
}
}
func AddClient(group string, c Client) (*Group, error) { func AddClient(group string, c Client) (*Group, error) {
g, err := Add(group, nil) g, err := Add(group, nil)
if err != nil { if err != nil {
...@@ -317,6 +351,7 @@ func AddClient(group string, c Client) (*Group, error) { ...@@ -317,6 +351,7 @@ func AddClient(group string, c Client) (*Group, error) {
} }
g.clients[c.Id()] = c g.clients[c.Id()] = c
g.timestamp = time.Now()
go func(clients []Client) { go func(clients []Client) {
u := c.Username() u := c.Username()
...@@ -344,6 +379,7 @@ func DelClient(c Client) { ...@@ -344,6 +379,7 @@ func DelClient(c Client) {
return return
} }
delete(g.clients, c.Id()) delete(g.clients, c.Id())
g.timestamp = time.Now()
go func(clients []Client) { go func(clients []Client) {
for _, cc := range clients { for _, cc := range clients {
...@@ -437,16 +473,10 @@ func (g *Group) AddToChatHistory(id, user string, time int64, kind, value string ...@@ -437,16 +473,10 @@ func (g *Group) AddToChatHistory(id, user string, time int64, kind, value string
) )
} }
func discardObsoleteHistory(h []ChatHistoryEntry, seconds int) []ChatHistoryEntry { func discardObsoleteHistory(h []ChatHistoryEntry, duration time.Duration) []ChatHistoryEntry {
now := time.Now()
d := 4 * time.Hour
if seconds > 0 {
d = time.Duration(seconds) * time.Second
}
i := 0 i := 0
for i < len(h) { for i < len(h) {
if now.Sub(FromJSTime(h[i].Time)) <= d { if time.Since(FromJSTime(h[i].Time)) <= duration {
break break
} }
i++ i++
...@@ -462,7 +492,9 @@ func (g *Group) GetChatHistory() []ChatHistoryEntry { ...@@ -462,7 +492,9 @@ func (g *Group) GetChatHistory() []ChatHistoryEntry {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
g.history = discardObsoleteHistory(g.history, g.description.MaxHistoryAge) g.history = discardObsoleteHistory(
g.history, maxHistoryAge(g.description),
)
h := make([]ChatHistoryEntry, len(g.history)) h := make([]ChatHistoryEntry, len(g.history))
copy(h, g.history) copy(h, g.history)
...@@ -504,6 +536,15 @@ type description struct { ...@@ -504,6 +536,15 @@ type description struct {
Other []ClientCredentials `json:"other,omitempty"` Other []ClientCredentials `json:"other,omitempty"`
} }
const DefaultMaxHistoryAge = 4 * time.Hour
func maxHistoryAge(desc *description) time.Duration {
if desc.MaxHistoryAge != 0 {
return time.Duration(desc.MaxHistoryAge) * time.Second
}
return DefaultMaxHistoryAge
}
func openDescriptionFile(name string) (*os.File, string, bool, error) { func openDescriptionFile(name string) (*os.File, string, bool, error) {
isParent := false isParent := false
for name != "" { for name != "" {
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"syscall" "syscall"
"time"
"sfu/diskwriter" "sfu/diskwriter"
"sfu/group" "sfu/group"
...@@ -94,10 +95,19 @@ func main() { ...@@ -94,10 +95,19 @@ func main() {
terminate := make(chan os.Signal, 1) terminate := make(chan os.Signal, 1)
signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM) signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM)
select {
case <-terminate: ticker := time.NewTicker(15 * time.Minute)
webserver.Shutdown() defer ticker.Stop()
case <-serverDone:
os.Exit(1) for {
select {
case <-ticker.C:
go group.Expire()
case <-terminate:
webserver.Shutdown()
return
case <-serverDone:
os.Exit(1)
}
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment