event_store_subscriptions 1.0.0 → 1.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: 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