oldmoe-neverblock 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/active_record/connection_adapters/neverblock_mysql_adapter.rb +78 -0
- data/lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb +97 -0
- data/lib/never_block/db/fibered_mysql_connection.rb +97 -0
- data/lib/never_block/db/fibered_postgres_connection.rb +109 -0
- data/lib/never_block/db/pooled_fibered_mysql_connection.rb +78 -0
- data/lib/never_block/db/pooled_fibered_postgres_connection.rb +81 -0
- data/lib/never_block/extensions/fiber_extensions.rb +8 -3
- data/lib/never_block/frameworks/activerecord.rb +7 -1
- data/lib/never_block/frameworks/rails.rb +2 -1
- data/lib/never_block/pool/fiber_pool.rb +2 -2
- data/lib/never_block/pool/fibered_connection_pool.rb +5 -1
- data/lib/never_block/servers/thin.rb +12 -0
- data/lib/never_block.rb +65 -2
- data/lib/neverblock-mysql.rb +4 -0
- data/lib/neverblock-pg.rb +4 -0
- data/neverblock.gemspec +13 -3
- metadata +10 -1
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'activesupport'
|
2
|
+
require 'never_block/frameworks/activerecord'
|
3
|
+
require 'active_record/connection_adapters/mysql_adapter'
|
4
|
+
require 'neverblock-mysql'
|
5
|
+
|
6
|
+
class ActiveRecord::ConnectionAdapters::NeverBlockMysqlAdapter < ActiveRecord::ConnectionAdapters::MysqlAdapter
|
7
|
+
|
8
|
+
# Returns 'NeverBlockMySQL' as adapter name for identification purposes
|
9
|
+
def adapter_name
|
10
|
+
'NeverBlockMySQL'
|
11
|
+
end
|
12
|
+
|
13
|
+
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
14
|
+
begin_db_transaction
|
15
|
+
super sql, name
|
16
|
+
id_value || @connection.insert_id
|
17
|
+
commit_db_transaction
|
18
|
+
end
|
19
|
+
|
20
|
+
def update_sql(sql, name = nil) #:nodoc:
|
21
|
+
begin_db_transaction
|
22
|
+
super
|
23
|
+
@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
|
+
end
|
38
|
+
|
39
|
+
def connect
|
40
|
+
@connection = ::NB::DB::PooledFiberedMysqlConnection.new(@connection_options.shift) do
|
41
|
+
conn = ::NB::DB::FMysql.init
|
42
|
+
encoding = @config[:encoding]
|
43
|
+
if encoding
|
44
|
+
conn.options(::NB::DB::FMysql::SET_CHARSET_NAME, encoding) rescue nil
|
45
|
+
end
|
46
|
+
conn.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher]) if @config[:sslkey]
|
47
|
+
conn.real_connect(*@connection_options)
|
48
|
+
conn.query("SET NAMES '#{encoding}'") if encoding
|
49
|
+
# By default, MySQL 'where id is null' selects the last inserted id.
|
50
|
+
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
51
|
+
# conn.query("SET SQL_AUTO_IS_NULL=0")
|
52
|
+
conn.register_with_event_loop(:em)
|
53
|
+
conn
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
class ActiveRecord::Base
|
60
|
+
# Establishes a connection to the database that's used by all Active Record objects
|
61
|
+
def self.neverblock_mysql_connection(config) # :nodoc:
|
62
|
+
config = config.symbolize_keys
|
63
|
+
host = config[:host]
|
64
|
+
port = config[:port]
|
65
|
+
socket = config[:socket]
|
66
|
+
username = config[:username] ? config[:username].to_s : 'root'
|
67
|
+
password = config[:password].to_s
|
68
|
+
size = config[:connections] || 4
|
69
|
+
|
70
|
+
if config.has_key?(:database)
|
71
|
+
database = config[:database]
|
72
|
+
else
|
73
|
+
raise ArgumentError, "No database specified. Missing argument: database."
|
74
|
+
end
|
75
|
+
MysqlCompat.define_all_hashes_method!
|
76
|
+
::ActiveRecord::ConnectionAdapters::NeverBlockMysqlAdapter.new(nil, logger, [size.to_i, host, username, password, database, port, socket, nil], config)
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
2
|
+
require 'neverblock-pg'
|
3
|
+
require 'never_block/frameworks/activerecord'
|
4
|
+
|
5
|
+
|
6
|
+
class ActiveRecord::ConnectionAdapters::NeverBlockPostgreSQLAdapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
7
|
+
# Returns 'FiberedPostgreSQL' as adapter name for identification purposes.
|
8
|
+
def adapter_name
|
9
|
+
'NeverBlockPostgreSQL'
|
10
|
+
end
|
11
|
+
|
12
|
+
def begin_db_transaction
|
13
|
+
@connection.begin_db_transaction
|
14
|
+
end
|
15
|
+
|
16
|
+
def commit_db_transaction
|
17
|
+
@connection.commit_db_transaction
|
18
|
+
end
|
19
|
+
|
20
|
+
def rollback_db_transaction
|
21
|
+
@connection.rollback_db_transaction
|
22
|
+
end
|
23
|
+
# Executes an INSERT query and returns the new record's ID, this wont
|
24
|
+
# work on earlier versions of PostgreSQL but they don't suppor the async
|
25
|
+
# interface anyway
|
26
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
27
|
+
@connection.exec(sql << " returning id ")
|
28
|
+
end
|
29
|
+
|
30
|
+
def connect
|
31
|
+
size = @connection_parameters.shift
|
32
|
+
@connection = ::NB::DB::PooledFiberedPostgresConnection.new(@connection_parameters, size)
|
33
|
+
|
34
|
+
PGconn.translate_results = false if PGconn.respond_to?(:translate_results=)
|
35
|
+
|
36
|
+
# Ignore async_exec and async_query when using postgres-pr.
|
37
|
+
@async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec)
|
38
|
+
|
39
|
+
# Use escape string syntax if available. We cannot do this lazily when encountering
|
40
|
+
# the first string, because that could then break any transactions in progress.
|
41
|
+
# See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
|
42
|
+
# If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't
|
43
|
+
# support escape string syntax. Don't override the inherited quoted_string_prefix.
|
44
|
+
@connection.begin_db_transaction
|
45
|
+
if supports_standard_conforming_strings?
|
46
|
+
self.class.instance_eval do
|
47
|
+
define_method(:quoted_string_prefix) { 'E' }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
|
52
|
+
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
|
53
|
+
# should know about this but can't detect it there, so deal with it here.
|
54
|
+
money_precision = (postgresql_version >= 80300) ? 19 : 10
|
55
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.module_eval(<<-end_eval)
|
56
|
+
def extract_precision(sql_type)
|
57
|
+
if sql_type =~ /^money$/
|
58
|
+
#{money_precision}
|
59
|
+
else
|
60
|
+
super
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end_eval
|
64
|
+
|
65
|
+
configure_connection
|
66
|
+
@connection.commit_db_transaction
|
67
|
+
end
|
68
|
+
|
69
|
+
# Close then reopen the connection.
|
70
|
+
def reconnect!
|
71
|
+
disconnect!
|
72
|
+
connect
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
class ActiveRecord::Base
|
78
|
+
# Establishes a connection to the database that's used by all Active Record objects
|
79
|
+
def self.neverblock_postgresql_connection(config) # :nodoc:
|
80
|
+
config = config.symbolize_keys
|
81
|
+
host = config[:host]
|
82
|
+
port = config[:port] || 5432
|
83
|
+
username = config[:username].to_s
|
84
|
+
password = config[:password].to_s
|
85
|
+
size = config[:connections] || 4
|
86
|
+
|
87
|
+
if config.has_key?(:database)
|
88
|
+
database = config[:database]
|
89
|
+
else
|
90
|
+
raise ArgumentError, "No database specified. Missing argument: database."
|
91
|
+
end
|
92
|
+
|
93
|
+
# The postgres drivers don't allow the creation of an unconnected PGconn object,
|
94
|
+
# so just pass a nil connection object for the time being.
|
95
|
+
::ActiveRecord::ConnectionAdapters::NeverBlockPostgreSQLAdapter.new(nil, logger, [size, host, port, nil, nil, database, username, password], config)
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'mysql'
|
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 FiberedMysqlConnection < Mysql
|
15
|
+
# needed to access the sockect by the event loop
|
16
|
+
attr_reader :fd, :io
|
17
|
+
|
18
|
+
# Creates a new mysql connection, sets it
|
19
|
+
# to nonblocking and wraps the descriptor in an IO
|
20
|
+
# object.
|
21
|
+
def real_connect(*args)
|
22
|
+
super(*args)
|
23
|
+
@fd = socket
|
24
|
+
@io = IO.new(socket)
|
25
|
+
end
|
26
|
+
#alias :real_connect :initialize
|
27
|
+
#alias :connect :initialize
|
28
|
+
|
29
|
+
# Assuming the use of NeverBlock fiber extensions and that the exec is run in
|
30
|
+
# the context of a fiber. One that have the value :neverblock set to true.
|
31
|
+
# All neverblock IO classes check this value, setting it to false will force
|
32
|
+
# the execution in a blocking way.
|
33
|
+
def query(sql)
|
34
|
+
if Fiber.respond_to? :current and Fiber.current[:neverblock]
|
35
|
+
send_query sql
|
36
|
+
@fiber = Fiber.current
|
37
|
+
Fiber.yield
|
38
|
+
else
|
39
|
+
super(sql)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Attaches the connection socket to an event loop.
|
44
|
+
# Currently only supports EM, but Rev support will be
|
45
|
+
# completed soon.
|
46
|
+
def register_with_event_loop(loop)
|
47
|
+
if loop == :em
|
48
|
+
unless EM.respond_to?(:attach)
|
49
|
+
puts "invalide EM version, please download the modified gem from: (http://github.com/riham/eventmachine)"
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
if EM.reactor_running?
|
53
|
+
@em_connection = EM::attach(@io,EMConnectionHandler,self)
|
54
|
+
else
|
55
|
+
raise "REACTOR NOT RUNNING YA ZALAMA"
|
56
|
+
end
|
57
|
+
elsif loop.class.name == "REV::Loop"
|
58
|
+
loop.attach(RevConnectionHandler.new(socket))
|
59
|
+
else
|
60
|
+
raise "could not register with the event loop"
|
61
|
+
end
|
62
|
+
@loop = loop
|
63
|
+
end
|
64
|
+
|
65
|
+
# Unattaches the connection socket from the event loop
|
66
|
+
def unregister_from_event_loop
|
67
|
+
if @loop == :em
|
68
|
+
@em_connection.unattach(false)
|
69
|
+
else
|
70
|
+
raise NotImplementedError.new("unregister_from_event_loop not implemented for #{@loop}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# The callback, this is called whenever
|
75
|
+
# there is data available at the socket
|
76
|
+
def process_command
|
77
|
+
@fiber.resume get_result
|
78
|
+
end
|
79
|
+
|
80
|
+
end #FiberedPostgresConnection
|
81
|
+
|
82
|
+
# A connection handler for EM
|
83
|
+
# More to follow.
|
84
|
+
module EMConnectionHandler
|
85
|
+
def initialize connection
|
86
|
+
@connection = connection
|
87
|
+
end
|
88
|
+
def notify_readable
|
89
|
+
@connection.process_command
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end #DB
|
94
|
+
|
95
|
+
end #NeverBlock
|
96
|
+
|
97
|
+
NeverBlock::DB::FMysql = NeverBlock::DB::FiberedMysqlConnection
|
@@ -0,0 +1,109 @@
|
|
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: (http://github.com/riham/eventmachine)"
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
if EM.reactor_running?
|
52
|
+
@em_connection = 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
|
+
@loop = loop
|
62
|
+
end
|
63
|
+
|
64
|
+
# Unattaches the connection socket from the event loop
|
65
|
+
# As with register, EM is the only one supported for now
|
66
|
+
def unregister_from_event_loop
|
67
|
+
if @loop == :em
|
68
|
+
@em_connection.unattach(false)
|
69
|
+
else
|
70
|
+
raise NotImplementedError.new("unregister_from_event_loop not implemented for #{@loop}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# The callback, this is called whenever
|
75
|
+
# there is data available at the socket
|
76
|
+
def process_command
|
77
|
+
# make sure all commands are sent
|
78
|
+
# before attempting to read
|
79
|
+
return unless self.flush
|
80
|
+
self.consume_input
|
81
|
+
unless is_busy
|
82
|
+
res, data = 0, []
|
83
|
+
while res != nil
|
84
|
+
res = self.get_result
|
85
|
+
data << res unless res.nil?
|
86
|
+
end
|
87
|
+
#let the fiber continue its work
|
88
|
+
@fiber.resume(data.last)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end #FiberedPostgresConnection
|
93
|
+
|
94
|
+
# A connection handler for EM
|
95
|
+
# More to follow.
|
96
|
+
module EMConnectionHandler
|
97
|
+
def initialize connection
|
98
|
+
@connection = connection
|
99
|
+
end
|
100
|
+
def notify_readable
|
101
|
+
@connection.process_command
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end #DB
|
106
|
+
|
107
|
+
end #NeverBlock
|
108
|
+
|
109
|
+
NeverBlock::DB::FPGconn = NeverBlock::DB::FiberedPostgresConnection
|
@@ -0,0 +1,78 @@
|
|
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 exec method
|
18
|
+
# quries the pool to get a connection first
|
19
|
+
def exec(query)
|
20
|
+
@pool.hold do |conn|
|
21
|
+
conn.query(query)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :query :exec
|
26
|
+
# This method must be called for transactions to work correctly.
|
27
|
+
# One cannot just send "begin" as you never know which connection
|
28
|
+
# will be available next. This method ensures you get the same connection
|
29
|
+
# while in a transaction.
|
30
|
+
def begin_db_transaction
|
31
|
+
@pool.hold(true) do |conn|
|
32
|
+
conn.exec("begin")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# see =begin_db_transaction
|
37
|
+
def rollback_db_transaction
|
38
|
+
@pool.hold do |conn|
|
39
|
+
conn.exec("rollback")
|
40
|
+
@pool.release(Fiber.current,conn)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# see =begin_db_transaction
|
45
|
+
def commit_db_transaction
|
46
|
+
@pool.hold do |conn|
|
47
|
+
conn.exec("commit")
|
48
|
+
@pool.release(Fiber.current,conn)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#close all connections and remove them from the event loop
|
53
|
+
def close
|
54
|
+
@pool.all_connections do |conn|
|
55
|
+
conn.unregister_from_event_loop
|
56
|
+
conn.close
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Pass unknown methods to the connection
|
61
|
+
def method_missing(method, *args)
|
62
|
+
@pool.hold do |conn|
|
63
|
+
conn.send(method, *args)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Pass method queries to the connection
|
68
|
+
def respond_to?(method)
|
69
|
+
@pool.hold do |conn|
|
70
|
+
conn.respond_to?(method)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
NeverBlock::DB::PFMysql = NeverBlock::DB::PooledFiberedMysqlConnection
|
@@ -0,0 +1,81 @@
|
|
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
|
+
|
@@ -2,16 +2,21 @@
|
|
2
2
|
# Copyright:: Copyright (c) 2008 eSpace, Inc.
|
3
3
|
# License:: Distributes under the same terms as Ruby
|
4
4
|
|
5
|
-
|
5
|
+
# If this file is meant to be used out of neverblock, then uncomment
|
6
|
+
# the following line
|
7
|
+
#require 'fiber'
|
6
8
|
|
7
9
|
class Fiber
|
8
10
|
|
9
|
-
#Attribute Reference--Returns the value of a fiber-local variable, using
|
11
|
+
#Attribute Reference--Returns the value of a fiber-local variable, using
|
12
|
+
#either a symbol or a string name. If the specified variable does not exist,
|
13
|
+
#returns nil.
|
10
14
|
def [](key)
|
11
15
|
local_fiber_variables[key]
|
12
16
|
end
|
13
17
|
|
14
|
-
#Attribute Assignment--Sets or creates the value of a fiber-local variable,
|
18
|
+
#Attribute Assignment--Sets or creates the value of a fiber-local variable,
|
19
|
+
#using either a symbol or a string. See also Fiber#[].
|
15
20
|
def []=(key,value)
|
16
21
|
local_fiber_variables[key] = value
|
17
22
|
end
|
@@ -1,11 +1,17 @@
|
|
1
|
-
require 'never_block/frameworks/rails'
|
2
1
|
require 'activerecord'
|
3
2
|
|
4
3
|
# Patch ActiveRecord to store transaction depth information
|
5
4
|
# in fibers instead of threads. AR does not support nested
|
6
5
|
# transactions which makes the job easy.
|
6
|
+
# We also need to override the scoped methods to store
|
7
|
+
# the scope in the fiber context
|
7
8
|
class ActiveRecord::Base
|
8
9
|
|
10
|
+
def single_threaded_scoped_methods #:nodoc:
|
11
|
+
scoped_methods = (Fiber.current[:scoped_methods] ||= {})
|
12
|
+
scoped_methods[self] ||= []
|
13
|
+
end
|
14
|
+
|
9
15
|
def self.transaction(&block)
|
10
16
|
increment_open_transactions
|
11
17
|
begin
|
@@ -5,7 +5,7 @@
|
|
5
5
|
# Copyright:: Copyright (c) 2008 eSpace, Inc.
|
6
6
|
# License:: Distributes under the same terms as Ruby
|
7
7
|
|
8
|
-
require 'fiber'
|
8
|
+
#require 'fiber'
|
9
9
|
|
10
10
|
module NeverBlock
|
11
11
|
module Pool
|
@@ -64,7 +64,7 @@ module NeverBlock
|
|
64
64
|
# use it, otherwise, leave it to linger in a queue
|
65
65
|
def spawn(evented = true, &block)
|
66
66
|
if fiber = @fibers.shift
|
67
|
-
fiber[:
|
67
|
+
fiber[:neverblock] = evented
|
68
68
|
fiber.resume(block)
|
69
69
|
else
|
70
70
|
@queue << block
|
@@ -31,4 +31,16 @@ module Thin
|
|
31
31
|
|
32
32
|
end # Connection
|
33
33
|
|
34
|
+
module Backends
|
35
|
+
class Base
|
36
|
+
def config
|
37
|
+
# EM.epoll
|
38
|
+
# Set the maximum number of socket descriptors that the server may open.
|
39
|
+
# The process needs to have required privilege to set it higher the 1024 on
|
40
|
+
# some systems.
|
41
|
+
@maximum_connections = EventMachine.set_descriptor_table_size(@maximum_connections) unless Thin.win?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end # Backends
|
45
|
+
|
34
46
|
end # Thin
|
data/lib/never_block.rb
CHANGED
@@ -4,7 +4,70 @@
|
|
4
4
|
|
5
5
|
$:.unshift File.expand_path(File.dirname(__FILE__))
|
6
6
|
|
7
|
-
|
7
|
+
unless defined? Fiber
|
8
|
+
require 'thread'
|
9
|
+
require 'singleton'
|
10
|
+
class FiberError < StandardError; end
|
11
|
+
class Fiber
|
12
|
+
def initialize
|
13
|
+
raise ArgumentError, 'new Fiber requires a block' unless block_given?
|
14
|
+
|
15
|
+
@yield = Queue.new
|
16
|
+
@resume = Queue.new
|
17
|
+
|
18
|
+
@thread = Thread.new{ @yield.push [ *yield(*@resume.pop) ] }
|
19
|
+
@thread.abort_on_exception = true
|
20
|
+
@thread[:fiber] = self
|
21
|
+
end
|
22
|
+
attr_reader :thread
|
23
|
+
|
24
|
+
def resume *args
|
25
|
+
raise FiberError, 'dead fiber called' unless @thread.alive?
|
26
|
+
@resume.push(args)
|
27
|
+
result = @yield.pop
|
28
|
+
result.size > 1 ? result : result.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def yield *args
|
32
|
+
@yield.push(args)
|
33
|
+
result = @resume.pop
|
34
|
+
result.size > 1 ? result : result.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.yield *args
|
38
|
+
raise FiberError, "can't yield from root fiber" unless fiber = Thread.current[:fiber]
|
39
|
+
fiber.yield(*args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.current
|
43
|
+
Thread.current[:fiber] or raise FiberError, 'not inside a fiber'
|
44
|
+
end
|
45
|
+
|
46
|
+
def inspect
|
47
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)}>"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class RootFiber < Fiber
|
52
|
+
include Singleton
|
53
|
+
def initialize
|
54
|
+
end
|
55
|
+
|
56
|
+
def resume *args
|
57
|
+
raise FiberError, "can't resume root fiber"
|
58
|
+
end
|
59
|
+
|
60
|
+
def yield *args
|
61
|
+
raise FiberError, "can't yield from root fiber"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
#attach the root fiber to the main thread
|
66
|
+
Thread.main[:fiber] = RootFiber.instance
|
67
|
+
else
|
68
|
+
require 'fiber'
|
69
|
+
end
|
70
|
+
|
8
71
|
require 'never_block/extensions/fiber_extensions'
|
9
72
|
require 'never_block/pool/fiber_pool'
|
10
73
|
require 'never_block/pool/fibered_connection_pool'
|
@@ -12,4 +75,4 @@ require 'never_block/pool/fibered_connection_pool'
|
|
12
75
|
module NeverBlock
|
13
76
|
end
|
14
77
|
|
15
|
-
NB = NeverBlock
|
78
|
+
NB = NeverBlock
|
data/neverblock.gemspec
CHANGED
@@ -1,25 +1,35 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "neverblock"
|
3
|
-
s.version = "0.1.
|
3
|
+
s.version = "0.1.1"
|
4
4
|
s.date = "2008-08-13"
|
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"
|
8
8
|
s.description = "NeverBlock is a collection of classes and modules that help you write evented non-blocking applications in a seemingly blocking mannner."
|
9
9
|
s.has_rdoc = true
|
10
|
-
s.authors = ["Muhammad A. Ali", "Ahmed Sobhi"]
|
10
|
+
s.authors = ["Muhammad A. Ali", "Ahmed Sobhi", "Osama Brekaa"]
|
11
11
|
s.files = [
|
12
12
|
"neverblock.gemspec",
|
13
13
|
"README",
|
14
14
|
"lib/neverblock.rb",
|
15
15
|
"lib/never_block.rb",
|
16
|
+
"lib/neverblock-pg.rb",
|
17
|
+
"lib/neverblock-mysql.rb",
|
16
18
|
"lib/never_block/extensions/fiber_extensions.rb",
|
17
19
|
"lib/never_block/pool/fiber_pool.rb",
|
18
20
|
"lib/never_block/pool/fibered_connection_pool.rb",
|
19
21
|
"lib/never_block/frameworks/rails.rb",
|
20
22
|
"lib/never_block/frameworks/activerecord.rb",
|
21
|
-
"lib/never_block/servers/thin.rb"
|
23
|
+
"lib/never_block/servers/thin.rb",
|
24
|
+
"lib/never_block/db/fibered_postgres_connection.rb",
|
25
|
+
"lib/never_block/db/pooled_fibered_postgres_connection.rb",
|
26
|
+
"lib/never_block/db/fibered_mysql_connection.rb",
|
27
|
+
"lib/never_block/db/pooled_fibered_mysql_connection.rb",
|
28
|
+
"lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb",
|
29
|
+
"lib/active_record/connection_adapters/neverblock_mysql_adapter.rb"
|
30
|
+
]
|
22
31
|
s.rdoc_options = ["--main", "README"]
|
23
32
|
s.extra_rdoc_files = ["README"]
|
24
33
|
end
|
25
34
|
|
35
|
+
|
metadata
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oldmoe-neverblock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Muhammad A. Ali
|
8
8
|
- Ahmed Sobhi
|
9
|
+
- Osama Brekaa
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
@@ -27,12 +28,20 @@ files:
|
|
27
28
|
- README
|
28
29
|
- lib/neverblock.rb
|
29
30
|
- lib/never_block.rb
|
31
|
+
- lib/neverblock-pg.rb
|
32
|
+
- lib/neverblock-mysql.rb
|
30
33
|
- lib/never_block/extensions/fiber_extensions.rb
|
31
34
|
- lib/never_block/pool/fiber_pool.rb
|
32
35
|
- lib/never_block/pool/fibered_connection_pool.rb
|
33
36
|
- lib/never_block/frameworks/rails.rb
|
34
37
|
- lib/never_block/frameworks/activerecord.rb
|
35
38
|
- lib/never_block/servers/thin.rb
|
39
|
+
- lib/never_block/db/fibered_postgres_connection.rb
|
40
|
+
- lib/never_block/db/pooled_fibered_postgres_connection.rb
|
41
|
+
- lib/never_block/db/fibered_mysql_connection.rb
|
42
|
+
- lib/never_block/db/pooled_fibered_mysql_connection.rb
|
43
|
+
- lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb
|
44
|
+
- lib/active_record/connection_adapters/neverblock_mysql_adapter.rb
|
36
45
|
has_rdoc: true
|
37
46
|
homepage: http://github.com/oldmoe/neverblock
|
38
47
|
post_install_message:
|