leader_lock_cache.go 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. package repository
  2. import (
  3. "context"
  4. "time"
  5. "github.com/Wei-Shaw/sub2api/internal/service"
  6. "github.com/redis/go-redis/v9"
  7. )
  8. const leaderLockKeyPrefix = "leader:lock:"
  9. // leaderLockReleaseScript releases a leader lock only when the caller still owns
  10. // it (compare-and-delete by owner token). This prevents a previous holder whose
  11. // lock already expired — and was re-acquired by another instance — from deleting
  12. // the new owner's lock.
  13. var leaderLockReleaseScript = redis.NewScript(`
  14. if redis.call("GET", KEYS[1]) == ARGV[1] then
  15. return redis.call("DEL", KEYS[1])
  16. end
  17. return 0
  18. `)
  19. type leaderLockCache struct {
  20. rdb *redis.Client
  21. }
  22. // NewLeaderLockCache returns a Redis-backed implementation of
  23. // service.LeaderLockCache used by periodic background jobs to elect a single
  24. // runner across instances.
  25. func NewLeaderLockCache(rdb *redis.Client) service.LeaderLockCache {
  26. return &leaderLockCache{rdb: rdb}
  27. }
  28. func (c *leaderLockCache) TryAcquireLeaderLock(ctx context.Context, key, owner string, ttl time.Duration) (bool, error) {
  29. return c.rdb.SetNX(ctx, leaderLockKeyPrefix+key, owner, ttl).Result()
  30. }
  31. func (c *leaderLockCache) ReleaseLeaderLock(ctx context.Context, key, owner string) error {
  32. return leaderLockReleaseScript.Run(ctx, c.rdb, []string{leaderLockKeyPrefix + key}, owner).Err()
  33. }