espace-neverblock-pg 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +8 -0
- data/lib/never_block/db/fibered_postgres_connection.rb +98 -0
- data/lib/never_block/db/pooled_fibered_postgres_connection.rb +73 -0
- data/lib/neverblock-pg.rb +4 -0
- data/neverblock-pg.gemspec +23 -0
- data/test/test.rb +101 -0
- metadata +77 -0
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,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
|
+
|