189 lines
4.9 KiB
Go
189 lines
4.9 KiB
Go
package home
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func HashKeyPart(value string) string {
|
|
sum := sha256.Sum256([]byte(value))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func CurrentKVClient() (*Client, bool, error) {
|
|
client := Current()
|
|
if client == nil {
|
|
return nil, false, nil
|
|
}
|
|
if !client.Enabled() {
|
|
return nil, true, fmt.Errorf("home kv store unavailable: %w", ErrDisabled)
|
|
}
|
|
if !client.HeartbeatOK() {
|
|
return nil, true, fmt.Errorf("home kv store unavailable: %w", ErrNotConnected)
|
|
}
|
|
return client, true, nil
|
|
}
|
|
|
|
func KVGetJSONRequired(ctx context.Context, key string, out any) (bool, bool, error) {
|
|
client, homeMode, errClient := CurrentKVClient()
|
|
if !homeMode || errClient != nil {
|
|
return homeMode, false, errClient
|
|
}
|
|
raw, found, errGet := client.KVGet(ctx, key)
|
|
if errGet != nil || !found {
|
|
return true, false, errGet
|
|
}
|
|
if errUnmarshal := json.Unmarshal(raw, out); errUnmarshal != nil {
|
|
return true, false, errUnmarshal
|
|
}
|
|
return true, true, nil
|
|
}
|
|
|
|
func KVSetJSONRequired(ctx context.Context, key string, value any, ttl time.Duration) (bool, error) {
|
|
raw, errMarshal := json.Marshal(value)
|
|
if errMarshal != nil {
|
|
return false, errMarshal
|
|
}
|
|
return KVSetBytesRequired(ctx, key, raw, ttl)
|
|
}
|
|
|
|
func KVSetBytesRequired(ctx context.Context, key string, value []byte, ttl time.Duration) (bool, error) {
|
|
client, homeMode, errClient := CurrentKVClient()
|
|
if !homeMode || errClient != nil {
|
|
return homeMode, errClient
|
|
}
|
|
written, errSet := client.KVSet(ctx, key, value, kvSetOptionsForTTL(ttl))
|
|
if errSet != nil {
|
|
return true, errSet
|
|
}
|
|
if !written {
|
|
return true, fmt.Errorf("home kv store unavailable")
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func KVSetNXRequired(ctx context.Context, key string, value []byte, ttl time.Duration) (bool, bool, error) {
|
|
client, homeMode, errClient := CurrentKVClient()
|
|
if !homeMode || errClient != nil {
|
|
return homeMode, false, errClient
|
|
}
|
|
written, errSet := client.KVSetNX(ctx, key, value, ttl)
|
|
return true, written, errSet
|
|
}
|
|
|
|
func KVDelRequired(ctx context.Context, keys ...string) (bool, int64, error) {
|
|
client, homeMode, errClient := CurrentKVClient()
|
|
if !homeMode || errClient != nil {
|
|
return homeMode, 0, errClient
|
|
}
|
|
deleted, errDel := client.KVDel(ctx, keys...)
|
|
return true, deleted, errDel
|
|
}
|
|
|
|
func KVExpireRequired(ctx context.Context, key string, ttl time.Duration) (bool, error) {
|
|
client, homeMode, errClient := CurrentKVClient()
|
|
if !homeMode || errClient != nil {
|
|
return homeMode, errClient
|
|
}
|
|
_, errExpire := client.KVExpire(ctx, key, ttl)
|
|
return true, errExpire
|
|
}
|
|
|
|
func KVGetJSONBestEffort(ctx context.Context, key string, out any) (bool, bool) {
|
|
homeMode, found, errGet := KVGetJSONRequired(ctx, key, out)
|
|
if errGet != nil {
|
|
log.Errorf("home kv best-effort get failed prefix=%s: %v", kvLogPrefix(key), errGet)
|
|
return homeMode, false
|
|
}
|
|
return homeMode, found
|
|
}
|
|
|
|
func KVSetJSONBestEffort(ctx context.Context, key string, value any, ttl time.Duration) bool {
|
|
raw, errMarshal := json.Marshal(value)
|
|
if errMarshal != nil {
|
|
log.Errorf("home kv best-effort set failed prefix=%s: %v", kvLogPrefix(key), errMarshal)
|
|
return false
|
|
}
|
|
return KVSetBytesBestEffort(ctx, key, raw, ttl)
|
|
}
|
|
|
|
func KVSetBytesBestEffort(ctx context.Context, key string, value []byte, ttl time.Duration) bool {
|
|
homeMode, errSet := KVSetBytesRequired(ctx, key, value, ttl)
|
|
if !homeMode {
|
|
return false
|
|
}
|
|
if errSet != nil {
|
|
log.Errorf("home kv best-effort set failed prefix=%s: %v", kvLogPrefix(key), errSet)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func KVSetNXBestEffort(ctx context.Context, key string, value []byte, ttl time.Duration) bool {
|
|
homeMode, written, errSet := KVSetNXRequired(ctx, key, value, ttl)
|
|
if !homeMode {
|
|
return false
|
|
}
|
|
if errSet != nil {
|
|
log.Errorf("home kv best-effort setnx failed prefix=%s: %v", kvLogPrefix(key), errSet)
|
|
return false
|
|
}
|
|
return written
|
|
}
|
|
|
|
func KVDelBestEffort(ctx context.Context, keys ...string) bool {
|
|
homeMode, _, errDel := KVDelRequired(ctx, keys...)
|
|
if !homeMode {
|
|
return false
|
|
}
|
|
if errDel != nil {
|
|
log.Errorf("home kv best-effort del failed prefix=%s: %v", kvLogPrefix(firstKVKey(keys)), errDel)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func KVExpireBestEffort(ctx context.Context, key string, ttl time.Duration) bool {
|
|
homeMode, errExpire := KVExpireRequired(ctx, key, ttl)
|
|
if !homeMode {
|
|
return false
|
|
}
|
|
if errExpire != nil {
|
|
log.Errorf("home kv best-effort expire failed prefix=%s: %v", kvLogPrefix(key), errExpire)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func kvSetOptionsForTTL(ttl time.Duration) KVSetOptions {
|
|
if ttl <= 0 {
|
|
return KVSetOptions{}
|
|
}
|
|
return KVSetOptions{EX: ttl}
|
|
}
|
|
|
|
func kvLogPrefix(key string) string {
|
|
key = strings.TrimSpace(key)
|
|
if key == "" {
|
|
return "unknown"
|
|
}
|
|
parts := strings.Split(key, ":")
|
|
if len(parts) >= 2 {
|
|
return parts[0] + ":" + parts[1] + ":*"
|
|
}
|
|
return parts[0] + ":*"
|
|
}
|
|
|
|
func firstKVKey(keys []string) string {
|
|
if len(keys) == 0 {
|
|
return ""
|
|
}
|
|
return keys[0]
|
|
}
|