client.rb 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. ##
  3. # Redlock implementation for distributed locking.
  4. #
  5. # TODO: Replace (and remove) this custom class with the existing redlock
  6. # gem [0], once we get rid of supported linux distributions that are not
  7. # providing a redis version 6+.
  8. #
  9. # [0] https://github.com/leandromoreira/redlock-rb
  10. module Redlock
  11. class Client
  12. def initialize(server)
  13. @id = SecureRandom.uuid
  14. @redis = Redis.new(driver: :hiredis, url: server)
  15. end
  16. def lock(resource, ttl, options = {}, &block)
  17. return extend_ttl(options[:extend]) if options[:extend]
  18. return if !@redis.set(resource, @id, nx: true, px: ttl)
  19. if block
  20. begin
  21. yield block
  22. ensure
  23. unlock({ resource: resource, value: @id })
  24. end
  25. else
  26. { resource: resource, value: @id }
  27. end
  28. end
  29. def unlock(lock_info)
  30. value = @redis.get(lock_info[:resource])
  31. return 0 if value != lock_info[:value]
  32. @redis.del(lock_info[:resource])
  33. end
  34. def locked?(resource)
  35. @redis.exists?(resource)
  36. end
  37. private
  38. def extend_ttl(lock_info)
  39. return 0 if !locked?(lock_info[:resource])
  40. value = @redis.get(lock_info[:resource])
  41. return 0 if value != lock_info[:value]
  42. ttl = @redis.pttl(lock_info[:resource])
  43. return if ttl <= 0 || ttl > lock_info[:validity]
  44. @redis.pexpire(lock_info[:resource], lock_info[:validity])
  45. end
  46. end
  47. end