event_store_client 2.1.5 → 2.3.0.pre.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/docs/configuration.md +93 -9
  3. data/lib/event_store_client/adapters/grpc/client.rb +22 -12
  4. data/lib/event_store_client/adapters/grpc/cluster/gossip_discover.rb +10 -3
  5. data/lib/event_store_client/adapters/grpc/cluster/insecure_connection.rb +1 -1
  6. data/lib/event_store_client/adapters/grpc/cluster/queryless_discover.rb +8 -2
  7. data/lib/event_store_client/adapters/grpc/cluster/secure_connection.rb +1 -1
  8. data/lib/event_store_client/adapters/grpc/commands/command.rb +7 -5
  9. data/lib/event_store_client/adapters/grpc/commands/streams/append_multiple.rb +3 -1
  10. data/lib/event_store_client/adapters/grpc/commands/streams/link_to.rb +1 -1
  11. data/lib/event_store_client/adapters/grpc/commands/streams/link_to_multiple.rb +1 -2
  12. data/lib/event_store_client/adapters/grpc/commands/streams/read.rb +5 -2
  13. data/lib/event_store_client/adapters/grpc/commands/streams/read_paginated.rb +8 -6
  14. data/lib/event_store_client/adapters/grpc/commands/streams/subscribe.rb +5 -2
  15. data/lib/event_store_client/adapters/grpc/connection.rb +20 -31
  16. data/lib/event_store_client/adapters/grpc/discover.rb +47 -27
  17. data/lib/event_store_client/adapters/grpc/options/streams/read_options.rb +5 -5
  18. data/lib/event_store_client/adapters/grpc/shared/streams/process_response.rb +8 -1
  19. data/lib/event_store_client/adapters/grpc/shared/streams/process_responses.rb +8 -1
  20. data/lib/event_store_client/config.rb +55 -0
  21. data/lib/event_store_client/connection/url.rb +8 -0
  22. data/lib/event_store_client/data_decryptor.rb +0 -2
  23. data/lib/event_store_client/mapper/default.rb +8 -4
  24. data/lib/event_store_client/mapper/encrypted.rb +10 -7
  25. data/lib/event_store_client/serializer/event_deserializer.rb +7 -9
  26. data/lib/event_store_client/version.rb +1 -1
  27. data/lib/event_store_client.rb +45 -4
  28. metadata +5 -5
  29. data/lib/event_store_client/configuration.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4b94342dbb48eeba88c4f87758497bf3604ec01d1954163af2d9add2e88e46f
4
- data.tar.gz: 195564b4fded0a7c5986b4f3b5b76a19ad97018113238389a26254df27191f49
3
+ metadata.gz: 96e1f2743b3265c8776ffe39636f086af2be0bc19ff46e693f1fbf7373eeb3d4
4
+ data.tar.gz: 7595e367ccced285bb4e061d9875bf6ba84d9e999aa60f404609f0258aea1426
5
5
  SHA512:
6
- metadata.gz: 62c2faaed828d92add77f8c8af76f7bae5fe3fa990e9820e4fc5dd6b74ad7b3fbe0e84aa7d2975c6903e91e959d6969d5ae30aa3d2916300086d2cfa81fce8b0
7
- data.tar.gz: 5b282b014013149ecb61531ad02fe654321765f15da09fc4ffeadabc2fbefc21c919a769d657cfc0fe7922d80338dab6908bf1a00a21b672d9128603062e448f
6
+ metadata.gz: 88e2b3324a0b59773f39077ad954e8a0f98ca0fd0bd67217a67c742bdc5ae6efbdf36878a0feeff9e2532e8f1d97f5a6eee0581268dc24cda631728fe5fc65d3
7
+ data.tar.gz: 73be7b894f397e323807adec3667b78ddc7f7b5973942cb7d77cb5872af1d6b01eee9f9e7c7440db213d3ea3fb23256e99066ce4d0e23af3c26bbd14c3c4bb56
@@ -6,15 +6,52 @@ Currently only one setup is supported. For example, you can't configure a connec
6
6
 
7
7
  Configuration options:
8
8
 
9
- | name | value | default value | description |
10
- |----------------------|--------------------------------------------------------------------------------------|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
11
- | eventstore_url | String | `'esdb://localhost:2113'` | Connection string. See description of possible values below. |
12
- | per_page | Integer | `20` | Number of events to return in one response. |
13
- | mapper | `EventStoreClient::Mapper::Default.new` or `EventStoreClient::Mapper::Encrypted.new` | `EventStoreClient::Mapper::Default.new` | An object that is responsible for serialization / deserialization and encryption / decryption of events. |
14
- | default_event_class | `DeserializedEvent` or any class, inherited from it | `DeserializedEvent` | This class will be used during the deserialization process when deserializer fails to resolve an event's class from response. |
15
- | logger | `Logger` | `nil` | A logger that would log messages from `event_store_client` and `grpc` gems. |
16
- | skip_deserialization | Boolean | `false` | Whether to skip event deserialization using the given `mapper` setting. If you set it to `true` decryption will be skipped as well. It is useful when you want to defer deserialization and handle it later by yourself. |
17
- | skip_decryption | Boolean | `false` | Whether to skip decrypting encrypted event payloads. |
9
+ | name | value | default value | description |
10
+ |----------------------|--------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
11
+ | eventstore_url | String | `'esdb://localhost:2113'` | Connection string. See description of possible values below. |
12
+ | per_page | Integer | `20` | Number of events to return in one response. |
13
+ | mapper | `EventStoreClient::Mapper::Default.new` or `EventStoreClient::Mapper::Encrypted.new` | `EventStoreClient::Mapper::Default.new` | An object that is responsible for serialization / deserialization and encryption / decryption of events. |
14
+ | default_event_class | `DeserializedEvent` or any class, inherited from it | `DeserializedEvent` | This class will be used during the deserialization process when deserializer fails to resolve an event's class from response. |
15
+ | logger | `Logger` | `nil` | A logger that would log messages from `event_store_client` and `grpc` gems. |
16
+ | skip_deserialization | Boolean | `false` | Whether to skip event deserialization using the given `mapper` setting. If you set it to `true` decryption will be skipped as well. It is useful when you want to defer deserialization and handle it later by yourself. |
17
+ | skip_decryption | Boolean | `false` | Whether to skip decrypting encrypted event payloads. |
18
+ | channel_args | Hash | `{ 'grpc.min_reconnect_backoff_ms' => 100, 'grpc.max_reconnect_backoff_ms' => 100, 'grpc.initial_reconnect_backoff_ms' => 100 }` | GRPC-specific connection options. This hash will be passed into the `:channel_args` argument of a Stub class of your request. More GRPC options can be found [here](https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/grpc_types.h). |
19
+
20
+ ## A note about channel_args option
21
+
22
+ When defining the custom value of this option - the default value will be reverse-merged into it. So, for example, if you would like to set `grpc.min_reconnect_backoff_ms` like so:
23
+
24
+ ```ruby
25
+ EventStoreClient.configure do |config|
26
+ config.channel_args = { 'grpc.min_reconnect_backoff_ms' => 200 }
27
+ end
28
+ ```
29
+
30
+ the resulting `channel_args` value will be
31
+
32
+ ```ruby
33
+ {
34
+ "grpc.min_reconnect_backoff_ms" => 200,
35
+ "grpc.max_reconnect_backoff_ms" => 100,
36
+ "grpc.initial_reconnect_backoff_ms" => 100
37
+ }
38
+ ```
39
+
40
+ This behaviour is intentional. So, if you want to override them all - you should do it explicitly. Example:
41
+
42
+ ```ruby
43
+ EventStoreClient.configure do |config|
44
+ config.channel_args = {
45
+ 'grpc.min_reconnect_backoff_ms' => 500,
46
+ 'grpc.max_reconnect_backoff_ms' => 500,
47
+ 'grpc.initial_reconnect_backoff_ms' => 500
48
+ }
49
+ end
50
+ ```
51
+
52
+ It could be useful when you have high ping to your EventStore DB server.
53
+
54
+ Besides, there is an option which you can't change - `grpc.enable_retries`. It always defaults to `0`. This is because `event_store_client` implements its own retry functional.
18
55
 
19
56
  ## Connection string
20
57
 
@@ -91,3 +128,50 @@ esdb://localhost:2113/?tls=false
91
128
  esdb+discover://localhost:2113/?grpcRetryAttempts=3&grpcRetryInterval=300&discoverInterval=200
92
129
  esdb://localhost:2113,localhost:2114,localhost:2115/?gossipTimeout=500&maxDiscoverAttempts=3
93
130
  ```
131
+
132
+ ### Multiple configurations
133
+
134
+ `event_store_client` provides a possibility to configure as much connections as you want. To do so, you have to name your configuration, and later provide that name to `EventStoreClient` client.
135
+
136
+ Setup your configs:
137
+ ```ruby
138
+ EventStoreClient.configure(name: :es_db_1) do |config|
139
+ # adjust your config here
140
+ config.eventstore_url = 'esdb://localhost:2113'
141
+ end
142
+ EventStoreClient.configure(name: :es_db_2) do |config|
143
+ # adjust your second config here
144
+ config.eventstore_url = 'esdb://localhost:2114'
145
+ end
146
+ ```
147
+
148
+ Tell `EventStoreClient` which config you want to use:
149
+
150
+ ```ruby
151
+ client1 = EventStoreClient.client(config_name: :es_db_1)
152
+ client2 = EventStoreClient.client(config_name: :es_db_2)
153
+ # Read from $all stream using :es_db_1 config
154
+ client1.read('$all')
155
+ # Read from $all stream using :es_db_2 config
156
+ client2.read('$all')
157
+ ```
158
+
159
+ #### Default config
160
+
161
+ If you only have one config - you don't have to bother naming it or passing a config name to the client when performing any operations. You can configure it as usual.
162
+
163
+ Setup your default config:
164
+ ```ruby
165
+ EventStoreClient.configure do |config|
166
+ # config goes here
167
+ config.eventstore_url = 'esdb://localhost:2113'
168
+ end
169
+ ```
170
+
171
+ Use it:
172
+
173
+ ```ruby
174
+ client = EventStoreClient.client
175
+ # Read from $all stream using your default config
176
+ client.read('$all')
177
+ ```
@@ -5,7 +5,13 @@
5
5
  module EventStoreClient
6
6
  module GRPC
7
7
  class Client
8
- include Configuration
8
+ attr_reader :config
9
+ private :config
10
+
11
+ # @param config [EventStoreClient::Config]
12
+ def initialize(config)
13
+ @config = config
14
+ end
9
15
 
10
16
  # @param stream_name [String]
11
17
  # @param events_or_event [EventStoreClient::DeserializedEvent, Array<EventStoreClient::DeserializedEvent>]
@@ -30,11 +36,11 @@ module EventStoreClient
30
36
  # Returns monads' Success/Failure in case whether request was performed.
31
37
  def append_to_stream(stream_name, events_or_event, options: {}, credentials: {}, &blk)
32
38
  if events_or_event.is_a?(Array)
33
- Commands::Streams::AppendMultiple.new(**credentials).call(
39
+ Commands::Streams::AppendMultiple.new(config: config, **credentials).call(
34
40
  stream_name, events_or_event, options: options
35
41
  )
36
42
  else
37
- Commands::Streams::Append.new(**credentials).call(
43
+ Commands::Streams::Append.new(config: config, **credentials).call(
38
44
  stream_name, events_or_event, options: options, &blk
39
45
  )
40
46
  end
@@ -91,7 +97,7 @@ module EventStoreClient
91
97
  # @return [Dry::Monads::Success, Dry::Monads::Failure]
92
98
  def read(stream_name, options: {}, skip_deserialization: config.skip_deserialization,
93
99
  skip_decryption: config.skip_decryption, credentials: {}, &blk)
94
- Commands::Streams::Read.new(**credentials).call(
100
+ Commands::Streams::Read.new(config: config, **credentials).call(
95
101
  stream_name,
96
102
  options: options,
97
103
  skip_deserialization: skip_deserialization,
@@ -106,7 +112,7 @@ module EventStoreClient
106
112
  def read_paginated(stream_name, options: {}, credentials: {},
107
113
  skip_deserialization: config.skip_deserialization,
108
114
  skip_decryption: config.skip_decryption, &blk)
109
- Commands::Streams::ReadPaginated.new(**credentials).call(
115
+ Commands::Streams::ReadPaginated.new(config: config, **credentials).call(
110
116
  stream_name,
111
117
  options: options,
112
118
  skip_deserialization: skip_deserialization,
@@ -133,7 +139,9 @@ module EventStoreClient
133
139
  # ```
134
140
  # @return [Dry::Monads::Success, Dry::Monads::Failure]
135
141
  def hard_delete_stream(stream_name, options: {}, credentials: {}, &blk)
136
- Commands::Streams::HardDelete.new(**credentials).call(stream_name, options: options, &blk)
142
+ Commands::Streams::HardDelete.
143
+ new(config: config, **credentials).
144
+ call(stream_name, options: options, &blk)
137
145
  end
138
146
 
139
147
  # Refs https://developers.eventstore.com/server/v5/streams.html#soft-delete-and-truncatebefore
@@ -154,7 +162,9 @@ module EventStoreClient
154
162
  # ```
155
163
  # @return [Dry::Monads::Success, Dry::Monads::Failure]
156
164
  def delete_stream(stream_name, options: {}, credentials: {}, &blk)
157
- Commands::Streams::Delete.new(**credentials).call(stream_name, options: options, &blk)
165
+ Commands::Streams::Delete.
166
+ new(config: config, **credentials).
167
+ call(stream_name, options: options, &blk)
158
168
  end
159
169
 
160
170
  # Subscribe to the given stream and listens for events. Note, that it will block execution of
@@ -213,7 +223,7 @@ module EventStoreClient
213
223
  def subscribe_to_stream(stream_name, handler:, options: {}, credentials: {},
214
224
  skip_deserialization: config.skip_deserialization,
215
225
  skip_decryption: config.skip_decryption, &blk)
216
- Commands::Streams::Subscribe.new(**credentials).call(
226
+ Commands::Streams::Subscribe.new(config: config, **credentials).call(
217
227
  stream_name,
218
228
  handler: handler,
219
229
  options: options,
@@ -229,7 +239,7 @@ module EventStoreClient
229
239
  def subscribe_to_all(handler:, options: {}, credentials: {},
230
240
  skip_deserialization: config.skip_deserialization,
231
241
  skip_decryption: config.skip_decryption, &blk)
232
- Commands::Streams::Subscribe.new(**credentials).call(
242
+ Commands::Streams::Subscribe.new(config: config, **credentials).call(
233
243
  '$all',
234
244
  handler: handler,
235
245
  options: options,
@@ -246,14 +256,14 @@ module EventStoreClient
246
256
  # @see #append_to_stream for available params and returned value
247
257
  def link_to(stream_name, events_or_event, options: {}, credentials: {}, &blk)
248
258
  if events_or_event.is_a?(Array)
249
- Commands::Streams::LinkToMultiple.new(**credentials).call(
259
+ Commands::Streams::LinkToMultiple.new(config: config, **credentials).call(
250
260
  stream_name,
251
261
  events_or_event,
252
262
  options: options,
253
263
  &blk
254
264
  )
255
265
  else
256
- Commands::Streams::LinkTo.new(**credentials).call(
266
+ Commands::Streams::LinkTo.new(config: config, **credentials).call(
257
267
  stream_name,
258
268
  events_or_event,
259
269
  options: options,
@@ -267,7 +277,7 @@ module EventStoreClient
267
277
  # @option credentials [String] :password
268
278
  # @return [Dry::Monads::Success, Dry::Monads::Failure]
269
279
  def cluster_info(credentials: {})
270
- Commands::Gossip::ClusterInfo.new(**credentials).call
280
+ Commands::Gossip::ClusterInfo.new(config: config, **credentials).call
271
281
  end
272
282
  end
273
283
  end
@@ -6,8 +6,6 @@ module EventStoreClient
6
6
  module GRPC
7
7
  module Cluster
8
8
  class GossipDiscover
9
- include Configuration
10
-
11
9
  DiscoverError = Class.new(StandardError)
12
10
 
13
11
  # Order is important - it plays role of states priority as well
@@ -15,6 +13,14 @@ module EventStoreClient
15
13
  ALLOWED_NODE_STATES =
16
14
  (%i[Leader Follower] + READ_ONLY_STATES).freeze
17
15
 
16
+ attr_reader :config
17
+ private :config
18
+
19
+ # @param config [EventStoreClient::Config]
20
+ def initialize(config:)
21
+ @config = config
22
+ end
23
+
18
24
  # @param nodes [Array<EventStoreClient::Connection::Url::Node>]
19
25
  # @param failed_member [EventStoreClient::GRPC::Cluster::Member, nil]
20
26
  # @return [EventStoreClient::GRPC::Cluster::Member]
@@ -58,7 +64,8 @@ module EventStoreClient
58
64
  connection = Connection.new(
59
65
  host: node.host,
60
66
  port: node.port,
61
- timeout: config.eventstore_url.gossip_timeout
67
+ timeout: config.eventstore_url.gossip_timeout,
68
+ config: config
62
69
  )
63
70
  members =
64
71
  connection.
@@ -11,7 +11,7 @@ module EventStoreClient
11
11
  stub_class.new(
12
12
  "#{host}:#{port}",
13
13
  :this_channel_is_insecure,
14
- channel_args: channel_args,
14
+ channel_args: config.channel_args,
15
15
  timeout: (timeout / 1000.0 if timeout)
16
16
  )
17
17
  end
@@ -4,10 +4,16 @@ module EventStoreClient
4
4
  module GRPC
5
5
  module Cluster
6
6
  class QuerylessDiscover
7
- include Configuration
8
-
9
7
  NoHostError = Class.new(StandardError)
10
8
 
9
+ attr_reader :config
10
+ private :config
11
+
12
+ # @param config [EventStoreClient::Config]
13
+ def initialize(config:)
14
+ @config = config
15
+ end
16
+
11
17
  # @param nodes [EventStoreClient::Connection::Url::Node]
12
18
  # @return [EventStoreClient::GRPC::Cluster::Member]
13
19
  def call(nodes)
@@ -13,7 +13,7 @@ module EventStoreClient
13
13
  stub_class.new(
14
14
  "#{host}:#{port}",
15
15
  channel_credentials,
16
- channel_args: channel_args,
16
+ channel_args: config.channel_args,
17
17
  timeout: (timeout / 1000.0 if timeout)
18
18
  )
19
19
  end
@@ -14,19 +14,20 @@ module EventStoreClient
14
14
  end
15
15
  end
16
16
 
17
- include Configuration
18
17
  include Dry::Monads[:try, :result]
19
18
 
20
- attr_reader :connection
21
- private :connection
19
+ attr_reader :connection, :config
20
+ private :connection, :config
22
21
 
22
+ # @param config [EventStoreClient::Config]
23
23
  # @param conn_options [Hash]
24
24
  # @option conn_options [String] :host
25
25
  # @option conn_options [Integer] :port
26
26
  # @option conn_options [String] :username
27
27
  # @option conn_options [String] :password
28
- def initialize(**conn_options)
29
- @connection = EventStoreClient::GRPC::Connection.new(**conn_options)
28
+ def initialize(config:, **conn_options)
29
+ @config = config
30
+ @connection = EventStoreClient::GRPC::Connection.new(config: config, **conn_options)
30
31
  end
31
32
 
32
33
  # Override it in your implementation of command.
@@ -74,6 +75,7 @@ module EventStoreClient
74
75
  config.logger&.debug("Request failed. Reason: #{e.class}. Retying.")
75
76
  retry
76
77
  end
78
+ Discover.current_member(config: config)&.failed_endpoint = true
77
79
  raise
78
80
  end
79
81
  end
@@ -12,7 +12,9 @@ module EventStoreClient
12
12
  def call(stream_name, events, options:, &blk)
13
13
  result = []
14
14
  events.each.with_index do |event, index|
15
- response = Commands::Streams::Append.new(**connection_options).call(
15
+ response = Commands::Streams::Append.new(
16
+ config: config, **connection_options
17
+ ).call(
16
18
  stream_name, event, options: options
17
19
  ) do |req_opts, proposed_msg_opts|
18
20
  req_opts.options.revision += index if has_revision_option?(req_opts.options)
@@ -7,7 +7,7 @@ module EventStoreClient
7
7
  class LinkTo < Command
8
8
  # @see {EventStoreClient::GRPC::Client#hard_delete_stream}
9
9
  def call(stream_name, event, options:, &blk)
10
- append_cmd = Append.new(**connection_options)
10
+ append_cmd = Append.new(config: config, **connection_options)
11
11
  link_event = DeserializedEvent.new(
12
12
  id: event.id, type: DeserializedEvent::LINK_TYPE, data: event.title
13
13
  )
@@ -11,12 +11,11 @@ module EventStoreClient
11
11
  # @see {EventStoreClient::GRPC::Client#link_to}
12
12
  def call(stream_name, events, options:, &blk)
13
13
  result = []
14
- link_cmd = Commands::Streams::LinkTo.new(**connection_options)
14
+ link_cmd = Commands::Streams::LinkTo.new(config: config, **connection_options)
15
15
  events.each.with_index do |event, index|
16
16
  response =
17
17
  link_cmd.call(stream_name, event, options: options) do |req_opts, proposed_msg_opts|
18
18
  req_opts.options.revision += index if has_revision_option?(req_opts.options)
19
-
20
19
  yield(req_opts, proposed_msg_opts) if blk
21
20
  end
22
21
  result.push(response)
@@ -15,7 +15,7 @@ module EventStoreClient
15
15
  yield options if block_given?
16
16
  result =
17
17
  retry_request { service.read(request.new(options: options), metadata: metadata).to_a }
18
- EventStoreClient::GRPC::Shared::Streams::ProcessResponses.new.call(
18
+ EventStoreClient::GRPC::Shared::Streams::ProcessResponses.new(config: config).call(
19
19
  result,
20
20
  skip_deserialization,
21
21
  skip_decryption
@@ -28,7 +28,10 @@ module EventStoreClient
28
28
  # @param options [Hash]
29
29
  # @return [EventStore::Client::Streams::ReadReq::Options]
30
30
  def normalize_options(stream_name, options)
31
- options = Options::Streams::ReadOptions.new(stream_name, options).request_options
31
+ options =
32
+ Options::Streams::ReadOptions.
33
+ new(stream_name, options, config: config).
34
+ request_options
32
35
  EventStore::Client::Streams::ReadReq::Options.new(options)
33
36
  end
34
37
  end
@@ -22,7 +22,7 @@ module EventStoreClient
22
22
  Enumerator.new do |yielder|
23
23
  loop do
24
24
  response =
25
- Read.new(**connection_options).call(
25
+ Read.new(config: config, **connection_options).call(
26
26
  stream_name,
27
27
  options: options,
28
28
  skip_deserialization: true,
@@ -45,11 +45,13 @@ module EventStoreClient
45
45
  raise StopIteration
46
46
  end
47
47
  processed_response =
48
- EventStoreClient::GRPC::Shared::Streams::ProcessResponses.new.call(
49
- response.success,
50
- skip_deserialization,
51
- skip_decryption
52
- )
48
+ EventStoreClient::GRPC::Shared::Streams::ProcessResponses.
49
+ new(config: config).
50
+ call(
51
+ response.success,
52
+ skip_deserialization,
53
+ skip_decryption
54
+ )
53
55
  yielder << processed_response if processed_response.success.any?
54
56
  raise StopIteration if end_reached?(response.success, max_count)
55
57
 
@@ -20,7 +20,7 @@ module EventStoreClient
20
20
  yield options if block_given?
21
21
 
22
22
  callback = proc do |response|
23
- result = Shared::Streams::ProcessResponse.new.call(
23
+ result = Shared::Streams::ProcessResponse.new(config: config).call(
24
24
  response,
25
25
  skip_deserialization,
26
26
  skip_decryption
@@ -41,7 +41,10 @@ module EventStoreClient
41
41
  # @param options [Hash]
42
42
  # @return [EventStore::Client::Streams::ReadReq::Options]
43
43
  def normalize_options(stream_name, options)
44
- options = Options::Streams::ReadOptions.new(stream_name, options).request_options
44
+ options =
45
+ Options::Streams::ReadOptions.
46
+ new(stream_name, options, config: config).
47
+ request_options
45
48
  EventStore::Client::Streams::ReadReq::Options.new(options).tap do |opts|
46
49
  opts.subscription =
47
50
  EventStore::Client::Streams::ReadReq::Options::SubscriptionOptions.new
@@ -7,18 +7,7 @@ require 'net/http'
7
7
  module EventStoreClient
8
8
  module GRPC
9
9
  class Connection
10
- include Configuration
11
- include Extensions::OptionsExtension
12
-
13
- option(:host) { Discover.current_member.host }
14
- option(:port) { Discover.current_member.port }
15
- option(:username) { config.eventstore_url.username }
16
- option(:password) { config.eventstore_url.password }
17
- option(:timeout) { config.eventstore_url.timeout }
18
-
19
10
  class << self
20
- include Configuration
21
-
22
11
  # Resolve which connection class we instantiate, based on config.eventstore_url.tls config
23
12
  # option. If :new method is called from SecureConnection or InsecureConnection class - then
24
13
  # that particular class will be instantiated despite on config.eventstore_url.tls config
@@ -35,13 +24,14 @@ module EventStoreClient
35
24
  # Cluster::InsecureConnection.new
36
25
  # # => #<EventStoreClient::GRPC::Cluster::InsecureConnection>
37
26
  # ```
38
- def new(*args, **kwargs, &blk)
27
+ # @param config [EventStoreClient::Config]
28
+ def new(config:, **options)
39
29
  return super unless self == Connection
40
30
 
41
31
  if config.eventstore_url.tls
42
- Cluster::SecureConnection.new(*args, **kwargs, &blk)
32
+ Cluster::SecureConnection.new(config: config, **options)
43
33
  else
44
- Cluster::InsecureConnection.new(*args, **kwargs, &blk)
34
+ Cluster::InsecureConnection.new(config: config, **options)
45
35
  end
46
36
  end
47
37
 
@@ -52,25 +42,24 @@ module EventStoreClient
52
42
  end
53
43
  end
54
44
 
55
- def call(stub_class)
56
- raise NotImplementedError
57
- end
45
+ include Extensions::OptionsExtension
46
+
47
+ option(:host) { Discover.current_member(config: config).host }
48
+ option(:port) { Discover.current_member(config: config).port }
49
+ option(:username) { config.eventstore_url.username }
50
+ option(:password) { config.eventstore_url.password }
51
+ option(:timeout) { config.eventstore_url.timeout }
58
52
 
59
- private
53
+ attr_reader :config
54
+ private :config
60
55
 
61
- # Common channel arguments for all GRPC requests.
62
- # Available channel arguments are described here
63
- # https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/grpc_types.h
64
- # @return [Hash]
65
- def channel_args
66
- {
67
- # disable build-in GRPC retries functional
68
- 'grpc.enable_retries' => 0,
69
- # These three options reduce delays between failed requests.
70
- 'grpc.min_reconnect_backoff_ms' => 100, # milliseconds
71
- 'grpc.max_reconnect_backoff_ms' => 100, # milliseconds
72
- 'grpc.initial_reconnect_backoff_ms' => 100 # milliseconds
73
- }
56
+ def initialize(config:, **options)
57
+ @config = config
58
+ super
59
+ end
60
+
61
+ def call(stub_class)
62
+ raise NotImplementedError
74
63
  end
75
64
  end
76
65
  end
@@ -5,55 +5,75 @@
5
5
  module EventStoreClient
6
6
  module GRPC
7
7
  class Discover
8
- include Configuration
9
-
10
8
  class << self
9
+ # @param config [EventStoreClient::Config]
11
10
  # @return [EventStoreClient::GRPC::Cluster::Member]
12
- def current_member
13
- @exception = nil
14
- return @current_member if member_alive?
15
-
16
- semaphore.synchronize do
17
- raise @exception if @exception
18
- return @current_member if member_alive?
19
-
20
- failed_member = @current_member if @current_member&.failed_endpoint
21
- @current_member =
22
- begin
23
- new.call(failed_member: failed_member)
24
- rescue StandardError => e
25
- @exception = e
26
- nil
27
- end
11
+ def current_member(config:)
12
+ @exception[config.name] = nil
13
+ return @current_member[config.name] if member_alive?(@current_member[config.name])
14
+
15
+ semaphore(config.name).synchronize do
16
+ current_member = @current_member[config.name]
17
+ raise @exception[config.name] if @exception[config.name]
18
+ return current_member if member_alive?(current_member)
19
+
20
+ failed_member = current_member&.failed_endpoint ? current_member : nil
21
+ begin
22
+ @current_member[config.name] = new(config: config).call(failed_member: failed_member)
23
+ rescue StandardError => e
24
+ @exception[config.name] = e
25
+ @current_member[config.name] = nil
26
+ raise
27
+ end
28
28
  end
29
- raise @exception if @exception
30
29
 
31
- @current_member
30
+ @current_member[config.name]
32
31
  end
33
32
 
33
+ # @param member [EventStoreClient::GRPC::Cluster::Member, nil]
34
34
  # @return [Boolean]
35
- def member_alive?
36
- return false if @current_member&.failed_endpoint
35
+ def member_alive?(member)
36
+ return false if member&.failed_endpoint
37
+
38
+ !member.nil?
39
+ end
37
40
 
38
- !@current_member.nil?
41
+ # @return [void]
42
+ def init_default_discover_vars
43
+ @exception = {}
44
+ @current_member = {}
45
+ @semaphore = {}
39
46
  end
40
47
 
41
48
  private
42
49
 
50
+ # @param config_name [String, Symbol]
43
51
  # @return [Thread::Mutex]
44
- def semaphore
45
- @semaphore ||= Thread::Mutex.new
52
+ def semaphore(config_name)
53
+ @semaphore[config_name] ||= Thread::Mutex.new
46
54
  end
47
55
  end
48
56
 
57
+ init_default_discover_vars
58
+
59
+ attr_reader :config
60
+ private :config
61
+
62
+ # @param config [EventStoreClient::Config]
63
+ def initialize(config:)
64
+ @config = config
65
+ end
66
+
49
67
  # @param failed_member [EventStoreClient::GRPC::Cluster::Member, nil]
50
68
  # @return [EventStoreClient::GRPC::Cluster::Member]
51
69
  def call(failed_member: nil)
52
70
  if needs_discover?
53
- return Cluster::GossipDiscover.new.call(nodes, failed_member: failed_member)
71
+ discovery =
72
+ Cluster::GossipDiscover.new(config: config).call(nodes, failed_member: failed_member)
73
+ return discovery
54
74
  end
55
75
 
56
- Cluster::QuerylessDiscover.new.call(config.eventstore_url.nodes.to_a)
76
+ Cluster::QuerylessDiscover.new(config: config).call(config.eventstore_url.nodes.to_a)
57
77
  end
58
78
 
59
79
  private
@@ -7,10 +7,8 @@ module EventStoreClient
7
7
  module Options
8
8
  module Streams
9
9
  class ReadOptions
10
- include Configuration
11
-
12
- attr_reader :options, :stream_name
13
- private :options, :stream_name
10
+ attr_reader :options, :stream_name, :config
11
+ private :options, :stream_name, :config
14
12
 
15
13
  # @param stream_name [String]
16
14
  # @param options [Hash]
@@ -39,9 +37,11 @@ module EventStoreClient
39
37
  # @option [Hash] :filter see
40
38
  # {EventStoreClient::GRPC::Shared::Options::FilterOptions#initialize} for available
41
39
  # values
42
- def initialize(stream_name, options)
40
+ # @param config [EventStoreClient::Config]
41
+ def initialize(stream_name, options, config:)
43
42
  @stream_name = stream_name
44
43
  @options = options
44
+ @config = config
45
45
  end
46
46
 
47
47
  # @return [Hash] see event_store.client.streams.ReadReq.Options for available options
@@ -6,7 +6,14 @@ module EventStoreClient
6
6
  module Streams
7
7
  class ProcessResponse
8
8
  include Dry::Monads[:result]
9
- include Configuration
9
+
10
+ attr_reader :config
11
+ private :config
12
+
13
+ # @param config [EventStoreClient::Config]
14
+ def initialize(config:)
15
+ @config = config
16
+ end
10
17
 
11
18
  # @api private
12
19
  # @param response [EventStore::Client::Streams::ReadResp]
@@ -6,7 +6,14 @@ module EventStoreClient
6
6
  module Streams
7
7
  class ProcessResponses
8
8
  include Dry::Monads[:result]
9
- include Configuration
9
+
10
+ attr_reader :config
11
+ private :config
12
+
13
+ # @param config [EventStoreClient::Config]
14
+ def initialize(config:)
15
+ @config = config
16
+ end
10
17
 
11
18
  # @api private
12
19
  # @param responses [Array<EventStore::Client::Streams::ReadResp>]
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EventStoreClient
4
+ class Config
5
+ include Extensions::OptionsExtension
6
+
7
+ CHANNEL_ARGS_DEFAULTS = {
8
+ # These three options reduce delays between failed requests.
9
+ 'grpc.min_reconnect_backoff_ms' => 100, # milliseconds
10
+ 'grpc.max_reconnect_backoff_ms' => 100, # milliseconds
11
+ 'grpc.initial_reconnect_backoff_ms' => 100 # milliseconds
12
+ }.freeze
13
+
14
+ option(:eventstore_url) { 'esdb://localhost:2113' }
15
+ option(:per_page) { 20 }
16
+ option(:mapper) { Mapper::Default.new(config: self) }
17
+ option(:default_event_class) { DeserializedEvent }
18
+ option(:logger)
19
+ option(:skip_deserialization) { false }
20
+ option(:skip_decryption) { false }
21
+ # GRPC-specific connection options. This hash will be passed into the `:channel_args` argument
22
+ # of a Stub class of your request. More GRPC options can be found here
23
+ # https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/grpc_types.h
24
+ option(:channel_args) # Hash
25
+ option(:name) { :default }
26
+
27
+ def eventstore_url=(value)
28
+ @eventstore_url =
29
+ if value.is_a?(Connection::Url)
30
+ value
31
+ else
32
+ Connection::UrlParser.new.call(value)
33
+ end
34
+ end
35
+
36
+ # @param logger [Logger, nil]
37
+ # @return [Logger, nil]
38
+ def logger=(logger)
39
+ ::GRPC.define_singleton_method :logger do
40
+ @logger ||= logger.nil? ? ::GRPC::DefaultLogger::NoopLogger.new : logger
41
+ end
42
+ @logger = logger
43
+ end
44
+
45
+ # @param val [Hash, nil]
46
+ # @return [Hash]
47
+ def channel_args=(val)
48
+ channel_args = CHANNEL_ARGS_DEFAULTS.merge(val&.transform_keys(&:to_s) || {})
49
+ # This options always defaults to `0`. This is because `event_store_client` implements its
50
+ # own retry functional.
51
+ channel_args['grpc.enable_retries'] = 0
52
+ @channel_args = channel_args
53
+ end
54
+ end
55
+ end
@@ -52,6 +52,14 @@ module EventStoreClient
52
52
  option(:grpc_retry_attempts) { 3 }
53
53
  # Delay between GRPC request retries
54
54
  option(:grpc_retry_interval) { 100 } # milliseconds
55
+
56
+ # @param other [EventStoreClient::Connection::Url, Object]
57
+ # @return [Boolean]
58
+ def ==(other)
59
+ return false unless other.is_a?(Url)
60
+
61
+ options_hash == other.options_hash
62
+ end
55
63
  end
56
64
  end
57
65
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  module EventStoreClient
4
4
  class DataDecryptor
5
- include Configuration
6
-
7
5
  KeyNotFoundError = Class.new(StandardError)
8
6
 
9
7
  def call
@@ -5,12 +5,14 @@
5
5
  module EventStoreClient
6
6
  module Mapper
7
7
  class Default
8
- attr_reader :serializer
9
- private :serializer
8
+ attr_reader :serializer, :config
9
+ private :serializer, :config
10
10
 
11
+ # @param config [EventStoreClient::Config]
11
12
  # @param serializer [#serialize, #deserialize]
12
- def initialize(serializer: Serializer::Json)
13
+ def initialize(config:, serializer: Serializer::Json)
13
14
  @serializer = serializer
15
+ @config = config
14
16
  end
15
17
 
16
18
  # @param event [EventStoreClient::DeserializedEvent]
@@ -24,7 +26,9 @@ module EventStoreClient
24
26
  def deserialize(event_or_raw_event, **)
25
27
  return event_or_raw_event if event_or_raw_event.is_a?(EventStoreClient::DeserializedEvent)
26
28
 
27
- Serializer::EventDeserializer.call(event_or_raw_event, serializer: serializer)
29
+ Serializer::EventDeserializer.call(
30
+ event_or_raw_event, config: config, serializer: serializer
31
+ )
28
32
  end
29
33
  end
30
34
  end
@@ -8,20 +8,21 @@ require 'event_store_client/data_decryptor'
8
8
 
9
9
  module EventStoreClient
10
10
  module Mapper
11
- ##
12
11
  # Transforms given event's data and encrypts/decrypts selected subset of data
13
12
  # based on encryption schema stored in the event itself.
14
13
  class Encrypted
15
14
  MissingEncryptionKey = Class.new(StandardError)
16
15
 
17
- attr_reader :key_repository, :serializer
18
- private :key_repository, :serializer
16
+ attr_reader :key_repository, :serializer, :config
17
+ private :key_repository, :serializer, :config
19
18
 
20
19
  # @param key_repository [#find, #create, #encrypt, #decrypt]
21
20
  # See spec/support/dummy_repository.rb for the example of simple in-memory implementation
21
+ # @param config [EventStoreClient::Config]
22
22
  # @param serializer [#serialize, #deserialize]
23
- def initialize(key_repository, serializer: Serializer::Json)
23
+ def initialize(key_repository, config:, serializer: Serializer::Json)
24
24
  @key_repository = key_repository
25
+ @config = config
25
26
  @serializer = serializer
26
27
  end
27
28
 
@@ -29,7 +30,7 @@ module EventStoreClient
29
30
  # @return [Hash]
30
31
  def serialize(event)
31
32
  # Links don't need to be encrypted
32
- return Default.new(serializer: serializer).serialize(event) if event.link?
33
+ return Default.new(serializer: serializer, config: config).serialize(event) if event.link?
33
34
 
34
35
  serialized = Serializer::EventSerializer.call(event, serializer: serializer)
35
36
  encryption_schema =
@@ -54,14 +55,16 @@ module EventStoreClient
54
55
  # @return event [EventStoreClient::DeserializedEvent]
55
56
  def deserialize(event_or_raw_event, skip_decryption: false)
56
57
  if skip_decryption
57
- return Default.new(serializer: serializer).deserialize(event_or_raw_event)
58
+ return Default.new(serializer: serializer, config: config).deserialize(event_or_raw_event)
58
59
  end
59
60
 
60
61
  event =
61
62
  if event_or_raw_event.is_a?(EventStoreClient::DeserializedEvent)
62
63
  event_or_raw_event
63
64
  else
64
- Serializer::EventDeserializer.call(event_or_raw_event, serializer: serializer)
65
+ Serializer::EventDeserializer.call(
66
+ event_or_raw_event, config: config, serializer: serializer
67
+ )
65
68
  end
66
69
 
67
70
  decrypted_data =
@@ -8,18 +8,20 @@ module EventStoreClient
8
8
  class << self
9
9
  # @param raw_event [EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent, EventStore::Client::PersistentSubscriptions::ReadResp::ReadEvent::RecordedEvent]
10
10
  # @param serializer [#serialize, #deserialize]
11
+ # @param config [EventStoreClient::Config]
11
12
  # @return [EventStoreClient::DeserializedEvent]
12
- def call(raw_event, serializer: Serializer::Json)
13
- new(serializer: serializer).call(raw_event)
13
+ def call(raw_event, config:, serializer: Serializer::Json)
14
+ new(config: config, serializer: serializer).call(raw_event)
14
15
  end
15
16
  end
16
17
 
17
- attr_reader :serializer
18
- private :serializer
18
+ attr_reader :serializer, :config
19
+ private :serializer, :config
19
20
 
20
21
  # @param serializer [#serialize, #deserialize]
21
- def initialize(serializer:)
22
+ def initialize(serializer:, config:)
22
23
  @serializer = serializer
24
+ @config = config
23
25
  end
24
26
 
25
27
  # @param raw_event [EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent, EventStore::Client::PersistentSubscriptions::ReadResp::ReadEvent::RecordedEvent]
@@ -64,10 +66,6 @@ module EventStoreClient
64
66
 
65
67
  raw_data
66
68
  end
67
-
68
- def config
69
- EventStoreClient.config
70
- end
71
69
  end
72
70
  end
73
71
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EventStoreClient
4
- VERSION = '2.1.5'
4
+ VERSION = '2.3.0-beta'
5
5
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module EventStoreClient
4
- end
5
-
6
3
  require 'json'
7
4
  require 'set'
8
5
 
@@ -18,8 +15,52 @@ require 'event_store_client/connection/url'
18
15
  require 'event_store_client/connection/url_parser'
19
16
  require 'event_store_client/deserialized_event'
20
17
  require 'event_store_client/serialized_event'
21
- require 'event_store_client/configuration'
18
+ require 'event_store_client/config'
22
19
 
23
20
  require 'event_store_client/mapper'
24
21
 
25
22
  require 'event_store_client/adapters/grpc'
23
+
24
+ module EventStoreClient
25
+ class << self
26
+ # @param name [Symbol, String]
27
+ def configure(name: :default)
28
+ yield(config(name)) if block_given?
29
+ end
30
+
31
+ # @param name [Symbol, String]
32
+ # @return [EventStore::Config]
33
+ def config(name = :default)
34
+ @config[name] ||= Config.new(name: name)
35
+ end
36
+
37
+ # @param config_name [Symbol, String]
38
+ # @return [EventStore::GRPC::Client]
39
+ def client(config_name: :default)
40
+ GRPC::Client.new(_config(config_name))
41
+ end
42
+
43
+ # @return [void]
44
+ def init_default_config
45
+ @config = { default: Config.new }
46
+ end
47
+
48
+ private
49
+
50
+ # @param config [Symbol, String]
51
+ # @return [EventStoreClient::Config]
52
+ # @raise [RuntimeError]
53
+ def _config(config)
54
+ return @config[config] if @config[config]
55
+
56
+ error_message = <<~TEXT
57
+ Could not find #{config.inspect} config. You can define it in next way:
58
+ EventStoreClient.configure(name: #{config.inspect}) do |config|
59
+ # your config goes here
60
+ end
61
+ TEXT
62
+ raise error_message
63
+ end
64
+ end
65
+ init_default_config
66
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_store_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.5
4
+ version: 2.3.0.pre.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Wilgosz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-05 00:00:00.000000000 Z
11
+ date: 2022-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-monads
@@ -214,7 +214,7 @@ files:
214
214
  - lib/event_store_client/adapters/grpc/shared/options/stream_options.rb
215
215
  - lib/event_store_client/adapters/grpc/shared/streams/process_response.rb
216
216
  - lib/event_store_client/adapters/grpc/shared/streams/process_responses.rb
217
- - lib/event_store_client/configuration.rb
217
+ - lib/event_store_client/config.rb
218
218
  - lib/event_store_client/connection/url.rb
219
219
  - lib/event_store_client/connection/url_parser.rb
220
220
  - lib/event_store_client/data_decryptor.rb
@@ -247,9 +247,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
247
247
  version: '0'
248
248
  required_rubygems_version: !ruby/object:Gem::Requirement
249
249
  requirements:
250
- - - ">="
250
+ - - ">"
251
251
  - !ruby/object:Gem::Version
252
- version: '0'
252
+ version: 1.3.1
253
253
  requirements: []
254
254
  rubygems_version: 3.3.7
255
255
  signing_key:
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module EventStoreClient
4
- class Config
5
- include Extensions::OptionsExtension
6
-
7
- option(:eventstore_url) { 'esdb://localhost:2113' }
8
- option(:per_page) { 20 }
9
- option(:mapper) { Mapper::Default.new }
10
- option(:default_event_class) { DeserializedEvent }
11
- option(:logger)
12
- option(:skip_deserialization) { false }
13
- option(:skip_decryption) { false }
14
-
15
- def eventstore_url=(value)
16
- @eventstore_url =
17
- if value.is_a?(Connection::Url)
18
- value
19
- else
20
- Connection::UrlParser.new.call(value)
21
- end
22
- end
23
-
24
- # @param logger [Logger, nil]
25
- # @return [Logger, nil]
26
- def logger=(logger)
27
- ::GRPC.define_singleton_method :logger do
28
- @logger ||= logger.nil? ? ::GRPC::DefaultLogger::NoopLogger.new : logger
29
- end
30
- @logger = logger
31
- end
32
- end
33
-
34
- class << self
35
- def configure
36
- yield(config) if block_given?
37
- end
38
-
39
- def config
40
- @config ||= Config.new
41
- end
42
-
43
- def client
44
- GRPC::Client.new
45
- end
46
- end
47
-
48
- # Configuration module to be included in classes required configured variables
49
- # Usage: include EventStore::Configuration
50
- # config.eventstore_url
51
- #
52
- module Configuration
53
- # An instance of the EventStoreClient's configuration
54
- #
55
- def config
56
- EventStoreClient.config
57
- end
58
- end
59
- end