tile38/pkg/controller/expire.go

155 lines
3.2 KiB
Go
Raw Normal View History

2016-07-15 22:22:48 +03:00
package controller
import (
"log"
"math/rand"
2016-07-15 22:22:48 +03:00
"time"
"github.com/tidwall/btree"
2016-07-15 22:22:48 +03:00
"github.com/tidwall/resp"
"github.com/tidwall/tile38/pkg/server"
2016-07-15 22:22:48 +03:00
)
type exitem struct {
key, id string
at time.Time
}
func (a *exitem) Less(v btree.Item, ctx interface{}) bool {
b := v.(*exitem)
if a.at.Before(b.at) {
return true
}
if a.at.After(b.at) {
return false
}
if a.key < b.key {
return true
}
if a.key > b.key {
return false
}
return a.id < b.id
}
2017-09-30 21:06:10 +03:00
// fillExpiresList occurs once at startup
func (c *Controller) fillExpiresList() {
c.exlistmu.Lock()
c.exlist = c.exlist[:0]
for key, m := range c.expires {
for id, at := range m {
c.exlist = append(c.exlist, exitem{key, id, at})
}
}
c.exlistmu.Unlock()
2016-07-15 22:22:48 +03:00
}
// clearIDExpires clears a single item from the expires list.
func (c *Controller) clearIDExpires(key, id string) (cleared bool) {
if len(c.expires) == 0 {
return false
2016-07-15 22:22:48 +03:00
}
m, ok := c.expires[key]
if !ok {
return false
}
_, ok = m[id]
if !ok {
return false
2016-07-15 22:22:48 +03:00
}
delete(m, id)
return true
2016-07-15 22:22:48 +03:00
}
// clearKeyExpires clears all items that are marked as expires from a single key.
2016-07-15 22:22:48 +03:00
func (c *Controller) clearKeyExpires(key string) {
delete(c.expires, key)
}
// expireAt marks an item as expires at a specific time.
2016-07-15 22:22:48 +03:00
func (c *Controller) expireAt(key, id string, at time.Time) {
m := c.expires[key]
if m == nil {
m = make(map[string]time.Time)
c.expires[key] = m
}
m[id] = at
2017-09-30 21:06:10 +03:00
c.exlistmu.Lock()
c.exlist = append(c.exlist, exitem{key, id, at})
c.exlistmu.Unlock()
2016-07-15 22:22:48 +03:00
}
// getExpires returns the when an item expires.
2016-07-15 22:22:48 +03:00
func (c *Controller) getExpires(key, id string) (at time.Time, ok bool) {
if len(c.expires) == 0 {
return at, false
}
m, ok := c.expires[key]
if !ok {
return at, false
2016-07-15 22:22:48 +03:00
}
at, ok = m[id]
return at, ok
}
// hasExpired returns true if an item has expired.
func (c *Controller) hasExpired(key, id string) bool {
at, ok := c.getExpires(key, id)
if !ok {
return false
}
return time.Now().After(at)
}
// backgroundExpiring watches for when items that have expired must be purged
// from the database. It's executes 10 times a seconds.
2016-07-15 22:22:48 +03:00
func (c *Controller) backgroundExpiring() {
rand.Seed(time.Now().UnixNano())
2017-09-30 21:06:10 +03:00
var purgelist []exitem
2016-07-15 22:22:48 +03:00
for {
2017-09-30 17:29:03 +03:00
if c.stopBackgroundExpiring.on() {
return
}
now := time.Now()
2017-09-30 21:06:10 +03:00
purgelist = purgelist[:0]
c.exlistmu.Lock()
for i := 0; i < 20 && len(c.exlist) > 0; i++ {
ix := rand.Int() % len(c.exlist)
if now.After(c.exlist[ix].at) {
2017-09-30 21:06:10 +03:00
// purge from exlist
purgelist = append(purgelist, c.exlist[ix])
c.exlist[ix] = c.exlist[len(c.exlist)-1]
c.exlist = c.exlist[:len(c.exlist)-1]
}
}
c.exlistmu.Unlock()
if len(purgelist) > 0 {
c.mu.Lock()
for _, item := range purgelist {
if c.hasExpired(item.key, item.id) {
// purge from database
msg := &server.Message{}
2017-09-30 21:06:10 +03:00
msg.Values = resp.MultiBulkValue("del", item.key, item.id).Array()
msg.Command = "del"
_, d, err := c.cmdDel(msg)
if err != nil {
c.mu.Unlock()
log.Fatal(err)
continue
2016-07-15 22:22:48 +03:00
}
if err := c.writeAOF(resp.ArrayValue(msg.Values), &d); err != nil {
c.mu.Unlock()
log.Fatal(err)
continue
}
2016-07-15 22:22:48 +03:00
}
}
2017-09-30 21:06:10 +03:00
c.mu.Unlock()
if len(purgelist) > 5 {
continue
}
}
time.Sleep(time.Second / 10)
2016-07-15 22:22:48 +03:00
}
}