event_store_client 1.4.9 → 2.0.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 +4 -4
- data/README.md +30 -145
- data/docs/appending_events.md +155 -0
- data/docs/catch_up_subscriptions.md +253 -0
- data/docs/configuration.md +83 -0
- data/docs/deleting_streams.md +25 -0
- data/docs/encrypting_events.md +84 -0
- data/docs/linking_events.md +149 -0
- data/docs/reading_events.md +200 -0
- data/lib/event_store_client/adapters/grpc/client.rb +244 -105
- data/lib/event_store_client/adapters/grpc/cluster/gossip_discover.rb +131 -0
- data/lib/event_store_client/adapters/grpc/cluster/insecure_connection.rb +21 -0
- data/lib/event_store_client/adapters/grpc/cluster/member.rb +18 -0
- data/lib/event_store_client/adapters/grpc/cluster/queryless_discover.rb +25 -0
- data/lib/event_store_client/adapters/grpc/cluster/secure_connection.rb +71 -0
- data/lib/event_store_client/adapters/grpc/command_registrar.rb +7 -7
- data/lib/event_store_client/adapters/grpc/commands/command.rb +63 -25
- data/lib/event_store_client/adapters/grpc/commands/gossip/cluster_info.rb +24 -0
- data/lib/event_store_client/adapters/grpc/commands/streams/append.rb +43 -68
- data/lib/event_store_client/adapters/grpc/commands/streams/append_multiple.rb +44 -0
- data/lib/event_store_client/adapters/grpc/commands/streams/delete.rb +21 -17
- data/lib/event_store_client/adapters/grpc/commands/streams/hard_delete.rb +39 -0
- data/lib/event_store_client/adapters/grpc/commands/streams/link_to.rb +7 -52
- data/lib/event_store_client/adapters/grpc/commands/streams/link_to_multiple.rb +44 -0
- data/lib/event_store_client/adapters/grpc/commands/streams/read.rb +20 -85
- data/lib/event_store_client/adapters/grpc/commands/streams/read_paginated.rb +174 -0
- data/lib/event_store_client/adapters/grpc/commands/streams/subscribe.rb +31 -106
- data/lib/event_store_client/adapters/grpc/connection.rb +56 -36
- data/lib/event_store_client/adapters/grpc/discover.rb +75 -0
- data/lib/event_store_client/adapters/grpc/generated/cluster_pb.rb +106 -18
- data/lib/event_store_client/adapters/grpc/generated/cluster_services_pb.rb +12 -12
- data/lib/event_store_client/adapters/grpc/generated/code_pb.rb +34 -0
- data/lib/event_store_client/adapters/grpc/generated/gossip_pb.rb +3 -2
- data/lib/event_store_client/adapters/grpc/generated/gossip_services_pb.rb +3 -3
- data/lib/event_store_client/adapters/grpc/generated/monitoring_pb.rb +25 -0
- data/lib/event_store_client/adapters/grpc/generated/monitoring_services_pb.rb +26 -0
- data/lib/event_store_client/adapters/grpc/generated/operations_pb.rb +2 -1
- data/lib/event_store_client/adapters/grpc/generated/operations_services_pb.rb +8 -7
- data/lib/event_store_client/adapters/grpc/generated/persistent_pb.rb +199 -38
- data/lib/event_store_client/adapters/grpc/generated/persistent_services_pb.rb +7 -3
- data/lib/event_store_client/adapters/grpc/generated/projections_pb.rb +9 -26
- data/lib/event_store_client/adapters/grpc/generated/projections_services_pb.rb +4 -3
- data/lib/event_store_client/adapters/grpc/generated/serverfeatures_pb.rb +29 -0
- data/lib/event_store_client/adapters/grpc/generated/serverfeatures_services_pb.rb +26 -0
- data/lib/event_store_client/adapters/grpc/generated/shared_pb.rb +54 -12
- data/lib/event_store_client/adapters/grpc/generated/status_pb.rb +23 -0
- data/lib/event_store_client/adapters/grpc/generated/streams_pb.rb +104 -64
- data/lib/event_store_client/adapters/grpc/generated/streams_services_pb.rb +3 -2
- data/lib/event_store_client/adapters/grpc/generated/users_services_pb.rb +2 -2
- data/lib/event_store_client/adapters/grpc/options/streams/read_options.rb +78 -0
- data/lib/event_store_client/adapters/grpc/options/streams/write_options.rb +39 -0
- data/lib/event_store_client/adapters/grpc/shared/event_deserializer.rb +52 -0
- data/lib/event_store_client/adapters/grpc/shared/options/filter_options.rb +76 -0
- data/lib/event_store_client/adapters/grpc/shared/options/stream_options.rb +91 -0
- data/lib/event_store_client/adapters/grpc/shared/streams/process_response.rb +28 -0
- data/lib/event_store_client/adapters/grpc/shared/streams/process_responses.rb +33 -0
- data/lib/event_store_client/adapters/grpc.rb +28 -12
- data/lib/event_store_client/configuration.rb +39 -54
- data/lib/event_store_client/connection/url.rb +57 -0
- data/lib/event_store_client/connection/url_parser.rb +144 -0
- data/lib/event_store_client/data_decryptor.rb +2 -9
- data/lib/event_store_client/deserialized_event.rb +35 -10
- data/lib/event_store_client/encryption_metadata.rb +0 -1
- data/lib/event_store_client/event.rb +4 -2
- data/lib/event_store_client/extensions/options_extension.rb +87 -0
- data/lib/event_store_client/mapper/default.rb +12 -9
- data/lib/event_store_client/mapper/encrypted.rb +18 -17
- data/lib/event_store_client/types.rb +1 -1
- data/lib/event_store_client/utils.rb +30 -0
- data/lib/event_store_client/version.rb +1 -1
- data/lib/event_store_client.rb +8 -7
- metadata +74 -83
- data/lib/event_store_client/adapters/grpc/Protos/cluster.proto +0 -149
- data/lib/event_store_client/adapters/grpc/Protos/gossip.proto +0 -44
- data/lib/event_store_client/adapters/grpc/Protos/operations.proto +0 -45
- data/lib/event_store_client/adapters/grpc/Protos/persistent.proto +0 -180
- data/lib/event_store_client/adapters/grpc/Protos/projections.proto +0 -174
- data/lib/event_store_client/adapters/grpc/Protos/shared.proto +0 -22
- data/lib/event_store_client/adapters/grpc/Protos/streams.proto +0 -242
- data/lib/event_store_client/adapters/grpc/Protos/users.proto +0 -119
- data/lib/event_store_client/adapters/grpc/README.md +0 -16
- data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/create.rb +0 -46
- data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/delete.rb +0 -34
- data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/read.rb +0 -77
- data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/settings_schema.rb +0 -38
- data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/update.rb +0 -48
- data/lib/event_store_client/adapters/grpc/commands/projections/create.rb +0 -48
- data/lib/event_store_client/adapters/grpc/commands/projections/delete.rb +0 -34
- data/lib/event_store_client/adapters/grpc/commands/projections/update.rb +0 -44
- data/lib/event_store_client/adapters/grpc/commands/streams/read_all.rb +0 -43
- data/lib/event_store_client/adapters/grpc/commands/streams/tombstone.rb +0 -35
- data/lib/event_store_client/adapters/http/README.md +0 -16
- data/lib/event_store_client/adapters/http/client.rb +0 -161
- data/lib/event_store_client/adapters/http/commands/command.rb +0 -27
- data/lib/event_store_client/adapters/http/commands/persistent_subscriptions/ack.rb +0 -15
- data/lib/event_store_client/adapters/http/commands/persistent_subscriptions/create.rb +0 -35
- data/lib/event_store_client/adapters/http/commands/persistent_subscriptions/read.rb +0 -60
- data/lib/event_store_client/adapters/http/commands/projections/create.rb +0 -33
- data/lib/event_store_client/adapters/http/commands/projections/update.rb +0 -31
- data/lib/event_store_client/adapters/http/commands/streams/append.rb +0 -49
- data/lib/event_store_client/adapters/http/commands/streams/delete.rb +0 -16
- data/lib/event_store_client/adapters/http/commands/streams/link_to.rb +0 -49
- data/lib/event_store_client/adapters/http/commands/streams/read.rb +0 -52
- data/lib/event_store_client/adapters/http/commands/streams/tombstone.rb +0 -17
- data/lib/event_store_client/adapters/http/connection.rb +0 -46
- data/lib/event_store_client/adapters/http/request_method.rb +0 -28
- data/lib/event_store_client/adapters/http.rb +0 -17
- data/lib/event_store_client/adapters/in_memory.rb +0 -144
- data/lib/event_store_client/broker.rb +0 -40
- data/lib/event_store_client/catch_up_subscription.rb +0 -42
- data/lib/event_store_client/catch_up_subscriptions.rb +0 -92
- data/lib/event_store_client/client.rb +0 -73
- data/lib/event_store_client/error_handler.rb +0 -10
- data/lib/event_store_client/subscription.rb +0 -23
- data/lib/event_store_client/subscriptions.rb +0 -38
- data/lib/event_store_client/value_objects/read_direction.rb +0 -43
@@ -1,103 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'grpc'
|
4
|
-
require 'event_store_client/adapters/grpc/generated/
|
5
|
-
require 'event_store_client/adapters/grpc/generated/streams_services_pb.rb'
|
6
|
-
|
7
|
-
require 'event_store_client/configuration'
|
8
|
-
require 'event_store_client/adapters/grpc/commands/command'
|
3
|
+
require 'event_store_client/adapters/grpc/generated/streams_pb'
|
4
|
+
require 'event_store_client/adapters/grpc/generated/streams_services_pb'
|
9
5
|
|
10
6
|
module EventStoreClient
|
11
7
|
module GRPC
|
12
8
|
module Commands
|
13
9
|
module Streams
|
14
10
|
class Read < Command
|
15
|
-
include Configuration
|
16
|
-
|
17
11
|
use_request EventStore::Client::Streams::ReadReq
|
18
12
|
use_service EventStore::Client::Streams::Streams::Stub
|
19
13
|
|
20
|
-
|
21
|
-
|
22
|
-
def call(
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
resolve_links: options[:resolve_links] || true,
|
33
|
-
count: options[:count] || config.per_page,
|
34
|
-
uuid_option: {
|
35
|
-
string: {}
|
36
|
-
},
|
37
|
-
no_filter: {}
|
38
|
-
}
|
39
|
-
options[:start] ||= 0
|
40
|
-
if options[:start].zero?
|
41
|
-
opts[:stream][:start] = {}
|
42
|
-
else
|
43
|
-
opts[:stream][:revision] = options[:start]
|
44
|
-
end
|
45
|
-
|
46
|
-
skip_decryption = options[:skip_decryption] || false
|
47
|
-
events =
|
48
|
-
if options[:skip_deserialization]
|
49
|
-
read_stream_raw(opts)
|
50
|
-
else
|
51
|
-
read_stream(opts, skip_decryption)
|
52
|
-
end
|
53
|
-
Success(events)
|
54
|
-
rescue StreamNotFound
|
55
|
-
Failure(:not_found)
|
14
|
+
# @api private
|
15
|
+
# @see {EventStoreClient::GRPC::Client#read}
|
16
|
+
def call(stream_name, options:, skip_deserialization:, skip_decryption:)
|
17
|
+
options = normalize_options(stream_name, options)
|
18
|
+
yield options if block_given?
|
19
|
+
result =
|
20
|
+
retry_request { service.read(request.new(options: options), metadata: metadata).to_a }
|
21
|
+
EventStoreClient::GRPC::Shared::Streams::ProcessResponses.new.call(
|
22
|
+
result,
|
23
|
+
skip_deserialization,
|
24
|
+
skip_decryption
|
25
|
+
)
|
56
26
|
end
|
57
27
|
|
58
28
|
private
|
59
29
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
rescue ::GRPC::Unavailable
|
67
|
-
sleep config.grpc_unavailable_retry_sleep
|
68
|
-
retry if (retries += 1) <= config.grpc_unavailable_retry_count
|
69
|
-
raise GRPCUnavailableRetryFailed
|
70
|
-
end
|
71
|
-
|
72
|
-
def read_stream_raw(options)
|
73
|
-
retries ||= 0
|
74
|
-
service.read(request.new(options: options), metadata: metadata).map do |res|
|
75
|
-
raise StreamNotFound if res.stream_not_found
|
76
|
-
res.event.event
|
77
|
-
end
|
78
|
-
rescue ::GRPC::Unavailable
|
79
|
-
sleep config.grpc_unavailable_retry_sleep
|
80
|
-
retry if (retries += 1) <= config.grpc_unavailable_retry_count
|
81
|
-
raise GRPCUnavailableRetryFailed
|
82
|
-
end
|
83
|
-
|
84
|
-
def deserialize_event(entry, skip_decryption: false)
|
85
|
-
data = (entry.data.nil? || entry.data.empty?) ? '{}' : entry.data
|
86
|
-
|
87
|
-
metadata =
|
88
|
-
JSON.parse(entry.custom_metadata || '{}').merge(
|
89
|
-
entry.metadata.to_h || {}
|
90
|
-
).to_json
|
91
|
-
|
92
|
-
event = EventStoreClient::Event.new(
|
93
|
-
id: entry.id.string,
|
94
|
-
title: "#{entry.stream_revision}@#{entry.stream_identifier.streamName}",
|
95
|
-
type: entry.metadata['type'],
|
96
|
-
data: data,
|
97
|
-
metadata: metadata
|
98
|
-
)
|
99
|
-
|
100
|
-
config.mapper.deserialize(event, skip_decryption: skip_decryption)
|
30
|
+
# @param stream_name [String]
|
31
|
+
# @param options [Hash]
|
32
|
+
# @return [EventStore::Client::Streams::ReadReq::Options]
|
33
|
+
def normalize_options(stream_name, options)
|
34
|
+
options = Options::Streams::ReadOptions.new(stream_name, options).request_options
|
35
|
+
EventStore::Client::Streams::ReadReq::Options.new(options)
|
101
36
|
end
|
102
37
|
end
|
103
38
|
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable all
|
4
|
+
|
5
|
+
module EventStoreClient
|
6
|
+
module GRPC
|
7
|
+
module Commands
|
8
|
+
module Streams
|
9
|
+
class ReadPaginated < Command
|
10
|
+
RecordsLimitError = Class.new(StandardError)
|
11
|
+
DEFAULT_READ_DIRECTION = :Forwards
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
# @see {EventStoreClient::GRPC::Client#read_paginated}
|
15
|
+
def call(stream_name, options:, skip_deserialization:, skip_decryption:, &blk)
|
16
|
+
# TODO: Improve the implementation by extracting the pagination into a separate class to
|
17
|
+
# allow persisting the pagination options(position, direction, max_count) among the
|
18
|
+
# whole instance. This approach will allow us to get rid of passing paginate options
|
19
|
+
# into private methods explicitly.
|
20
|
+
position, direction, max_count = nil
|
21
|
+
first_call = true
|
22
|
+
Enumerator.new do |yielder|
|
23
|
+
loop do
|
24
|
+
response =
|
25
|
+
Read.new(**connection_options).call(
|
26
|
+
stream_name,
|
27
|
+
options: options,
|
28
|
+
skip_deserialization: true,
|
29
|
+
skip_decryption: true
|
30
|
+
) do |opts|
|
31
|
+
if first_call
|
32
|
+
# Evaluate user-provided block only once
|
33
|
+
yield opts if blk
|
34
|
+
position = get_position(opts)
|
35
|
+
direction = get_direction(opts)
|
36
|
+
max_count = opts.count.to_i
|
37
|
+
validate_max_count(max_count)
|
38
|
+
first_call = false
|
39
|
+
end
|
40
|
+
|
41
|
+
paginate_options(opts, position)
|
42
|
+
end
|
43
|
+
unless response.success?
|
44
|
+
yielder << response
|
45
|
+
raise StopIteration
|
46
|
+
end
|
47
|
+
processed_response =
|
48
|
+
EventStoreClient::GRPC::Shared::Streams::ProcessResponses.new.call(
|
49
|
+
response.success,
|
50
|
+
skip_deserialization,
|
51
|
+
skip_decryption
|
52
|
+
)
|
53
|
+
yielder << processed_response if processed_response.success.any?
|
54
|
+
raise StopIteration if end_reached?(response.success, max_count)
|
55
|
+
|
56
|
+
position = calc_next_position(response.success, direction, stream_name)
|
57
|
+
raise StopIteration if position.negative?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# @param options [EventStore::Client::Streams::ReadReq::Options]
|
65
|
+
# @param position [Integer, nil]
|
66
|
+
# @return [EventStore::Client::Streams::ReadReq::Options, nil]
|
67
|
+
def paginate_options(options, position)
|
68
|
+
return unless position
|
69
|
+
return paginate_all_options(options, position) if options.stream.nil?
|
70
|
+
|
71
|
+
paginate_regular_options(options, position)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param options [EventStore::Client::Streams::ReadReq::Options]
|
75
|
+
# @param position [Integer]
|
76
|
+
# @return [EventStore::Client::Streams::ReadReq::Options]
|
77
|
+
def paginate_all_options(options, position)
|
78
|
+
options.all.position = EventStore::Client::Streams::ReadReq::Options::Position.new(
|
79
|
+
commit_position: position
|
80
|
+
)
|
81
|
+
options
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param options [EventStore::Client::Streams::ReadReq::Options]
|
85
|
+
# @param position [Integer]
|
86
|
+
# @return [EventStore::Client::Streams::ReadReq::Options]
|
87
|
+
def paginate_regular_options(options, position)
|
88
|
+
options.stream.revision = position
|
89
|
+
options
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param options [EventStore::Client::Streams::ReadReq::Options]
|
93
|
+
# @return [Symbol] :Backwards or :Forwards
|
94
|
+
def get_direction(options)
|
95
|
+
return options.read_direction if options.read_direction
|
96
|
+
|
97
|
+
DEFAULT_READ_DIRECTION
|
98
|
+
end
|
99
|
+
|
100
|
+
# @param options [EventStore::Client::Streams::ReadReq::Options]
|
101
|
+
# @return [Integer, nil]
|
102
|
+
def get_position(options)
|
103
|
+
# If start position is set to :end - then we need to wait for first response to get
|
104
|
+
# the value of the position
|
105
|
+
return if options.all&.end
|
106
|
+
return if options.stream&.end
|
107
|
+
|
108
|
+
# In case if user has provided a starting position value
|
109
|
+
return options.all&.position&.commit_position if options.all&.position&.commit_position
|
110
|
+
return options.stream&.revision if options.stream&.revision
|
111
|
+
|
112
|
+
0
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param raw_events [Array<EventStore::Client::Streams::ReadResp>]
|
116
|
+
# @param direction [Symbol] :Backwards or :Forwards
|
117
|
+
# @param stream_name [String]
|
118
|
+
# @return [Integer]
|
119
|
+
def calc_next_position(raw_events, direction, stream_name)
|
120
|
+
events = meaningful_events(raw_events).map { |e| e.event.event }
|
121
|
+
|
122
|
+
return next_position_for_all(events, direction) if stream_name == '$all'
|
123
|
+
|
124
|
+
next_position_for_regular(events, direction)
|
125
|
+
end
|
126
|
+
|
127
|
+
# @param events [Array<EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent>]
|
128
|
+
# @param direction [Symbol] :Backwards or :Forwards
|
129
|
+
# @return [Integer]
|
130
|
+
def next_position_for_all(events, direction)
|
131
|
+
return events.last.commit_position if direction == DEFAULT_READ_DIRECTION
|
132
|
+
|
133
|
+
events.first.commit_position
|
134
|
+
end
|
135
|
+
|
136
|
+
# @param events [Array<EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent>]
|
137
|
+
# @param direction [Symbol] :Backwards or :Forwards
|
138
|
+
# @return [Integer]
|
139
|
+
def next_position_for_regular(events, direction)
|
140
|
+
return events.last.stream_revision + 1 if direction == DEFAULT_READ_DIRECTION
|
141
|
+
|
142
|
+
events.last.stream_revision - 1
|
143
|
+
end
|
144
|
+
|
145
|
+
# @param raw_events [Array<EventStore::Client::Streams::ReadResp>]
|
146
|
+
# @return [Array<EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent>]
|
147
|
+
def meaningful_events(raw_events)
|
148
|
+
raw_events.select { |read_resp| read_resp.event&.event }
|
149
|
+
end
|
150
|
+
|
151
|
+
# @param raw_events [Array<EventStore::Client::Streams::ReadResp>]
|
152
|
+
# @param max_count [Integer]
|
153
|
+
# @return [Boolean]
|
154
|
+
def end_reached?(raw_events, max_count)
|
155
|
+
meaningful_events(raw_events).size < max_count
|
156
|
+
end
|
157
|
+
|
158
|
+
# @return [void]
|
159
|
+
# @raise [RecordsLimitError] raises error in case if max_count is less than 2
|
160
|
+
def validate_max_count(max_count)
|
161
|
+
return if max_count >= 2
|
162
|
+
|
163
|
+
raise(
|
164
|
+
RecordsLimitError,
|
165
|
+
'Pagination requires :max_count option to be greater than or equal to 2. ' \
|
166
|
+
"Current value is `#{max_count}'."
|
167
|
+
)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
# rubocop:enable all
|
@@ -1,129 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'grpc'
|
4
|
-
require 'event_store_client/adapters/grpc/generated/
|
5
|
-
require 'event_store_client/adapters/grpc/generated/streams_services_pb.rb'
|
6
|
-
|
7
|
-
require 'event_store_client/configuration'
|
8
|
-
require 'event_store_client/adapters/grpc/commands/command'
|
3
|
+
require 'event_store_client/adapters/grpc/generated/streams_pb'
|
4
|
+
require 'event_store_client/adapters/grpc/generated/streams_services_pb'
|
9
5
|
|
10
6
|
module EventStoreClient
|
11
7
|
module GRPC
|
12
8
|
module Commands
|
13
9
|
module Streams
|
14
10
|
class Subscribe < Command
|
15
|
-
include Configuration
|
16
|
-
|
17
11
|
use_request EventStore::Client::Streams::ReadReq
|
18
12
|
use_service EventStore::Client::Streams::Streams::Stub
|
19
13
|
|
20
|
-
|
14
|
+
def initialize(**conn_options)
|
15
|
+
# Subscriptions should never be timed out
|
16
|
+
super(**conn_options.merge(timeout: nil))
|
17
|
+
end
|
21
18
|
|
22
|
-
|
23
|
-
|
19
|
+
# @api private
|
20
|
+
# @see {EventStoreClient::GRPC::Client#read}
|
21
|
+
def call(stream_name, handler:, options:, skip_deserialization:, skip_decryption:)
|
22
|
+
options = normalize_options(stream_name, options)
|
23
|
+
yield options if block_given?
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
callback = proc do |response|
|
26
|
+
result = Shared::Streams::ProcessResponse.new.call(
|
27
|
+
response,
|
28
|
+
skip_deserialization,
|
29
|
+
skip_decryption
|
30
|
+
)
|
27
31
|
|
28
|
-
|
32
|
+
handler.call(result) if result
|
29
33
|
end
|
30
|
-
|
31
|
-
|
34
|
+
Success(
|
35
|
+
retry_request do
|
36
|
+
service.read(request.new(options: options), metadata: metadata, &callback)
|
37
|
+
end
|
38
|
+
)
|
32
39
|
end
|
33
40
|
|
34
41
|
private
|
35
42
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def position(event_or_checkpoint)
|
48
|
-
{
|
49
|
-
prepare_position: event_or_checkpoint.prepare_position,
|
50
|
-
commit_position: event_or_checkpoint.commit_position
|
51
|
-
}
|
52
|
-
end
|
53
|
-
|
54
|
-
def read_direction(direction)
|
55
|
-
EventStoreClient::ReadDirection.new(direction || 'forwards').to_sym
|
56
|
-
end
|
57
|
-
|
58
|
-
def options_with_defaults(options)
|
59
|
-
options[:without_system_events] = true unless options[:without_system_events] == false
|
60
|
-
opts = {
|
61
|
-
subscription: {},
|
62
|
-
read_direction: read_direction(options[:direction]),
|
63
|
-
resolve_links: options[:resolve_links] || true,
|
64
|
-
uuid_option: {
|
65
|
-
string: {}
|
66
|
-
}
|
67
|
-
}
|
68
|
-
if options[:stream]
|
69
|
-
opts[:stream] = {
|
70
|
-
stream_identifier: {
|
71
|
-
streamName: stream
|
72
|
-
}
|
73
|
-
}
|
74
|
-
else
|
75
|
-
opts[:all] = options[:all] || default_all_options
|
76
|
-
end
|
77
|
-
if options[:filter]
|
78
|
-
opts[:filter] = options[:filter]
|
79
|
-
elsif options[:without_system_events]
|
80
|
-
opts[:filter] = {
|
81
|
-
event_type: { regex: '^[^$].*' },
|
82
|
-
max: 32,
|
83
|
-
checkpointIntervalMultiplier: 1000
|
84
|
-
}
|
85
|
-
else
|
86
|
-
opts[:no_filter] = {}
|
87
|
-
end
|
88
|
-
|
89
|
-
options[:start] ||= 0
|
90
|
-
|
91
|
-
return opts unless options[:stream]
|
92
|
-
|
93
|
-
if options[:start].zero?
|
94
|
-
opts[:stream][:start] = {}
|
95
|
-
else
|
96
|
-
opts[:stream][:revision] = options[:start]
|
43
|
+
# @param stream_name [String]
|
44
|
+
# @param options [Hash]
|
45
|
+
# @return [EventStore::Client::Streams::ReadReq::Options]
|
46
|
+
def normalize_options(stream_name, options)
|
47
|
+
options = Options::Streams::ReadOptions.new(stream_name, options).request_options
|
48
|
+
EventStore::Client::Streams::ReadReq::Options.new(options).tap do |opts|
|
49
|
+
opts.subscription =
|
50
|
+
EventStore::Client::Streams::ReadReq::Options::SubscriptionOptions.new
|
97
51
|
end
|
98
|
-
opts
|
99
|
-
end
|
100
|
-
|
101
|
-
def default_all_options
|
102
|
-
{
|
103
|
-
position: {
|
104
|
-
commit_position: 0,
|
105
|
-
prepare_position: 0
|
106
|
-
}
|
107
|
-
}
|
108
|
-
end
|
109
|
-
|
110
|
-
def deserialize_event(entry)
|
111
|
-
data = (entry.data.nil? || entry.data.empty?) ? '{}' : entry.data
|
112
|
-
|
113
|
-
metadata =
|
114
|
-
JSON.parse(entry.custom_metadata || '{}').merge(
|
115
|
-
entry.metadata.to_h || {}
|
116
|
-
).to_json
|
117
|
-
|
118
|
-
event = EventStoreClient::Event.new(
|
119
|
-
id: entry.id.string,
|
120
|
-
title: "#{entry.stream_revision}@#{entry.stream_identifier.streamName}",
|
121
|
-
type: entry.metadata['type'],
|
122
|
-
data: data,
|
123
|
-
metadata: metadata
|
124
|
-
)
|
125
|
-
|
126
|
-
config.mapper.deserialize(event)
|
127
52
|
end
|
128
53
|
end
|
129
54
|
end
|
@@ -8,49 +8,69 @@ module EventStoreClient
|
|
8
8
|
module GRPC
|
9
9
|
class Connection
|
10
10
|
include Configuration
|
11
|
+
include Extensions::OptionsExtension
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
def call(stub_klass, options: {})
|
19
|
-
credentials =
|
20
|
-
options[:credentials] ||
|
21
|
-
Base64.encode64("#{config.eventstore_user}:#{config.eventstore_password}")
|
22
|
-
stub_klass.new(
|
23
|
-
"#{config.eventstore_url.host}:#{config.eventstore_url.port}",
|
24
|
-
channel_credentials,
|
25
|
-
channel_args: { 'authorization' => "Basic #{credentials.delete("\n")}" }
|
26
|
-
)
|
27
|
-
end
|
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 }
|
28
18
|
|
29
|
-
|
19
|
+
class << self
|
20
|
+
include Configuration
|
21
|
+
|
22
|
+
# Resolve which connection class we instantiate, based on config.eventstore_url.tls config
|
23
|
+
# option. If :new method is called from SecureConnection or InsecureConnection class - then
|
24
|
+
# that particular class will be instantiated despite on config.eventstore_url.tls config
|
25
|
+
# option. Example:
|
26
|
+
# ```ruby
|
27
|
+
# config.eventstore_url.tls = true
|
28
|
+
# Connection.new # => #<EventStoreClient::GRPC::Cluster::SecureConnection>
|
29
|
+
#
|
30
|
+
# config.eventstore_url.tls = false
|
31
|
+
# Connection.new # => #<EventStoreClient::GRPC::Cluster::InsecureConnection>
|
32
|
+
#
|
33
|
+
# Cluster::SecureConnection.new
|
34
|
+
# # => #<EventStoreClient::GRPC::Cluster::SecureConnection>
|
35
|
+
# Cluster::InsecureConnection.new
|
36
|
+
# # => #<EventStoreClient::GRPC::Cluster::InsecureConnection>
|
37
|
+
# ```
|
38
|
+
def new(*args, **kwargs, &blk)
|
39
|
+
return super unless self == Connection
|
30
40
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
sleep config.socket_error_retry_sleep
|
44
|
-
retry if (retries += 1) <= config.socket_error_retry_count
|
45
|
-
raise SocketErrorRetryFailed
|
41
|
+
if config.eventstore_url.tls
|
42
|
+
Cluster::SecureConnection.new(*args, **kwargs, &blk)
|
43
|
+
else
|
44
|
+
Cluster::InsecureConnection.new(*args, **kwargs, &blk)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Checks if connection class is secure
|
49
|
+
# @return [Boolean]
|
50
|
+
def secure?
|
51
|
+
self == Cluster::SecureConnection
|
52
|
+
end
|
46
53
|
end
|
47
54
|
|
48
|
-
def
|
49
|
-
|
55
|
+
def call(stub_class)
|
56
|
+
raise NotImplementedError
|
50
57
|
end
|
51
58
|
|
52
|
-
|
53
|
-
|
59
|
+
private
|
60
|
+
|
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
|
+
}
|
54
74
|
end
|
55
75
|
end
|
56
76
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
4
|
+
|
5
|
+
module EventStoreClient
|
6
|
+
module GRPC
|
7
|
+
class Discover
|
8
|
+
include Configuration
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# @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
|
28
|
+
end
|
29
|
+
raise @exception if @exception
|
30
|
+
|
31
|
+
@current_member
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean]
|
35
|
+
def member_alive?
|
36
|
+
return false if @current_member&.failed_endpoint
|
37
|
+
|
38
|
+
!@current_member.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# @return [Thread::Mutex]
|
44
|
+
def semaphore
|
45
|
+
@semaphore ||= Thread::Mutex.new
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param failed_member [EventStoreClient::GRPC::Cluster::Member, nil]
|
50
|
+
# @return [EventStoreClient::GRPC::Cluster::Member]
|
51
|
+
def call(failed_member: nil)
|
52
|
+
if needs_discover?
|
53
|
+
return Cluster::GossipDiscover.new.call(nodes, failed_member: failed_member)
|
54
|
+
end
|
55
|
+
|
56
|
+
Cluster::QuerylessDiscover.new.call(config.eventstore_url.nodes.to_a)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# @return [Array<EventStoreClient::Connection::Url::Node>]
|
62
|
+
def nodes
|
63
|
+
return [config.eventstore_url.nodes.first] if config.eventstore_url.dns_discover
|
64
|
+
|
65
|
+
config.eventstore_url.nodes.to_a
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Boolean]
|
69
|
+
def needs_discover?
|
70
|
+
config.eventstore_url.dns_discover || config.eventstore_url.nodes.size > 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|