Module: MasterLock
- Defined in:
- lib/master_lock.rb,
lib/master_lock/version.rb,
lib/master_lock/registry.rb,
lib/master_lock/redis_lock.rb,
lib/master_lock/redis_scripts.rb
Overview
MasterLock is a system for interprocess locking. Resources can be locked by a string identifier such that only one thread may have the lock at a time. Lock state and owners are stored on a Redis server shared by all processes. Locks are held until either the block of synchronized code completes or the thread that obtained the lock is killed. To prevent the locks from being held indefinitely in the event that the process dies without releasing them, the locks have an expiration time in Redis. While the thread owning the lock is alive, a separate thread will extend the lifetime of the locks so that they do not expire even when the code in the critical section takes a long time to execute.
Defined Under Namespace
Modules: RedisScripts Classes: Config, LockNotAcquiredError, NotStartedError, RedisLock, Registry, UnconfiguredError
Constant Summary collapse
- DEFAULT_ACQUIRE_TIMEOUT =
5
- DEFAULT_EXTEND_INTERVAL =
15
- DEFAULT_KEY_PREFIX =
"masterlock".freeze
- DEFAULT_SLEEP_TIME =
5
- DEFAULT_TTL =
60
- VERSION =
"0.10.0"
Class Method Summary collapse
-
.config ⇒ Config
MasterLock configuration settings.
-
.configure {|Config| ... } ⇒ Object
Configure MasterLock using block syntax.
-
.logger ⇒ Logger
Get the configured logger.
-
.start ⇒ Object
Starts the background thread to manage and extend currently held locks.
-
.synchronize(key, options = {}) ⇒ Object
Obtain a mutex around a critical section of code.
Class Method Details
.config ⇒ Config
Returns MasterLock configuration settings.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/master_lock.rb', line 120 def config if !defined?(@config) @config = Config.new @config.acquire_timeout = DEFAULT_ACQUIRE_TIMEOUT @config.extend_interval = DEFAULT_EXTEND_INTERVAL @config.hostname = Socket.gethostname @config.logger = Logger.new(STDOUT) @config.logger.progname = name @config.key_prefix = DEFAULT_KEY_PREFIX @config.sleep_time = DEFAULT_SLEEP_TIME @config.ttl = DEFAULT_TTL end @config end |
.configure {|Config| ... } ⇒ Object
Configure MasterLock using block syntax. Simply yields #config to the block.
139 140 141 |
# File 'lib/master_lock.rb', line 139 def configure yield config end |
.logger ⇒ Logger
Get the configured logger.
115 116 117 |
# File 'lib/master_lock.rb', line 115 def logger config.logger end |
.start ⇒ Object
Starts the background thread to manage and extend currently held locks. The thread remains alive for the lifetime of the process. This must be called before any locks may be acquired.
102 103 104 105 106 107 108 109 110 |
# File 'lib/master_lock.rb', line 102 def start @registry = Registry.new Thread.new do loop do @registry.extend_locks sleep(config.sleep_time) end end end |
.synchronize(key, options = {}) ⇒ Object
Obtain a mutex around a critical section of code. Only one thread on any machine can execute the given block at a time. Returns the result of the block.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/master_lock.rb', line 58 def synchronize(key, = {}) check_configured raise NotStartedError unless @registry ttl = [:ttl] || config.ttl acquire_timeout = [:acquire_timeout] || config.acquire_timeout extend_interval = [:extend_interval] || config.extend_interval raise ArgumentError, "extend_interval cannot be negative" if extend_interval < 0 raise ArgumentError, "ttl must be greater extend_interval" if ttl <= extend_interval if (.include?(:if) && ![:if]) || (.include?(:unless) && [:unless]) return yield end lock = RedisLock.new( redis: config.redis, key: key, ttl: ttl, owner: generate_owner ) if !lock.acquire(timeout: acquire_timeout) raise LockNotAcquiredError, key end registration = @registry.register(lock, extend_interval) logger.debug("Acquired lock #{key}") begin yield ensure @registry.unregister(registration) if lock.release logger.debug("Released lock #{key}") else logger.warn("Failed to release lock #{key}") end end end |