Files
sablier/pkg/store/valkey/valkey.go
Alexis Couvreur f29b13a55a refactor(storage): add store.Store interface
There is a first implementation with ValKey that will allow to use redis APIs as a backend for Sablier with Hight Availability
2025-02-02 18:13:45 -05:00

82 lines
1.9 KiB
Go

package valkey
import (
"context"
"encoding/json"
"github.com/sablierapp/sablier/app/instance"
"github.com/sablierapp/sablier/pkg/store"
"github.com/valkey-io/valkey-go"
"log/slog"
"strings"
"time"
)
var _ store.Store = (*ValKey)(nil)
type ValKey struct {
Client valkey.Client
}
func New(ctx context.Context, client valkey.Client) (store.Store, error) {
err := client.Do(ctx, client.B().Ping().Build()).Error()
if err != nil {
return nil, err
}
err = client.Do(ctx, client.B().ConfigSet().ParameterValue().
ParameterValue("notify-keyspace-events", "KEx").
Build()).Error()
if err != nil {
return nil, err
}
return &ValKey{Client: client}, nil
}
func (v *ValKey) Get(ctx context.Context, s string) (instance.State, error) {
b, err := v.Client.Do(ctx, v.Client.B().Get().Key(s).Build()).AsBytes()
if valkey.IsValkeyNil(err) {
return instance.State{}, store.ErrKeyNotFound
}
if err != nil {
return instance.State{}, err
}
var i instance.State
err = json.Unmarshal(b, &i)
if err != nil {
return instance.State{}, err
}
return i, nil
}
func (v *ValKey) Put(ctx context.Context, state instance.State, duration time.Duration) error {
value, err := json.Marshal(state)
if err != nil {
return err
}
return v.Client.Do(ctx, v.Client.B().Set().Key(state.Name).Value(string(value)).Ex(duration).Build()).Error()
}
func (v *ValKey) Delete(ctx context.Context, s string) error {
return v.Client.Do(ctx, v.Client.B().Del().Key(s).Build()).Error()
}
func (v *ValKey) OnExpire(ctx context.Context, f func(string)) error {
go func() {
err := v.Client.Receive(ctx, v.Client.B().Psubscribe().Pattern("__key*__:*").Build(), func(msg valkey.PubSubMessage) {
if msg.Message == "expired" {
split := strings.Split(msg.Channel, ":")
key := split[len(split)-1]
f(key)
}
})
if err != nil {
slog.Error("error subscribing", slog.Any("error", err))
}
}()
return nil
}