estore 0.0.1

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.
@@ -0,0 +1,67 @@
1
+ class Eventstore
2
+ class Connection
3
+ # Buffer receives data from the TCP connection, and parses the binary packages.
4
+ # Parsed packages are given back to the given handler as they are decoded.
5
+ class Buffer
6
+ attr_reader :buffer, :handler, :mutex
7
+ def initialize(&block)
8
+ @mutex = Mutex.new
9
+ @buffer = ''.force_encoding('BINARY')
10
+ @handler = block
11
+ end
12
+
13
+ def <<(bytes)
14
+ bytes = bytes.force_encoding('BINARY') if bytes.respond_to? :force_encoding
15
+ mutex.synchronize do
16
+ @buffer << bytes
17
+ end
18
+
19
+ consume_available_packages
20
+ end
21
+
22
+ def consume_available_packages
23
+ while consume_package
24
+ end
25
+ end
26
+
27
+ def consume_package
28
+ pkg = read_package
29
+ if pkg
30
+ handle(pkg)
31
+ discard_bytes(pkg)
32
+ true
33
+ else
34
+ false
35
+ end
36
+ end
37
+
38
+ def read_package
39
+ return nil if buffer.length < 4
40
+ package_length = buffer[0...4].unpack('l<').first
41
+ bytes = buffer[4...(4 + package_length)].dup
42
+ bytes if bytes.bytesize >= package_length
43
+ end
44
+
45
+ def discard_bytes(pkg)
46
+ mutex.synchronize do
47
+ @buffer = buffer[(4 + pkg.bytesize)..-1]
48
+ end
49
+ end
50
+
51
+ def handle(pkg)
52
+ code, flags, uuid_bytes, message = parse(pkg)
53
+ command = Eventstore::Connection.command_name(code)
54
+ handler.call(command, message, Package.parse_uuid(uuid_bytes), flags)
55
+ end
56
+
57
+ def parse(pkg)
58
+ [
59
+ pkg[0].unpack('C').first,
60
+ pkg[1].unpack('C').first,
61
+ pkg[2...(2 + 16)],
62
+ pkg[18..-1]
63
+ ]
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,90 @@
1
+ class Eventstore
2
+ # Mapping between command names and codes
3
+ # From https://github.com/EventStore/EventStore/blob/master/src/EventStore.ClientAPI/SystemData/TcpCommand.cs
4
+ class Connection
5
+ COMMANDS = {
6
+ 'HeartbeatRequestCommand'.freeze => 0x01,
7
+ 'HeartbeatResponseCommand'.freeze => 0x02,
8
+
9
+ 'Ping'.freeze => 0x03,
10
+ 'Pong'.freeze => 0x04,
11
+
12
+ 'PrepareAck'.freeze => 0x05,
13
+ 'CommitAck'.freeze => 0x06,
14
+
15
+ 'SlaveAssignment'.freeze => 0x07,
16
+ 'CloneAssignment'.freeze => 0x08,
17
+
18
+ 'SubscribeReplica'.freeze => 0x10,
19
+ 'ReplicaLogPositionAck'.freeze => 0x11,
20
+ 'CreateChunk'.freeze => 0x12,
21
+ 'RawChunkBulk'.freeze => 0x13,
22
+ 'DataChunkBulk'.freeze => 0x14,
23
+ 'ReplicaSubscriptionRetry'.freeze => 0x15,
24
+ 'ReplicaSubscribed'.freeze => 0x16,
25
+
26
+ # "CLIENT COMMANDS
27
+ # "CreateStream".freeze => 0x80,
28
+ # "CreateStreamCompleted".freeze => 0x81,
29
+
30
+ 'WriteEvents'.freeze => 0x82,
31
+ 'WriteEventsCompleted'.freeze => 0x83,
32
+
33
+ 'TransactionStart'.freeze => 0x84,
34
+ 'TransactionStartCompleted'.freeze => 0x85,
35
+ 'TransactionWrite'.freeze => 0x86,
36
+ 'TransactionWriteCompleted'.freeze => 0x87,
37
+ 'TransactionCommit'.freeze => 0x88,
38
+ 'TransactionCommitCompleted'.freeze => 0x89,
39
+
40
+ 'DeleteStream'.freeze => 0x8A,
41
+ 'DeleteStreamCompleted'.freeze => 0x8B,
42
+
43
+ 'ReadEvent'.freeze => 0xB0,
44
+ 'ReadEventCompleted'.freeze => 0xB1,
45
+ 'ReadStreamEventsForward'.freeze => 0xB2,
46
+ 'ReadStreamEventsForwardCompleted'.freeze => 0xB3,
47
+ 'ReadStreamEventsBackward'.freeze => 0xB4,
48
+ 'ReadStreamEventsBackwardCompleted'.freeze => 0xB5,
49
+ 'ReadAllEventsForward'.freeze => 0xB6,
50
+ 'ReadAllEventsForwardCompleted'.freeze => 0xB7,
51
+ 'ReadAllEventsBackward'.freeze => 0xB8,
52
+ 'ReadAllEventsBackwardCompleted'.freeze => 0xB9,
53
+
54
+ 'SubscribeToStream'.freeze => 0xC0,
55
+ 'SubscriptionConfirmation'.freeze => 0xC1,
56
+ 'StreamEventAppeared'.freeze => 0xC2,
57
+ 'UnsubscribeFromStream'.freeze => 0xC3,
58
+ 'SubscriptionDropped'.freeze => 0xC4,
59
+ 'ConnectToPersistentSubscription'.freeze => 0xC5,
60
+ 'PersistentSubscriptionConfirmation'.freeze => 0xC6,
61
+ 'PersistentSubscriptionStreamEventAppeared'.freeze => 0xC7,
62
+ 'CreatePersistentSubscription'.freeze => 0xC8,
63
+ 'CreatePersistentSubscriptionCompleted'.freeze => 0xC9,
64
+ 'DeletePersistentSubscription'.freeze => 0xCA,
65
+ 'DeletePersistentSubscriptionCompleted'.freeze => 0xCB,
66
+ 'PersistentSubscriptionAckEvents'.freeze => 0xCC,
67
+ 'PersistentSubscriptionNakEvents'.freeze => 0xCD,
68
+ 'UpdatePersistentSubscription'.freeze => 0xCE,
69
+ 'UpdatePersistentSubscriptionCompleted'.freeze => 0xCF,
70
+
71
+ 'ScavengeDatabase'.freeze => 0xD0,
72
+ 'ScavengeDatabaseCompleted'.freeze => 0xD1,
73
+
74
+ 'BadRequest'.freeze => 0xF0,
75
+ 'NotHandled'.freeze => 0xF1,
76
+ 'Authenticate'.freeze => 0xF2,
77
+ 'Authenticated'.freeze => 0xF3,
78
+ 'NotAuthenticated'.freeze => 0xF4
79
+ }
80
+
81
+ def self.command_name(code)
82
+ @names ||= reverse_lookup_table
83
+ @names.fetch(code)
84
+ end
85
+
86
+ def self.reverse_lookup_table
87
+ COMMANDS.inject({}) { |h, (k, v)| h.merge!(v => k) }
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,90 @@
1
+ require 'promise'
2
+
3
+ class Eventstore
4
+ # Extension of a Ruby implementation of the Promises/A+ spec
5
+ # that carries the correlation id of the command.
6
+ # @see https://github.com/lgierth/promise.rb
7
+ class Promise < ::Promise
8
+ attr_reader :correlation_id
9
+ def initialize(correlation_id)
10
+ super()
11
+ @correlation_id = correlation_id
12
+ end
13
+
14
+ def wait
15
+ t = Thread.current
16
+ resume = proc { t.wakeup }
17
+ self.then(resume, resume)
18
+ sleep
19
+ end
20
+ end
21
+
22
+ # Registry storing handlers for the outstanding commands and
23
+ # current subscriptions
24
+ class ConnectionContext
25
+ attr_reader :mutex, :requests, :targets
26
+ def initialize
27
+ @mutex = Mutex.new
28
+ @requests = {}
29
+ @targets = {}
30
+ end
31
+
32
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength
33
+ def register_command(uuid, command, target = nil)
34
+ # p fn: "register_command", uuid: uuid, command: command
35
+ case command
36
+ when 'Ping' then promise(uuid)
37
+ when 'ReadStreamEventsForward' then promise(uuid)
38
+ when 'SubscribeToStream' then promise(uuid, target)
39
+ when 'WriteEvents' then promise(uuid)
40
+ when 'HeartbeatResponseCommand' then :nothing_to_do
41
+ when 'UnsubscribeFromStream' then :nothing_to_do
42
+ else fail("Unknown command #{command}")
43
+ end
44
+ end
45
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength
46
+
47
+ def fulfilled_command(uuid, value)
48
+ prom = nil
49
+ mutex.synchronize do
50
+ prom = requests.delete(uuid)
51
+ end
52
+ # p fn: "fulfilled_command", uuid: uuid, prom: prom, requests: requests
53
+ prom.fulfill(value) if prom
54
+ end
55
+
56
+ def rejected_command(uuid, error)
57
+ prom = nil
58
+ mutex.synchronize do
59
+ prom = requests.delete(uuid)
60
+ end
61
+ # p fn: "fulfilled_command", uuid: uuid, prom: prom, requests: requests
62
+ prom.reject(error) if prom
63
+ end
64
+
65
+ def trigger(uuid, method, *args)
66
+ target = mutex.synchronize { targets[uuid] }
67
+ return if target.nil?
68
+ target.__send__(method, *args)
69
+ end
70
+
71
+ def on_error(error = nil, &block)
72
+ if block
73
+ @error_handler = block
74
+ else
75
+ @error_handler.call(error) if @error_handler
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def promise(uuid, target = nil)
82
+ prom = Promise.new(uuid)
83
+ mutex.synchronize do
84
+ requests[uuid] = prom
85
+ targets[uuid] = target
86
+ end
87
+ prom
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,4 @@
1
+ class Eventstore
2
+ class CannotConnectError < RuntimeError; end
3
+ class DisconnectionError < RuntimeError; end
4
+ end
@@ -0,0 +1,19 @@
1
+ class Eventstore
2
+ # @see https://github.com/EventStore/EventStore/blob/master/src/EventStore.Core/Data/ResolvedEvent.cs#L9
3
+ module OriginalEventMixin
4
+ def original_event
5
+ link ? link : event
6
+ end
7
+
8
+ def original_stream_id
9
+ original_event.event_stream_id
10
+ end
11
+
12
+ def original_event_number
13
+ original_event.event_number
14
+ end
15
+ end
16
+ end
17
+
18
+ Eventstore::ResolvedEvent.include(Eventstore::OriginalEventMixin)
19
+ Eventstore::ResolvedIndexedEvent.include(Eventstore::OriginalEventMixin)
@@ -0,0 +1,369 @@
1
+ ## Generated from vendor/proto/ClientMessageDtos.proto for EventStore.Client.Messages
2
+ require 'beefcake'
3
+
4
+ class Eventstore
5
+ module OperationResult
6
+ Success = 0
7
+ PrepareTimeout = 1
8
+ CommitTimeout = 2
9
+ ForwardTimeout = 3
10
+ WrongExpectedVersion = 4
11
+ StreamDeleted = 5
12
+ InvalidTransaction = 6
13
+ AccessDenied = 7
14
+ end
15
+
16
+ class NewEvent
17
+ include Beefcake::Message
18
+ end
19
+
20
+ class EventRecord
21
+ include Beefcake::Message
22
+ end
23
+
24
+ class ResolvedIndexedEvent
25
+ include Beefcake::Message
26
+ end
27
+
28
+ class ResolvedEvent
29
+ include Beefcake::Message
30
+ end
31
+
32
+ class WriteEvents
33
+ include Beefcake::Message
34
+ end
35
+
36
+ class WriteEventsCompleted
37
+ include Beefcake::Message
38
+ end
39
+
40
+ class DeleteStream
41
+ include Beefcake::Message
42
+ end
43
+
44
+ class DeleteStreamCompleted
45
+ include Beefcake::Message
46
+ end
47
+
48
+ class TransactionStart
49
+ include Beefcake::Message
50
+ end
51
+
52
+ class TransactionStartCompleted
53
+ include Beefcake::Message
54
+ end
55
+
56
+ class TransactionWrite
57
+ include Beefcake::Message
58
+ end
59
+
60
+ class TransactionWriteCompleted
61
+ include Beefcake::Message
62
+ end
63
+
64
+ class TransactionCommit
65
+ include Beefcake::Message
66
+ end
67
+
68
+ class TransactionCommitCompleted
69
+ include Beefcake::Message
70
+ end
71
+
72
+ class ReadEvent
73
+ include Beefcake::Message
74
+ end
75
+
76
+ class ReadEventCompleted
77
+ include Beefcake::Message
78
+
79
+ module ReadEventResult
80
+ Success = 0
81
+ NotFound = 1
82
+ NoStream = 2
83
+ StreamDeleted = 3
84
+ Error = 4
85
+ AccessDenied = 5
86
+ end
87
+ end
88
+
89
+ class ReadStreamEvents
90
+ include Beefcake::Message
91
+ end
92
+
93
+ class ReadStreamEventsCompleted
94
+ include Beefcake::Message
95
+
96
+ module ReadStreamResult
97
+ Success = 0
98
+ NoStream = 1
99
+ StreamDeleted = 2
100
+ NotModified = 3
101
+ Error = 4
102
+ AccessDenied = 5
103
+ end
104
+ end
105
+
106
+ class ReadAllEvents
107
+ include Beefcake::Message
108
+ end
109
+
110
+ class ReadAllEventsCompleted
111
+ include Beefcake::Message
112
+
113
+ module ReadAllResult
114
+ Success = 0
115
+ NotModified = 1
116
+ Error = 2
117
+ AccessDenied = 3
118
+ end
119
+ end
120
+
121
+ class SubscribeToStream
122
+ include Beefcake::Message
123
+ end
124
+
125
+ class SubscriptionConfirmation
126
+ include Beefcake::Message
127
+ end
128
+
129
+ class StreamEventAppeared
130
+ include Beefcake::Message
131
+ end
132
+
133
+ class UnsubscribeFromStream
134
+ include Beefcake::Message
135
+ end
136
+
137
+ class SubscriptionDropped
138
+ include Beefcake::Message
139
+
140
+ module SubscriptionDropReason
141
+ Unsubscribed = 0
142
+ AccessDenied = 1
143
+ end
144
+ end
145
+
146
+ class NotHandled
147
+ include Beefcake::Message
148
+
149
+ module NotHandledReason
150
+ NotReady = 0
151
+ TooBusy = 1
152
+ NotMaster = 2
153
+ end
154
+
155
+ class MasterInfo
156
+ include Beefcake::Message
157
+ end
158
+ end
159
+
160
+ class ScavengeDatabase
161
+ include Beefcake::Message
162
+ end
163
+
164
+ class ScavengeDatabaseCompleted
165
+ include Beefcake::Message
166
+
167
+ module ScavengeResult
168
+ Success = 0
169
+ InProgress = 1
170
+ Failed = 2
171
+ end
172
+ end
173
+
174
+ class NewEvent
175
+ required :event_id, :bytes, 1
176
+ required :event_type, :string, 2
177
+ required :data_content_type, :int32, 3
178
+ required :metadata_content_type, :int32, 4
179
+ required :data, :bytes, 5
180
+ optional :metadata, :bytes, 6
181
+ end
182
+
183
+ class EventRecord
184
+ required :event_stream_id, :string, 1
185
+ required :event_number, :int32, 2
186
+ required :event_id, :bytes, 3
187
+ required :event_type, :string, 4
188
+ required :data_content_type, :int32, 5
189
+ required :metadata_content_type, :int32, 6
190
+ required :data, :bytes, 7
191
+ optional :metadata, :bytes, 8
192
+ optional :created, :int64, 9
193
+ optional :created_epoch, :int64, 10
194
+ end
195
+
196
+ class ResolvedIndexedEvent
197
+ required :event, EventRecord, 1
198
+ optional :link, EventRecord, 2
199
+ end
200
+
201
+ class ResolvedEvent
202
+ required :event, EventRecord, 1
203
+ optional :link, EventRecord, 2
204
+ required :commit_position, :int64, 3
205
+ required :prepare_position, :int64, 4
206
+ end
207
+
208
+ class WriteEvents
209
+ required :event_stream_id, :string, 1
210
+ required :expected_version, :int32, 2
211
+ repeated :events, NewEvent, 3
212
+ required :require_master, :bool, 4
213
+ end
214
+
215
+ class WriteEventsCompleted
216
+ required :result, OperationResult, 1
217
+ optional :message, :string, 2
218
+ required :first_event_number, :int32, 3
219
+ required :last_event_number, :int32, 4
220
+ optional :prepare_position, :int64, 5
221
+ optional :commit_position, :int64, 6
222
+ end
223
+
224
+ class DeleteStream
225
+ required :event_stream_id, :string, 1
226
+ required :expected_version, :int32, 2
227
+ required :require_master, :bool, 3
228
+ optional :hard_delete, :bool, 4
229
+ end
230
+
231
+ class DeleteStreamCompleted
232
+ required :result, OperationResult, 1
233
+ optional :message, :string, 2
234
+ optional :prepare_position, :int64, 3
235
+ optional :commit_position, :int64, 4
236
+ end
237
+
238
+ class TransactionStart
239
+ required :event_stream_id, :string, 1
240
+ required :expected_version, :int32, 2
241
+ required :require_master, :bool, 3
242
+ end
243
+
244
+ class TransactionStartCompleted
245
+ required :transaction_id, :int64, 1
246
+ required :result, OperationResult, 2
247
+ optional :message, :string, 3
248
+ end
249
+
250
+ class TransactionWrite
251
+ required :transaction_id, :int64, 1
252
+ repeated :events, NewEvent, 2
253
+ required :require_master, :bool, 3
254
+ end
255
+
256
+ class TransactionWriteCompleted
257
+ required :transaction_id, :int64, 1
258
+ required :result, OperationResult, 2
259
+ optional :message, :string, 3
260
+ end
261
+
262
+ class TransactionCommit
263
+ required :transaction_id, :int64, 1
264
+ required :require_master, :bool, 2
265
+ end
266
+
267
+ class TransactionCommitCompleted
268
+ required :transaction_id, :int64, 1
269
+ required :result, OperationResult, 2
270
+ optional :message, :string, 3
271
+ required :first_event_number, :int32, 4
272
+ required :last_event_number, :int32, 5
273
+ optional :prepare_position, :int64, 6
274
+ optional :commit_position, :int64, 7
275
+ end
276
+
277
+ class ReadEvent
278
+ required :event_stream_id, :string, 1
279
+ required :event_number, :int32, 2
280
+ required :resolve_link_tos, :bool, 3
281
+ required :require_master, :bool, 4
282
+ end
283
+
284
+ class ReadEventCompleted
285
+ required :result, ReadEventCompleted::ReadEventResult, 1
286
+ required :event, ResolvedIndexedEvent, 2
287
+ optional :error, :string, 3
288
+ end
289
+
290
+ class ReadStreamEvents
291
+ required :event_stream_id, :string, 1
292
+ required :from_event_number, :int32, 2
293
+ required :max_count, :int32, 3
294
+ required :resolve_link_tos, :bool, 4
295
+ required :require_master, :bool, 5
296
+ end
297
+
298
+ class ReadStreamEventsCompleted
299
+ repeated :events, ResolvedIndexedEvent, 1
300
+ required :result, ReadStreamEventsCompleted::ReadStreamResult, 2
301
+ required :next_event_number, :int32, 3
302
+ required :last_event_number, :int32, 4
303
+ required :is_end_of_stream, :bool, 5
304
+ required :last_commit_position, :int64, 6
305
+ optional :error, :string, 7
306
+ end
307
+
308
+ class ReadAllEvents
309
+ required :commit_position, :int64, 1
310
+ required :prepare_position, :int64, 2
311
+ required :max_count, :int32, 3
312
+ required :resolve_link_tos, :bool, 4
313
+ required :require_master, :bool, 5
314
+ end
315
+
316
+ class ReadAllEventsCompleted
317
+ required :commit_position, :int64, 1
318
+ required :prepare_position, :int64, 2
319
+ repeated :events, ResolvedEvent, 3
320
+ required :next_commit_position, :int64, 4
321
+ required :next_prepare_position, :int64, 5
322
+ optional :result, ReadAllEventsCompleted::ReadAllResult, 6, default: ReadAllEventsCompleted::ReadAllResult::Success
323
+ optional :error, :string, 7
324
+ end
325
+
326
+ class SubscribeToStream
327
+ required :event_stream_id, :string, 1
328
+ required :resolve_link_tos, :bool, 2
329
+ end
330
+
331
+ class SubscriptionConfirmation
332
+ required :last_commit_position, :int64, 1
333
+ optional :last_event_number, :int32, 2
334
+ end
335
+
336
+ class StreamEventAppeared
337
+ required :event, ResolvedEvent, 1
338
+ end
339
+
340
+ class UnsubscribeFromStream
341
+ end
342
+
343
+ class SubscriptionDropped
344
+ optional :reason, SubscriptionDropped::SubscriptionDropReason, 1, default: SubscriptionDropped::SubscriptionDropReason::Unsubscribed
345
+ end
346
+
347
+ class NotHandled
348
+ class MasterInfo
349
+ required :external_tcp_address, :string, 1
350
+ required :external_tcp_port, :int32, 2
351
+ required :external_http_address, :string, 3
352
+ required :external_http_port, :int32, 4
353
+ optional :external_secure_tcp_address, :string, 5
354
+ optional :external_secure_tcp_port, :int32, 6
355
+ end
356
+ required :reason, NotHandled::NotHandledReason, 1
357
+ optional :additional_info, :bytes, 2
358
+ end
359
+
360
+ class ScavengeDatabase
361
+ end
362
+
363
+ class ScavengeDatabaseCompleted
364
+ required :result, ScavengeDatabaseCompleted::ScavengeResult, 1
365
+ optional :error, :string, 2
366
+ required :total_time_ms, :int32, 3
367
+ required :total_space_saved, :int64, 4
368
+ end
369
+ end