modelRT/diagram/redis_client.go

113 lines
3.1 KiB
Go

// Package diagram provide diagram data structure and operation
package diagram
import (
"context"
"fmt"
"sort"
"strconv"
"github.com/redis/go-redis/v9"
)
// RedisClient define struct to accessing redis data that does not require the use of distributed locks
type RedisClient struct {
Client *redis.Client
}
// QueryLatestMeasurementValue returns the score whose member contains the
// greatest numeric timestamp. Measurement ZSets currently store timestamp in
// member and measurement value in score.
func (rc *RedisClient) QueryLatestMeasurementValue(ctx context.Context, key string) (float64, error) {
values, err := rc.QueryLatestMeasurementValues(ctx, key, 1)
if err != nil {
return 0, err
}
return values[0], nil
}
// QueryLatestMeasurementValues returns up to size scores ordered by their
// numeric member timestamps from newest to oldest.
func (rc *RedisClient) QueryLatestMeasurementValues(ctx context.Context, key string, size int) ([]float64, error) {
if rc.Client == nil {
return nil, fmt.Errorf("redis client is not initialized")
}
if size <= 0 {
return nil, fmt.Errorf("measurement window size must be greater than 0, got %d", size)
}
members, err := rc.Client.ZRangeWithScores(ctx, key, 0, -1).Result()
if err != nil {
return nil, err
}
return latestMeasurementValues(members, key, size)
}
func latestMeasurementValue(members []redis.Z, key string) (float64, error) {
values, err := latestMeasurementValues(members, key, 1)
if err != nil {
return 0, err
}
return values[0], nil
}
func latestMeasurementValues(members []redis.Z, key string, size int) ([]float64, error) {
if size <= 0 {
return nil, fmt.Errorf("measurement window size must be greater than 0, got %d", size)
}
if len(members) == 0 {
return nil, fmt.Errorf("real-time measurement value not found for key %q", key)
}
type timestampedValue struct {
timestamp int64
value float64
}
values := make([]timestampedValue, 0, len(members))
for _, member := range members {
timestamp, err := strconv.ParseInt(fmt.Sprint(member.Member), 10, 64)
if err != nil {
continue
}
values = append(values, timestampedValue{timestamp: timestamp, value: member.Score})
}
if len(values) == 0 {
return nil, fmt.Errorf("real-time measurement timestamps are invalid for key %q", key)
}
sort.Slice(values, func(i, j int) bool {
return values[i].timestamp > values[j].timestamp
})
if size > len(values) {
size = len(values)
}
result := make([]float64, size)
for index := range size {
result[index] = values[index].value
}
return result, nil
}
// NewRedisClient define func of new redis client instance
func NewRedisClient() *RedisClient {
return &RedisClient{
Client: GetRedisClientInstance(),
}
}
// QueryByZRange define func to query real time data from redis zset
func (rc *RedisClient) QueryByZRange(ctx context.Context, key string, size int64) ([]redis.Z, error) {
client := rc.Client
args := redis.ZRangeArgs{
Key: key,
Start: 0,
Stop: size,
ByScore: false,
ByLex: false,
Rev: false,
Offset: 0,
Count: 0,
}
return client.ZRangeArgsWithScores(ctx, args).Result()
}