proxy-pool/internal/adapters/redisprovider/scripts.go
2026-07-30 14:15:48 +08:00

62 lines
1.8 KiB
Go

package redisprovider
import (
"bytes"
"context"
_ "embed"
"encoding/json"
"errors"
"fmt"
"io"
"github.com/redis/go-redis/v9"
controllerProvider "proxy-pool/internal/controller/provider"
)
type scriptReply struct {
Status string `json:"status"`
Generation string `json:"generation,omitempty"`
Epoch uint64 `json:"epoch,string,omitempty"`
WaitMS int64 `json:"waitMs,omitempty"`
}
//go:embed scripts/provider.lua
var providerSource string
var providerScript = redis.NewScript(providerSource)
func runScript(ctx context.Context, client redis.Scripter, keys upstreamKeys, args ...any) (scriptReply, error) {
var reply scriptReply
result, err := providerScript.Run(ctx, client, keys.all(), args...).Result()
if err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return reply, err
}
return reply, errors.Join(controllerProvider.ErrCoordinationUnavailable,
fmt.Errorf("run Redis provider script: %w", err))
}
var payload []byte
switch value := result.(type) {
case string:
payload = []byte(value)
case []byte:
payload = value
default:
return reply, errors.Join(controllerProvider.ErrCoordinationUnavailable,
fmt.Errorf("decode Redis provider script: unexpected reply type %T", result))
}
decoder := json.NewDecoder(bytes.NewReader(payload))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&reply); err != nil || reply.Status == "" {
return scriptReply{}, errors.Join(controllerProvider.ErrCoordinationUnavailable,
fmt.Errorf("decode Redis provider script reply: %w", err))
}
var trailing any
if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) {
return scriptReply{}, errors.Join(controllerProvider.ErrCoordinationUnavailable,
errors.New("decode Redis provider script reply: trailing value"))
}
return reply, nil
}