euston-eventstore 1.0.2-java

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.
Files changed (32) hide show
  1. data/Rakefile +126 -0
  2. data/euston-eventstore.gemspec +68 -0
  3. data/lib/euston-eventstore/commit.rb +77 -0
  4. data/lib/euston-eventstore/constants.rb +5 -0
  5. data/lib/euston-eventstore/dispatcher/asynchronous_dispatcher.rb +37 -0
  6. data/lib/euston-eventstore/dispatcher/null_dispatcher.rb +11 -0
  7. data/lib/euston-eventstore/dispatcher/synchronous_dispatcher.rb +21 -0
  8. data/lib/euston-eventstore/errors.rb +21 -0
  9. data/lib/euston-eventstore/event_message.rb +26 -0
  10. data/lib/euston-eventstore/optimistic_event_store.rb +68 -0
  11. data/lib/euston-eventstore/optimistic_event_stream.rb +106 -0
  12. data/lib/euston-eventstore/persistence/mongodb/mongo_commit.rb +82 -0
  13. data/lib/euston-eventstore/persistence/mongodb/mongo_commit_id.rb +16 -0
  14. data/lib/euston-eventstore/persistence/mongodb/mongo_config.rb +28 -0
  15. data/lib/euston-eventstore/persistence/mongodb/mongo_event_message.rb +31 -0
  16. data/lib/euston-eventstore/persistence/mongodb/mongo_persistence_engine.rb +167 -0
  17. data/lib/euston-eventstore/persistence/mongodb/mongo_persistence_factory.rb +31 -0
  18. data/lib/euston-eventstore/persistence/mongodb/mongo_snapshot.rb +32 -0
  19. data/lib/euston-eventstore/persistence/mongodb/mongo_stream_head.rb +29 -0
  20. data/lib/euston-eventstore/persistence/stream_head.rb +23 -0
  21. data/lib/euston-eventstore/snapshot.rb +21 -0
  22. data/lib/euston-eventstore/version.rb +5 -0
  23. data/lib/euston-eventstore.rb +7 -0
  24. data/spec/event_store/dispatcher/asynchronous_dispatcher_spec.rb +75 -0
  25. data/spec/event_store/dispatcher/synchronous_dispatcher_spec.rb +39 -0
  26. data/spec/event_store/optimistic_event_store_spec.rb +292 -0
  27. data/spec/event_store/optimistic_event_stream_spec.rb +318 -0
  28. data/spec/event_store/persistence/mongodb_spec.rb +301 -0
  29. data/spec/event_store/serialization/simple_message.rb +12 -0
  30. data/spec/spec_helper.rb +39 -0
  31. data/spec/support/array_enumeration_counter.rb +20 -0
  32. metadata +178 -0
@@ -0,0 +1,301 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe Euston::EventStore do
4
+ describe 'mongodb persistence' do
5
+ let(:uuid) { Uuid }
6
+ let(:database) { 'event_store_tests' }
7
+ let(:config) { Euston::EventStore::Persistence::Mongodb::Config }
8
+ let(:factory) { Euston::EventStore::Persistence::Mongodb::MongoPersistenceFactory }
9
+ let(:stream_id) { uuid.generate }
10
+
11
+ before do
12
+ config.instance.database = database
13
+
14
+ @persistence = factory.build
15
+ @persistence.init
16
+ end
17
+
18
+ context 'when a commit is successfully persisted' do
19
+ let(:now) { Time.now.utc + (60 * 60 * 24 * 7 * 52) }
20
+ let(:attempt) { new_attempt(:commit_timestamp => now) }
21
+
22
+ before do
23
+ @persistence.commit attempt
24
+
25
+ sleep 0.25
26
+
27
+ @persisted = @persistence.get_from(:stream_id => stream_id,
28
+ :min_revision => 0,
29
+ :max_revision => Euston::EventStore::FIXNUM_MAX).first
30
+ end
31
+
32
+ it('correctly persists the stream identifier') { @persisted.stream_id.should == attempt.stream_id }
33
+ it('correctly persists the stream revision') { @persisted.stream_revision.should == attempt.stream_revision }
34
+ it('correctly persists the commit identifier') { @persisted.commit_id.should == attempt.commit_id }
35
+ it('correctly persists the commit sequence') { @persisted.commit_sequence.should == attempt.commit_sequence }
36
+
37
+ # persistence engines have varying levels of precision with respect to time.
38
+ it('correctly persists the commit timestamp') { (@persisted.commit_timestamp - now.to_f).should be <= 1 }
39
+
40
+ it('correctly persists the headers') { @persisted.headers.should have(attempt.headers.length).items }
41
+ it('correctly persists the events') { @persisted.events.should have(attempt.events.length).items }
42
+ it('makes the commit available to be read from the stream') {
43
+ @persistence.get_from(:stream_id => stream_id,
44
+ :min_revision => 0,
45
+ :max_revision => Euston::EventStore::FIXNUM_MAX).first.commit_id.should == attempt.commit_id }
46
+
47
+ it('adds the commit to the set of undispatched commits') {
48
+ @persistence.get_undispatched_commits.detect { |x| x.commit_id == attempt.commit_id }.should_not be_nil }
49
+
50
+ it('causes the stream to be found in the list of streams to snapshot') {
51
+ @persistence.get_streams_to_snapshot(1).detect { |x| x.stream_id == stream_id }.should_not be_nil }
52
+ end
53
+
54
+ context 'when reading from a given revision' do
55
+ let(:load_from_commit_containing_revision) { 3 }
56
+ let(:up_to_commit_containing_revision) { 5 }
57
+ let(:oldest) { new_attempt }
58
+ let(:oldest2) { next_attempt(oldest) }
59
+ let(:oldest3) { next_attempt(oldest2) }
60
+ let(:newest) { next_attempt(oldest3) }
61
+
62
+ before do
63
+ @persistence.commit oldest
64
+ @persistence.commit oldest2
65
+ @persistence.commit oldest3
66
+ @persistence.commit newest
67
+
68
+ @committed = @persistence.get_from(:stream_id => stream_id,
69
+ :min_revision => load_from_commit_containing_revision,
70
+ :max_revision => up_to_commit_containing_revision).to_a
71
+
72
+ end
73
+
74
+ it('starts from the commit which contains the minimum stream revision specified') { @committed.first.commit_id.should == oldest2.commit_id }
75
+ it('reads up to the commit which contains the maximum stream revision specified') { @committed.last.commit_id.should == oldest3.commit_id }
76
+ end
77
+
78
+ context 'when committing a stream with the same revision' do
79
+ let(:persistence1) { factory.build }
80
+ let(:persistence2) { factory.build }
81
+ let(:attempt1) { new_attempt }
82
+ let(:attempt2) { new_attempt }
83
+
84
+ before do
85
+ persistence1.init
86
+ persistence2.init
87
+
88
+ persistence1.commit attempt1
89
+
90
+ begin
91
+ persistence2.commit attempt2
92
+ rescue Exception => e
93
+ @caught = e
94
+ end
95
+ end
96
+
97
+ it('raises a ConcurrencyError') { @caught.should be_an(Euston::EventStore::ConcurrencyError) }
98
+ end
99
+
100
+ context 'when committing a stream with the same sequence' do
101
+ let(:persistence1) { factory.build }
102
+ let(:persistence2) { factory.build }
103
+ let(:attempt1) { new_attempt }
104
+ let(:attempt2) { new_attempt }
105
+
106
+ before do
107
+ persistence1.init
108
+ persistence2.init
109
+
110
+ persistence1.commit attempt1
111
+
112
+ begin
113
+ persistence2.commit attempt2
114
+ rescue Exception => e
115
+ @caught = e
116
+ end
117
+ end
118
+
119
+ it('raises a ConcurrencyError') { @caught.should be_an(Euston::EventStore::ConcurrencyError) }
120
+ end
121
+
122
+ context 'when attempting to overwrite a committed sequence' do
123
+ let(:successful_attempt) { new_attempt }
124
+ let(:failed_attempt) { new_attempt }
125
+
126
+ before do
127
+ @persistence.commit successful_attempt
128
+
129
+ begin
130
+ @persistence.commit failed_attempt
131
+ rescue Exception => e
132
+ @caught = e
133
+ end
134
+ end
135
+
136
+ it('raises a ConcurrencyError') { @caught.should be_an(Euston::EventStore::ConcurrencyError) }
137
+ end
138
+
139
+ context 'when attempting to persist a commit twice' do
140
+ let(:attempt) { new_attempt }
141
+
142
+ before do
143
+ @persistence.commit attempt
144
+
145
+ begin
146
+ @persistence.commit attempt
147
+ rescue Exception => e
148
+ @caught = e
149
+ end
150
+ end
151
+
152
+ it('raises a DuplicateCommitError') { @caught.should be_an(Euston::EventStore::DuplicateCommitError) }
153
+ end
154
+
155
+ context 'when a commit has been marked as dispatched' do
156
+ let(:attempt) { new_attempt }
157
+
158
+ before do
159
+ @persistence.commit attempt
160
+ @persistence.mark_commit_as_dispatched attempt
161
+ end
162
+
163
+ it('is no longer found in the set of undispatched commits') {
164
+ @persistence.get_undispatched_commits.detect { |c| c.commit_id == attempt.commit_id }.should be_nil }
165
+ end
166
+
167
+ context 'when saving a snapshot' do
168
+ let(:snapshot) { Euston::EventStore::Snapshot.new stream_id, 1, { :key => :value } }
169
+
170
+ before do
171
+ @persistence.commit new_attempt
172
+
173
+ sleep 0.25
174
+
175
+ @added = @persistence.add_snapshot snapshot
176
+ end
177
+
178
+ it('indicates the snapshot was added') { @added.should be_true }
179
+ it('is able to retrieve the snapshot') { @persistence.get_snapshot(stream_id, snapshot.stream_revision).should_not be_nil }
180
+ end
181
+
182
+ context 'when retrieving a snapshot' do
183
+ let(:too_far_back) { Euston::EventStore::Snapshot.new stream_id, 1, {} }
184
+ let(:correct) { Euston::EventStore::Snapshot.new stream_id, 3, { 'key' => 'value' } }
185
+ let(:too_far_forward) { Euston::EventStore::Snapshot.new stream_id, 5, {} }
186
+ let(:commit1) { new_attempt }
187
+ let(:commit2) { next_attempt commit1 }
188
+ let(:commit3) { next_attempt commit2 }
189
+
190
+ before do
191
+ @persistence.commit commit1
192
+ @persistence.commit commit2
193
+ @persistence.commit commit3
194
+
195
+ sleep 0.25
196
+
197
+ @persistence.add_snapshot too_far_back
198
+ @persistence.add_snapshot correct
199
+ @persistence.add_snapshot too_far_forward
200
+
201
+ @snapshot = @persistence.get_snapshot stream_id, too_far_forward.stream_revision - 1
202
+ end
203
+
204
+ it('loads the most recent prior snapshot') { @snapshot.stream_revision.should == correct.stream_revision }
205
+ it('has the correct snapshot payload') { @snapshot.payload.should == correct.payload }
206
+ end
207
+
208
+ context 'when a snapshot has been added to the most recent commit of a stream' do
209
+ let(:oldest) { new_attempt }
210
+ let(:oldest2) { next_attempt oldest }
211
+ let(:newest) { next_attempt oldest2 }
212
+ let(:snapshot) { Euston::EventStore::Snapshot.new stream_id, newest.stream_revision, { :key => :value } }
213
+
214
+ before do
215
+ @persistence.commit oldest
216
+ @persistence.commit oldest2
217
+ @persistence.commit newest
218
+
219
+ sleep 0.25
220
+
221
+ @persistence.add_snapshot snapshot
222
+ end
223
+
224
+ it('no longer finds the stream in the set of streams to be snapshot') {
225
+ @persistence.get_streams_to_snapshot(1).detect { |x| x.stream_id == stream_id }.should be_nil }
226
+ end
227
+
228
+ # Timing issues with this one?
229
+ #
230
+ # context 'when adding a commit after a snapshot' do
231
+ # let(:within_threshold) { 2 }
232
+ # let(:over_threshold) { 3 }
233
+ # let(:snapshot_data) { { :key => :value } }
234
+ # let(:oldest) { new_attempt }
235
+ # let(:oldest2) { next_attempt oldest }
236
+ # let(:newest) { next_attempt oldest2 }
237
+
238
+ # before do
239
+ # @persistence.commit oldest
240
+ # @persistence.commit oldest2
241
+
242
+ # sleep 0.25
243
+
244
+ # @persistence.add_snapshot Euston::EventStore::Snapshot.new(stream_id, oldest2.stream_revision, snapshot_data)
245
+ # @persistence.commit newest
246
+ # end
247
+
248
+ # it 'finds the stream in the set of streams to be snapshot when within the threshold' do
249
+ # @persistence.get_streams_to_snapshot(within_threshold).detect { |x| x.stream_id == stream_id }.should_not be_nil
250
+ # end
251
+
252
+ # it 'does not find the stream in the set of stream to be snapshot when over the threshold' do
253
+ # @persistence.get_streams_to_snapshot(over_threshold).detect { |x| x.stream_id == stream_id }.should be_nil
254
+ # end
255
+ # end
256
+
257
+ context 'when reading all commits from a particular point in time' do
258
+ let(:now) { Time.now.utc + (60 * 60 * 24 * 7 * 52) }
259
+ let(:first) { new_attempt(:commit_timestamp => now + 1) }
260
+ let(:second) { next_attempt first }
261
+ let(:third) { next_attempt second }
262
+ let(:fourth) { next_attempt third }
263
+
264
+ before do
265
+ @persistence.commit first
266
+ @persistence.commit second
267
+ @persistence.commit third
268
+ @persistence.commit fourth
269
+
270
+ @committed = @persistence.get_from :timestamp => now
271
+ end
272
+
273
+ it('returns all commits on or after the point in time specified') { @committed.should have(4).items }
274
+ end
275
+
276
+ def new_attempt(options = {})
277
+ defaults = { :stream_id => stream_id,
278
+ :stream_revision => 2,
279
+ :commit_id => uuid.generate,
280
+ :commit_sequence => 1,
281
+ :commit_timestamp => Time.now.utc,
282
+ :headers => { 'A header' => 'A string value',
283
+ 'Another header' => 2 },
284
+ :events => [ Euston::EventStore::EventMessage.new(:some_property => 'test'),
285
+ Euston::EventStore::EventMessage.new(:some_property => 'test2') ] }
286
+
287
+ Euston::EventStore::Commit.new(defaults.merge options)
288
+ end
289
+
290
+ def next_attempt(attempt)
291
+ Euston::EventStore::Commit.new(:stream_id => attempt.stream_id,
292
+ :stream_revision => attempt.stream_revision + 2,
293
+ :commit_id => uuid.generate,
294
+ :commit_sequence => attempt.commit_sequence + 1,
295
+ :commit_timestamp => attempt.commit_timestamp,
296
+ :headers => {},
297
+ :events => [ Euston::EventStore::EventMessage.new(:some_property => 'Another test'),
298
+ Euston::EventStore::EventMessage.new(:some_property => 'Another test2') ])
299
+ end
300
+ end
301
+ end
@@ -0,0 +1,12 @@
1
+ module Euston
2
+ module EventStore
3
+ class SimpleMessage
4
+ def initialize
5
+ @contents = []
6
+ end
7
+
8
+ attr_accessor :id, :created, :value, :count
9
+ attr_reader :contents
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ require 'ap'
2
+ require 'euston-eventstore'
3
+
4
+ if RUBY_PLATFORM.to_s == 'java'
5
+ require 'jmongo'
6
+ else
7
+ require 'mongo'
8
+ end
9
+
10
+ if RUBY_PLATFORM.to_s == 'java'
11
+ module Uuid
12
+ def self.generate
13
+ Java::JavaUtil::UUID.randomUUID().toString()
14
+ end
15
+ end
16
+ else
17
+ require 'uuid'
18
+ Uuid = UUID.new
19
+ end
20
+
21
+ require 'rspec/core'
22
+ require 'rspec/core/rake_task'
23
+ require 'rspec/expectations'
24
+ require 'rspec/mocks'
25
+
26
+ require 'support/array_enumeration_counter'
27
+
28
+ mongo_config = Euston::EventStore::Persistence::Mongodb::Config.instance
29
+ mongo_config.database = 'event_store_tests'
30
+
31
+ RSpec.configure do |config|
32
+ config.fail_fast = true
33
+
34
+ config.before :each do
35
+ connection = Mongo::Connection.new(mongo_config.host, mongo_config.port, mongo_config.options)
36
+ db = connection.db(mongo_config.database)
37
+ db.collections.select { |c| c.name !~ /system/ }.each { |c| db.drop_collection c.name }
38
+ end
39
+ end
@@ -0,0 +1,20 @@
1
+ require 'delegate'
2
+
3
+ module Euston
4
+ module EventStore
5
+ class ArrayEnumerationCounter < DelegateClass(Array)
6
+ def initialize(obj)
7
+ super(obj)
8
+
9
+ @invocations = 0
10
+ end
11
+
12
+ def each
13
+ @invocations += 1
14
+ super
15
+ end
16
+
17
+ attr_reader :invocations
18
+ end
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: euston-eventstore
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.0.2
6
+ platform: java
7
+ authors:
8
+ - Lee Henson
9
+ - Guy Boertje
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-09-15 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ version_requirements: &2058 !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.9
22
+ none: false
23
+ requirement: *2058
24
+ prerelease: false
25
+ type: :runtime
26
+ - !ruby/object:Gem::Dependency
27
+ name: hash-keys
28
+ version_requirements: &2076 !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
+ none: false
34
+ requirement: *2076
35
+ prerelease: false
36
+ type: :runtime
37
+ - !ruby/object:Gem::Dependency
38
+ name: require_all
39
+ version_requirements: &2092 !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.2.0
44
+ none: false
45
+ requirement: *2092
46
+ prerelease: false
47
+ type: :runtime
48
+ - !ruby/object:Gem::Dependency
49
+ name: uuid
50
+ version_requirements: &2108 !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.3.0
55
+ none: false
56
+ requirement: *2108
57
+ prerelease: false
58
+ type: :runtime
59
+ - !ruby/object:Gem::Dependency
60
+ name: json-jruby
61
+ version_requirements: &2124 !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.5.0
66
+ none: false
67
+ requirement: *2124
68
+ prerelease: false
69
+ type: :runtime
70
+ - !ruby/object:Gem::Dependency
71
+ name: jmongo
72
+ version_requirements: &2140 !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.0.0
77
+ none: false
78
+ requirement: *2140
79
+ prerelease: false
80
+ type: :runtime
81
+ - !ruby/object:Gem::Dependency
82
+ name: awesome_print
83
+ version_requirements: &2156 !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ~>
86
+ - !ruby/object:Gem::Version
87
+ version: 0.4.0
88
+ none: false
89
+ requirement: *2156
90
+ prerelease: false
91
+ type: :development
92
+ - !ruby/object:Gem::Dependency
93
+ name: fuubar
94
+ version_requirements: &2174 !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ~>
97
+ - !ruby/object:Gem::Version
98
+ version: 0.0.0
99
+ none: false
100
+ requirement: *2174
101
+ prerelease: false
102
+ type: :development
103
+ - !ruby/object:Gem::Dependency
104
+ name: rspec
105
+ version_requirements: &2190 !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 2.6.0
110
+ none: false
111
+ requirement: *2190
112
+ prerelease: false
113
+ type: :development
114
+ description: Ruby port for Jonathan Oliver's EventStore. See https://github.com/joliver/EventStore for details.
115
+ email:
116
+ - lee.m.henson@gmail.com
117
+ - guyboertje@gmail.com
118
+ executables: []
119
+ extensions: []
120
+ extra_rdoc_files: []
121
+ files:
122
+ - Rakefile
123
+ - euston-eventstore.gemspec
124
+ - lib/euston-eventstore.rb
125
+ - lib/euston-eventstore/commit.rb
126
+ - lib/euston-eventstore/constants.rb
127
+ - lib/euston-eventstore/dispatcher/asynchronous_dispatcher.rb
128
+ - lib/euston-eventstore/dispatcher/null_dispatcher.rb
129
+ - lib/euston-eventstore/dispatcher/synchronous_dispatcher.rb
130
+ - lib/euston-eventstore/errors.rb
131
+ - lib/euston-eventstore/event_message.rb
132
+ - lib/euston-eventstore/optimistic_event_store.rb
133
+ - lib/euston-eventstore/optimistic_event_stream.rb
134
+ - lib/euston-eventstore/persistence/mongodb/mongo_commit.rb
135
+ - lib/euston-eventstore/persistence/mongodb/mongo_commit_id.rb
136
+ - lib/euston-eventstore/persistence/mongodb/mongo_config.rb
137
+ - lib/euston-eventstore/persistence/mongodb/mongo_event_message.rb
138
+ - lib/euston-eventstore/persistence/mongodb/mongo_persistence_engine.rb
139
+ - lib/euston-eventstore/persistence/mongodb/mongo_persistence_factory.rb
140
+ - lib/euston-eventstore/persistence/mongodb/mongo_snapshot.rb
141
+ - lib/euston-eventstore/persistence/mongodb/mongo_stream_head.rb
142
+ - lib/euston-eventstore/persistence/stream_head.rb
143
+ - lib/euston-eventstore/snapshot.rb
144
+ - lib/euston-eventstore/version.rb
145
+ - spec/event_store/dispatcher/asynchronous_dispatcher_spec.rb
146
+ - spec/event_store/dispatcher/synchronous_dispatcher_spec.rb
147
+ - spec/event_store/optimistic_event_store_spec.rb
148
+ - spec/event_store/optimistic_event_stream_spec.rb
149
+ - spec/event_store/persistence/mongodb_spec.rb
150
+ - spec/event_store/serialization/simple_message.rb
151
+ - spec/spec_helper.rb
152
+ - spec/support/array_enumeration_counter.rb
153
+ homepage: http://github.com/leemhenson/euston-eventstore
154
+ licenses: []
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ! '>='
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ none: false
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ! '>='
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ none: false
171
+ requirements: []
172
+ rubyforge_project:
173
+ rubygems_version: 1.8.9
174
+ signing_key:
175
+ specification_version: 3
176
+ summary: Event store for use with Euston.
177
+ test_files: []
178
+ ...