event_store_subscriptions 1.0.0 → 1.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: f261c1fb47c48ba5354b9d02c1b1a066e64813e397933657bbb75c396ec06f85
4
- data.tar.gz: ce40a5265ba3d2bc34f4392cae838df08a471c13c85bbecc292dd53628cf464f
3
+ metadata.gz: a7e6e16c2a7382f97ae35a90701acbcd6d1ddbb76f3de3d8d91fb4f5a5970301
4
+ data.tar.gz: 297942167731aa297bd5d1d036cba81fc8a5b2f050abc5a46c2918ffe43260c3
5
5
  SHA512:
6
- metadata.gz: 97ce25b1bbcd50a345b2be4ed7f1b98048f540732a91eafff55d36c31d6d513ad759614938dba6a07139a448d0e2c3b35bdfc1bb1e96b77ee2de51643b07b84d
7
- data.tar.gz: 995f4264d8a301b8dae0156ab0d08fbe3ca41744194b4357e6fddba9d29b73deec207d98d6c9859fad8f5cbc41e3204580c163d76b61e8f8ea4e92c814ab8ded
6
+ metadata.gz: f5425d9761f330cd9cbabf2b19947709cb75cfecb7a604015fc6c1a8f768ebc97d9d35d87191e14706db7f9bfe2b79fce2a39608cca004116333f80ea7356d95
7
+ data.tar.gz: b3e1c417218056c29a85ef135a270296e2683258cc57b49bc19c7e12a7949b8aac2076fc9aa45fbd6c1bc69c4809a77b89b56c68bc91467a6d39a89c0f7228b3
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EventStoreSubscriptions
4
+ module MakeAtomic
5
+ # Wraps method in Mutex#synchronize to make it atomic. You should have #semaphore method
6
+ # implemented in order this to work.
7
+ # @param method [Symbol] a name of the method
8
+ # @return [Symbol]
9
+ def make_atomic(method)
10
+ module_to_prepend = Module.new do
11
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
12
+ def #{method}(*args, **kwargs, &blk)
13
+ semaphore.synchronize do
14
+ super
15
+ end
16
+ end
17
+ RUBY
18
+ end
19
+ prepend module_to_prepend
20
+ method
21
+ end
22
+ end
23
+ end
@@ -10,7 +10,6 @@ module EventStoreSubscriptions
10
10
  STATES = %i(initial running halting stopped dead).freeze
11
11
 
12
12
  def initialize
13
- @semaphore = Thread::Mutex.new
14
13
  initial!
15
14
  end
16
15
 
@@ -18,13 +17,13 @@ module EventStoreSubscriptions
18
17
  # Checks whether the object is in appropriate state
19
18
  # @return [Boolean]
20
19
  define_method "#{state}?" do
21
- semaphore.synchronize { self.state == state }
20
+ self.state == state
22
21
  end
23
22
 
24
23
  # Sets the state.
25
24
  # @return [Symbol]
26
25
  define_method "#{state}!" do
27
- semaphore.synchronize { self.state = state }
26
+ self.state = state
28
27
  end
29
28
  end
30
29
 
@@ -3,11 +3,12 @@
3
3
  module EventStoreSubscriptions
4
4
  class Subscription
5
5
  include WaitForFinish
6
+ extend MakeAtomic
6
7
 
7
8
  FORCED_SHUTDOWN_DELAY = 60 # seconds
8
9
 
9
10
  attr_accessor :runner
10
- attr_reader :client, :setup, :state, :position, :statistic
11
+ attr_reader :client, :setup, :state, :position, :statistic, :semaphore
11
12
  private :runner, :runner=
12
13
 
13
14
  # @param position [EventStoreSubscriptions::SubscriptionPosition, EventStoreSubscriptions::SubscriptionRevision]
@@ -21,36 +22,19 @@ module EventStoreSubscriptions
21
22
  @state = ObjectState.new
22
23
  @statistic = statistic
23
24
  @runner = nil
25
+ @semaphore = Mutex.new
24
26
  end
25
27
 
26
28
  # Start listening for the events
27
29
  # @return [EventStoreSubscriptions::Subscription] returns self
28
- def listen
29
- self.runner ||=
30
- begin
31
- state.running!
32
- Thread.new do
33
- Thread.current.abort_on_exception = false
34
- Thread.current.report_on_exception = false
35
- client.subscribe_to_stream(
36
- *setup.args,
37
- **adjusted_kwargs,
38
- &setup.blk
39
- )
40
- rescue StandardError => e
41
- statistic.last_error = e
42
- statistic.errors_count += 1
43
- state.dead!
44
- raise
45
- end
46
- end
47
- self
30
+ make_atomic def listen
31
+ _listen
48
32
  end
49
33
 
50
34
  # Stops listening for events. This command is async - the result is not immediate. Use the #wait_for_finish
51
35
  # method to wait until the runner has fully stopped.
52
36
  # @return [EventStoreSubscriptions::Subscription] returns self
53
- def stop_listening
37
+ make_atomic def stop_listening
54
38
  return self unless runner&.alive?
55
39
 
56
40
  state.halting!
@@ -71,22 +55,38 @@ module EventStoreSubscriptions
71
55
  self
72
56
  end
73
57
 
74
- # Removes all properties of object and freezes it. You can't delete currently running
75
- # Subscription though. You must stop it first.
76
- # @return [EventStoreSubscriptions::Subscription] frozen object
77
- # @raise [EventStoreSubscriptions::ThreadNotDeadError] raises this error in case runner Thread
78
- # is still alive for some reason. Normally this should never happen.
79
- def delete
80
- if runner&.alive?
81
- raise ThreadNotDeadError, "Can not delete alive Subscription #{self.inspect}"
82
- end
58
+ make_atomic def restart
59
+ return self if runner&.alive?
83
60
 
84
- instance_variables.each { |var| instance_variable_set(var, nil) }
85
- freeze
61
+ statistic.last_restart_at = Time.now.utc
62
+ _listen
86
63
  end
87
64
 
88
65
  private
89
66
 
67
+ # @return [EventStoreSubscriptions::Subscription] returns self
68
+ def _listen
69
+ self.runner ||=
70
+ begin
71
+ state.running!
72
+ Thread.new do
73
+ Thread.current.abort_on_exception = false
74
+ Thread.current.report_on_exception = false
75
+ client.subscribe_to_stream(
76
+ *setup.args,
77
+ **adjusted_kwargs,
78
+ &setup.blk
79
+ )
80
+ rescue StandardError => e
81
+ statistic.last_error = e
82
+ statistic.errors_count += 1
83
+ state.dead!
84
+ raise
85
+ end
86
+ end
87
+ self
88
+ end
89
+
90
90
  # Wraps original handler into our own handler to provide extended functionality.
91
91
  # @param original_handler [#call]
92
92
  # @return [Proc]
@@ -3,6 +3,7 @@
3
3
  module EventStoreSubscriptions
4
4
  # Implements Subscription-s collection
5
5
  class Subscriptions
6
+ extend MakeAtomic
6
7
  ALL_STREAM = '$all'
7
8
 
8
9
  attr_reader :client
@@ -37,34 +38,34 @@ module EventStoreSubscriptions
37
38
  # Adds Subscription to the collection
38
39
  # @param subscription [EventStoreSubscriptions::Subscription]
39
40
  # @return [Array<EventStoreSubscriptions::Subscription>] current subscription's collection
40
- def add(subscription)
41
- semaphore.synchronize { @subscriptions << subscription }
41
+ make_atomic def add(subscription)
42
+ @subscriptions << subscription
42
43
  end
43
44
 
44
45
  # Removes subscription from the collection
45
46
  # @param subscription [EventStoreSubscriptions::Subscription]
46
47
  # @return [EventStoreSubscriptions::Subscription, nil] returns deleted subscription or nil if it
47
48
  # wasn't present in the collection
48
- def remove(subscription)
49
- semaphore.synchronize { @subscriptions.delete(subscription) }
49
+ make_atomic def remove(subscription)
50
+ @subscriptions.delete(subscription)
50
51
  end
51
52
 
52
53
  # Starts listening to all subscriptions in the collection
53
54
  # @return [Array<EventStoreSubscriptions::Subscription>]
54
- def listen_all
55
- semaphore.synchronize { @subscriptions.each(&:listen) }
55
+ make_atomic def listen_all
56
+ @subscriptions.each(&:listen)
56
57
  end
57
58
 
58
59
  # Stops listening to all subscriptions in the collection
59
60
  # @return [Array<EventStoreSubscriptions::Subscription>]
60
- def stop_all
61
- semaphore.synchronize { @subscriptions.each(&:stop_listening) }
61
+ make_atomic def stop_all
62
+ @subscriptions.each(&:stop_listening)
62
63
  end
63
64
 
64
65
  # @return [Array<EventStoreSubscriptions::Subscription>]
65
- def subscriptions
66
+ make_atomic def subscriptions
66
67
  # Duping original collection to prevent potential mutable operations over it from user's side
67
- semaphore.synchronize { @subscriptions.dup }
68
+ @subscriptions.dup
68
69
  end
69
70
 
70
71
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EventStoreSubscriptions
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -92,19 +92,8 @@ module EventStoreSubscriptions
92
92
  # @return [EventStoreSubscriptions::Subscription] newly created Subscription
93
93
  def restart_subscription(failed_sub)
94
94
  return if restart_terminator&.call(failed_sub)
95
- # Check if no one else did this job
96
- return unless collection.remove(failed_sub)
97
95
 
98
- new_sub = Subscription.new(
99
- position: failed_sub.position,
100
- client: failed_sub.client,
101
- setup: failed_sub.setup,
102
- statistic: failed_sub.statistic
103
- )
104
- new_sub.statistic.last_restart_at = Time.now.utc
105
- collection.add(new_sub)
106
- failed_sub.delete
107
- new_sub.listen
96
+ failed_sub.restart
108
97
  end
109
98
  end
110
99
  end
@@ -4,6 +4,7 @@ require 'event_store_client'
4
4
  require_relative 'event_store_subscriptions/version'
5
5
  require_relative 'event_store_subscriptions/error'
6
6
  require_relative 'event_store_subscriptions/wait_for_finish'
7
+ require_relative 'event_store_subscriptions/make_atomic'
7
8
  require_relative 'event_store_subscriptions/object_state'
8
9
  require_relative 'event_store_subscriptions/subscription_statistic'
9
10
  require_relative 'event_store_subscriptions/subscription'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_store_subscriptions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Dzyzenko
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-03 00:00:00.000000000 Z
11
+ date: 2022-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: event_store_client
@@ -77,6 +77,7 @@ files:
77
77
  - README.md
78
78
  - lib/event_store_subscriptions.rb
79
79
  - lib/event_store_subscriptions/error.rb
80
+ - lib/event_store_subscriptions/make_atomic.rb
80
81
  - lib/event_store_subscriptions/object_state.rb
81
82
  - lib/event_store_subscriptions/subscription.rb
82
83
  - lib/event_store_subscriptions/subscription_position.rb