Module: Agent::Lock::Store
- Defined in:
- lib/agent/lock/store.rb,
lib/agent/lock/store/redis_store.rb,
lib/agent/lock/store/file_system_store.rb
Overview
Where locks are kept. One tree, one store, chosen on purpose.
AGENT_LOCK_BACKEND wins when set. Otherwise a tree that already has a marker keeps using it. A virgin tree defaults to Redis when one answers locally, file when none does.
That default is still never a runtime auto-switch: once a marker exists, every later process in that tree is bound to it regardless of what Redis is doing, because an agent that quietly moved to a different store than the agent beside it would give the two of them two stores in which neither can see the other's locks, a lock that is worse than no lock, because it reports success.
The one moment that default is decided is also the one moment two
processes could race: both find a virgin tree, both probe Redis, and a
flaky answer could hand them different defaults before either writes the
marker. So the marker is claimed atomically (first O_CREAT|O_EXCL wins)
and every process builds from whatever ends up on disk, never from its
own guess, so a race can pick either backend but never a split.
Defined Under Namespace
Classes: FileSystemStore, Mismatch, RedisStore
Constant Summary collapse
- MARKER =
"backend"
Class Method Summary collapse
- .build(name, tree) ⇒ Store::FileSystemStore, Store::RedisStore
-
.claim(tree, name) ⇒ String
Atomically claims the marker for a virgin tree with
name, or, when a concurrent process already claimed it first, reads back whatever that process wrote instead. -
.default_backend ⇒ String
"redis" when one answers on REDIS_URL, "file" otherwise.
- .for(tree) ⇒ Store::FileSystemStore, Store::RedisStore
- .local_redis_available? ⇒ Boolean
- .marker_path(tree) ⇒ Object
-
.recorded(tree) ⇒ String?
The backend this tree already uses.
Class Method Details
.build(name, tree) ⇒ Store::FileSystemStore, Store::RedisStore
56 57 58 59 60 61 62 |
# File 'lib/agent/lock/store.rb', line 56 def build(name, tree) case name when "redis" then RedisStore.new(tree) when "file" then FileSystemStore.new(tree) else raise Mismatch, "unknown backend #{name.inspect}, expected file or redis" end end |
.claim(tree, name) ⇒ String
Atomically claims the marker for a virgin tree with name, or, when a
concurrent process already claimed it first, reads back whatever that
process wrote instead. Either way, every caller ends up building the
same backend for this tree.
80 81 82 83 84 85 86 87 88 89 |
# File 'lib/agent/lock/store.rb', line 80 def claim(tree, name) path = marker_path(tree) FileUtils.mkdir_p(File.dirname(path)) File.write(path, "#{name}\n", mode: File::WRONLY | File::CREAT | File::EXCL) name rescue Errno::EEXIST recorded(tree) || name rescue SystemCallError name end |
.default_backend ⇒ String
Returns "redis" when one answers on REDIS_URL, "file" otherwise.
94 95 96 |
# File 'lib/agent/lock/store.rb', line 94 def default_backend local_redis_available? ? "redis" : "file" end |
.for(tree) ⇒ Store::FileSystemStore, Store::RedisStore
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/agent/lock/store.rb', line 39 def for(tree) requested = ENV["AGENT_LOCK_BACKEND"]&.downcase established = recorded(tree) if established if requested && requested != established raise Mismatch, "this tree's locks live in #{established}, not #{requested}, " \ "release them before switching backends" end return build(established, tree) end build(claim(tree, requested || default_backend), tree) end |
.local_redis_available? ⇒ Boolean
98 99 100 101 |
# File 'lib/agent/lock/store.rb', line 98 def local_redis_available? client, error = RedisStore.create_client !client.nil? && error.nil? end |
.marker_path(tree) ⇒ Object
91 |
# File 'lib/agent/lock/store.rb', line 91 def marker_path(tree) = File.join(tree.store_dir, MARKER) |
.recorded(tree) ⇒ String?
Returns the backend this tree already uses.
65 66 67 68 69 70 |
# File 'lib/agent/lock/store.rb', line 65 def recorded(tree) path = marker_path(tree) File.exist?(path) ? File.read(path).strip : nil rescue SystemCallError nil end |