lusnoc 0.1.2.16548 → 0.9.0.44532
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +8 -8
- data/lib/lusnoc/guard.rb +40 -27
- data/lib/lusnoc/mutex.rb +30 -8
- data/lib/lusnoc/session.rb +8 -2
- data/lib/lusnoc/version.rb +1 -1
- data/lib/lusnoc/watcher.rb +16 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05d4b47c5281bbcf7adc5cff1e398bd4ce37e2e8953b6a09e143404777c016dc
|
4
|
+
data.tar.gz: 6a7afa4d21df7cd7b19cd56290ef35d58befa6013fa2f713973da2059e74297d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34da3a07c4705381614e65b9baf6b5d6f7200abb9912ad68baa1debb043fd9b4cbb32f3c3eed37cd50cee2b1a1b7243fb8794b0be8eb1f0cb7279ab0ce889a5f
|
7
|
+
data.tar.gz: b3ae1f8dae1f35c346378e3c53075807346c157cbe50c7f8cb200e47cb00f7c845db92f8a43a2e83c6423188a620a6dafc9978781d82cd4cc531127173b56553
|
data/README.md
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/lusnoc)
|
4
4
|
[](https://rubygems.org/gems/lusnoc/versions)
|
5
|
+
[](http://www.rubydoc.info/gems/lusnoc)
|
5
6
|
|
6
|
-
[](https://lysander.x.rnds.pro/api/v1/badges/lusnoc_vulnerable.html)
|
7
|
+
[](https://lysander.rnds.pro/api/v1/badges/lusnoc_coverage.html)
|
8
|
+
[](https://lysander.rnds.pro/api/v1/badges/lusnoc_quality.html)
|
9
|
+
[](https://lysander.rnds.pro/api/v1/badges/lusnoc_outdated.html)
|
10
|
+
[](https://lysander.rnds.pro/api/v1/badges/lusnoc_vulnerable.html)
|
11
11
|
|
12
12
|
Lusnoc is reliable gem to deal with [Consul](https://www.consul.io). It is designed to be simple and work without dark background magic.
|
13
|
-
It is inspired by [consul-mutex](https://github.com/
|
13
|
+
It is inspired by [consul-mutex](https://github.com/kinnalru/consul-mutex)(which has hard background magic).
|
14
14
|
|
15
15
|
## FAQ
|
16
16
|
|
@@ -18,7 +18,7 @@ It is inspired by [consul-mutex](https://github.com/discourse/consul-mutex)(whic
|
|
18
18
|
|
19
19
|
Lusnoc allows you to interact with Consul to provide distributed locks(mutex) to your application.
|
20
20
|
|
21
|
-
#### What's the difference between lusnoc and [consul-mutex](https://github.com/
|
21
|
+
#### What's the difference between lusnoc and [consul-mutex](https://github.com/kinnalru/consul-mutex) or [diplomat](https://github.com/WeAreFarmGeek/diplomat)
|
22
22
|
* consul-mutex starts background thread and ***the block of code that you pass to #synchronize runs on a separate thread, and can be killed without warning if the mutex determines that it no longer holds the lock.***
|
23
23
|
* diplomat provides the basic session/locks functionality but no automated control over it
|
24
24
|
|
@@ -43,7 +43,7 @@ TTL will be used in session creation on `#synchronize`:
|
|
43
43
|
puts "We are exclusively owns resource"
|
44
44
|
end
|
45
45
|
```
|
46
|
-
If mutex cannot be acquired within given timeout Lusnoc::TimeoutError is raised.
|
46
|
+
If mutex cannot be acquired within given timeout `Lusnoc::TimeoutError` is raised.
|
47
47
|
By default, the "value" of the lock resource will be the hostname of the
|
48
48
|
machine that it's running on (so you know who has the lock). If, for some
|
49
49
|
reason, you'd like to set the value to something else, you can do that, too:
|
data/lib/lusnoc/guard.rb
CHANGED
@@ -6,33 +6,26 @@ module Lusnoc
|
|
6
6
|
|
7
7
|
include Helper
|
8
8
|
|
9
|
+
attr_reader :callbacks
|
10
|
+
|
9
11
|
def initialize(base_url)
|
10
12
|
@base_url = base_url
|
13
|
+
@callbacks = {}
|
11
14
|
yield(self) if block_given?
|
12
15
|
end
|
13
16
|
|
14
17
|
def condition(&block)
|
15
|
-
@condition = block
|
18
|
+
@callbacks[:condition] = block
|
16
19
|
self
|
17
20
|
end
|
18
21
|
|
19
22
|
def then(&block)
|
20
|
-
@
|
23
|
+
@callbacks[:then] = block
|
21
24
|
self
|
22
25
|
end
|
23
26
|
|
24
27
|
def run
|
25
|
-
th =
|
26
|
-
logger.info "Guard[#{@base_url.inspect}] thread started"
|
27
|
-
watch_forever(@base_url)
|
28
|
-
fire!
|
29
|
-
rescue StandardError => e
|
30
|
-
logger.error "Guard[#{@base_url.inspect}] error: #{e.inspect}"
|
31
|
-
fire!
|
32
|
-
ensure
|
33
|
-
logger.info "Guard[#{@base_url.inspect}] finihsed"
|
34
|
-
end
|
35
|
-
|
28
|
+
th = start_thread
|
36
29
|
yield
|
37
30
|
ensure
|
38
31
|
th.kill rescue nil
|
@@ -40,28 +33,48 @@ module Lusnoc
|
|
40
33
|
|
41
34
|
private
|
42
35
|
|
43
|
-
def
|
44
|
-
|
45
|
-
@
|
36
|
+
def start_thread
|
37
|
+
Thread.new do
|
38
|
+
logger.info "Guard[#{@base_url.inspect}] thread started"
|
39
|
+
watch_forever(@base_url)
|
40
|
+
fire!
|
41
|
+
rescue StandardError => e
|
42
|
+
logger.error "Guard[#{@base_url.inspect}] error: #{e.inspect}"
|
43
|
+
logger.error e.backtrace
|
44
|
+
fire!(e)
|
45
|
+
ensure
|
46
|
+
logger.info "Guard[#{@base_url.inspect}] finihsed"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def fire!(*args)
|
51
|
+
@callbacks[:then]&.tap do |cb|
|
52
|
+
@callbacks[:then] = nil
|
46
53
|
logger.info "Guard[#{@base_url.inspect}] fired"
|
47
|
-
cb.call
|
54
|
+
cb.call(*args)
|
48
55
|
end
|
49
56
|
end
|
50
57
|
|
51
58
|
def watch_forever(base_url)
|
52
|
-
|
53
|
-
|
54
|
-
Kernel.loop do
|
55
|
-
resp = Lusnoc.http_get("#{base_url}?index=#{last_x_consul_index}&wait=10s", timeout: 15)
|
56
|
-
logger.debug "Guard[#{@base_url.inspect}] response: #{resp.body}"
|
57
|
-
return unless @condition.call(resp.body)
|
58
|
-
|
59
|
-
index = [Integer(resp['x-consul-index']), 1].max
|
60
|
-
last_x_consul_index = (index < last_x_consul_index ? 1 : index)
|
61
|
-
sleep 1
|
59
|
+
Lusnoc::Watcher.new(base_url).run(max_consul_wait: 10) do |body|
|
60
|
+
return true unless @callbacks[:condition].call(body)
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
64
|
+
# def watch_forever(base_url)
|
65
|
+
# last_x_consul_index = 1
|
66
|
+
|
67
|
+
# Kernel.loop do
|
68
|
+
# resp = Lusnoc.http_get("#{base_url}?index=#{last_x_consul_index}&wait=10s", timeout: 15)
|
69
|
+
# logger.debug "Guard[#{@base_url.inspect}] response: #{resp.body}"
|
70
|
+
# return unless @callbacks[:condition].call(resp.body)
|
71
|
+
|
72
|
+
# index = [Integer(resp['x-consul-index']), 1].max
|
73
|
+
# last_x_consul_index = (index < last_x_consul_index ? 1 : index)
|
74
|
+
# sleep 0.4
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
|
65
78
|
end
|
66
79
|
end
|
67
80
|
|
data/lib/lusnoc/mutex.rb
CHANGED
@@ -46,7 +46,7 @@ module Lusnoc
|
|
46
46
|
return acquisition_loop! key, session, value, t, &block
|
47
47
|
ensure
|
48
48
|
release(key, session.id, timeout: 2) rescue nil
|
49
|
-
logger.info("
|
49
|
+
logger.info("Mutex[#{key}] released for Session[#{session.name}:#{session.id}]")
|
50
50
|
@owner = nil
|
51
51
|
@session = nil
|
52
52
|
end
|
@@ -59,7 +59,7 @@ module Lusnoc
|
|
59
59
|
return false if resp.body.chomp != 'true'
|
60
60
|
|
61
61
|
@owner = Thread.current
|
62
|
-
logger.info("
|
62
|
+
logger.info("Mutex[#{key}] acquired for Session[#{session.name}:#{session.id}]")
|
63
63
|
renew
|
64
64
|
true
|
65
65
|
end
|
@@ -69,22 +69,44 @@ module Lusnoc
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def acquisition_loop!(key, session, value, t)
|
72
|
-
|
72
|
+
if acquire(key, session, value)
|
73
|
+
prepare_guard(session, key).run do
|
74
|
+
return yield(self)
|
75
|
+
end
|
76
|
+
end
|
73
77
|
|
74
|
-
logger.debug("
|
78
|
+
logger.debug("Mutex[#{key}] run acquisition loop for Session[#{session.name}:#{session.id}]")
|
75
79
|
t.loop! do
|
76
80
|
session.alive!(TimeoutError)
|
77
81
|
wait_for_key_released(key, t.left)
|
78
82
|
|
79
|
-
|
83
|
+
if acquire(key, session, value)
|
84
|
+
prepare_guard(session, key).run do
|
85
|
+
return yield(self)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
logger.debug("Mutex[#{key}] acquisition failed for Session[#{session.name}:#{session.id}]")
|
90
|
+
sleep 0.4
|
91
|
+
end
|
92
|
+
end
|
80
93
|
|
81
|
-
|
82
|
-
|
94
|
+
def prepare_guard(session, key)
|
95
|
+
Lusnoc::Guard.new(build_url("/v1/kv/#{key}")) do |guard|
|
96
|
+
guard.condition do |body|
|
97
|
+
JSON.parse(body).first['Session'] == session.id rescue false
|
98
|
+
end
|
99
|
+
|
100
|
+
guard.then do
|
101
|
+
@owner = nil
|
102
|
+
logger.info("Mutex[#{key}] LOST for Session[#{session.name}:#{session.id}]")
|
103
|
+
@on_mutex_lost&.call(self)
|
104
|
+
end
|
83
105
|
end
|
84
106
|
end
|
85
107
|
|
86
108
|
def wait_for_key_released(key, timeout = nil)
|
87
|
-
logger.debug
|
109
|
+
logger.debug("Mutex[#{key}] start waiting of key releasing...")
|
88
110
|
Lusnoc::Watcher.new(build_url("/v1/kv/#{key}"),
|
89
111
|
timeout: timeout,
|
90
112
|
eclass: TimeoutError,
|
data/lib/lusnoc/session.rb
CHANGED
@@ -8,10 +8,14 @@ module Lusnoc
|
|
8
8
|
|
9
9
|
attr_reader :id, :name, :ttl, :alive, :expired_at
|
10
10
|
|
11
|
-
def initialize(name, ttl: 20)
|
11
|
+
def initialize(name, ttl: 20, &block)
|
12
12
|
@name = name
|
13
13
|
@ttl = ttl
|
14
14
|
|
15
|
+
run(&block) if block_given?
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
15
19
|
@id = create_session(name, ttl)
|
16
20
|
|
17
21
|
prepare_guard(@id).run do
|
@@ -38,7 +42,7 @@ module Lusnoc
|
|
38
42
|
end
|
39
43
|
|
40
44
|
def alive!(exception_class = ExpiredError)
|
41
|
-
alive
|
45
|
+
@alive || (raise exception_class.new("Session[#{@name}:#{@id}] expired"))
|
42
46
|
end
|
43
47
|
|
44
48
|
def renew
|
@@ -50,6 +54,8 @@ module Lusnoc
|
|
50
54
|
|
51
55
|
def on_session_die(&block)
|
52
56
|
@session_die_cb = block
|
57
|
+
@session_die_cb&.call(self) if @alive == false
|
58
|
+
self
|
53
59
|
end
|
54
60
|
|
55
61
|
private
|
data/lib/lusnoc/version.rb
CHANGED
data/lib/lusnoc/watcher.rb
CHANGED
@@ -12,19 +12,19 @@ module Lusnoc
|
|
12
12
|
eclass: Lusnoc::TimeoutError,
|
13
13
|
emessage: 'watch timeout')
|
14
14
|
@base_url = base_url
|
15
|
-
@timeout
|
16
|
-
@eclass
|
15
|
+
@timeout = timeout
|
16
|
+
@eclass = eclass
|
17
17
|
@emessage = emessage
|
18
18
|
end
|
19
19
|
|
20
20
|
# run Consul blocking request in a loop with timeout support.
|
21
21
|
# break condition yielded by block call with response body
|
22
|
-
def run
|
22
|
+
def run(max_consul_wait: nil)
|
23
23
|
logger.debug "Watch #{@base_url} with #{@timeout.inspect} timeout"
|
24
24
|
last_x_consul_index = 1
|
25
25
|
|
26
26
|
Timeouter.loop!(@timeout, eclass: @eclass, message: @emessage) do |t|
|
27
|
-
wait_condition =
|
27
|
+
wait_condition = build_wait_condition(@base_url, t.left, max_consul_wait)
|
28
28
|
url = "#{@base_url}?index=#{last_x_consul_index}#{wait_condition}"
|
29
29
|
|
30
30
|
resp = Lusnoc.http_get(url, timeout: t.left)
|
@@ -34,7 +34,18 @@ module Lusnoc
|
|
34
34
|
|
35
35
|
index = [Integer(resp['x-consul-index']), 1].max
|
36
36
|
last_x_consul_index = (index < last_x_consul_index ? 1 : index)
|
37
|
-
sleep
|
37
|
+
sleep 0.4
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_wait_condition(_url, time_left, max_consul_wait)
|
42
|
+
if time_left
|
43
|
+
max = [time_left.to_i, max_consul_wait.to_i].max
|
44
|
+
"&wait=#{max}s"
|
45
|
+
elsif max_consul_wait
|
46
|
+
"&wait=#{max_consul_wait.to_i}s"
|
47
|
+
else
|
48
|
+
''
|
38
49
|
end
|
39
50
|
end
|
40
51
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lusnoc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0.44532
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samoilenko Yuri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|