initial commit

This commit is contained in:
2024-08-01 23:42:30 +03:00
commit 09fef3c113
21 changed files with 931 additions and 0 deletions

117
cache.go Normal file
View File

@@ -0,0 +1,117 @@
package commander
import (
"context"
"fmt"
"sync"
"time"
"github.com/vmihailenco/msgpack/v5"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
const cacheTTL = 5 * time.Second
type Cache interface {
Set(key string, value []byte, ttl time.Duration) error
Get(key string) ([]byte, error)
}
type commandCache struct {
tracer trace.Tracer
cache Cache
ttl time.Duration
locks sync.Map
}
func newCommandCache(cache Cache, ttl time.Duration) *commandCache {
tracer := otel.Tracer(tracerName)
if ttl <= 0 {
ttl = cacheTTL
}
return &commandCache{
tracer: tracer,
cache: cache,
ttl: ttl,
}
}
func (c *commandCache) CommandCacheGet(ctx context.Context, key string, commands ...Command) bool {
ctx, span := c.tracer.Start(ctx, "command_cache.get")
defer span.End()
defer c.locker(ctx, key)()
res, err := c.cache.Get(key)
if err != nil {
span.RecordError(fmt.Errorf("cache get: %w", err))
span.SetStatus(codes.Error, err.Error())
return false
}
if len(res) == 0 {
span.AddEvent("cache miss")
return false
}
span.AddEvent("cache hit")
if err := c.unmarshal(res, commands); err != nil {
span.RecordError(fmt.Errorf("unmarshal commands: %w", err))
span.SetStatus(codes.Error, err.Error())
return false
}
return true
}
func (c *commandCache) CommandCacheStore(ctx context.Context, key string, commands ...Command) {
ctx, span := c.tracer.Start(ctx, "command_cache.store")
defer span.End()
defer c.locker(ctx, key)()
marshaled, err := c.marshal(commands)
if err != nil {
span.RecordError(fmt.Errorf("marshal commands: %w", err))
span.SetStatus(codes.Error, err.Error())
return
}
if err := c.cache.Set(key, marshaled, c.ttl); err != nil {
span.RecordError(fmt.Errorf("set cache: %w", err))
span.SetStatus(codes.Error, err.Error())
}
}
func (c *commandCache) locker(ctx context.Context, key string) (unlock func()) {
ctx, span := c.tracer.Start(ctx, "locker")
defer span.End()
for {
if _, loaded := c.locks.LoadOrStore(key, true); !loaded {
break
}
}
return func() {
c.locks.Delete(key)
}
}
func (c *commandCache) marshal(commands []Command) ([]byte, error) {
return msgpack.Marshal(commands)
}
func (c *commandCache) unmarshal(res []byte, commands []Command) error {
return msgpack.Unmarshal(res, &commands)
}