event_sourcery-postgres 0.4.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e45ae5ad105bd165a2fb4b59be6032488ec925df
4
- data.tar.gz: c36491b6e980803520d2b3a900207500f694b500
2
+ SHA256:
3
+ metadata.gz: e0846cda7986faeccb438d159a1fe87e0388855b2a898a5703dbc95d8bbce425
4
+ data.tar.gz: c29112988c1049f8978e95c276f1bb96bfc1f5bdf36b491f68dad0dd9ac2b9ce
5
5
  SHA512:
6
- metadata.gz: a86c88d7db9e0368d3ddbea1b6b7ce869d8f3465dd1bce3990f0038edffcb5712da10d4182fd0ce46427b2e3aa08b67b7da6712d7aec0abd16f766741a492556
7
- data.tar.gz: 3f286cd47b3eb36e3b3e65929d6df0d51a1bc8762f1256d1b21c6f8041861fc8087bde5978989c7abb58d6f274add9dca368ca992105a12e5e7af6069e390dc7
6
+ metadata.gz: 38a3097e2582d651b4af1d421a4f431f888f09b8cc369f44c059276449f17e9dbac29a2f08bfc1eea7dbf9eae39eebcb3788d5f1d9d72f8702e309a7b3cfba14
7
+ data.tar.gz: c384b9737f5888df616f1dc776db9411616434f0025d5ef888d6ad273d9757ebecfec56ff5ee4dc9eca1e05553f5c3cc6dc375aecbb8a979c62e1199b7dbacfb
@@ -1,4 +1,5 @@
1
1
  # Change Log
2
+
2
3
  All notable changes to this project will be documented in this file.
3
4
 
4
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
@@ -6,7 +7,53 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
6
7
 
7
8
  ## [Unreleased]
8
9
 
9
- ## [0.4.0] - 2017-6-21
10
+ ## [0.8.1] - 2020-10-02
11
+ ### Added
12
+ - Add Ruby 2.6 and 2.7 to the CI test matrix.
13
+
14
+ ### Removed
15
+ - Remove Ruby 2.2 from the CI test matrix.
16
+ - Support for Boxen.
17
+
18
+ ### Fixed
19
+ - Upgrade development dependency Rake to version 13. This resolves
20
+ [CVE-2020-8130](https://github.com/advisories/GHSA-jppv-gw3r-w3q8).
21
+
22
+ - Resolve warnings raised when running on Ruby 2.7.
23
+
24
+ ## [0.8.0] - 2018-08-06
25
+ ### Added
26
+ - Add a `on_events_recorded` config option, that defaults to a no-op proc, \
27
+ to handle any app specific logic after the events are recoded on `EventStore#sink`
28
+
29
+ ## [0.7.0] - 2018-05-23
30
+ ### Added
31
+ - Add a `projector_transaction_size` config option to control how many events
32
+ are processed before the transaction is commited. The default value is 1 to
33
+ match the existing behavour.
34
+
35
+ We suggest setting this to match the number of events returned from the event
36
+ store subscription. This is [now configurable](https://github.com/envato/event_sourcery/pull/197)
37
+ in event_sourcery by configuring `subscription_batch_size`.
38
+
39
+ ### Removed
40
+ - Remove upper bound version restriction on `sequel` gem. Now accepts versions
41
+ 5 and higher.
42
+
43
+ ## [0.6.0] - 2018-01-02
44
+ ### Changed
45
+
46
+ - Only send info log after processing a group of events
47
+
48
+ ### Removed
49
+ - Remove `processes_events` and `projects_events` as these have been [removed
50
+ in event_sourcery](https://github.com/envato/event_sourcery/pull/161).
51
+
52
+ ## [0.5.0] - 2017-07-27
53
+ - First Version of YARD documentation.
54
+ - Fix Sequel deprecation by globally loading pg extensions
55
+
56
+ ## [0.4.0] - 2017-06-21
10
57
  ### Changed
11
58
  - Reactors store the UUID of the event being processed in the `causation_id`
12
59
  of any emitted events. This replaces the old behaviour of storing id of the
@@ -19,27 +66,37 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
19
66
  - Added index on the `events` table for `correlation_id` and `causation_id`
20
67
  columns.
21
68
 
22
- ## [0.3.0] - 2017-6-16
69
+ ## [0.3.0] - 2017-06-16
23
70
  ### Changed
24
71
  - The event store persists the event `correlation_id` and `causation_id`.
25
72
  To facilitate this `correlation_id` and `causation_id` columns have been
26
73
  added to the `events` table and the `write_events` function has been
27
74
  altered. Event Sourcery apps will need to ensure these DB changes have
28
75
  been applied to use this version of Event Sourcery.
76
+ - The emit_events method now accepts typed events instead of symbols
77
+ - Remove dynamic emit events methods from reactors (e.g. emit_item_added)
29
78
 
30
- ## [0.2.0] - 2017-6-1
79
+ ## [0.2.0] - 2017-06-01
31
80
  ### Changed
32
81
  - Make `EventSourcery::Postgres::OptimisedEventPollWaiter#shutdown` private
33
82
  - Updated `EventSourcery::Postgres::OptimisedEventPollWaiter#poll` to ensure that `#shutdown!` is run when an error is raised
34
83
  or when the loop stops
35
- - Remove dynamic emit events methods from reactors (e.g. emit_item_added)
36
- - The emit_events method now accepts typed events instead of symbols
37
84
 
38
85
  ### Added
39
86
  - Configure projector tracker table name via `EventSourcery::Postgres.configure`
40
87
 
41
- ## [0.1.0] - 2017-5-26
88
+ ## 0.1.0 - 2017-05-26
42
89
  ### Changed
43
90
  - Imported code from the [event_sourcery](https://github.com/envato/event_sourcery).
44
91
  - Postgres related configuration is through `EventSourcery::Postgres.configure`
45
92
  instead of `EventSourcery.configure`.
93
+
94
+ [Unreleased]: https://github.com/envato/event_sourcery-postgres/compare/v0.8.1...HEAD
95
+ [0.8.1]: https://github.com/envato/event_sourcery-postgres/compare/v0.8.0...v0.8.1
96
+ [0.8.0]: https://github.com/envato/event_sourcery-postgres/compare/v0.7.0...v0.8.0
97
+ [0.7.0]: https://github.com/envato/event_sourcery-postgres/compare/v0.6.0...v0.7.0
98
+ [0.6.0]: https://github.com/envato/event_sourcery-postgres/compare/v0.5.0...v0.6.0
99
+ [0.5.0]: https://github.com/envato/event_sourcery-postgres/compare/v0.4.0...v0.5.0
100
+ [0.4.0]: https://github.com/envato/event_sourcery-postgres/compare/v0.3.0...v0.4.0
101
+ [0.3.0]: https://github.com/envato/event_sourcery-postgres/compare/v0.2.0...v0.3.0
102
+ [0.2.0]: https://github.com/envato/event_sourcery-postgres/compare/v0.1.0...v0.2.0
data/README.md CHANGED
@@ -22,7 +22,6 @@ gem 'event_sourcery-postgres'
22
22
  EventSourcery::Postgres.configure do |config|
23
23
  config.event_store_database = Sequel.connect(...)
24
24
  config.projections_database = Sequel.connect(...)
25
- config.use_optimistic_concurrency = true
26
25
  config.write_events_function_name = 'writeEvents'
27
26
  config.events_table_name = :events
28
27
  config.aggregates_table_name = :aggregates
@@ -91,11 +90,8 @@ To release a new version:
91
90
 
92
91
  1. Update the version number in `lib/event_sourcery/postgres/version.rb`
93
92
  2. Get this change onto master via the normal PR process
94
- 3. Run `gem_push=false be rake release`,
95
- this will create a git tag for the version,
96
- push tags up to GitHub, and package the code in a `.gem` file.
97
- 4. Manually upload the generated gem file (`pkg/event_sourcery-postgres-#{version}.gem`) to
98
- [rubygems.envato.com](https://rubygems.envato.com).
93
+ 3. Run `bundle exec rake release`, this will create a git tag for the
94
+ version, push tags up to GitHub, and upload the gem to rubygems.org.
99
95
 
100
96
  ## Contributing
101
97
 
@@ -12,6 +12,11 @@ Gem::Specification.new do |spec|
12
12
 
13
13
  spec.summary = 'Postgres event store for use with EventSourcery'
14
14
  spec.homepage = 'https://github.com/envato/event_sourcery-postgres'
15
+ spec.metadata = {
16
+ 'bug_tracker_uri' => 'https://github.com/envato/event_sourcery-postgres/issues',
17
+ 'changelog_uri' => 'https://github.com/envato/event_sourcery-postgres/blob/master/CHANGELOG.md',
18
+ 'source_code_uri' => 'https://github.com/envato/event_sourcery-postgres',
19
+ }
15
20
 
16
21
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
22
  f.match(%r{^(\.|bin/|Gemfile|Rakefile|script/|spec/)})
@@ -22,11 +27,11 @@ Gem::Specification.new do |spec|
22
27
 
23
28
  spec.required_ruby_version = '>= 2.2.0'
24
29
 
25
- spec.add_dependency 'sequel', '~> 4.38'
30
+ spec.add_dependency 'sequel', '>= 4.38'
26
31
  spec.add_dependency 'pg'
27
32
  spec.add_dependency 'event_sourcery', '>= 0.14.0'
28
- spec.add_development_dependency 'bundler', '~> 1.10'
29
- spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'bundler'
34
+ spec.add_development_dependency 'rake', '~> 13'
30
35
  spec.add_development_dependency 'rspec', '~> 3.0'
31
36
  spec.add_development_dependency 'pry'
32
37
  spec.add_development_dependency 'benchmark-ips'
@@ -1,6 +1,5 @@
1
1
  require 'sequel'
2
2
 
3
- Sequel.extension :pg_json
4
3
  Sequel.default_timezone = :utc
5
4
 
6
5
  require 'event_sourcery'
@@ -1,8 +1,7 @@
1
1
  module EventSourcery
2
2
  module Postgres
3
3
  class Config
4
- attr_accessor :event_store_database,
5
- :lock_table_to_guarantee_linear_sequence_id_growth,
4
+ attr_accessor :lock_table_to_guarantee_linear_sequence_id_growth,
6
5
  :write_events_function_name,
7
6
  :events_table_name,
8
7
  :aggregates_table_name,
@@ -10,10 +9,15 @@ module EventSourcery
10
9
  :callback_interval_if_no_new_events,
11
10
  :auto_create_projector_tracker,
12
11
  :event_tracker,
13
- :projections_database,
14
- :event_store,
15
- :event_source,
16
- :event_sink
12
+ :projector_transaction_size,
13
+ :on_events_recorded
14
+
15
+ attr_writer :event_store,
16
+ :event_source,
17
+ :event_sink
18
+
19
+ attr_reader :event_store_database,
20
+ :projections_database
17
21
 
18
22
  def initialize
19
23
  @lock_table_to_guarantee_linear_sequence_id_growth = true
@@ -24,6 +28,8 @@ module EventSourcery
24
28
  @callback_interval_if_no_new_events = 10
25
29
  @event_store_database = nil
26
30
  @auto_create_projector_tracker = true
31
+ @projector_transaction_size = 1
32
+ @on_events_recorded = ->(events) {}
27
33
  end
28
34
 
29
35
  def event_store
@@ -38,9 +44,25 @@ module EventSourcery
38
44
  @event_sink ||= ::EventSourcery::EventStore::EventSink.new(event_store)
39
45
  end
40
46
 
41
- def projections_database=(sequel_connection)
42
- @projections_database = sequel_connection
43
- @event_tracker = Postgres::Tracker.new(sequel_connection)
47
+ def event_store_database=(db_connection)
48
+ setup_connection(db_connection)
49
+
50
+ @event_store_database = db_connection
51
+ end
52
+
53
+ def projections_database=(db_connection)
54
+ setup_connection(db_connection)
55
+
56
+ @projections_database = db_connection
57
+ @event_tracker = Postgres::Tracker.new(db_connection)
58
+ end
59
+
60
+ private
61
+
62
+ def setup_connection(db_connection)
63
+ return unless db_connection
64
+
65
+ db_connection.extension :pg_json
44
66
  end
45
67
  end
46
68
  end
@@ -3,25 +3,39 @@ module EventSourcery
3
3
  class EventStore
4
4
  include EventSourcery::EventStore::EachByRange
5
5
 
6
- def initialize(pg_connection,
6
+ def initialize(db_connection,
7
7
  events_table_name: EventSourcery::Postgres.config.events_table_name,
8
8
  lock_table: EventSourcery::Postgres.config.lock_table_to_guarantee_linear_sequence_id_growth,
9
9
  write_events_function_name: EventSourcery::Postgres.config.write_events_function_name,
10
- event_builder: EventSourcery.config.event_builder)
11
- @pg_connection = pg_connection
10
+ event_builder: EventSourcery.config.event_builder,
11
+ on_events_recorded: EventSourcery::Postgres.config.on_events_recorded)
12
+ @db_connection = db_connection
12
13
  @events_table_name = events_table_name
13
14
  @write_events_function_name = write_events_function_name
14
15
  @lock_table = lock_table
15
16
  @event_builder = event_builder
17
+ @on_events_recorded = on_events_recorded
16
18
  end
17
19
 
20
+ # Like water flowing into a sink eventually it will go down the drain
21
+ # into the goodness of the plumbing system.
22
+ # So to will the given events you put in this 'sink'. Except the plumbing
23
+ # system is the data base events table.
24
+ # This can raise db connection errors.
25
+ #
26
+ # @param event_or_events the event or events to save
27
+ # @param expected_version the version to save with the event, default nil
28
+ #
29
+ # @raise [DatabaseError] if something goes wrong with the database
30
+ # @raise [ConcurrencyError] if there was a concurrency conflict
18
31
  def sink(event_or_events, expected_version: nil)
19
32
  events = Array(event_or_events)
20
33
  aggregate_ids = events.map(&:aggregate_id).uniq
21
34
  raise AtomicWriteToMultipleAggregatesNotSupported unless aggregate_ids.count == 1
22
35
  sql = write_events_sql(aggregate_ids.first, events, expected_version)
23
- @pg_connection.run(sql)
36
+ @db_connection.run(sql)
24
37
  log_events_saved(events)
38
+ on_events_recorded.call(events)
25
39
  true
26
40
  rescue Sequel::DatabaseError => e
27
41
  if e.message =~ /Concurrency conflict/
@@ -31,15 +45,29 @@ module EventSourcery
31
45
  end
32
46
  end
33
47
 
48
+ # Get the next set of events from the given event id. You can
49
+ # specify event types and a limit.
50
+ # Default limit is 1000 and the default event types will be all.
51
+ #
52
+ # @param id the event id to get next events from
53
+ # @param event_types the event types to filter, default nil = all
54
+ # @param limit the limit to the results, default 1000
55
+ #
56
+ # @return [Array] array of found events
34
57
  def get_next_from(id, event_types: nil, limit: 1000)
35
58
  query = events_table.
36
59
  order(:id).
37
60
  where(Sequel.lit('id >= ?', id)).
38
61
  limit(limit)
39
62
  query = query.where(type: event_types) if event_types
40
- query.map { |event_row| build_event(event_row) }
63
+ query.map { |event_row| build_event(**event_row) }
41
64
  end
42
65
 
66
+ # Get last event id for a given event types.
67
+ #
68
+ # @param event_types the type of event(s) to filter
69
+ #
70
+ # @return the latest event id
43
71
  def latest_event_id(event_types: nil)
44
72
  latest_event = events_table
45
73
  latest_event = latest_event.where(type: event_types) if event_types
@@ -51,14 +79,25 @@ module EventSourcery
51
79
  end
52
80
  end
53
81
 
82
+ # Get the events for a given aggregate id.
83
+ #
84
+ # @param aggregate_id the aggregate id to filter for
85
+ #
86
+ # @return [Array] of found events
54
87
  def get_events_for_aggregate_id(aggregate_id)
55
88
  events_table.where(aggregate_id: aggregate_id.to_str).order(:version).map do |event_hash|
56
- build_event(event_hash)
89
+ build_event(**event_hash)
57
90
  end
58
91
  end
59
92
 
93
+ # Subscribe to events.
94
+ #
95
+ # @param from_id subscribe from a starting event id. default will be from the start.
96
+ # @param event_types the event_types to subscribe to, default all.
97
+ # @param after_listen the after listen call back block. default nil.
98
+ # @param subscription_master the subscription master block
60
99
  def subscribe(from_id:, event_types: nil, after_listen: nil, subscription_master:, &block)
61
- poll_waiter = OptimisedEventPollWaiter.new(pg_connection: @pg_connection, after_listen: after_listen)
100
+ poll_waiter = OptimisedEventPollWaiter.new(db_connection: @db_connection, after_listen: after_listen)
62
101
  args = {
63
102
  poll_waiter: poll_waiter,
64
103
  event_store: self,
@@ -68,17 +107,19 @@ module EventSourcery
68
107
  subscription_master: subscription_master,
69
108
  on_new_events: block
70
109
  }
71
- EventSourcery::EventStore::Subscription.new(args).tap(&:start)
110
+ EventSourcery::EventStore::Subscription.new(**args).tap(&:start)
72
111
  end
73
112
 
74
113
  private
75
114
 
115
+ attr_reader :on_events_recorded
116
+
76
117
  def events_table
77
- @pg_connection[@events_table_name]
118
+ @db_connection[@events_table_name]
78
119
  end
79
120
 
80
- def build_event(data)
81
- @event_builder.build(data)
121
+ def build_event(**data)
122
+ @event_builder.build(**data)
82
123
  end
83
124
 
84
125
  def write_events_sql(aggregate_id, events, expected_version)
@@ -123,7 +164,7 @@ module EventSourcery
123
164
  else
124
165
  value
125
166
  end
126
- @pg_connection.literal(wrapped_value)
167
+ @db_connection.literal(wrapped_value)
127
168
  end
128
169
 
129
170
  def log_events_saved(events)
@@ -4,8 +4,8 @@ module EventSourcery
4
4
  class OptimisedEventPollWaiter
5
5
  ListenThreadDied = Class.new(StandardError)
6
6
 
7
- def initialize(pg_connection:, timeout: 30, after_listen: proc {})
8
- @pg_connection = pg_connection
7
+ def initialize(db_connection:, timeout: 30, after_listen: proc {})
8
+ @db_connection = db_connection
9
9
  @timeout = timeout
10
10
  @events_queue = QueueWithIntervalCallback.new
11
11
  @after_listen = after_listen
@@ -65,7 +65,7 @@ module EventSourcery
65
65
  end
66
66
 
67
67
  def listen_for_new_events(loop: true, after_listen: nil, timeout: 30)
68
- @pg_connection.listen('new_event',
68
+ @db_connection.listen('new_event',
69
69
  loop: loop,
70
70
  after_listen: after_listen,
71
71
  timeout: timeout) do |_channel, _pid, _payload|
@@ -10,7 +10,6 @@ module EventSourcery
10
10
 
11
11
  class << self
12
12
  alias_method :project, :process
13
- alias_method :projects_events, :processes_events
14
13
  alias_method :projector_name, :processor_name
15
14
  end
16
15
  end
@@ -18,23 +17,31 @@ module EventSourcery
18
17
 
19
18
  module InstanceMethods
20
19
  def initialize(tracker: EventSourcery::Postgres.config.event_tracker,
21
- db_connection: EventSourcery::Postgres.config.projections_database)
20
+ db_connection: EventSourcery::Postgres.config.projections_database,
21
+ transaction_size: EventSourcery::Postgres.config.projector_transaction_size)
22
22
  @tracker = tracker
23
23
  @db_connection = db_connection
24
+ @transaction_size = transaction_size
24
25
  end
25
26
 
26
27
  private
27
28
 
29
+ attr_reader :transaction_size
30
+
28
31
  def process_events(events, subscription_master)
29
- events.each do |event|
32
+ events.each_slice(transaction_size) do |slice_of_events|
30
33
  subscription_master.shutdown_if_requested
34
+
31
35
  db_connection.transaction do
32
- process(event)
33
- tracker.processed_event(processor_name, event.id)
36
+ slice_of_events.each do |event|
37
+ process(event)
38
+ EventSourcery.logger.debug { "[#{processor_name}] Processed event: #{event.inspect}" }
39
+ end
40
+ tracker.processed_event(processor_name, slice_of_events.last.id)
34
41
  end
35
- EventSourcery.logger.debug { "[#{processor_name}] Processed event: #{event.inspect}" }
36
- EventSourcery.logger.info { "[#{processor_name}] Processed up to event id: #{events.last.id}" }
37
42
  end
43
+
44
+ EventSourcery.logger.info { "[#{processor_name}] Processed up to event id: #{events.last.id}" }
38
45
  end
39
46
  end
40
47
  end
@@ -11,18 +11,29 @@ module EventSourcery
11
11
  end
12
12
 
13
13
  module ClassMethods
14
+ # Assign the types of events this reactor can emit.
15
+ #
16
+ # @param event_types the types of events this reactor can emit
14
17
  def emits_events(*event_types)
15
18
  @emits_event_types = event_types
16
19
  end
17
20
 
21
+ # @return [Array] an array of the types of events this reactor can emit
18
22
  def emit_events
19
23
  @emits_event_types ||= []
20
24
  end
21
25
 
26
+ # This will tell you if this reactor emits any type of event.
27
+ #
28
+ # @return [true, false] true if this emits events, false if not
22
29
  def emits_events?
23
30
  !emit_events.empty?
24
31
  end
25
32
 
33
+ # Will check if this reactor emits the given type of event.
34
+ #
35
+ # @param event_type the event type to check
36
+ # @return [true, false] true if it does emit the given event false if not
26
37
  def emits_event?(event_type)
27
38
  emit_events.include?(event_type)
28
39
  end
@@ -3,6 +3,12 @@ module EventSourcery
3
3
  module Schema
4
4
  module_function
5
5
 
6
+ # This will create the event store tables and functions
7
+ # (event, aggregates, tracker and create or update functions)
8
+ # for the given Postgres database.
9
+ # The default will be the one specified in the config.
10
+ #
11
+ # @param db the Postgres database to use
6
12
  def create_event_store(db: EventSourcery::Postgres.config.event_store_database,
7
13
  events_table_name: EventSourcery::Postgres.config.events_table_name,
8
14
  aggregates_table_name: EventSourcery::Postgres.config.aggregates_table_name,
@@ -12,6 +18,11 @@ module EventSourcery
12
18
  create_or_update_functions(db: db, events_table_name: events_table_name, function_name: write_events_function_name, aggregates_table_name: aggregates_table_name)
13
19
  end
14
20
 
21
+ # Create the events table. Needs the database and the table name.
22
+ # The defaults will be whats specified in config.
23
+ #
24
+ # @param db the Postgres database to use
25
+ # @param table_name the name of the events table
15
26
  def create_events(db: EventSourcery::Postgres.config.event_store_database,
16
27
  table_name: EventSourcery::Postgres.config.events_table_name)
17
28
  db.run 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
@@ -34,14 +45,27 @@ module EventSourcery
34
45
  end
35
46
  end
36
47
 
48
+ # Create the aggregates table. Needs the database and the table name.
49
+ # The defaults will be whats specified in config.
50
+ #
51
+ # @param db the Postgres database to use
52
+ # @param table_name the name of the aggregates table
37
53
  def create_aggregates(db: EventSourcery::Postgres.config.event_store_database,
38
54
  table_name: EventSourcery::Postgres.config.aggregates_table_name)
39
55
  db.create_table(table_name) do
40
- primary_key :aggregate_id, :uuid
56
+ uuid :aggregate_id, primary_key: true
41
57
  column :version, :bigint, default: 1
42
58
  end
43
59
  end
44
60
 
61
+ # Create the 'create or update' functions.
62
+ # Needs the database, table name, function name and aggregates table name.
63
+ # The defaults will be whats specified in config.
64
+ #
65
+ # @param db the Postgres database to use
66
+ # @param function_name the name of the write events function
67
+ # @param events_table_name the name of the events table
68
+ # @param aggregates_table_name the name of the aggregates table
45
69
  def create_or_update_functions(db: EventSourcery::Postgres.config.event_store_database,
46
70
  function_name: EventSourcery::Postgres.config.write_events_function_name,
47
71
  events_table_name: EventSourcery::Postgres.config.events_table_name,
@@ -138,6 +162,11 @@ $$ language plpgsql;
138
162
  SQL
139
163
  end
140
164
 
165
+ # Create the projector tracker table. Needs the database and the table name.
166
+ # The defaults will be whats specified in config.
167
+ #
168
+ # @param db the Postgres database to use
169
+ # @param table_name the name of the aggregates table
141
170
  def create_projector_tracker(db: EventSourcery::Postgres.config.projections_database,
142
171
  table_name: EventSourcery::Postgres.config.tracker_table_name)
143
172
  db.create_table(table_name) do
@@ -9,15 +9,23 @@ module EventSourcery
9
9
  end
10
10
 
11
11
  module ClassMethods
12
+ # Hash of the tables and their corresponding blocks.
13
+ #
14
+ # @return [Hash] hash keyed by table names and block values
12
15
  def tables
13
16
  @tables ||= {}
14
17
  end
15
18
 
19
+ # For the given table name assign to give block as the value.
20
+ #
21
+ # @param name the name of the table
22
+ # @param block the block of code to assign for the table
16
23
  def table(name, &block)
17
24
  tables[name] = block
18
25
  end
19
26
  end
20
27
 
28
+ # Create each table.
21
29
  def setup
22
30
  self.class.tables.each do |table_name, schema_block|
23
31
  prefixed_name = table_name_prefixed(table_name)
@@ -26,6 +34,7 @@ module EventSourcery
26
34
  super if defined?(super)
27
35
  end
28
36
 
37
+ # Reset by dropping each table.
29
38
  def reset
30
39
  self.class.tables.keys.each do |table_name|
31
40
  prefixed_name = table_name_prefixed(table_name)
@@ -37,6 +46,8 @@ module EventSourcery
37
46
  setup
38
47
  end
39
48
 
49
+ # This will truncate all the tables and reset the tracker back to 0,
50
+ # done as a transaction.
40
51
  def truncate
41
52
  self.class.tables.each do |table_name, _|
42
53
  @db_connection.transaction do
@@ -1,14 +1,21 @@
1
1
  module EventSourcery
2
2
  module Postgres
3
+ # This will set up a persisted event id tracker for processors.
3
4
  class Tracker
4
- def initialize(connection = EventSourcery::Postgres.config.projections_database,
5
+
6
+ def initialize(db_connection = EventSourcery::Postgres.config.projections_database,
5
7
  table_name: EventSourcery::Postgres.config.tracker_table_name,
6
8
  obtain_processor_lock: true)
7
- @connection = connection
9
+ @db_connection = db_connection
8
10
  @table_name = table_name.to_sym
9
11
  @obtain_processor_lock = obtain_processor_lock
10
12
  end
11
13
 
14
+ # Set up the given processor.
15
+ # This will create the projector tracker table if it does not exist.
16
+ # If given a processor_name it will then attempt to get a lock on the db.
17
+ #
18
+ # @param processor_name the name of the processor
12
19
  def setup(processor_name = nil)
13
20
  create_table_if_not_exists if EventSourcery::Postgres.config.auto_create_projector_tracker
14
21
 
@@ -24,6 +31,11 @@ module EventSourcery
24
31
  end
25
32
  end
26
33
 
34
+ # This will updated the tracker table to the given event id value
35
+ # for the given processor name.
36
+ #
37
+ # @param processor_name the name of the processor to update
38
+ # @param event_id the event id number to update to
27
39
  def processed_event(processor_name, event_id)
28
40
  table.
29
41
  where(name: processor_name.to_s).
@@ -31,22 +43,38 @@ module EventSourcery
31
43
  true
32
44
  end
33
45
 
46
+ # This allows you to process an event and update the tracker table in
47
+ # a single transaction. Will yield the given block first then update the
48
+ # the tracker table to the give event id for the given processor name.
49
+ #
50
+ # @param processor_name the name of the processor to update
51
+ # @param event_id the event id number to update to
34
52
  def processing_event(processor_name, event_id)
35
- @connection.transaction do
53
+ @db_connection.transaction do
36
54
  yield
37
55
  processed_event(processor_name, event_id)
38
56
  end
39
57
  end
40
58
 
59
+ # This will reset the tracker to the start (0) for the given processor name.
60
+ #
61
+ # @param processor_name the name of the processor to reset to 0
41
62
  def reset_last_processed_event_id(processor_name)
42
63
  table.where(name: processor_name.to_s).update(last_processed_event_id: 0)
43
64
  end
44
65
 
66
+ # This will return the last processed event id for the given processor name.
67
+ #
68
+ # @param processor_name the name of the processor you want to look up
69
+ # @return [Int, nil] the value of the last event_id processed
45
70
  def last_processed_event_id(processor_name)
46
71
  track_entry = table.where(name: processor_name.to_s).first
47
72
  track_entry[:last_processed_event_id] if track_entry
48
73
  end
49
74
 
75
+ # Will return an array of all known tracked processors.
76
+ #
77
+ # @return [Array] array of all known tracked processors
50
78
  def tracked_processors
51
79
  table.select_map(:name)
52
80
  end
@@ -54,7 +82,7 @@ module EventSourcery
54
82
  private
55
83
 
56
84
  def obtain_global_lock_on_processor(processor_name)
57
- lock_obtained = @connection.fetch("select pg_try_advisory_lock(#{@track_entry_id})").to_a.first[:pg_try_advisory_lock]
85
+ lock_obtained = @db_connection.fetch("select pg_try_advisory_lock(#{@track_entry_id})").to_a.first[:pg_try_advisory_lock]
58
86
  if lock_obtained == false
59
87
  raise UnableToLockProcessorError, "Unable to get a lock on #{processor_name} #{@track_entry_id}"
60
88
  end
@@ -63,7 +91,7 @@ module EventSourcery
63
91
  def create_table_if_not_exists
64
92
  unless tracker_table_exists?
65
93
  EventSourcery.logger.info { "Projector tracker missing - attempting to create 'projector_tracker' table" }
66
- EventSourcery::Postgres::Schema.create_projector_tracker(db: @connection, table_name: @table_name)
94
+ EventSourcery::Postgres::Schema.create_projector_tracker(db: @db_connection, table_name: @table_name)
67
95
  end
68
96
  end
69
97
 
@@ -77,11 +105,11 @@ module EventSourcery
77
105
  end
78
106
 
79
107
  def table
80
- @connection[@table_name]
108
+ @db_connection[@table_name]
81
109
  end
82
110
 
83
111
  def tracker_table_exists?
84
- @connection.table_exists?(@table_name)
112
+ @db_connection.table_exists?(@table_name)
85
113
  end
86
114
  end
87
115
  end
@@ -1,5 +1,5 @@
1
1
  module EventSourcery
2
2
  module Postgres
3
- VERSION = '0.4.0'.freeze
3
+ VERSION = '0.8.1'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_sourcery-postgres
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Envato
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-06-21 00:00:00.000000000 Z
11
+ date: 2020-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4.38'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.38'
27
27
  - !ruby/object:Gem::Dependency
@@ -56,30 +56,30 @@ dependencies:
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '1.10'
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '1.10'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '10.0'
75
+ version: '13'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '10.0'
82
+ version: '13'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -122,7 +122,7 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
- description:
125
+ description:
126
126
  email:
127
127
  - rubygems@envato.com
128
128
  executables: []
@@ -147,8 +147,11 @@ files:
147
147
  - lib/event_sourcery/postgres/version.rb
148
148
  homepage: https://github.com/envato/event_sourcery-postgres
149
149
  licenses: []
150
- metadata: {}
151
- post_install_message:
150
+ metadata:
151
+ bug_tracker_uri: https://github.com/envato/event_sourcery-postgres/issues
152
+ changelog_uri: https://github.com/envato/event_sourcery-postgres/blob/master/CHANGELOG.md
153
+ source_code_uri: https://github.com/envato/event_sourcery-postgres
154
+ post_install_message:
152
155
  rdoc_options: []
153
156
  require_paths:
154
157
  - lib
@@ -163,9 +166,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
166
  - !ruby/object:Gem::Version
164
167
  version: '0'
165
168
  requirements: []
166
- rubyforge_project:
167
- rubygems_version: 2.6.11
168
- signing_key:
169
+ rubygems_version: 3.1.4
170
+ signing_key:
169
171
  specification_version: 4
170
172
  summary: Postgres event store for use with EventSourcery
171
173
  test_files: []