move recorder to interface
This commit is contained in:
75
pkg/weather/recorder/recorders/memory/get.go
Normal file
75
pkg/weather/recorder/recorders/memory/get.go
Normal file
@ -0,0 +1,75 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"slices"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
|
||||
pb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
|
||||
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
|
||||
)
|
||||
|
||||
func (r *MemoryRecorder) Get(ctx context.Context, req *pb.GetWeatherRequest) (
|
||||
[]*weather.WeatherUpdate, error,
|
||||
) {
|
||||
ctx, span := r.tracer.Start(ctx, "memoryRecorder.Get")
|
||||
defer span.End()
|
||||
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
|
||||
span.AddEvent("acquired lock on recorder cache")
|
||||
|
||||
updates := r.updates
|
||||
|
||||
if r.count() == 0 {
|
||||
err := errors.New("no recorded updates to get")
|
||||
span.RecordError(err)
|
||||
return nil, err
|
||||
} else if r.count() <= int(*req.Limit) {
|
||||
span.RecordError(errors.New("requested more updates than recorded"))
|
||||
}
|
||||
|
||||
// Filter by Station Name if requested
|
||||
if req.Opts.StationName != nil && *req.Opts.StationName != "" {
|
||||
updates = slices.DeleteFunc(updates, func(u *weather.WeatherUpdate) bool {
|
||||
return u.StationConfig == nil || u.StationConfig.Name != *req.Opts.StationName
|
||||
})
|
||||
}
|
||||
|
||||
// Filter by Station Type if requested
|
||||
if req.Opts.StationType != nil && *req.Opts.StationType != "" {
|
||||
updates = slices.DeleteFunc(updates, func(u *weather.WeatherUpdate) bool {
|
||||
return u.StationType == nil || *u.StationType != *req.Opts.StationType
|
||||
})
|
||||
}
|
||||
|
||||
// Limit results
|
||||
if len(updates) > int(*req.Limit) {
|
||||
updates = updates[len(updates)-int(*req.Limit):]
|
||||
}
|
||||
|
||||
span.SetAttributes(attribute.Int("retrieved", len(updates)))
|
||||
span.SetStatus(codes.Ok, "")
|
||||
|
||||
return updates, nil
|
||||
}
|
||||
|
||||
func (r *MemoryRecorder) Count(ctx context.Context) int {
|
||||
_, span := r.tracer.Start(ctx, "countWeatherRecorder")
|
||||
defer span.End()
|
||||
|
||||
count := r.count()
|
||||
|
||||
span.SetAttributes(attribute.Int("count", count))
|
||||
span.SetStatus(codes.Ok, "")
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func (r *MemoryRecorder) count() int {
|
||||
return len(r.updates)
|
||||
}
|
35
pkg/weather/recorder/recorders/memory/memory.go
Normal file
35
pkg/weather/recorder/recorders/memory/memory.go
Normal file
@ -0,0 +1,35 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel"
|
||||
|
||||
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
|
||||
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather/recorder/recorders"
|
||||
)
|
||||
|
||||
const defRetainLast = 120
|
||||
|
||||
type MemoryRecorder struct {
|
||||
baseCtx context.Context
|
||||
updates []*weather.WeatherUpdate
|
||||
tracer trace.Tracer
|
||||
keep int
|
||||
*sync.RWMutex
|
||||
}
|
||||
|
||||
func (r *MemoryRecorder) Init(ctx context.Context, opts *recorders.RecorderOpts) {
|
||||
if opts.RetainLast < 1 {
|
||||
opts.RetainLast = defRetainLast
|
||||
}
|
||||
|
||||
r.updates = make([]*weather.WeatherUpdate, 0, opts.RetainLast)
|
||||
r.keep = opts.RetainLast
|
||||
r.baseCtx = opts.BaseCtx
|
||||
r.RWMutex = &sync.RWMutex{}
|
||||
r.tracer = otel.GetTracer(r.baseCtx, "memoryRecorder")
|
||||
}
|
38
pkg/weather/recorder/recorders/memory/set.go
Normal file
38
pkg/weather/recorder/recorders/memory/set.go
Normal file
@ -0,0 +1,38 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
|
||||
)
|
||||
|
||||
func (r *MemoryRecorder) Set(ctx context.Context, u *weather.WeatherUpdate) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
ctx, span := r.tracer.Start(ctx, "memoryRecorder.Set")
|
||||
span.SetAttributes(
|
||||
attribute.Int("countWeatherUpdates", r.Count(ctx)),
|
||||
attribute.Int("keepUpdates", r.keep),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
return r.set(ctx, u)
|
||||
}
|
||||
|
||||
func (r *MemoryRecorder) set(ctx context.Context, u *weather.WeatherUpdate) error {
|
||||
span := trace.SpanFromContext(ctx)
|
||||
|
||||
if len(r.updates) > r.keep {
|
||||
r.updates = r.updates[1:]
|
||||
span.AddEvent("trimmed recorded updates by 1")
|
||||
}
|
||||
|
||||
r.updates = append(r.updates, u)
|
||||
span.AddEvent("recorded weather update")
|
||||
|
||||
return nil
|
||||
}
|
20
pkg/weather/recorder/recorders/recorders.go
Normal file
20
pkg/weather/recorder/recorders/recorders.go
Normal file
@ -0,0 +1,20 @@
|
||||
package recorders
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/api/v1alpha1/weather"
|
||||
"gitea.libretechconsulting.com/rmcguire/ambient-local-exporter/pkg/weather"
|
||||
)
|
||||
|
||||
type RecorderOpts struct {
|
||||
RetainLast int
|
||||
BaseCtx context.Context
|
||||
}
|
||||
|
||||
type Recorder interface {
|
||||
Init(context.Context, *RecorderOpts)
|
||||
Set(context.Context, *weather.WeatherUpdate) error
|
||||
Get(context.Context, *pb.GetWeatherRequest) ([]*weather.WeatherUpdate, error)
|
||||
Count(context.Context) int // Best Effort
|
||||
}
|
Reference in New Issue
Block a user