espace-neverblock 0.1.4 → 0.1.5
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/lib/active_record/connection_adapters/neverblock_mysql_adapter.rb +20 -32
- data/lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb +29 -38
- data/lib/never_block.rb +18 -0
- data/lib/never_block/db/fibered_db_connection.rb +64 -0
- data/lib/never_block/db/fibered_mysql_connection.rb +38 -94
- data/lib/never_block/db/fibered_postgres_connection.rb +23 -85
- data/lib/never_block/db/pooled_db_connection.rb +43 -0
- data/lib/never_block/extensions/fiber_extensions.rb +2 -1
- data/lib/never_block/pool/fiber_pool.rb +46 -49
- data/lib/never_block/pool/fibered_connection_pool.rb +61 -54
- data/lib/never_block/servers/thin.rb +6 -11
- data/lib/neverblock-mysql.rb +2 -1
- data/lib/neverblock-pg.rb +3 -2
- data/neverblock.gemspec +5 -4
- metadata +14 -6
- data/lib/never_block/db/pooled_fibered_mysql_connection.rb +0 -77
- data/lib/never_block/db/pooled_fibered_postgres_connection.rb +0 -81
|
@@ -11,50 +11,38 @@ class ActiveRecord::ConnectionAdapters::NeverBlockMysqlAdapter < ActiveRecord::C
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
|
14
|
-
begin_db_transaction
|
|
15
14
|
super sql, name
|
|
16
15
|
id_value || @connection.insert_id
|
|
17
|
-
commit_db_transaction
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
def update_sql(sql, name = nil) #:nodoc:
|
|
21
|
-
begin_db_transaction
|
|
22
19
|
super
|
|
23
20
|
@connection.affected_rows
|
|
24
|
-
commit_db_transaction
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def begin_db_transaction
|
|
28
|
-
@connection.begin_db_transaction
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def commit_db_transaction
|
|
32
|
-
@connection.commit_db_transaction
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def rollback_db_transaction
|
|
36
|
-
@connection.rollback_db_transaction
|
|
37
21
|
end
|
|
38
22
|
|
|
39
23
|
def connect
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
conn.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
24
|
+
#initialize the connection pool
|
|
25
|
+
unless @connection
|
|
26
|
+
@connection = ::NB::DB::PooledDBConnection.new(@connection_options[0]) do
|
|
27
|
+
conn = ::NB::DB::FMysql.init
|
|
28
|
+
encoding = @config[:encoding]
|
|
29
|
+
if encoding
|
|
30
|
+
conn.options(::NB::DB::FMysql::SET_CHARSET_NAME, encoding) rescue nil
|
|
31
|
+
end
|
|
32
|
+
conn.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher]) if @config[:sslkey]
|
|
33
|
+
conn.real_connect(*@connection_options[1..(@connection_options.length-1)])
|
|
34
|
+
NB.neverblock(false) do
|
|
35
|
+
conn.query("SET NAMES '#{encoding}'") if encoding
|
|
36
|
+
# By default, MySQL 'where id is null' selects the last inserted id.
|
|
37
|
+
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
|
38
|
+
conn.query("SET SQL_AUTO_IS_NULL=0")
|
|
39
|
+
end
|
|
40
|
+
conn
|
|
53
41
|
end
|
|
54
|
-
|
|
55
|
-
|
|
42
|
+
else # we have a connection pool, we need to recover a connection
|
|
43
|
+
@connection.replace_acquired_connection
|
|
56
44
|
end
|
|
57
|
-
end
|
|
45
|
+
end
|
|
58
46
|
|
|
59
47
|
end
|
|
60
48
|
|
|
@@ -28,47 +28,38 @@ class ActiveRecord::ConnectionAdapters::NeverBlockPostgreSQLAdapter < ActiveReco
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def connect
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@connection.begin_db_transaction
|
|
47
|
-
if supports_standard_conforming_strings?
|
|
48
|
-
self.class.instance_eval do
|
|
49
|
-
define_method(:quoted_string_prefix) { 'E' }
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
|
|
54
|
-
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
|
|
55
|
-
# should know about this but can't detect it there, so deal with it here.
|
|
56
|
-
money_precision = (postgresql_version >= 80300) ? 19 : 10
|
|
57
|
-
::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.module_eval(<<-end_eval)
|
|
58
|
-
def extract_precision(sql_type)
|
|
59
|
-
if sql_type =~ /^money$/
|
|
60
|
-
#{money_precision}
|
|
61
|
-
else
|
|
62
|
-
super
|
|
31
|
+
@connection = ::NB::DB::PooledFiberedPostgresConnection.new(@connection_options[0]) do
|
|
32
|
+
conn = PGconn.connect(*@connection_options[1..(@connection_options.length-1)])
|
|
33
|
+
PGconn.translate_results = false if PGconn.respond_to?(:translate_results=)
|
|
34
|
+
# Ignore async_exec and async_query when using postgres-pr.
|
|
35
|
+
@async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec)
|
|
36
|
+
# Use escape string syntax if available. We cannot do this lazily when encountering
|
|
37
|
+
# the first string, because that could then break any transactions in progress.
|
|
38
|
+
# See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
|
|
39
|
+
# If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't
|
|
40
|
+
# support escape string syntax. Don't override the inherited quoted_string_prefix.
|
|
41
|
+
NB.neverblock(false) do
|
|
42
|
+
if supports_standard_conforming_strings?
|
|
43
|
+
self.class.instance_eval do
|
|
44
|
+
define_method(:quoted_string_prefix) { 'E' }
|
|
63
45
|
end
|
|
64
46
|
end
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
47
|
+
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
|
|
48
|
+
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
|
|
49
|
+
# should know about this but can't detect it there, so deal with it here.
|
|
50
|
+
money_precision = (postgresql_version >= 80300) ? 19 : 10
|
|
51
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.module_eval(<<-end_eval)
|
|
52
|
+
def extract_precision(sql_type)
|
|
53
|
+
if sql_type =~ /^money$/
|
|
54
|
+
#{money_precision}
|
|
55
|
+
else
|
|
56
|
+
super
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end_eval
|
|
60
|
+
#configure_connection
|
|
61
|
+
end
|
|
70
62
|
end
|
|
71
|
-
|
|
72
63
|
end
|
|
73
64
|
|
|
74
65
|
# Close then reopen the connection.
|
data/lib/never_block.rb
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
$:.unshift File.expand_path(File.dirname(__FILE__))
|
|
6
6
|
|
|
7
|
+
# Thanks to Aman Gupta for writing the Ruby 1.8 Fiber simulator
|
|
7
8
|
unless defined? Fiber
|
|
8
9
|
require 'thread'
|
|
9
10
|
require 'singleton'
|
|
@@ -73,12 +74,29 @@ require 'never_block/pool/fiber_pool'
|
|
|
73
74
|
require 'never_block/pool/fibered_connection_pool'
|
|
74
75
|
|
|
75
76
|
module NeverBlock
|
|
77
|
+
|
|
78
|
+
# Checks if we should be working in a non-blocking mode
|
|
79
|
+
def self.neverblocking?
|
|
80
|
+
Fiber.respond_to?(:current) && Fiber.current[:neverblock]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.event_loop_available?
|
|
84
|
+
defined?(EM) && EM.reactor_running?
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The given block will run its queries either in blocking or non-blocking
|
|
88
|
+
# mode based on the first parameter
|
|
76
89
|
def self.neverblock(nb = true, &block)
|
|
77
90
|
status = Fiber.current[:neverblock]
|
|
78
91
|
Fiber.current[:neverblock] = nb
|
|
79
92
|
block.call
|
|
80
93
|
Fiber.current[:neverblock] = status
|
|
81
94
|
end
|
|
95
|
+
|
|
96
|
+
# Exception to be thrown for all neverblock internal errors
|
|
97
|
+
class NBError < StandardError
|
|
98
|
+
end
|
|
99
|
+
|
|
82
100
|
end
|
|
83
101
|
|
|
84
102
|
NB = NeverBlock
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module NeverBlock
|
|
2
|
+
module DB
|
|
3
|
+
module FiberedDBConnection
|
|
4
|
+
|
|
5
|
+
# Attaches the connection socket to an event loop and adds a callback
|
|
6
|
+
# to the fiber's callbacks that unregisters the connection from event loop
|
|
7
|
+
# Raises NB::NBError
|
|
8
|
+
def register_with_event_loop
|
|
9
|
+
#puts ">>>>>register_with_event_loop"
|
|
10
|
+
if EM.reactor_running?
|
|
11
|
+
@fiber = Fiber.current
|
|
12
|
+
#puts ">>>>>register_with_event_loop fiber #{@fiber.inspect}"
|
|
13
|
+
# When there's no previous em_connection
|
|
14
|
+
unless @fiber[:em_connection]
|
|
15
|
+
@fiber[:em_connection] = EM::attach(socket,EMConnectionHandler,self)
|
|
16
|
+
@fiber[:callbacks] << self.method(:unregister_from_event_loop)
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
raise ::NB::NBError.new("FiberedDBConnection: EventMachine reactor not running")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Unattaches the connection socket from the event loop
|
|
24
|
+
def unregister_from_event_loop
|
|
25
|
+
#puts ">>>>>unregister_from_event_loop #{self.inspect} #{@fiber.inspect}"
|
|
26
|
+
if em_c = @fiber[:em_connection]
|
|
27
|
+
em_c.detach
|
|
28
|
+
@fiber[:em_connection] = nil
|
|
29
|
+
true
|
|
30
|
+
else
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Removes the unregister_from_event_loop callback from the fiber's
|
|
36
|
+
# callbacks. It should be used when errors occur in an already registered
|
|
37
|
+
# connection
|
|
38
|
+
def remove_unregister_from_event_loop_callbacks
|
|
39
|
+
@fiber[:callbacks].delete self.method(:unregister_from_event_loop)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Closes the connection using event loop
|
|
43
|
+
def event_loop_connection_close
|
|
44
|
+
@fiber[:em_connection].close_connection if @fiber[:em_connection]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The callback, this is called whenever
|
|
48
|
+
# there is data available at the socket
|
|
49
|
+
def resume_command
|
|
50
|
+
@fiber.resume if @fiber
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module EMConnectionHandler
|
|
56
|
+
def initialize connection
|
|
57
|
+
@db_connection = connection
|
|
58
|
+
end
|
|
59
|
+
def notify_readable
|
|
60
|
+
@db_connection.resume_command
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -3,115 +3,59 @@ require 'mysqlplus'
|
|
|
3
3
|
module NeverBlock
|
|
4
4
|
|
|
5
5
|
module DB
|
|
6
|
-
# A modified
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
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
|
|
6
|
+
# A modified mysql connection driver. It builds on the original pg driver.
|
|
7
|
+
# This driver is able to register the socket at a certain backend (EM)
|
|
8
|
+
# and then whenever the query is executed within the scope of a friendly
|
|
9
|
+
# fiber. It will be done in async mode and the fiber will yield
|
|
14
10
|
class FiberedMysqlConnection < Mysql
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
me = super(*args)
|
|
23
|
-
me.init_descriptor
|
|
24
|
-
me
|
|
11
|
+
|
|
12
|
+
include FiberedDBConnection
|
|
13
|
+
|
|
14
|
+
# Initializes the connection and remembers the connection params
|
|
15
|
+
def initialize(*args)
|
|
16
|
+
@connection_params = args
|
|
17
|
+
super(*@connection_params)
|
|
25
18
|
end
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
def
|
|
30
|
-
@
|
|
31
|
-
|
|
19
|
+
|
|
20
|
+
# Does a normal real_connect if arguments are passed. If no arguments are
|
|
21
|
+
# passed it uses the ones it remembers
|
|
22
|
+
def real_connect(*args)
|
|
23
|
+
@connection_params = args unless args.empty?
|
|
24
|
+
super(*@connection_params)
|
|
32
25
|
end
|
|
33
|
-
|
|
26
|
+
|
|
27
|
+
alias_method :connect, :real_connect
|
|
28
|
+
|
|
34
29
|
# Assuming the use of NeverBlock fiber extensions and that the exec is run in
|
|
35
30
|
# the context of a fiber. One that have the value :neverblock set to true.
|
|
36
31
|
# All neverblock IO classes check this value, setting it to false will force
|
|
37
32
|
# the execution in a blocking way.
|
|
38
33
|
def query(sql)
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
if NB.event_loop_available? && NB.neverblocking?
|
|
35
|
+
raise ::NB::NBError.new("FiberedMysqlConnection: The running fiber is attached to a connection other than the current one") if (c = Fiber.current[:connection]) && c != self
|
|
36
|
+
begin
|
|
41
37
|
send_query sql
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# reset the connection
|
|
55
|
-
# and reattach to the
|
|
56
|
-
# event loop
|
|
57
|
-
def reconnect
|
|
58
|
-
unregister_from_event_loop
|
|
59
|
-
super
|
|
60
|
-
init_descriptor
|
|
61
|
-
register_with_event_loop(@loop)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Attaches the connection socket to an event loop.
|
|
65
|
-
# Currently only supports EM, but Rev support will be
|
|
66
|
-
# completed soon.
|
|
67
|
-
def register_with_event_loop(loop)
|
|
68
|
-
if loop == :em
|
|
69
|
-
unless EM.respond_to?(:attach)
|
|
70
|
-
puts "invalide EM version, please download the modified gem from: (http://github.com/riham/eventmachine)"
|
|
71
|
-
exit
|
|
38
|
+
Fiber.yield register_with_event_loop
|
|
39
|
+
get_result
|
|
40
|
+
rescue Exception => e
|
|
41
|
+
if error = ['not connected', 'gone away', 'Lost connection'].detect{|msg| e.message.include? msg}
|
|
42
|
+
event_loop_connection_close
|
|
43
|
+
unregister_from_event_loop
|
|
44
|
+
remove_unregister_from_event_loop_callbacks
|
|
45
|
+
#connect
|
|
46
|
+
end
|
|
47
|
+
raise e
|
|
72
48
|
end
|
|
73
|
-
if EM.reactor_running?
|
|
74
|
-
@em_connection = EM::attach(@io,EMConnectionHandler,self)
|
|
75
|
-
else
|
|
76
|
-
raise "REACTOR NOT RUNNING YA ZALAMA"
|
|
77
|
-
end
|
|
78
|
-
elsif loop.class.name == "REV::Loop"
|
|
79
|
-
loop.attach(RevConnectionHandler.new(socket))
|
|
80
49
|
else
|
|
81
|
-
|
|
82
|
-
end
|
|
83
|
-
@loop = loop
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Unattaches the connection socket from the event loop
|
|
87
|
-
def unregister_from_event_loop
|
|
88
|
-
if @loop == :em
|
|
89
|
-
@em_connection.unattach(false)
|
|
90
|
-
else
|
|
91
|
-
raise NotImplementedError.new("unregister_from_event_loop not implemented for #{@loop}")
|
|
50
|
+
super(sql)
|
|
92
51
|
end
|
|
93
52
|
end
|
|
94
|
-
|
|
95
|
-
# The callback, this is called whenever
|
|
96
|
-
# there is data available at the socket
|
|
97
|
-
def resume_command
|
|
98
|
-
@fiber.resume
|
|
99
|
-
end
|
|
100
53
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
#
|
|
104
|
-
# More to follow.
|
|
105
|
-
module EMConnectionHandler
|
|
106
|
-
def initialize connection
|
|
107
|
-
@connection = connection
|
|
108
|
-
end
|
|
109
|
-
def notify_readable
|
|
110
|
-
@connection.resume_command
|
|
111
|
-
end
|
|
112
|
-
end
|
|
54
|
+
alias_method :exec, :query
|
|
55
|
+
|
|
56
|
+
end #FiberedMySQLConnection
|
|
113
57
|
|
|
114
|
-
|
|
58
|
+
end #DB
|
|
115
59
|
|
|
116
60
|
end #NeverBlock
|
|
117
61
|
|
|
@@ -3,6 +3,7 @@ require 'pg'
|
|
|
3
3
|
module NeverBlock
|
|
4
4
|
|
|
5
5
|
module DB
|
|
6
|
+
|
|
6
7
|
# A modified postgres connection driver
|
|
7
8
|
# builds on the original pg driver.
|
|
8
9
|
# This driver is able to register the socket
|
|
@@ -12,32 +13,21 @@ module NeverBlock
|
|
|
12
13
|
# it will be done in async mode and the fiber
|
|
13
14
|
# will yield
|
|
14
15
|
class FiberedPostgresConnection < PGconn
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
init_descriptor
|
|
24
|
-
#setnonblocking(true)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def init_descriptor
|
|
28
|
-
@fd = socket
|
|
29
|
-
@io = IO.new(socket)
|
|
30
|
-
end
|
|
16
|
+
|
|
17
|
+
include FiberedDBConnection
|
|
18
|
+
|
|
31
19
|
# Assuming the use of NeverBlock fiber extensions and that the exec is run in
|
|
32
20
|
# the context of a fiber. One that have the value :neverblock set to true.
|
|
33
21
|
# All neverblock IO classes check this value, setting it to false will force
|
|
34
22
|
# the execution in a blocking way.
|
|
35
23
|
def exec(sql)
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
# TODO Still not "killing the query process"-proof
|
|
25
|
+
# In some cases, the query is simply sent but the fiber never yields
|
|
26
|
+
if NB.event_loop_available? && NB.neverblocking?
|
|
27
|
+
begin
|
|
38
28
|
send_query sql
|
|
39
|
-
@fiber = Fiber.current
|
|
40
|
-
Fiber.yield
|
|
29
|
+
@fiber = Fiber.current
|
|
30
|
+
Fiber.yield register_with_event_loop
|
|
41
31
|
while is_busy
|
|
42
32
|
consume_input
|
|
43
33
|
Fiber.yield if is_busy
|
|
@@ -47,77 +37,25 @@ module NeverBlock
|
|
|
47
37
|
res = self.get_result
|
|
48
38
|
data << res unless res.nil?
|
|
49
39
|
end
|
|
50
|
-
data.last
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
# reset the connection
|
|
61
|
-
# and reattach to the
|
|
62
|
-
# event loop
|
|
63
|
-
def reset
|
|
64
|
-
unregister_from_event_loop
|
|
65
|
-
super
|
|
66
|
-
init_descriptor
|
|
67
|
-
register_with_event_loop(@loop)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Attaches the connection socket to an event loop.
|
|
71
|
-
# Currently only supports EM, but Rev support will be
|
|
72
|
-
# completed soon.
|
|
73
|
-
def register_with_event_loop(loop)
|
|
74
|
-
if loop == :em
|
|
75
|
-
unless EM.respond_to?(:attach)
|
|
76
|
-
puts "invalide EM version, please download the modified gem from: (http://github.com/riham/eventmachine)"
|
|
77
|
-
exit
|
|
40
|
+
data.last
|
|
41
|
+
rescue Exception => e
|
|
42
|
+
if error = ['not connected', 'gone away', 'Lost connection','no connection'].detect{|msg| e.message.include? msg}
|
|
43
|
+
#event_loop_connection_close
|
|
44
|
+
unregister_from_event_loop
|
|
45
|
+
reset
|
|
46
|
+
end
|
|
47
|
+
raise e
|
|
48
|
+
ensure
|
|
49
|
+
unregister_from_event_loop
|
|
78
50
|
end
|
|
79
|
-
if EM.reactor_running?
|
|
80
|
-
@em_connection = EM::attach(@io,EMConnectionHandler,self)
|
|
81
|
-
else
|
|
82
|
-
raise "REACTOR NOT RUNNING YA ZALAMA"
|
|
83
|
-
end
|
|
84
|
-
elsif loop.class.name == "REV::Loop"
|
|
85
|
-
loop.attach(RevConnectionHandler.new(socket))
|
|
86
51
|
else
|
|
87
|
-
|
|
52
|
+
super(sql)
|
|
88
53
|
end
|
|
89
|
-
@loop = loop
|
|
90
54
|
end
|
|
91
55
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def unregister_from_event_loop
|
|
95
|
-
if @loop == :em
|
|
96
|
-
@em_connection.unattach(false)
|
|
97
|
-
else
|
|
98
|
-
raise NotImplementedError.new("unregister_from_event_loop not implemented for #{@loop}")
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# The callback, this is called whenever
|
|
103
|
-
# there is data available at the socket
|
|
104
|
-
def resume_command
|
|
105
|
-
#let the fiber continue its work
|
|
106
|
-
@fiber.resume
|
|
107
|
-
end
|
|
108
|
-
|
|
56
|
+
alias_method :query, :exec
|
|
57
|
+
|
|
109
58
|
end #FiberedPostgresConnection
|
|
110
|
-
|
|
111
|
-
# A connection handler for EM
|
|
112
|
-
# More to follow.
|
|
113
|
-
module EMConnectionHandler
|
|
114
|
-
def initialize connection
|
|
115
|
-
@connection = connection
|
|
116
|
-
end
|
|
117
|
-
def notify_readable
|
|
118
|
-
@connection.resume_command
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
59
|
|
|
122
60
|
end #DB
|
|
123
61
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module NeverBlock
|
|
2
|
+
module DB
|
|
3
|
+
# a proxy for pooled fibered connections
|
|
4
|
+
class PooledDBConnection
|
|
5
|
+
# Requires a block with connection parameters
|
|
6
|
+
# and a pool size (defaults to 4)
|
|
7
|
+
def initialize(size=4, &block)
|
|
8
|
+
@pool = NB::Pool::FiberedConnectionPool.new(:size=>size, :eager=>true) do
|
|
9
|
+
yield
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# A proxy for the connection's query method
|
|
14
|
+
# quries the pool to get a connection first
|
|
15
|
+
def query(query)
|
|
16
|
+
@pool.hold do |conn|
|
|
17
|
+
conn.query(query)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
alias :exec :query
|
|
22
|
+
|
|
23
|
+
# Replaces the current connection with a brand new one
|
|
24
|
+
def replace_acquired_connection
|
|
25
|
+
@pool.replace_acquired_connection
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Pass unknown methods to the connection
|
|
29
|
+
def method_missing(method, *args)
|
|
30
|
+
@pool.hold do |conn|
|
|
31
|
+
conn.send(method, *args)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Pass method queries to the connection
|
|
36
|
+
def respond_to?(method)
|
|
37
|
+
@pool.hold do |conn|
|
|
38
|
+
conn.respond_to?(method)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
# we need Fiber.current
|
|
2
|
-
# so we must require
|
|
3
|
-
# fiber
|
|
4
|
-
# Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
|
|
5
|
-
# Copyright:: Copyright (c) 2008 eSpace, Inc.
|
|
6
|
-
# License:: Distributes under the same terms as Ruby
|
|
7
|
-
|
|
8
|
-
#require 'fiber'
|
|
9
|
-
|
|
10
1
|
module NeverBlock
|
|
11
2
|
module Pool
|
|
12
3
|
|
|
@@ -31,46 +22,52 @@ module NeverBlock
|
|
|
31
22
|
# end
|
|
32
23
|
# end
|
|
33
24
|
#
|
|
34
|
-
|
|
25
|
+
class FiberPool
|
|
26
|
+
|
|
27
|
+
# gives access to the currently free fibers
|
|
28
|
+
attr_reader :fibers
|
|
29
|
+
|
|
30
|
+
# Prepare a list of fibers that are able to run different blocks of code
|
|
31
|
+
# every time. Once a fiber is done with its block, it attempts to fetch
|
|
32
|
+
# another one from the queue
|
|
33
|
+
def initialize(count = 50)
|
|
34
|
+
@fibers,@busy_fibers,@queue = [],{},[]
|
|
35
|
+
count.times do |i|
|
|
36
|
+
fiber = Fiber.new do |block|
|
|
37
|
+
loop do
|
|
38
|
+
block.call
|
|
39
|
+
# callbacks are called in a reverse order, much like c++ destructor
|
|
40
|
+
Fiber.current[:callbacks].pop.call while Fiber.current[:callbacks].length > 0
|
|
41
|
+
unless @queue.empty?
|
|
42
|
+
block = @queue.shift
|
|
43
|
+
else
|
|
44
|
+
@busy_fibers.delete(Fiber.current.object_id)
|
|
45
|
+
@fibers << Fiber.current
|
|
46
|
+
block = Fiber.yield
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
fiber[:callbacks] = []
|
|
51
|
+
fiber[:neverblock] = true
|
|
52
|
+
@fibers << fiber
|
|
53
|
+
end
|
|
54
|
+
end
|
|
35
55
|
|
|
36
|
-
|
|
37
|
-
|
|
56
|
+
# If there is an available fiber use it, otherwise, leave it to linger
|
|
57
|
+
# in a queue
|
|
58
|
+
def spawn(evented = true, &block)
|
|
59
|
+
if fiber = @fibers.shift
|
|
60
|
+
fiber[:callbacks] = []
|
|
61
|
+
@busy_fibers[fiber.object_id] = fiber
|
|
62
|
+
fiber[:neverblock] = evented
|
|
63
|
+
fiber.resume(block)
|
|
64
|
+
else
|
|
65
|
+
@queue << block
|
|
66
|
+
end
|
|
67
|
+
self # we are keen on hiding our queue
|
|
68
|
+
end
|
|
38
69
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# once a fiber is done with its block
|
|
43
|
-
# it attempts to fetch another one
|
|
44
|
-
# from the queue.
|
|
45
|
-
def initialize(count = 50)
|
|
46
|
-
@fibers,@queue = [],[]
|
|
47
|
-
count.times do |i|
|
|
48
|
-
fiber = Fiber.new do |block|
|
|
49
|
-
loop do
|
|
50
|
-
block.call
|
|
51
|
-
unless @queue.empty?
|
|
52
|
-
block = @queue.shift
|
|
53
|
-
else
|
|
54
|
-
block = Fiber.yield @fibers << Fiber.current
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
fiber[:neverblock] = true
|
|
59
|
-
@fibers << fiber
|
|
60
|
-
end
|
|
61
|
-
end
|
|
70
|
+
end # FiberPool
|
|
71
|
+
end # Pool
|
|
72
|
+
end # NeverBlock
|
|
62
73
|
|
|
63
|
-
# If there is an available fiber
|
|
64
|
-
# use it, otherwise, leave it to linger in a queue
|
|
65
|
-
def spawn(evented = true, &block)
|
|
66
|
-
if fiber = @fibers.shift
|
|
67
|
-
fiber[:neverblock] = evented
|
|
68
|
-
fiber.resume(block)
|
|
69
|
-
else
|
|
70
|
-
@queue << block
|
|
71
|
-
end
|
|
72
|
-
self # we are keen on hiding our queue
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
# Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
|
|
2
|
-
# Copyright:: Copyright (c) 2008 eSpace, Inc.
|
|
3
|
-
# License:: Distributes under the same terms as Ruby
|
|
4
|
-
|
|
5
1
|
module NeverBlock
|
|
6
2
|
module Pool
|
|
7
|
-
|
|
8
|
-
# Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
|
|
9
|
-
# Copyright:: Copyright (c) 2008 eSpace, Inc.
|
|
10
|
-
# License:: Distributes under the same terms as Ruby
|
|
11
|
-
#
|
|
12
3
|
# This class represents a pool of connections,
|
|
13
4
|
# you hold or release conncetions from the pool
|
|
14
5
|
# hold requests that cannot be fullfiled will be queued
|
|
@@ -25,19 +16,28 @@ module NeverBlock
|
|
|
25
16
|
# end
|
|
26
17
|
# 32.times do
|
|
27
18
|
# Fiber.new do
|
|
28
|
-
#
|
|
29
|
-
#
|
|
19
|
+
# # acquire a connection from the pool
|
|
20
|
+
# pool.hold do |conn|
|
|
21
|
+
# conn.execute('something') # you can use the connection normally now
|
|
22
|
+
# end
|
|
30
23
|
# end.resume
|
|
31
24
|
# end
|
|
32
25
|
#
|
|
33
|
-
# The pool has support for transactions, just pass true to the
|
|
34
|
-
# and the connection will not be released after the block
|
|
26
|
+
# The pool has support for transactions, just pass true to the
|
|
27
|
+
# pool#hold method and the connection will not be released after the block
|
|
28
|
+
# is finished
|
|
35
29
|
# It is the responsibility of client code to release the connection
|
|
36
30
|
class FiberedConnectionPool
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
#
|
|
31
|
+
|
|
32
|
+
attr_reader :size
|
|
33
|
+
|
|
34
|
+
# initialize the connection pool using the supplied proc to create
|
|
35
|
+
# the connections
|
|
36
|
+
# You can choose to start them eagerly or lazily (lazy by default)
|
|
37
|
+
# Available options are
|
|
38
|
+
# :size => the maximum number of connections to be created in the pool
|
|
39
|
+
# :eager => (true|false) indicates whether connections should be
|
|
40
|
+
# created initially or when need
|
|
41
41
|
def initialize(options = {}, &block)
|
|
42
42
|
@connections, @busy_connections, @queue = [], {},[]
|
|
43
43
|
@connection_proc = block
|
|
@@ -45,38 +45,23 @@ module NeverBlock
|
|
|
45
45
|
if options[:eager]
|
|
46
46
|
@size.times do
|
|
47
47
|
@connections << @connection_proc.call
|
|
48
|
-
end
|
|
48
|
+
end
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# before releasing the connection
|
|
59
|
-
# if inside a transaction, don't release the fiber
|
|
60
|
-
def hold(transactional = false)
|
|
61
|
-
fiber = Fiber.current
|
|
62
|
-
if conn = @busy_connections[fiber]
|
|
63
|
-
return yield(conn)
|
|
64
|
-
end
|
|
65
|
-
conn = acquire(fiber)
|
|
66
|
-
begin
|
|
67
|
-
yield conn
|
|
68
|
-
ensure
|
|
69
|
-
release(fiber, conn) unless transactional
|
|
70
|
-
process_queue
|
|
71
|
-
end
|
|
72
|
-
end
|
|
52
|
+
def replace_acquired_connection
|
|
53
|
+
fiber = Fiber.current
|
|
54
|
+
conn = @connection_proc.call
|
|
55
|
+
@busy_connections[fiber] = conn
|
|
56
|
+
fiber[:connection] = conn
|
|
57
|
+
end
|
|
73
58
|
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
59
|
+
# If a connection is available, pass it to the block, otherwise pass
|
|
60
|
+
# the fiber to the queue till a connection is available
|
|
61
|
+
def hold()
|
|
62
|
+
fiber = Fiber.current
|
|
63
|
+
conn = acquire(fiber)
|
|
64
|
+
yield conn
|
|
80
65
|
end
|
|
81
66
|
|
|
82
67
|
def all_connections
|
|
@@ -89,14 +74,36 @@ module NeverBlock
|
|
|
89
74
|
# Can we create one?
|
|
90
75
|
# Wait in the queue then
|
|
91
76
|
def acquire(fiber)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
77
|
+
# A special case for rails when doing ActiveRecord stuff when not yet
|
|
78
|
+
# running in the context of a request (fiber) like in the case of AR
|
|
79
|
+
# queries in environment.rb (Root Fiber)
|
|
80
|
+
return @connections.first unless fiber[:callbacks]
|
|
81
|
+
|
|
82
|
+
return fiber[:connection] if fiber[:connection]
|
|
83
|
+
conn = if !@connections.empty?
|
|
84
|
+
@connections.shift
|
|
85
|
+
elsif (@connections.length + @busy_connections.length) < @size
|
|
86
|
+
@connection_proc.call
|
|
87
|
+
else
|
|
98
88
|
Fiber.yield @queue << fiber
|
|
99
|
-
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# They're called in reverse order i.e. release then process_queue
|
|
92
|
+
fiber[:callbacks] << self.method(:process_queue)
|
|
93
|
+
fiber[:callbacks] << self.method(:release)
|
|
94
|
+
|
|
95
|
+
@busy_connections[fiber] = conn
|
|
96
|
+
fiber[:connection] = conn
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Give the fiber's connection back to the pool
|
|
100
|
+
def release()
|
|
101
|
+
fiber = Fiber.current
|
|
102
|
+
if fiber[:connection]
|
|
103
|
+
@busy_connections.delete(fiber)
|
|
104
|
+
@connections << fiber[:connection]
|
|
105
|
+
fiber[:connection] = nil
|
|
106
|
+
end
|
|
100
107
|
end
|
|
101
108
|
|
|
102
109
|
# Check if there are waiting fibers and
|
|
@@ -106,8 +113,8 @@ module NeverBlock
|
|
|
106
113
|
fiber = @queue.shift
|
|
107
114
|
# What is really happening here?
|
|
108
115
|
# we are resuming a fiber from within
|
|
109
|
-
# another, should we call transfer
|
|
110
|
-
fiber.resume @
|
|
116
|
+
# another, should we call transfer instead?
|
|
117
|
+
fiber.resume @connections.shift
|
|
111
118
|
end
|
|
112
119
|
end
|
|
113
120
|
|
|
@@ -4,10 +4,8 @@ require 'thin'
|
|
|
4
4
|
|
|
5
5
|
module Thin
|
|
6
6
|
|
|
7
|
-
# Patch the thin server to use
|
|
8
|
-
#
|
|
9
|
-
# to be able to wrap requests
|
|
10
|
-
# in fibers
|
|
7
|
+
# Patch the thin server to use NeverBlock::Pool::FiberPool to be able to
|
|
8
|
+
# wrap requests in fibers
|
|
11
9
|
class Server
|
|
12
10
|
|
|
13
11
|
DEFAULT_FIBER_POOL_SIZE = 20
|
|
@@ -18,17 +16,14 @@ module Thin
|
|
|
18
16
|
|
|
19
17
|
end # Server
|
|
20
18
|
|
|
21
|
-
# A request is processed by wrapping it
|
|
22
|
-
#
|
|
23
|
-
# the
|
|
24
|
-
# wait in a queue to be picked up later.
|
|
25
|
-
# Meanwhile, the server will still be
|
|
26
|
-
# processing requests
|
|
19
|
+
# A request is processed by wrapping it in a fiber from the fiber pool.
|
|
20
|
+
# If all the fibers are busy the request will wait in a queue to be picked up
|
|
21
|
+
# later. Meanwhile, the server will still be processing requests
|
|
27
22
|
class Connection < EventMachine::Connection
|
|
28
23
|
|
|
29
24
|
def process
|
|
30
25
|
@request.threaded = false
|
|
31
|
-
@backend.server.fiber_pool.spawn{post_process(pre_process)}
|
|
26
|
+
@backend.server.fiber_pool.spawn {post_process(pre_process)}
|
|
32
27
|
end
|
|
33
28
|
|
|
34
29
|
end # Connection
|
data/lib/neverblock-mysql.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
$:.unshift File.expand_path(File.dirname(__FILE__))
|
|
2
2
|
require 'neverblock'
|
|
3
|
+
require 'never_block/db/fibered_db_connection'
|
|
3
4
|
require 'never_block/db/fibered_mysql_connection'
|
|
4
|
-
require 'never_block/db/
|
|
5
|
+
require 'never_block/db/pooled_db_connection'
|
data/lib/neverblock-pg.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
$:.unshift File.expand_path(File.dirname(__FILE__))
|
|
2
|
-
|
|
2
|
+
require 'neverblock'
|
|
3
|
+
require 'never_block/db/fibered_db_connection'
|
|
4
|
+
require 'never_block/db/pooled_db_connection'
|
|
3
5
|
require 'never_block/db/fibered_postgres_connection'
|
|
4
|
-
require 'never_block/db/pooled_fibered_postgres_connection'
|
data/neverblock.gemspec
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = "neverblock"
|
|
3
|
-
s.version = "0.1.
|
|
4
|
-
s.date = "2008-
|
|
3
|
+
s.version = "0.1.5"
|
|
4
|
+
s.date = "2008-11-06"
|
|
5
5
|
s.summary = "Utilities for non-blocking stack components"
|
|
6
6
|
s.email = "oldmoe@gmail.com"
|
|
7
7
|
s.homepage = "http://github.com/oldmoe/neverblock"
|
|
@@ -23,14 +23,15 @@ Gem::Specification.new do |s|
|
|
|
23
23
|
"lib/never_block/servers/thin.rb",
|
|
24
24
|
"lib/never_block/servers/mongrel.rb",
|
|
25
25
|
"lib/never_block/db/fibered_postgres_connection.rb",
|
|
26
|
-
"lib/never_block/db/
|
|
26
|
+
"lib/never_block/db/pooled_db_connection.rb",
|
|
27
27
|
"lib/never_block/db/fibered_mysql_connection.rb",
|
|
28
|
-
"lib/never_block/db/
|
|
28
|
+
"lib/never_block/db/fibered_db_connection.rb",
|
|
29
29
|
"lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb",
|
|
30
30
|
"lib/active_record/connection_adapters/neverblock_mysql_adapter.rb"
|
|
31
31
|
]
|
|
32
32
|
s.rdoc_options = ["--main", "README"]
|
|
33
33
|
s.extra_rdoc_files = ["README"]
|
|
34
|
+
s.add_dependency('eventmachine', '>= 0.12.2')
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: espace-neverblock
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Muhammad A. Ali
|
|
@@ -11,10 +11,18 @@ autorequire:
|
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
13
|
|
|
14
|
-
date: 2008-
|
|
14
|
+
date: 2008-11-06 00:00:00 -08:00
|
|
15
15
|
default_executable:
|
|
16
|
-
dependencies:
|
|
17
|
-
|
|
16
|
+
dependencies:
|
|
17
|
+
- !ruby/object:Gem::Dependency
|
|
18
|
+
name: eventmachine
|
|
19
|
+
version_requirement:
|
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
21
|
+
requirements:
|
|
22
|
+
- - ">="
|
|
23
|
+
- !ruby/object:Gem::Version
|
|
24
|
+
version: 0.12.2
|
|
25
|
+
version:
|
|
18
26
|
description: NeverBlock is a collection of classes and modules that help you write evented non-blocking applications in a seemingly blocking mannner.
|
|
19
27
|
email: oldmoe@gmail.com
|
|
20
28
|
executables: []
|
|
@@ -38,9 +46,9 @@ files:
|
|
|
38
46
|
- lib/never_block/servers/thin.rb
|
|
39
47
|
- lib/never_block/servers/mongrel.rb
|
|
40
48
|
- lib/never_block/db/fibered_postgres_connection.rb
|
|
41
|
-
- lib/never_block/db/
|
|
49
|
+
- lib/never_block/db/pooled_db_connection.rb
|
|
42
50
|
- lib/never_block/db/fibered_mysql_connection.rb
|
|
43
|
-
- lib/never_block/db/
|
|
51
|
+
- lib/never_block/db/fibered_db_connection.rb
|
|
44
52
|
- lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb
|
|
45
53
|
- lib/active_record/connection_adapters/neverblock_mysql_adapter.rb
|
|
46
54
|
has_rdoc: true
|
|
@@ -1,77 +0,0 @@
|
|
|
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 PooledFiberedMysqlConnection
|
|
8
|
-
|
|
9
|
-
# Requires a hash or an array with connection parameters
|
|
10
|
-
# and a pool size (defaults to 4)
|
|
11
|
-
def initialize(size=4, &block)
|
|
12
|
-
@pool = NB::Pool::FiberedConnectionPool.new(:size=>size, :eager=>true) do
|
|
13
|
-
yield
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# A proxy for the connection's query method
|
|
18
|
-
# quries the pool to get a connection first
|
|
19
|
-
def query(query)
|
|
20
|
-
@pool.hold do |conn|
|
|
21
|
-
conn.query(query)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# This method must be called for transactions to work correctly.
|
|
26
|
-
# One cannot just send "begin" as you never know which connection
|
|
27
|
-
# will be available next. This method ensures you get the same connection
|
|
28
|
-
# while in a transaction.
|
|
29
|
-
def begin_db_transaction
|
|
30
|
-
@pool.hold(true) do |conn|
|
|
31
|
-
conn.query("begin")
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# see =begin_db_transaction
|
|
36
|
-
def rollback_db_transaction
|
|
37
|
-
@pool.hold do |conn|
|
|
38
|
-
conn.query("rollback")
|
|
39
|
-
@pool.release(Fiber.current,conn)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# see =begin_db_transaction
|
|
44
|
-
def commit_db_transaction
|
|
45
|
-
@pool.hold do |conn|
|
|
46
|
-
conn.query("commit")
|
|
47
|
-
@pool.release(Fiber.current,conn)
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
#close all connections and remove them from the event loop
|
|
52
|
-
def close
|
|
53
|
-
@pool.all_connections do |conn|
|
|
54
|
-
conn.unregister_from_event_loop
|
|
55
|
-
conn.close
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Pass unknown methods to the connection
|
|
60
|
-
def method_missing(method, *args)
|
|
61
|
-
@pool.hold do |conn|
|
|
62
|
-
conn.send(method, *args)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Pass method queries to the connection
|
|
67
|
-
def respond_to?(method)
|
|
68
|
-
@pool.hold do |conn|
|
|
69
|
-
conn.respond_to?(method)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
NeverBlock::DB::PFMysql = NeverBlock::DB::PooledFiberedMysqlConnection
|
|
@@ -1,81 +0,0 @@
|
|
|
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
|
-
#close all connections and remove them from the event loop
|
|
55
|
-
def close
|
|
56
|
-
@pool.all_connections do |conn|
|
|
57
|
-
conn.unregister_from_event_loop
|
|
58
|
-
conn.close
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Pass unknown methods to the connection
|
|
63
|
-
def method_missing(method, *args)
|
|
64
|
-
@pool.hold do |conn|
|
|
65
|
-
conn.send(method, *args)
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Pass method queries to the connection
|
|
70
|
-
def respond_to?(method)
|
|
71
|
-
@pool.hold do |conn|
|
|
72
|
-
conn.respond_to?(method)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
NB::DB::PFPGconn = NeverBlock::DB::FiberedPostgresConnection
|
|
81
|
-
|