event_store_client 1.4.9 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|