lusnoc 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6cc7a974c1a89a7b374164bd8cc85f5c5508b63f991191ec65457e5b39eadf1d
4
- data.tar.gz: aae9f0d4a9ca1cee4185dac92f539454501849ad959c236e27f4a621099cea8d
3
+ metadata.gz: e9d7357a1c2a8113aeb8609ee1dae3cae2ee40a75d5b7f89c33dd8dbb6025590
4
+ data.tar.gz: 186e2664d1f8659482abad06570f25445e17946f81e57ce62ef5a4a831ee4165
5
5
  SHA512:
6
- metadata.gz: bcb475e4079562bc5bb84942c8745ee9916c72e84313f26abd105fddae6f8ca65693f68f374792ae4e8377ea44ad8dec6d9e3a7dd392dd05d0c1619c29fc3cd5
7
- data.tar.gz: 959fb4bc41b1ff256bd31b0a7bca87ef7db35a87696d281113a3fda4336af58f357327d12bc4c96e852fd79b5525d3ebf0c2cd7d2f1083489d042054745bd416
6
+ metadata.gz: d75571ec0a10776b116463c144a414dfa348848fe7671c4a57719179433d41cb0b833c905ca4d73eb84a28a5c17a80921f14b007613438ec88af9e37d862c4f0
7
+ data.tar.gz: 550e11d35d35a614d50c7714752d750e24db7f990f401362df781ee5751be34a8f036bedcef39ce55905cb9e2324c2d16b6aa85f8accddb26385307dea50072f
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # Lusnoc
2
+
3
+ Lusnoc is reliable gem to deal with [Consul](https://www.consul.io). It is designed to be simple and without any background magic.
4
+ It is inspired by [consul-mutex](https://github.com/discourse/consul-mutex)(which has hard background magic).
5
+
6
+ ## FAQ
7
+
8
+ #### What's Lusnoc for?
9
+
10
+ Lusnoc allows you to interact with Consul to provide distributed locks(mutex) to your application.
11
+
12
+ #### What's the difference between lusnoc and [consul-mutex](https://github.com/discourse/consul-mutex) or [diplomat](https://github.com/WeAreFarmGeek/diplomat)
13
+ * 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.***
14
+ * diplomat provides the basic session/locks functionality but no automated control over it
15
+
16
+ #### How luscon deal with sessions/mutexes?
17
+ * Luscon ensures session creation/destruction upon block execution
18
+ * Luscon uses only sessions with TTL to protect you system from stale sessions/locks
19
+ * Luscon enforces you to manualy renew session(through callback or explicit check) but provide background session checker
20
+ * Luscon tries to carefuly handle timeouts and expiration using Consul [blocking queries](https://www.consul.io/api/features/blocking.html)
21
+
22
+ # Usage
23
+
24
+ Simply instantiate a new `Luscon::Mutex`, giving it the key you want to use
25
+ as the "lock":
26
+
27
+ ```ruby
28
+ require 'luscon/mutex'
29
+ mutex = Luscon::Mutex.new('/locks/mx1', ttl: 20)
30
+ ```
31
+ TTL will be used in session creation on `#synchronize`:
32
+ ```ruby
33
+ mutex.synchronize(timeout: 10) do |mx|
34
+ puts "We are exclusively owns resource"
35
+ end
36
+ ```
37
+ If mutex cannot be acquired within given timeout Lusnoc::TimeoutError is raised.
38
+ By default, the "value" of the lock resource will be the hostname of the
39
+ machine that it's running on (so you know who has the lock). If, for some
40
+ reason, you'd like to set the value to something else, you can do that, too:
41
+ ```ruby
42
+ Luscon::Mutex.new('/some/key', value: {time: Time.now}).synchronize do |mx|
43
+ #...
44
+ end
45
+ ```
46
+ Session invalidation/renewval handled through mutex instance:
47
+ ```ruby
48
+ Luscon::Mutex.new('/some/key').synchronize do |mx|
49
+ mx.time_to_expiration # seconds to session expiration in consul.
50
+ mx.ttl # session ttl.
51
+ mx.need_renew? # true when time_to_expiration less than half of ttl
52
+
53
+ mx.need_renew? # false
54
+ sleep (mx.ttl / 2) + 1
55
+ mx.need_renew? # true
56
+
57
+ mx.on_mutex_lost do |mutex|
58
+ # this callback will be called from other(guard) thread when mutex is lost(session invalidated)
59
+ end
60
+
61
+ mx.locked? # true while session is not expired or invalidated by admin
62
+ mx.owned? # true while session is not expired or invalidated by admin and owner is a Thread.current
63
+ mx.session_id # id of Consul session
64
+ mx.expired? # is session expired?
65
+ mx.alive? # is session alive?
66
+ mx.alive! # ensures session alive or raise Lusnoc::ExpiredError
67
+ mx.renew # renew session or raise Lusnoc::ExpiredError if session already expired
68
+ end
69
+ ```
70
+
71
+ You can use only Session:
72
+ ```ruby
73
+ Session.new("session_name", ttl: 20) do |session|
74
+ session.on_session_die do
75
+ # this callback will be called from other(guard) thread when session invalidated
76
+ end
77
+
78
+ session.expired? # is session expired?
79
+ session.alive? # is session alive?
80
+ session.alive! # ensures session alive or raise Lusnoc::ExpiredError
81
+ session.renew # renew session or raise Lusnoc::ExpiredError if session already expired
82
+ end
83
+ ```
84
+ Typical usage scenario:
85
+
86
+ ```ruby
87
+ Luscon::Mutex.new('/some/key').synchronize do |mx|
88
+ # do some work
89
+ mx.renew if mx.need_renew?
90
+ # do other work
91
+ mx.renew if mx.need_renew?
92
+ # ...
93
+ rescue Lusnoc::ExpiredError => e
94
+ # Session was invalidated and mutex was lost!
95
+ end
96
+ ```
97
+
data/lib/lusnoc/mutex.rb CHANGED
@@ -7,7 +7,7 @@ module Lusnoc
7
7
  include Helper
8
8
  attr_reader :key, :value, :owner
9
9
 
10
- def initialize(key, value = Socket.gethostname, ttl: 20)
10
+ def initialize(key, value: Socket.gethostname, ttl: 20)
11
11
  @key = key
12
12
  @value = value
13
13
  @ttl = ttl
@@ -25,10 +25,14 @@ module Lusnoc
25
25
  @session&.id
26
26
  end
27
27
 
28
- [:time_to_expiration, :need_renew?, :ttl, :expired?, :live?, :live!, :renew].each do |m|
28
+ [:time_to_expiration, :need_renew?, :ttl, :expired?, :alive?, :alive!, :renew].each do |m|
29
29
  define_method(m) { @session&.public_send(m) }
30
30
  end
31
31
 
32
+ def on_mutex_lost(&block)
33
+ @on_mutex_lost = block
34
+ end
35
+
32
36
  def synchronize(timeout: 0, &block)
33
37
  timeouter = Timeouter.new(timeout,
34
38
  exception_class: TimeoutError,
@@ -38,6 +42,7 @@ module Lusnoc
38
42
  @session = session
39
43
  session.on_session_die do
40
44
  @owner = nil
45
+ @on_mutex_lost&.call(self)
41
46
  end
42
47
 
43
48
  return acquisition_loop! key, session, value, timeouter, &block
@@ -70,7 +75,7 @@ module Lusnoc
70
75
 
71
76
  logger.debug("Start #{key} acquisition loop for session #{session.name}[#{session.id}]")
72
77
  timeouter.loop! do
73
- session.live!(TimeoutError)
78
+ session.alive!(TimeoutError)
74
79
  wait_for_key_released(key, timeouter.left)
75
80
 
76
81
  return yield(self) if acquire(key, session, value)
@@ -5,7 +5,7 @@ module Lusnoc
5
5
 
6
6
  include Helper
7
7
 
8
- attr_reader :id, :name, :ttl, :live, :expired_at
8
+ attr_reader :id, :name, :ttl, :alive, :expired_at
9
9
 
10
10
  def initialize(name, ttl: 20)
11
11
  @name = name
@@ -18,7 +18,7 @@ module Lusnoc
18
18
  end
19
19
 
20
20
  def expired?
21
- !live?
21
+ !alive?
22
22
  end
23
23
 
24
24
  def time_to_expiration
@@ -29,16 +29,16 @@ module Lusnoc
29
29
  time_to_expiration && time_to_expiration < (@ttl / 2.0)
30
30
  end
31
31
 
32
- def live?
33
- @live
32
+ def alive?
33
+ @alive
34
34
  end
35
35
 
36
- def live!(exception_class = ExpiredError)
37
- live? || (raise exception_class.new("Session #{id} expired"))
36
+ def alive!(exception_class = ExpiredError)
37
+ alive? || (raise exception_class.new("Session #{id} expired"))
38
38
  end
39
39
 
40
40
  def renew
41
- live!
41
+ alive!
42
42
  Lusnoc.http_put(build_url("/v1/session/renew/#{@id}"), nil, timeout: 1)
43
43
  @expired_at = Time.now + ttl
44
44
  logger.info "Session renewed: #{name}[#{@id}]. Next expiration: #{@expired_at}"
@@ -57,7 +57,7 @@ module Lusnoc
57
57
  session_id = JSON.parse(resp.body)['ID']
58
58
  @expired_at = Time.now + ttl
59
59
  logger.info "Session created: #{name}[#{session_id}]. TTL:#{ttl}s. Next expiration: #{@expired_at}"
60
- @live = true
60
+ @alive = true
61
61
  @th = start_watch_thread(session_id)
62
62
  session_id
63
63
  end
@@ -68,7 +68,7 @@ module Lusnoc
68
68
  nil,
69
69
  timeout: 1) rescue nil
70
70
  logger.info "Session destroyed: #{name}[#{session_id}]"
71
- @live = false
71
+ @alive = false
72
72
  @expired_at = nil
73
73
  end
74
74
 
@@ -78,7 +78,7 @@ module Lusnoc
78
78
 
79
79
  if wait_forever_for_session_gone(session_id)
80
80
  logger.error "Session #{name}[#{session_id}] is gone"
81
- @live = false
81
+ @alive = false
82
82
  @expired_at = nil
83
83
  @session_die_cb&.call(self)
84
84
  else
@@ -1,6 +1,6 @@
1
1
  module Lusnoc
2
2
 
3
- VERSION = '0.0.2'.freeze
3
+ VERSION = '0.1.0'.freeze
4
4
 
5
5
  end
6
6
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lusnoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samoilenko Yuri
@@ -107,6 +107,7 @@ executables: []
107
107
  extensions: []
108
108
  extra_rdoc_files: []
109
109
  files:
110
+ - README.md
110
111
  - lib/lusnoc.rb
111
112
  - lib/lusnoc/configuration.rb
112
113
  - lib/lusnoc/exceptions.rb