espace-neverblock-pg 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,8 @@
1
+ == NeverBlock PG
2
+
3
+ A fibered, evented PostgreSQL database driver. Blocking queries running in fibers will run in async mode and yield the fiber.
4
+
5
+ The connection socket can be accessed for future notifications. This driver can be used together with other NeverBlock components to provide a full non-blocking stack for Ruby applications.
6
+
7
+ === License
8
+ Ruby License, http://www.ruby-lang.org/en/LICENSE.txt.
@@ -0,0 +1,98 @@
1
+ require 'pg'
2
+
3
+ module NeverBlock
4
+
5
+ module DB
6
+ # A modified postgres connection driver
7
+ # builds on the original pg driver.
8
+ # This driver is able to register the socket
9
+ # at a certain backend (EM or Rev)
10
+ # and then whenever the query is executed
11
+ # within the scope of a friendly fiber
12
+ # it will be done in async mode and the fiber
13
+ # will yield
14
+ class FiberedPostgresConnection < PGconn
15
+ # needed to access the sockect by the event loop
16
+ attr_reader :fd, :io
17
+
18
+ # Creates a new postgresql connection, sets it
19
+ # to nonblocking and wraps the descriptor in an IO
20
+ # object.
21
+ def initialize(*args)
22
+ super(*args)
23
+ @fd = socket
24
+ @io = IO.new(socket)
25
+ setnonblocking(true)
26
+ end
27
+
28
+ # Assuming the use of NeverBlock fiber extensions and that the exec is run in
29
+ # the context of a fiber. One that have the value :neverblock set to true.
30
+ # All neverblock IO classes check this value, setting it to false will force
31
+ # the execution in a blocking way.
32
+ def exec(sql)
33
+ if Fiber.respond_to? :current and Fiber.current[:neverblock]
34
+ self.send_query sql
35
+ @fiber = Fiber.current
36
+ Fiber.yield
37
+ else
38
+ super(sql)
39
+ end
40
+ end
41
+
42
+ # Attaches the connection socket to an event loop.
43
+ # Currently only supports EM, but Rev support will be
44
+ # completed soon.
45
+ def register_with_event_loop(loop)
46
+ if loop == :em
47
+ unless EM.respond_to?(:attach)
48
+ puts "invalide EM version, please download the modified gem from: (TBA) "
49
+ exit
50
+ end
51
+ if EM.reactor_running?
52
+ EM::attach(@io,EMConnectionHandler,self)
53
+ else
54
+ raise "REACTOR NOT RUNNING YA ZALAMA"
55
+ end
56
+ elsif loop.class.name == "REV::Loop"
57
+ loop.attach(RevConnectionHandler.new(socket))
58
+ else
59
+ raise "could not register with the event loop"
60
+ end
61
+ end
62
+
63
+ # The callback, this is called whenever
64
+ # there is data available at the socket
65
+ def process_command
66
+ # make sure all commands are sent
67
+ # before attempting to read
68
+ return unless self.flush
69
+ self.consume_input
70
+ unless is_busy
71
+ res, data = 0, []
72
+ while res != nil
73
+ res = self.get_result
74
+ data << res unless res.nil?
75
+ end
76
+ #let the fiber continue its work
77
+ @fiber.resume(data.last)
78
+ end
79
+ end
80
+
81
+ end #FiberedPostgresConnection
82
+
83
+ # A connection handler for EM
84
+ # More to follow.
85
+ module EMConnectionHandler
86
+ def initialize connection
87
+ @connection = connection
88
+ end
89
+ def notify_readable
90
+ @connection.process_command
91
+ end
92
+ end
93
+
94
+ end #DB
95
+
96
+ end #NeverBlock
97
+
98
+ NeverBlock::DB::FPGconn = NeverBlock::DB::FiberedPostgresConnection
@@ -0,0 +1,73 @@
1
+ module NeverBlock
2
+ module DB
3
+ # A pooled postgres connection class.
4
+ # This class represents a proxy interface
5
+ # to a connection pool of fibered postgresql
6
+ # connections.
7
+ class PooledFiberedPostgresConnection
8
+
9
+ # Requires a hash or an array with connection parameters
10
+ # and a pool size (defaults to 4)
11
+ def initialize(conn_params, size=4)
12
+ @pool = NB::Pool::FiberedConnectionPool.new(:size=>size, :eager=>true) do
13
+ conn = NB::DB::FPGconn.new(*conn_params) if conn_params.is_a? Array
14
+ conn = NB::DB::FPGconn.new(conn_params) if conn_params.is_a? Hash
15
+ conn.register_with_event_loop(:em)
16
+ conn
17
+ end
18
+ end
19
+
20
+ # A proxy for the connection's exec method
21
+ # quries the pool to get a connection first
22
+ def exec(query)
23
+ @pool.hold do |conn|
24
+ conn.exec(query)
25
+ end
26
+ end
27
+
28
+ # This method must be called for transactions to work correctly.
29
+ # One cannot just send "begin" as you never know which connection
30
+ # will be available next. This method ensures you get the same connection
31
+ # while in a transaction.
32
+ def begin_db_transaction
33
+ @pool.hold(true) do |conn|
34
+ conn.exec("begin")
35
+ end
36
+ end
37
+
38
+ # see =begin_db_transaction
39
+ def rollback_db_transaction
40
+ @pool.hold do |conn|
41
+ conn.exec("rollback")
42
+ @pool.release(Fiber.current,conn)
43
+ end
44
+ end
45
+
46
+ # see =begin_db_transaction
47
+ def commit_db_transaction
48
+ @pool.hold do |conn|
49
+ conn.exec("commit")
50
+ @pool.release(Fiber.current,conn)
51
+ end
52
+ end
53
+
54
+ # Pass unknown methods to the connection
55
+ def method_missing(method, *args)
56
+ @pool.hold do |conn|
57
+ conn.send(method, *args)
58
+ end
59
+ end
60
+
61
+ # Pass method queries to the connection
62
+ def respond_to?(method)
63
+ @pool.hold do |conn|
64
+ conn.respond_to?(method)
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+
72
+ NB::DB::PFPGconn = NeverBlock::DB::FiberedPostgresConnection
73
+
@@ -0,0 +1,4 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+
3
+ require 'never_block/db/fibered_postgres_connection'
4
+ require 'never_block/db/pooled_fibered_postgres_connection'
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "neverblock-pg"
3
+ s.version = "0.1.0"
4
+ s.date = "2008-08-13"
5
+ s.summary = "Fibered PostgreSQL connection"
6
+ s.email = "oldmoe@gmail.com"
7
+ s.homepage = "http://github.com/oldmoe/neverblock-pg"
8
+ s.description = "Fibered PostgreSQL connection."
9
+ s.has_rdoc = true
10
+ s.authors = ["Muhammad A. Ali", "Ahmed Sobhi"]
11
+ s.files = [
12
+ "neverblock-pg.gemspec",
13
+ "README",
14
+ "test/test.rb",
15
+ "lib/neverblock-pg.rb",
16
+ "lib/never_block/db/fibered_postgres_connection.rb",
17
+ "lib/never_block/db/pooled_fibered_postgres_connection.rb"]
18
+ s.rdoc_options = ["--main", "README"]
19
+ s.extra_rdoc_files = ["README"]
20
+ s.add_dependency("neverblock")
21
+ s.add_dependency("pg")
22
+ end
23
+
data/test/test.rb ADDED
@@ -0,0 +1,101 @@
1
+ require 'neverblock'
2
+ require 'neverblock-pg'
3
+
4
+ $fpool = NB::Pool::FiberPool.new(24)
5
+
6
+ $long_count = ARGV[0].to_i
7
+ $freq = ARGV[1].to_i
8
+ $done = false
9
+
10
+ $connections = {}
11
+ $sockets = []
12
+ $cpool = NB::Pool::FiberedConnectionPool.new(:size=>10, :eager=>true) {
13
+ conn = NB::DB::FPGconn.new({:host=>'localhost',:user=>'postgres',:dbname=>'evented'})
14
+ $sockets << socket = IO.new(conn.socket)
15
+ $connections[socket] = conn
16
+ }
17
+
18
+ def $cpool.exec(sql)
19
+ hold do |conn|
20
+ conn.exec(sql)
21
+ end
22
+ end
23
+
24
+ def $cpool.[](sql)
25
+ self.exec(sql)
26
+ end
27
+
28
+ def $cpool.begin_db_transaction
29
+ hold(true) do |conn|
30
+ conn.exec("begin")
31
+ end
32
+ end
33
+ def $cpool.rollback_db_transaction
34
+ hold do |conn|
35
+ conn.exec("rollback")
36
+ release(Fiber.current,conn)
37
+ end
38
+ end
39
+ def $cpool.commit_db_transaction
40
+ hold do |conn|
41
+ conn.exec("commit")
42
+ release(Fiber.current,conn)
43
+ end
44
+ end
45
+
46
+ $long_query = "select sleep(1)"
47
+ $short_query = "select 1"
48
+
49
+ def run_blocking
50
+ t = Time.now
51
+ $long_count.times do |i|
52
+ $cpool[$long_query]
53
+ $freq.times do |j|
54
+ $cpool[$short_query].each{|r|r}
55
+ end
56
+ end
57
+ Time.now - t
58
+ end
59
+ print "finished blocking queries in : "
60
+ puts $b_time = run_blocking
61
+
62
+ def run_evented
63
+ $count = 0
64
+ $count_long = 0
65
+ $finished = 0
66
+ $long_count.times do |i|
67
+ $fpool.spawn do
68
+ $cpool[$long_query].each{|r|r}
69
+ $finished = $finished + 1
70
+ if $finished == ($long_count * ($freq+1))
71
+ puts ($e_l_time = Time.now - $t)
72
+ puts "advantage = #{(100 - ( $e_l_time / $b_time ) * 100).to_i}%"
73
+ stop_loop
74
+ end
75
+ end
76
+ $freq.times do |j|
77
+ $fpool.spawn do
78
+ $cpool[$short_query].each{|r|r}
79
+ $finished = $finished + 1
80
+ if $finished == ($long_count * ($freq+1))
81
+ puts ($e_l_time = Time.now - $t)
82
+ puts "advantage = #{(100 - ( $e_l_time / $b_time ) * 100).to_i}%"
83
+ stop_loop
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def stop_loop
91
+ $done = true
92
+ end
93
+
94
+ $t = Time.now
95
+ print "finished evented queries in : "
96
+ run_evented
97
+ loop do
98
+ res = select($sockets,nil,nil,nil)
99
+ res.first.each{ |s|$connections[s].process_command } if res
100
+ break if $done
101
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: espace-neverblock-pg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Muhammad A. Ali
8
+ - Ahmed Sobhi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2008-08-13 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: neverblock
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: pg
27
+ version_requirement:
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: "0"
33
+ version:
34
+ description: Fibered PostgreSQL connection.
35
+ email: oldmoe@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README
42
+ files:
43
+ - neverblock-pg.gemspec
44
+ - README
45
+ - test/test.rb
46
+ - lib/neverblock-pg.rb
47
+ - lib/never_block/db/fibered_postgres_connection.rb
48
+ - lib/never_block/db/pooled_fibered_postgres_connection.rb
49
+ has_rdoc: true
50
+ homepage: http://github.com/oldmoe/neverblock-pg
51
+ post_install_message:
52
+ rdoc_options:
53
+ - --main
54
+ - README
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ requirements: []
70
+
71
+ rubyforge_project:
72
+ rubygems_version: 1.2.0
73
+ signing_key:
74
+ specification_version: 2
75
+ summary: Fibered PostgreSQL connection
76
+ test_files: []
77
+