package monitor import ( "errors" "time" "github.com/golang-collections/go-datastructures/queue" ) type Monitor struct { newItems chan Item triggeredItems chan Item } func New(newItems, triggeredItems chan Item) (*Monitor, error) { return &Monitor{ newItems: newItems, triggeredItems: triggeredItems, }, nil } func (monitor *Monitor) Start() error { queue := queue.NewPriorityQueue(1) active := make(map[string]*Item) nextEvent := time.Date(2099, time.January, 1, 1, 1, 1, 1, time.UTC) for { select { case newItem := <-monitor.newItems: if newItem == (Item{}) { return nil } if oldItem, ok := active[newItem.url]; ok { *oldItem = newItem } else { queue.Put(&newItem) } var err error if nextEvent, err = nextEventTime(queue); err != nil { return err } case <-time.After(nextEvent.Sub(time.Now())): items, err := queue.Get(1) if err != nil { return err } if len(items) == 0 { continue } item, ok := items[0].(*Item) if !ok { return errors.New("queue contains illegal item") } select { case monitor.triggeredItems <- *item: item.increment() case <-time.After(time.Second * 10): } queue.Put(item) if nextEvent, err = nextEventTime(queue); err != nil { return err } } } return nil } func nextEventTime(q *queue.PriorityQueue) (time.Time, error) { qitem := q.Peek() item, ok := qitem.(*Item) if !ok { return time.Unix(0, 0), errors.New("non-item in priority queue") } return item.next, nil } func (monitor *Monitor) Stop() error { select { case monitor.newItems <- Item{}: return nil case <-time.After(time.Second * 10): } return errors.New("could not stop monitor") }