lusnoc 0.0.2 → 0.1.0

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 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