Files
kmsvc-manage/internal/redis/watermark.go
T

73 lines
2.7 KiB
Go
Raw Normal View History

package redis
import (
"context"
"fmt"
"strconv"
"time"
"github.com/redis/go-redis/v9"
)
// AddPending records a freshly-consumed offset as not-yet-acked, per design.md
// §3/§4's `kmsvc:pending:` ZSET.
func AddPending(ctx context.Context, rdb *redis.Client, queue, shardID string, partition int32, offset int64) error {
key := PendingKey(queue, shardID, partition)
if err := rdb.ZAdd(ctx, key, redis.Z{Score: float64(offset), Member: offset}).Err(); err != nil {
return fmt.Errorf("add pending %s: %w", key, err)
}
return nil
}
// MinPending returns the lowest not-yet-acked offset for a (shard, partition),
// or ok=false if nothing is pending — i.e. the watermark can advance to the
// last consumed offset.
func MinPending(ctx context.Context, rdb *redis.Client, queue, shardID string, partition int32) (offset int64, ok bool, err error) {
res, err := rdb.ZRangeWithScores(ctx, PendingKey(queue, shardID, partition), 0, 0).Result()
if err != nil {
return 0, false, fmt.Errorf("min pending %s: %w", PendingKey(queue, shardID, partition), err)
}
if len(res) == 0 {
return 0, false, nil
}
return int64(res[0].Score), true, nil
}
// SetWatermark stores the committable watermark for a (shard, partition),
// per design.md §3's background committer.
func SetWatermark(ctx context.Context, rdb *redis.Client, queue, shardID string, partition int32, offset int64) error {
key := WatermarkKey(queue, shardID, partition)
if err := rdb.Set(ctx, key, offset, 0).Err(); err != nil {
return fmt.Errorf("set watermark %s: %w", key, err)
}
return nil
}
// GetWatermark returns the last-committed watermark for a (shard, partition),
// or ok=false if none has been committed yet.
func GetWatermark(ctx context.Context, rdb *redis.Client, queue, shardID string, partition int32) (offset int64, ok bool, err error) {
v, err := rdb.Get(ctx, WatermarkKey(queue, shardID, partition)).Result()
if err == redis.Nil {
return 0, false, nil
}
if err != nil {
return 0, false, fmt.Errorf("get watermark %s: %w", WatermarkKey(queue, shardID, partition), err)
}
n, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return 0, false, fmt.Errorf("parse watermark %s: %w", WatermarkKey(queue, shardID, partition), err)
}
return n, true, nil
}
// AcquireFIFOLock claims the per-group exclusivity gate (design.md §3/§4's
// `kmsvc:fifo_lock:` row) so only one in-flight message per MessageGroupId
// is ever handed out.
func AcquireFIFOLock(ctx context.Context, rdb *redis.Client, queue, groupID, receiptHandle string, ttl time.Duration) (bool, error) {
ok, err := rdb.SetNX(ctx, FIFOLockKey(queue, groupID), receiptHandle, ttl).Result()
if err != nil {
return false, fmt.Errorf("acquire fifo lock %s: %w", groupID, err)
}
return ok, nil
}