2020-11-29 03:22:39 +05:00
|
|
|
package memory
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-12-23 13:27:17 +05:00
|
|
|
"errors"
|
2020-11-29 03:22:39 +05:00
|
|
|
"log"
|
|
|
|
"sync"
|
2020-12-23 13:27:17 +05:00
|
|
|
|
|
|
|
"go.dev.pztrn.name/metricator/internal/models"
|
2020-11-29 03:22:39 +05:00
|
|
|
)
|
|
|
|
|
2020-12-23 13:27:17 +05:00
|
|
|
var ErrMetricNotFound = errors.New("metric not found")
|
|
|
|
|
2020-11-29 03:22:39 +05:00
|
|
|
// Storage is an in-memory storage.
|
|
|
|
type Storage struct {
|
|
|
|
ctx context.Context
|
|
|
|
doneChan chan struct{}
|
|
|
|
name string
|
|
|
|
|
2020-12-23 13:27:17 +05:00
|
|
|
data map[string]models.Metric
|
2020-11-29 03:22:39 +05:00
|
|
|
dataMutex sync.RWMutex
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewStorage creates new in-memory storage to use.
|
|
|
|
func NewStorage(ctx context.Context, name string) (*Storage, chan struct{}) {
|
|
|
|
s := &Storage{
|
|
|
|
ctx: ctx,
|
|
|
|
doneChan: make(chan struct{}),
|
|
|
|
name: name,
|
|
|
|
}
|
|
|
|
s.initialize()
|
|
|
|
|
|
|
|
return s, s.doneChan
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns data from storage by key.
|
2020-12-23 13:27:17 +05:00
|
|
|
func (s *Storage) Get(key string) (models.Metric, error) {
|
2020-11-29 03:22:39 +05:00
|
|
|
s.dataMutex.RLock()
|
|
|
|
defer s.dataMutex.RUnlock()
|
|
|
|
|
|
|
|
data, found := s.data[key]
|
|
|
|
if !found {
|
2020-12-23 13:45:33 +05:00
|
|
|
return models.NewMetric("", "", "", nil), ErrMetricNotFound
|
2020-11-29 03:22:39 +05:00
|
|
|
}
|
|
|
|
|
2020-12-23 13:27:17 +05:00
|
|
|
return data, nil
|
2020-11-29 03:22:39 +05:00
|
|
|
}
|
|
|
|
|
2020-12-23 13:37:52 +05:00
|
|
|
// GetAsSlice returns all data from storage as slice.
|
|
|
|
func (s *Storage) GetAsSlice() []models.Metric {
|
|
|
|
metrics := make([]models.Metric, 0, len(s.data))
|
|
|
|
|
|
|
|
for _, metric := range s.data {
|
|
|
|
metrics = append(metrics, metric)
|
|
|
|
}
|
|
|
|
|
|
|
|
return metrics
|
|
|
|
}
|
|
|
|
|
2020-11-29 03:22:39 +05:00
|
|
|
// GetDoneChan returns a channel which should be used to block execution
|
|
|
|
// until storage's routines are completed.
|
|
|
|
func (s *Storage) GetDoneChan() chan struct{} {
|
|
|
|
return s.doneChan
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initializes internal things.
|
|
|
|
func (s *Storage) initialize() {
|
2020-12-23 13:27:17 +05:00
|
|
|
s.data = make(map[string]models.Metric)
|
2020-11-29 03:22:39 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Put puts passed data into storage.
|
2020-12-23 13:27:17 +05:00
|
|
|
func (s *Storage) Put(data map[string]models.Metric) {
|
2020-11-29 03:22:39 +05:00
|
|
|
s.dataMutex.Lock()
|
|
|
|
defer s.dataMutex.Unlock()
|
|
|
|
|
|
|
|
for k, v := range data {
|
2020-12-23 15:40:48 +05:00
|
|
|
// We should not put valueless metrics.
|
|
|
|
if v.Value != "" {
|
|
|
|
s.data[k] = v
|
|
|
|
}
|
2020-11-29 03:22:39 +05:00
|
|
|
}
|
2020-11-29 05:50:35 +05:00
|
|
|
|
|
|
|
log.Println("Put", len(data), "items in", s.name)
|
2020-11-29 03:22:39 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Start starts asynchronous things if needed.
|
|
|
|
func (s *Storage) Start() {
|
|
|
|
// The Context Listening Goroutine.
|
|
|
|
go func() {
|
|
|
|
<-s.ctx.Done()
|
|
|
|
|
|
|
|
log.Println("In-memory storage", s.name, "done")
|
|
|
|
|
|
|
|
s.doneChan <- struct{}{}
|
|
|
|
}()
|
|
|
|
}
|