Skip to content
Snippets Groups Projects
results.go 2.3 KiB
Newer Older
ale's avatar
ale committed
package probes

import (
	"fmt"
	"sync"
	"time"
)

// Result of the execution of a probe.
ale's avatar
ale committed
type Result struct {
	ID       string
	Spec     SpecCommon
	Ok       bool
ale's avatar
ale committed
	Start    time.Time
	Duration time.Duration
	Logs     string
}

// Nice displayable string.
func (r *Result) String() string {
	return fmt.Sprintf("%s(%s)", r.Spec.Name, r.ID)
}

// ResultStore keeps the most recent probe results in storage, and
// allows access to them (mostly for debugging purposes, as this part
// isn't required by the prober functionality). The results buffer has
// a fixed size, so older results will be dropped as more recent ones
// come in. Failures are kept in a separate buffer, with the hope of
// keeping errors around for longer for inspection purposes, even
// after they've recovered.
ale's avatar
ale committed
type ResultStore interface {
	Push(*Result)
	Each(func(*Result))
	EachErrs(func(*Result))
ale's avatar
ale committed
	Find(string) *Result
}

// Very simple overwriting circular buffer of results.
type resultBuffer struct {
	buf      []*Result
	sz, head int
}

func newResultBuffer(sz int) *resultBuffer {
	return &resultBuffer{
		buf: make([]*Result, sz),
		sz:  sz,
	}
}

func (b *resultBuffer) Push(result *Result) {
	b.buf[b.head] = result
	b.head++
	if b.head >= b.sz {
		b.head -= b.sz
	}
}

func (b *resultBuffer) Each(f func(*Result)) {
	for i := 0; i < b.sz; i++ {
		pos := b.head + i
		if pos >= b.sz {
			pos -= b.sz
		}
		if b.buf[pos] != nil {
			f(b.buf[pos])
		}
	}
}

// ResultStore that keeps recent results in memory.
ale's avatar
ale committed
type memResultStore struct {
	mx   sync.Mutex
	last *resultBuffer
	errs *resultBuffer
ale's avatar
ale committed
}

func NewResultStore(numOk, numErrs int) ResultStore {
	return &memResultStore{
		last: newResultBuffer(numOk),
		errs: newResultBuffer(numErrs),
ale's avatar
ale committed
	}
}

func (r *memResultStore) Push(result *Result) {
	r.mx.Lock()
	r.last.Push(result)
ale's avatar
ale committed
	if !result.Ok {
		r.errs.Push(result)
ale's avatar
ale committed
	}
	r.mx.Unlock()
}

func (r *memResultStore) Each(f func(*Result)) {
ale's avatar
ale committed
	r.mx.Lock()
	r.last.Each(f)
ale's avatar
ale committed
	r.mx.Unlock()
}

func (r *memResultStore) EachErrs(f func(*Result)) {
ale's avatar
ale committed
	r.mx.Lock()
	r.errs.Each(f)
ale's avatar
ale committed
	r.mx.Unlock()
}

func (r *memResultStore) Find(id string) (out *Result) {
	r.mx.Lock()
	defer r.mx.Unlock()
	r.last.Each(func(r *Result) {
		if r.ID == id {
			out = r
ale's avatar
ale committed
		}
	})
	if out != nil {
		return
	}
	r.errs.Each(func(r *Result) {
		if r.ID == id {
			out = r
		}
	})
ale's avatar
ale committed
	return
}