em-pg-client-12 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,157 @@
1
+ require 'coveralls/rake/task'
2
+ Coveralls::RakeTask.new
3
+ task :test_with_coveralls => ['test:all', 'coveralls:push']
4
+
5
+ $:.unshift "lib"
6
+
7
+ task :default => [:test]
8
+
9
+ $gem_name = "em-pg-client"
10
+
11
+ def windows_os?
12
+ RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/
13
+ end
14
+
15
+ desc "Run tests"
16
+ task :test => :'test:safe'
17
+
18
+ namespace :test do
19
+ env_common = {'PGDATABASE' => 'test'}
20
+ env_unix = env_common.merge('PGHOST' => ENV['PGHOST_UNIX'] || '/tmp')
21
+ env_inet = env_common.merge('PGHOST' => ENV['PGHOST_INET'] || 'localhost')
22
+
23
+ task :warn do
24
+ puts "WARNING: The tests needs to be run with an available local PostgreSQL server"
25
+ end
26
+
27
+ desc "Run specs only"
28
+ task :spec => [:spec_defer, :spec_pool, :spec_client]
29
+
30
+ task :spec_client do
31
+ sh({'COVNAME'=>'spec:client'}, "rspec spec/pg_em_client_*.rb")
32
+ end
33
+
34
+ task :spec_pool do
35
+ sh({'COVNAME'=>'spec:pool'}, "rspec spec/pg_em_connection_pool.rb")
36
+ end
37
+
38
+ task :spec_defer do
39
+ sh({'COVNAME'=>'spec:deferrable'}, "rspec spec/pg_em_featured_deferrable.rb")
40
+ end
41
+
42
+ desc "Run safe tests only"
43
+ task :safe => [:warn, :spec, :async, :fiber, :on_connect, :pool]
44
+ task :async => [:async_inet, :async_unix]
45
+ task :fiber => [:fiber_inet, :fiber_unix]
46
+ task :on_connect => [:on_connect_inet, :on_connect_unix]
47
+ task :pool => [:pool_inet, :pool_unix]
48
+
49
+ task :pool_inet do
50
+ sh env_inet.merge('COVNAME'=>'pool:inet'), "rspec spec/em_connection_pool.rb"
51
+ end
52
+
53
+ task :pool_unix do
54
+ sh env_unix.merge('COVNAME'=>'pool:unix'), "rspec spec/em_connection_pool.rb" unless windows_os?
55
+ end
56
+
57
+ task :on_connect_inet do
58
+ sh env_inet.merge('COVNAME'=>'on_connect:inet'), "rspec spec/em_client_on_connect.rb"
59
+ end
60
+
61
+ task :on_connect_unix do
62
+ sh env_unix.merge('COVNAME'=>'on_connect:unix'), "rspec spec/em_client_on_connect.rb" unless windows_os?
63
+ end
64
+
65
+ task :async_inet do
66
+ sh env_inet.merge('COVNAME'=>'async:inet'), "rspec spec/em_client.rb"
67
+ end
68
+
69
+ task :async_unix do
70
+ sh env_unix.merge('COVNAME'=>'async:unix'), "rspec spec/em_client.rb" unless windows_os?
71
+ end
72
+
73
+ task :fiber_inet do
74
+ sh env_inet.merge('COVNAME'=>'fiber:inet'), "rspec spec/em_synchrony_client.rb"
75
+ end
76
+
77
+ task :fiber_unix do
78
+ sh env_unix.merge('COVNAME'=>'fiber:unix'), "rspec spec/em_synchrony_client.rb" unless windows_os?
79
+ end
80
+
81
+ task :pgdata_check do
82
+ unless ENV['PGDATA'] || (ENV['PG_CTL_STOP_CMD'] && ENV['PG_CTL_START_CMD'])
83
+ raise "Set PGDATA environment variable before running the autoreconnect tests."
84
+ end
85
+ end
86
+
87
+ desc "Run unsafe tests only"
88
+ task :unsafe => [:warn, :pgdata_check,
89
+ :async_autoreconnect_inet,
90
+ :async_autoreconnect_unix,
91
+ :fiber_autoreconnect_inet,
92
+ :fiber_autoreconnect_unix]
93
+
94
+ task :async_autoreconnect_inet do
95
+ sh env_inet.merge('COVNAME'=>'async:autoreconnect:inet'), "rspec spec/em_client_autoreconnect.rb"
96
+ end
97
+
98
+ task :async_autoreconnect_unix do
99
+ sh env_unix.merge('COVNAME'=>'async:autoreconnect:unix'), "rspec spec/em_client_autoreconnect.rb"
100
+ end
101
+
102
+ task :fiber_autoreconnect_inet do
103
+ sh env_inet.merge('COVNAME'=>'fiber:autoreconnect:inet'), "rspec spec/em_synchrony_client_autoreconnect.rb"
104
+ end
105
+
106
+ task :fiber_autoreconnect_unix do
107
+ sh env_unix.merge('COVNAME'=>'fiber:autoreconnect:unix'), "rspec spec/em_synchrony_client_autoreconnect.rb"
108
+ end
109
+
110
+ desc "Run safe and unsafe tests"
111
+ task :all => [:spec, :safe, :unsafe]
112
+ end
113
+
114
+ desc "Build the gem"
115
+ task :gem do
116
+ sh "gem build #$gem_name.gemspec"
117
+ end
118
+
119
+ desc "Install the library at local machnie"
120
+ task :install => :gem do
121
+ sh "gem install #$gem_name -l"
122
+ end
123
+
124
+ desc "Uninstall the library from local machnie"
125
+ task :uninstall do
126
+ sh "gem uninstall #$gem_name"
127
+ end
128
+
129
+ desc "Clean"
130
+ task :clean do
131
+ sh "rm #$gem_name*.gem"
132
+ end
133
+
134
+ desc "Documentation"
135
+ task :doc do
136
+ sh "yardoc"
137
+ end
138
+
139
+ desc "Benchmark"
140
+ task :benchmark do
141
+ require "./benchmarks/em_pg.rb"
142
+ [10, 100, 1000].each do |i|
143
+ puts "Repeat: #{i}"
144
+ benchmark(i)
145
+ end
146
+ end
147
+
148
+ desc "Console"
149
+ task :console do
150
+ require 'irb'
151
+ require 'irb/completion'
152
+ require 'em-synchrony'
153
+ require 'em-pg-client'
154
+ require 'pg/em/connection_pool'
155
+ ARGV.clear
156
+ IRB.start
157
+ end
@@ -0,0 +1,96 @@
1
+ $:.unshift('./lib')
2
+ require 'eventmachine'
3
+ require 'em-synchrony'
4
+ require 'pg/em/connection_pool'
5
+ require "em-synchrony/fiber_iterator"
6
+ require 'pp'
7
+ require 'benchmark'
8
+
9
+ def benchmark(repeat=100)
10
+ Benchmark.bm(20) do |b|
11
+ b.report('single:') { single(repeat) }
12
+ puts
13
+ b.report('parallel 90000/1:') { parallel(repeat, 90000, 1) }
14
+ b.report('parallel 5000/5:') { parallel(repeat, 5000, 5) }
15
+ b.report('parallel 2000/10:') { parallel(repeat, 2000, 10) }
16
+ b.report('parallel 1000/20:') { parallel(repeat, 1000, 20) }
17
+ puts
18
+ patch_blocking
19
+ b.report('blocking 90000/1:') { parallel(repeat, 90000, 1) }
20
+ b.report('blocking 5000/5:') { parallel(repeat, 5000, 5) }
21
+ b.report('blocking 2000/10:') { parallel(repeat, 2000, 10) }
22
+ b.report('blocking 1000/20:') { parallel(repeat, 1000, 20) }
23
+ patch_remove_blocking
24
+ end
25
+ end
26
+
27
+ def patch_remove_blocking
28
+ PG::EM::Client::Watcher.module_eval <<-EOE
29
+ alias_method :fetch_results, :original_fetch_results
30
+ alias_method :notify_readable, :fetch_results
31
+ undef :original_fetch_results
32
+ EOE
33
+ end
34
+
35
+ def patch_blocking
36
+ PG::Connection.class_eval <<-EOE
37
+ alias_method :blocking_get_last_result, :get_last_result
38
+ EOE
39
+ PG::EM::Client::Watcher.module_eval <<-EOE
40
+ alias_method :original_fetch_results, :fetch_results
41
+ def fetch_results
42
+ self.notify_readable = false
43
+ begin
44
+ result = @client.blocking_get_last_result
45
+ rescue Exception => e
46
+ @deferrable.fail(e)
47
+ else
48
+ @deferrable.succeed(result)
49
+ end
50
+ end
51
+ alias_method :notify_readable, :fetch_results
52
+ EOE
53
+ end
54
+
55
+ # retrieve resources using single select query
56
+ def single(repeat=1)
57
+ rowcount = 0
58
+ p = PGconn.new
59
+ p.query('select count(*) from resources') do |result|
60
+ rowcount = result.getvalue(0,0).to_i
61
+ end
62
+ repeat.times do
63
+ p.query('select * from resources order by cdate') do |result|
64
+ $resources = result.values
65
+ end
66
+ end
67
+ # raise "invalid count #{$resources.length} != #{rowcount}" if $resources.length != rowcount
68
+ end
69
+
70
+ # retrieve resources using parallel queries
71
+ def parallel(repeat=1, chunk_size=2000, concurrency=10)
72
+ resources = []
73
+ rowcount = 0
74
+ EM.synchrony do
75
+ p = PG::EM::ConnectionPool.new size: concurrency
76
+ p.query('select count(*) from resources') do |result|
77
+ rowcount = result.getvalue(0,0).to_i
78
+ end
79
+ offsets = (rowcount / chunk_size.to_f).ceil.times.map {|n| n*chunk_size }
80
+ repeat.times do
81
+ EM::Synchrony::FiberIterator.new(offsets, concurrency).each do |offset|
82
+ p.query('select * from resources order by cdate limit $1 offset $2', [chunk_size, offset]) do |result|
83
+ resources[offset, chunk_size] = result.values
84
+ end
85
+ end
86
+ end
87
+ EM.stop
88
+ end
89
+ # raise "invalid count #{resources.length} != #{rowcount}" if resources.length != rowcount
90
+ # raise "resources != $resources" if resources != $resources
91
+ resources
92
+ end
93
+
94
+ if $0 == __FILE__
95
+ benchmark ARGV[0].to_i.nonzero? || 10
96
+ end
@@ -0,0 +1,88 @@
1
+ $:.unshift('./lib')
2
+ require 'pg/em/connection_pool'
3
+ require 'em-synchrony'
4
+ require 'em-synchrony/fiber_iterator'
5
+ require 'pp'
6
+ require 'benchmark'
7
+
8
+ TABLE_NAME = 'resources'
9
+ LIMIT_ROWS = 5000
10
+
11
+ include EM::Synchrony
12
+
13
+ unless PG::EM::Client.single_row_mode?
14
+ raise 'compile pg against pqlib >= 9.2 to support single row mode'
15
+ end
16
+
17
+ def benchmark(repeat=40)
18
+ Benchmark.bm(20) do |b|
19
+ puts
20
+ b.report("threads #{repeat/1}x1:") { threads(repeat, 1) }
21
+ b.report("threads #{repeat/5}x5:") { threads(repeat, 5) }
22
+ b.report("threads #{repeat/10}x10:") { threads(repeat, 10) }
23
+ b.report("threads #{repeat/20}x20:") { threads(repeat, 20) }
24
+ b.report("threads #{repeat/40}x40:") { threads(repeat, 40) }
25
+ puts
26
+ b.report("fibers #{repeat/1}x1:") { fibers(repeat, 1) }
27
+ b.report("fibers #{repeat/5}x5:") { fibers(repeat, 5) }
28
+ b.report("fibers #{repeat/10}x10:") { fibers(repeat, 10) }
29
+ b.report("fibers #{repeat/20}x20:") { fibers(repeat, 20) }
30
+ b.report("fibers #{repeat/40}x40:") { fibers(repeat, 40) }
31
+ end
32
+ end
33
+
34
+ def threads(repeat, concurrency)
35
+ db = Hash.new { |pool, id| pool[id] = PG::Connection.new }
36
+ (0...concurrency).map do |i|
37
+ Thread.new do
38
+ (repeat/concurrency).times do
39
+ stream_results(db[i])
40
+ end
41
+ end
42
+ end.each(&:join)
43
+ db.each_value(&:finish).clear
44
+ end
45
+
46
+ def fibers(repeat, concurrency)
47
+ EM.synchrony do
48
+ db = PG::EM::ConnectionPool.new size: concurrency, lazy: true
49
+ FiberIterator.new((0...concurrency), concurrency).each do
50
+ db.hold do |pg|
51
+ (repeat/concurrency).times do
52
+ stream_results(pg)
53
+ end
54
+ end
55
+ end
56
+ db.finish
57
+ EM.stop
58
+ end
59
+ end
60
+
61
+ def stream_results(pg)
62
+ pg.send_query("select * from #{TABLE_NAME}")
63
+ pg.set_single_row_mode
64
+ rows = 0
65
+ last_time = Time.now
66
+ while result = pg.get_result
67
+ begin
68
+ result.check
69
+ result.each do |tuple|
70
+ rows += 1
71
+ if rows >= LIMIT_ROWS
72
+ pg.reset
73
+ break
74
+ end
75
+ end
76
+ rescue PG::Error => e
77
+ pg.get_last_result
78
+ raise e
79
+ ensure
80
+ result.clear
81
+ end
82
+ end
83
+ end
84
+
85
+ if $0 == __FILE__
86
+ benchmark ARGV[0].to_i.nonzero? || 40
87
+ end
88
+
@@ -0,0 +1,34 @@
1
+ $:.unshift "lib"
2
+ require 'pg/em-version'
3
+
4
+ files = `git ls-files`.split("\n")
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "em-pg-client-12"
8
+ s.version = PG::EM::VERSION
9
+ s.required_ruby_version = ">= 1.9.2"
10
+ s.date = "#{Time.now.strftime("%Y-%m-%d")}"
11
+ s.summary = "EventMachine PostgreSQL client"
12
+ s.email = "rafal@yeondir.com"
13
+ s.homepage = "http://github.com/royaltm/ruby-em-pg-client"
14
+ s.license = "MIT"
15
+ s.require_path = "lib"
16
+ s.description = "PostgreSQL asynchronous EventMachine client, based on pg interface (PG::Connection)"
17
+ s.authors = ["Rafal Michalski"]
18
+ s.files = files - ['.gitignore']
19
+ s.test_files = Dir.glob("spec/**/*")
20
+ s.rdoc_options << "--title" << "em-pg-client" <<
21
+ "--main" << "README.md"
22
+ s.has_rdoc = true
23
+ s.extra_rdoc_files = [
24
+ files.grep(/^benchmarks\/.*\.rb$/),
25
+ "README.md", "BENCHMARKS.md", "LICENSE", "HISTORY.md"
26
+ ].flatten
27
+ s.requirements << "PostgreSQL server"
28
+ s.add_runtime_dependency "pg", ">= 0.17.0"
29
+ s.add_runtime_dependency "eventmachine", "~> 1.2"
30
+ s.add_development_dependency "rspec", "~> 2.14"
31
+ s.add_development_dependency "em-synchrony", "~> 1.0"
32
+ s.add_development_dependency "coveralls", ">= 0.7.0"
33
+ s.add_development_dependency "simplecov", ">= 0.8.2"
34
+ end
@@ -0,0 +1,57 @@
1
+ # Demonstation of fully asynchronous query data streaming.
2
+ #
3
+ # This is a low-level method which has its:
4
+ #
5
+ # upside: it's the same way you would work with PG::Connection
6
+ # downside: it's a little verbose and doesn't support automatic re-connects
7
+ gem 'em-pg-client', '>= 0.3.2'
8
+ require 'pg/em/connection_pool'
9
+ require 'em-synchrony'
10
+ require 'em-synchrony/fiber_iterator'
11
+
12
+ TABLE_NAME = 'resources'
13
+
14
+ unless PG::EM::Client.single_row_mode?
15
+ raise 'compile pg against pqlib >= 9.2 to support single row mode'
16
+ end
17
+
18
+ EM.synchrony do
19
+ EM.add_periodic_timer(0.01) { print ' ' }
20
+
21
+ db = PG::EM::ConnectionPool.new size: 3
22
+
23
+ 10.times do
24
+
25
+ EM::Synchrony::FiberIterator.new(%w[@ * #], 3).each do |mark|
26
+
27
+ db.hold do |pg|
28
+ pg.send_query("select * from #{TABLE_NAME}")
29
+ pg.set_single_row_mode
30
+ rows = 0
31
+ while result = pg.get_result
32
+ begin
33
+ result.check
34
+ result.each do |tuple|
35
+ rows += 1
36
+ # process tuple
37
+ print mark
38
+ # break stream cleanly
39
+ pg.reset if rows > 1000
40
+ end
41
+ rescue PG::Error => e
42
+ # cleanup connection
43
+ pg.get_last_result
44
+ raise e
45
+ ensure
46
+ result.clear
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ puts
54
+ puts '='*80
55
+ end
56
+ EM.stop
57
+ end
@@ -0,0 +1 @@
1
+ require 'pg/em'
@@ -0,0 +1,3 @@
1
+ # for backward compatibility with
2
+ # require 'em-synchrony/pg'
3
+ require 'pg/em'
@@ -0,0 +1,5 @@
1
+ module PG
2
+ module EM
3
+ VERSION = '0.3.4'
4
+ end
5
+ end