euston-eventstore 1.0.2-java

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