// 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() }