Skip to content

Instantly share code, notes, and snippets.

@gallir
Created August 2, 2016 22:21
Show Gist options
  • Save gallir/5231bd5e836883625fb7245853b3f3fc to your computer and use it in GitHub Desktop.
Save gallir/5231bd5e836883625fb7245853b3f3fc to your computer and use it in GitHub Desktop.
A time serie cache
package main
import (
"sync"
"time"
)
type SerieElement struct {
ts time.Time
data *BookingData
}
type Serie struct {
sync.RWMutex
ids map[string]bool
elements []*SerieElement
maxCache time.Duration
lastTs time.Time
removeCh chan bool
}
func NewSerie(maxCache time.Duration) (s *Serie) {
s = &Serie{
elements: make([]*SerieElement, 0),
maxCache: maxCache,
ids: make(map[string]bool),
removeCh: make(chan bool),
}
go s.removeOldActor()
return
}
func (s *Serie) Append(b *BookingData, ts time.Time) {
s.Lock()
defer s.Unlock()
defer s.removeOld()
_, ok := s.ids[b.id]
if ok {
// Element already inserted
return
}
e := &SerieElement{
ts: ts,
data: b,
}
s.ids[e.data.id] = true
s.elements = append(s.elements, e)
if ts.After(s.lastTs) {
s.lastTs = ts
}
}
func (s *Serie) Get(fromTime time.Time) (elements []*SerieElement) {
s.RLock()
defer s.RUnlock()
defer s.removeOld()
first := len(s.elements)
for i := first - 1; i >= 0; i-- {
if s.elements[i].ts.After(fromTime) {
first = i
} else {
break
}
}
if first < len(s.elements) {
elements = s.elements[first:]
}
return
}
func (s *Serie) removeOld() {
// Asynchronous, the Actor will ignore if there are frequent calls
select {
case s.removeCh <- true:
default:
}
}
func (s *Serie) removeOldActor() {
for {
<-s.removeCh
s.Lock()
now := time.Now()
olds := 0
for _, e := range s.elements {
if e.ts.Before(now.Add(-s.maxCache)) {
delete(s.ids, e.data.id)
olds++
} else {
break
}
}
if olds > 0 {
s.elements = s.elements[olds:]
}
s.Unlock()
time.Sleep(5 * time.Second) // To ignore frequent and innecesary calls
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment