evt-event_source-postgres 0.10.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cbb3a9b61a7f4a379cd86bc1fca7462a767dce24
4
+ data.tar.gz: d47872d6c1d73a06db41da3bc77cbf06ac652e16
5
+ SHA512:
6
+ metadata.gz: 96ab2094cec2f21a4b38967f76ca90c7b97cf11050b496d3aee119878d4d7b5550921126b6693e1cee9367580ed82709767f379a17cc1ea3a41e7403b51e39e5
7
+ data.tar.gz: 579b568775c5a3ed2b9542d5c10f0208c07f3896996fb2b2aacb70e172434b6d9b531c17bbe0470a0c6bddcfe8167746300a003441f35deaf31f0caca2c490bf
@@ -0,0 +1 @@
1
+ postgres.rb
@@ -0,0 +1,20 @@
1
+ require 'pg'
2
+
3
+ require 'event_source'
4
+ require 'cycle'
5
+
6
+ require 'log'
7
+ require 'telemetry'
8
+ require 'settings'; Settings.activate
9
+
10
+ require 'event_source/postgres/log'
11
+
12
+ require 'event_source/postgres/settings'
13
+ require 'event_source/postgres/session'
14
+
15
+ require 'event_source/postgres/put'
16
+ require 'event_source/postgres/write'
17
+
18
+ require 'event_source/postgres/get/select_statement'
19
+ require 'event_source/postgres/get'
20
+ require 'event_source/postgres/read'
@@ -0,0 +1,7 @@
1
+ require 'event_source/controls'
2
+
3
+ require 'event_source/postgres/controls/category'
4
+ require 'event_source/postgres/controls/stream_name'
5
+ require 'event_source/postgres/controls/stream'
6
+ require 'event_source/postgres/controls/event_data'
7
+ require 'event_source/postgres/controls/put'
@@ -0,0 +1,7 @@
1
+ module EventSource
2
+ module Postgres
3
+ module Controls
4
+ Category = EventSource::Controls::Category
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module EventSource
2
+ module Postgres
3
+ module Controls
4
+ EventData = EventSource::Controls::EventData
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ module EventSource
2
+ module Postgres
3
+ module Controls
4
+ module Put
5
+ def self.call(instances: nil, stream_name: nil, event: nil, category: nil)
6
+ instances ||= 1
7
+ stream_name ||= StreamName.example(category: category)
8
+ event ||= EventData::Write.example
9
+
10
+ instances.times do
11
+ EventSource::Postgres::Put.(event, stream_name)
12
+ end
13
+
14
+ stream_name
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ module EventSource
2
+ module Postgres
3
+ module Controls
4
+ Stream = EventSource::Controls::Stream
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module EventSource
2
+ module Postgres
3
+ module Controls
4
+ StreamName = EventSource::Controls::StreamName
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,91 @@
1
+ module EventSource
2
+ module Postgres
3
+ class Get
4
+ include Log::Dependency
5
+
6
+ initializer :batch_size, :precedence
7
+
8
+ dependency :session, Session
9
+
10
+ def self.build(batch_size: nil, precedence: nil, session: nil)
11
+ new(batch_size, precedence).tap do |instance|
12
+ instance.configure(session: session)
13
+ end
14
+ end
15
+
16
+ def self.configure(receiver, attr_name: nil, position: nil, batch_size: nil, precedence: nil, session: nil)
17
+ attr_name ||= :get
18
+ instance = build(batch_size: batch_size, precedence: precedence, session: session)
19
+ receiver.public_send "#{attr_name}=", instance
20
+ end
21
+
22
+ def configure(session: nil)
23
+ Session.configure self, session: session
24
+ end
25
+
26
+ def self.call(stream_name, position: nil, batch_size: nil, precedence: nil, session: nil)
27
+ instance = build(batch_size: batch_size, precedence: precedence, session: session)
28
+ instance.(stream_name, position: position)
29
+ end
30
+
31
+ def call(stream_name, position: nil)
32
+ logger.trace { "Getting event data (Position: #{position.inspect}, Stream Name: #{stream_name}, Batch Size: #{batch_size.inspect}, Precedence: #{precedence.inspect})" }
33
+
34
+ records = get_records(stream_name, position)
35
+
36
+ events = convert(records)
37
+
38
+ logger.info { "Finished getting event data (Count: #{events.length}, Position: #{position.inspect}, Stream Name: #{stream_name}, Batch Size: #{batch_size.inspect}, Precedence: #{precedence.inspect})" }
39
+ logger.info(tags: [:data, :event_data]) { events.pretty_inspect }
40
+
41
+ events
42
+ end
43
+
44
+ def get_records(stream_name, position)
45
+ logger.trace { "Getting records (Stream: #{stream_name}, Position: #{position.inspect}, Batch Size: #{batch_size.inspect}, Precedence: #{precedence.inspect})" }
46
+
47
+ select_statement = SelectStatement.build(stream_name, offset: position, batch_size: batch_size, precedence: precedence)
48
+
49
+ records = session.execute(select_statement.sql)
50
+
51
+ logger.debug { "Finished getting records (Count: #{records.ntuples}, Stream: #{stream_name}, Position: #{position.inspect}, Batch Size: #{batch_size.inspect}, Precedence: #{precedence.inspect})" }
52
+
53
+ records
54
+ end
55
+
56
+ def convert(records)
57
+ logger.trace { "Converting records to event data (Records Count: #{records.ntuples})" }
58
+
59
+ events = records.map do |record|
60
+ record['data'] = Deserialize.data(record['data'])
61
+ record['metadata'] = Deserialize.metadata(record['metadata'])
62
+ record['time'] = Time.utc_coerced(record['time'])
63
+
64
+ EventData::Read.build record
65
+ end
66
+
67
+ logger.debug { "Converted records to event data (Event Data Count: #{events.length})" }
68
+
69
+ events
70
+ end
71
+
72
+ module Deserialize
73
+ def self.data(serialized_data)
74
+ return nil if serialized_data.nil?
75
+ Transform::Read.(serialized_data, EventData::Hash, :json)
76
+ end
77
+
78
+ def self.metadata(serialized_metadata)
79
+ return nil if serialized_metadata.nil?
80
+ Transform::Read.(serialized_metadata, EventData::Hash, :json)
81
+ end
82
+ end
83
+
84
+ module Time
85
+ def self.utc_coerced(local_time)
86
+ Clock::UTC.coerce(local_time)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,89 @@
1
+ module EventSource
2
+ module Postgres
3
+ class Get
4
+ class SelectStatement
5
+ include Log::Dependency
6
+
7
+ initializer :stream, w(:offset), w(:batch_size), w(:precedence)
8
+
9
+ def offset
10
+ @offset ||= Defaults.offset
11
+ end
12
+
13
+ def batch_size
14
+ @batch_size ||= Defaults.batch_size
15
+ end
16
+
17
+ def precedence
18
+ @precedence ||= Defaults.precedence
19
+ end
20
+
21
+ def stream_name
22
+ stream.name
23
+ end
24
+
25
+ def stream_type
26
+ stream.type
27
+ end
28
+
29
+ def self.build(stream_name, offset: nil, batch_size: nil, precedence: nil)
30
+ stream = Stream.new(stream_name)
31
+ new(stream, offset, batch_size, precedence)
32
+ end
33
+
34
+ def sql
35
+ logger.trace(tag: :sql) { "Composing select statement (Stream: #{stream_name}, Category: #{stream.category?}, Type: #{stream_type}, Position: #{offset}, Batch Size: #{batch_size}, Precedence: #{precedence})" }
36
+
37
+ statement = <<-SQL
38
+ SELECT
39
+ stream_name::varchar,
40
+ position::int,
41
+ type::varchar,
42
+ global_position::bigint,
43
+ data::varchar,
44
+ metadata::varchar,
45
+ time::timestamp
46
+ FROM
47
+ events
48
+ WHERE
49
+ #{where_clause_field} = '#{stream_name}'
50
+ ORDER BY
51
+ global_position #{precedence.to_s.upcase}
52
+ LIMIT
53
+ #{batch_size}
54
+ OFFSET
55
+ #{offset}
56
+ ;
57
+ SQL
58
+
59
+ logger.debug(tag: :sql) { "Composed select statement (Stream: #{stream_name}, Category: #{stream.category?}, Type: #{stream_type}, Position: #{offset}, Batch Size: #{batch_size}, Precedence: #{precedence})" }
60
+ logger.debug(tags: [:data, :sql]) { "Statement: #{statement}" }
61
+
62
+ statement
63
+ end
64
+
65
+ def where_clause_field
66
+ unless stream.category?
67
+ 'stream_name'
68
+ else
69
+ 'category(stream_name)'
70
+ end
71
+ end
72
+
73
+ module Defaults
74
+ def self.offset
75
+ 0
76
+ end
77
+
78
+ def self.batch_size
79
+ 1000
80
+ end
81
+
82
+ def self.precedence
83
+ :asc
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,11 @@
1
+ module EventSource
2
+ module Postgres
3
+ class Log < ::Log
4
+ def tag!(tags)
5
+ tags << :event_source_postgres
6
+ tags << :library
7
+ tags << :verbose
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,137 @@
1
+ module EventSource
2
+ module Postgres
3
+ class Put
4
+ include Log::Dependency
5
+
6
+ dependency :session, Session
7
+
8
+ def self.build(session: nil)
9
+ new.tap do |instance|
10
+ instance.configure(session: session)
11
+ end
12
+ end
13
+
14
+ def configure(session: nil)
15
+ Session.configure(self, session: session)
16
+ end
17
+
18
+ def self.configure(receiver, session: nil, attr_name: nil)
19
+ attr_name ||= :put
20
+ instance = build(session: session)
21
+ receiver.public_send "#{attr_name}=", instance
22
+ end
23
+
24
+ def self.call(write_event, stream_name, expected_version: nil, session: nil)
25
+ instance = build(session: session)
26
+ instance.(write_event, stream_name, expected_version: expected_version)
27
+ end
28
+
29
+ def call(write_event, stream_name, expected_version: nil)
30
+ logger.trace { "Putting event data (Stream Name: #{stream_name}, Type: #{write_event.type}, Expected Version: #{expected_version.inspect})" }
31
+ logger.trace(tags: [:data, :event_data]) { write_event.pretty_inspect }
32
+
33
+ type, data, metadata = destructure_event(write_event)
34
+ expected_version = ExpectedVersion.canonize(expected_version)
35
+
36
+ insert_event(stream_name, type, data, metadata, expected_version).tap do |position|
37
+ logger.info { "Put event data (Position: #{position}, Stream Name: #{stream_name}, Type: #{write_event.type}, Expected Version: #{expected_version.inspect})" }
38
+ logger.info(tags: [:data, :event_data]) { write_event.pretty_inspect }
39
+ end
40
+ end
41
+
42
+ def destructure_event(write_event)
43
+ type = write_event.type
44
+ data = write_event.data
45
+ metadata = write_event.metadata
46
+
47
+ logger.debug(tags: [:data, :event_data]) { "Data: #{data.pretty_inspect}" }
48
+ logger.debug(tags: [:data, :event_data]) { "Metadata: #{metadata.pretty_inspect}" }
49
+
50
+ return type, data, metadata
51
+ end
52
+
53
+ def insert_event(stream_name, type, data, metadata, expected_version)
54
+ serialized_data = serialized_data(data)
55
+ serialized_metadata = serialized_metadata(metadata)
56
+ records = execute_query(stream_name, type, serialized_data, serialized_metadata, expected_version)
57
+ position(records)
58
+ end
59
+
60
+ def execute_query(stream_name, type, serialized_data, serialized_metadata, expected_version)
61
+ logger.trace { "Executing insert (Stream Name: #{stream_name}, Type: #{type}, Expected Version: #{expected_version.inspect})" }
62
+
63
+ params = [
64
+ stream_name,
65
+ type,
66
+ serialized_data,
67
+ serialized_metadata,
68
+ expected_version
69
+ ]
70
+
71
+ begin
72
+ records = session.execute(self.class.statement, params)
73
+ rescue PG::RaiseException => e
74
+ raise_error e
75
+ end
76
+
77
+ logger.debug { "Executed insert (Stream Name: #{stream_name}, Type: #{type}, Expected Version: #{expected_version.inspect})" }
78
+
79
+ records
80
+ end
81
+
82
+ def self.statement
83
+ @statement ||= "SELECT write_event($1::varchar, $2::varchar, $3::jsonb, $4::jsonb, $5::int);"
84
+ end
85
+
86
+ def serialized_data(data)
87
+ serialized_data = nil
88
+
89
+ if data.is_a?(Hash) && data.empty?
90
+ data = nil
91
+ end
92
+
93
+ unless data.nil?
94
+ serializable_data = EventData::Hash[data]
95
+ serialized_data = Transform::Write.(serializable_data, :json)
96
+ end
97
+
98
+ logger.debug(tags: [:data, :serialize]) { "Serialized Data: #{serialized_data.inspect}" }
99
+ serialized_data
100
+ end
101
+
102
+ def serialized_metadata(metadata)
103
+ serialized_metadata = nil
104
+
105
+ if metadata.is_a?(Hash) && metadata.empty?
106
+ metadata = nil
107
+ end
108
+
109
+ unless metadata.nil?
110
+ serializable_metadata = EventData::Hash[metadata]
111
+ serialized_metadata = Transform::Write.(serializable_metadata, :json)
112
+ end
113
+
114
+ logger.debug(tags: [:data, :serialize]) { "Serialized Metadata: #{serialized_metadata.inspect}" }
115
+ serialized_metadata
116
+ end
117
+
118
+ def position(records)
119
+ position = nil
120
+ unless records[0].nil?
121
+ position = records[0].values[0]
122
+ end
123
+ position
124
+ end
125
+
126
+ def raise_error(pg_error)
127
+ error_message = pg_error.message
128
+ if error_message.include? 'Wrong expected version'
129
+ error_message.gsub!('ERROR:', '').strip!
130
+ logger.error { error_message }
131
+ raise ExpectedVersion::Error, error_message
132
+ end
133
+ raise pg_error
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,11 @@
1
+ module EventSource
2
+ module Postgres
3
+ class Read
4
+ include EventSource::Read
5
+
6
+ def configure(batch_size: nil, precedence: nil, session: nil)
7
+ Get.configure(self, batch_size: batch_size, precedence: precedence, session: session)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,120 @@
1
+ module EventSource
2
+ module Postgres
3
+ class Session
4
+ include Log::Dependency
5
+
6
+ def self.settings
7
+ Settings.names
8
+ end
9
+
10
+ settings.each do |s|
11
+ setting s
12
+ end
13
+
14
+ attr_accessor :connection
15
+
16
+ def self.build(settings: nil)
17
+ new.tap do |instance|
18
+ settings ||= Settings.instance
19
+ settings.set(instance)
20
+ end
21
+ end
22
+
23
+ def self.configure(receiver, session: nil, attr_name: nil)
24
+ attr_name ||= :session
25
+
26
+ instance = session || build
27
+ receiver.public_send "#{attr_name}=", instance
28
+ instance
29
+ end
30
+
31
+ def connect
32
+ logger.trace "Connecting to database"
33
+
34
+ if connected?
35
+ logger.debug { "Already connected. A new connection will not be built." }
36
+ return
37
+ end
38
+
39
+ logger.debug { "Not connected. A new connection will be built." }
40
+ connection = self.class.build_connection(self)
41
+ self.connection = connection
42
+
43
+ logger.debug { "Connected to database" }
44
+
45
+ connection
46
+ end
47
+
48
+ def self.build_connection(instance)
49
+ settings = instance.settings
50
+ logger.trace { "Building new connection to database (Settings: #{LogText.settings(settings).inspect})" }
51
+
52
+ connection = PG::Connection.open(settings)
53
+ connection.type_map_for_results = PG::BasicTypeMapForResults.new(connection)
54
+
55
+ logger.trace { "Built new connection to database (Settings: #{LogText.settings(settings).inspect})" }
56
+
57
+ connection
58
+ end
59
+
60
+ def connected?
61
+ !connection.nil? && connection.status == PG::CONNECTION_OK
62
+ end
63
+ alias :open? :connected?
64
+
65
+ def close
66
+ connection.close
67
+ connection = nil
68
+ end
69
+
70
+ def reset
71
+ connection.reset
72
+ end
73
+
74
+ def settings
75
+ settings = {}
76
+ self.class.settings.each do |s|
77
+ val = public_send(s)
78
+ settings[s] = val unless val.nil?
79
+ end
80
+ settings
81
+ end
82
+
83
+ def execute(statement, params=nil)
84
+ unless connected?
85
+ connect
86
+ end
87
+
88
+ if params.nil?
89
+ connection.exec(statement)
90
+ else
91
+ connection.exec_params(statement, params)
92
+ end
93
+ end
94
+
95
+ def transaction(&blk)
96
+ unless connected?
97
+ connect
98
+ end
99
+
100
+ connection.transaction(&blk)
101
+ end
102
+
103
+ def self.logger
104
+ @logger ||= Log.get self
105
+ end
106
+
107
+ module LogText
108
+ def self.settings(settings)
109
+ s = settings.dup
110
+
111
+ if s.has_key?(:password)
112
+ s[:password] = '(hidden)'
113
+ end
114
+
115
+ s
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,30 @@
1
+ module EventSource
2
+ module Postgres
3
+ class Settings < ::Settings
4
+ def self.instance
5
+ @instance ||= build
6
+ end
7
+
8
+ def self.data_source
9
+ 'settings/event_source_postgres.json'
10
+ end
11
+
12
+ def self.names
13
+ [
14
+ :dbname,
15
+ :host,
16
+ :hostaddr,
17
+ :port,
18
+ :user,
19
+ :password,
20
+ :connect_timeout,
21
+ :options,
22
+ :sslmode,
23
+ :krbsrvname,
24
+ :gsslib,
25
+ :service
26
+ ]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ module EventSource
2
+ module Postgres
3
+ class Write
4
+ include EventSource::Write
5
+
6
+ def configure(session: nil)
7
+ Put.configure(self, session: session)
8
+ end
9
+ end
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: evt-event_source-postgres
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.0.2
5
+ platform: ruby
6
+ authors:
7
+ - The Eventide Project
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: evt-event_source
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: evt-log
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: evt-cycle
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: evt-settings
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pg
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: test_bench
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: " "
98
+ email: opensource@eventide-project.org
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - lib/event_source/loader.rb
104
+ - lib/event_source/postgres.rb
105
+ - lib/event_source/postgres/controls.rb
106
+ - lib/event_source/postgres/controls/category.rb
107
+ - lib/event_source/postgres/controls/event_data.rb
108
+ - lib/event_source/postgres/controls/put.rb
109
+ - lib/event_source/postgres/controls/stream.rb
110
+ - lib/event_source/postgres/controls/stream_name.rb
111
+ - lib/event_source/postgres/get.rb
112
+ - lib/event_source/postgres/get/select_statement.rb
113
+ - lib/event_source/postgres/log.rb
114
+ - lib/event_source/postgres/put.rb
115
+ - lib/event_source/postgres/read.rb
116
+ - lib/event_source/postgres/session.rb
117
+ - lib/event_source/postgres/settings.rb
118
+ - lib/event_source/postgres/write.rb
119
+ homepage: https://github.com/eventide-project/event-source-postgres
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 2.3.3
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.5.2
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Event source client for PostgreSQL
143
+ test_files: []