jrmey-mysqlplus 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README +49 -0
- data/Rakefile +52 -0
- data/TODO_LIST +12 -0
- data/ext/error_const.h +539 -0
- data/ext/extconf.rb +133 -0
- data/ext/mysql.c +2828 -0
- data/lib/mysqlplus.rb +23 -0
- data/mysqlplus.gemspec +30 -0
- data/test/RUN_ALL_TESTS.RB +9 -0
- data/test/all_hashes_test.rb +37 -0
- data/test/async_query_with_block_test.rb +7 -0
- data/test/c_threaded_test.rb +36 -0
- data/test/connect_failure2_test.rb +22 -0
- data/test/connect_failure_test.rb +17 -0
- data/test/create_test_db.rb +22 -0
- data/test/evented_test.rb +31 -0
- data/test/gc_benchmark_test.rb +40 -0
- data/test/many_requests_test.rb +6 -0
- data/test/native_threaded_test.rb +36 -0
- data/test/out_of_sync_test.rb +34 -0
- data/test/query_with_result_false_test.rb +47 -0
- data/test/reconnected_test.rb +18 -0
- data/test/test_helper.rb +198 -0
- data/test/threaded_sequel_test.rb +24 -0
- metadata +89 -0
data/lib/mysqlplus.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/mysql' # load our version of mysql--note
|
2
|
+
# if someone does a require 'mysql' after a require 'mysqlplus' then their screen will be littered with warnings
|
3
|
+
# and the "old" mysql will override the "new" mysqlplus, so be careful.
|
4
|
+
|
5
|
+
#
|
6
|
+
# The mysqlplus library is a [slightly updated] fork of the Mysql class, with asynchronous capability added
|
7
|
+
# See http://www.kitebird.com/articles/ruby-mysql.html for details, as well as the test directory within the gem
|
8
|
+
#
|
9
|
+
class Mysql
|
10
|
+
|
11
|
+
def ruby_async_query(sql, timeout = nil) # known to deadlock TODO
|
12
|
+
send_query(sql)
|
13
|
+
select [ (@sockets ||= {})[socket] ||= IO.new(socket) ], nil, nil, nil
|
14
|
+
get_result
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
alias_method :async_query, :c_async_query
|
19
|
+
rescue NameError => e
|
20
|
+
raise LoadError.new("error loading mysqlplus--this may mean you ran a require 'mysql' before a require 'mysqplus', which must come first -- possibly also run gem uninstall mysql")
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/mysqlplus.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "jrmey-mysqlplus"
|
3
|
+
s.version = "0.1.3"
|
4
|
+
s.date = "2010-07-04"
|
5
|
+
s.summary = "Enhanced Ruby MySQL driver with Ruby 1.9 support"
|
6
|
+
s.email = "jeremysuriel@gmail.com"
|
7
|
+
s.homepage = "http://github.com/jrmey/mysqlplus"
|
8
|
+
s.description = "Enhanced Ruby MySQL driver"
|
9
|
+
s.has_rdoc = true
|
10
|
+
s.authors = ["Muhammad A. Ali", "Jeremy Suriel"]
|
11
|
+
s.platform = Gem::Platform::RUBY
|
12
|
+
s.files = %w[
|
13
|
+
README
|
14
|
+
Rakefile
|
15
|
+
TODO_LIST
|
16
|
+
ext/error_const.h
|
17
|
+
ext/extconf.rb
|
18
|
+
ext/mysql.c
|
19
|
+
lib/mysqlplus.rb
|
20
|
+
mysqlplus.gemspec
|
21
|
+
] + Dir.glob('test/*')
|
22
|
+
s.rdoc_options = ["--main", "README"]
|
23
|
+
s.extra_rdoc_files = ["README"]
|
24
|
+
s.extensions << "ext/extconf.rb"
|
25
|
+
|
26
|
+
if s.respond_to? :specification_version then
|
27
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
28
|
+
s.specification_version = 3
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# I suppose if all the tests don't blow up, that probably means pass
|
2
|
+
require 'mysqlplus'
|
3
|
+
for file in Dir.glob('*_test.rb') do
|
4
|
+
puts 'testing ' + file
|
5
|
+
# fork so we don't run out of connections to the mysql db, as few tests ever clean up their old processes
|
6
|
+
pid = Process.fork { load file }
|
7
|
+
Process.wait(pid)
|
8
|
+
end
|
9
|
+
puts 'successful'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'create_test_db'
|
2
|
+
|
3
|
+
use_the_all_hashes_method = true
|
4
|
+
|
5
|
+
$count = 5
|
6
|
+
|
7
|
+
$start = Time.now
|
8
|
+
|
9
|
+
$connections = []
|
10
|
+
$count.times do
|
11
|
+
$connections << Mysql.real_connect('localhost','root', '', 'local_test_db')
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
$threads = []
|
16
|
+
$count.times do |i|
|
17
|
+
$threads << Thread.new do
|
18
|
+
|
19
|
+
query = "select * from test_table"
|
20
|
+
puts "sending query on connection #{i}"
|
21
|
+
conn = $connections[i]
|
22
|
+
result = conn.async_query(query)
|
23
|
+
if use_the_all_hashes_method
|
24
|
+
saved = result.all_hashes
|
25
|
+
else
|
26
|
+
saved = []
|
27
|
+
result.each_hash {|h| saved << h }
|
28
|
+
end
|
29
|
+
result.free
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
puts 'waiting on threads'
|
35
|
+
$threads.each{|t| t.join }
|
36
|
+
|
37
|
+
puts Time.now - $start
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
ThreadedMysqlTest.new( 10, "Threaded, C, very small overhead" ) do |test|
|
4
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
5
|
+
test.per_query_overhead = 0.005
|
6
|
+
test.query_with = :c_async_query
|
7
|
+
test.run!
|
8
|
+
end
|
9
|
+
|
10
|
+
ThreadedMysqlTest.new( 10, "Threaded, C, small overhead" ) do |test|
|
11
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
12
|
+
test.per_query_overhead = 0.1
|
13
|
+
test.query_with = :c_async_query
|
14
|
+
test.run!
|
15
|
+
end
|
16
|
+
|
17
|
+
ThreadedMysqlTest.new( 10, "Threaded, C, medium overhead" ) do |test|
|
18
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
19
|
+
test.per_query_overhead = 1
|
20
|
+
test.query_with = :c_async_query
|
21
|
+
test.run!
|
22
|
+
end
|
23
|
+
|
24
|
+
ThreadedMysqlTest.new( 10, "Threaded, C, large overhead" ) do |test|
|
25
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
26
|
+
test.per_query_overhead = 3
|
27
|
+
test.query_with = :c_async_query
|
28
|
+
test.run!
|
29
|
+
end
|
30
|
+
|
31
|
+
ThreadedMysqlTest.new( 10, "Threaded, C, random overhead" ) do |test|
|
32
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
33
|
+
test.per_query_overhead = :random
|
34
|
+
test.query_with = :c_async_query
|
35
|
+
test.run!
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# If this script returns without the word pass
|
2
|
+
# you may have compiled mysqlplus using ruby and
|
3
|
+
# run it using a different version of ruby
|
4
|
+
|
5
|
+
if RUBY_VERSION >= "1.9.1"
|
6
|
+
require 'mysqlplus'
|
7
|
+
require 'socket'
|
8
|
+
require 'timeout'
|
9
|
+
TCPServer.new '0.0.0.0', 8002
|
10
|
+
Thread.new {
|
11
|
+
sleep 2
|
12
|
+
print "pass"
|
13
|
+
system("kill -9 #{Process.pid}")
|
14
|
+
}
|
15
|
+
Timeout::timeout(1) {
|
16
|
+
# uncomment this line to do the 'real' test
|
17
|
+
# which hangs otherwise (blows up if code is bad, otherwise hangs)
|
18
|
+
Mysql.real_connect '127.0.0.1', 'root', 'pass', 'db', 8002
|
19
|
+
}
|
20
|
+
raise 'should never get here'
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'mysqlplus'
|
2
|
+
begin
|
3
|
+
Mysql.real_connect('fakehost','root', '', 'local_leadgen_dev')
|
4
|
+
rescue Mysql::Error
|
5
|
+
end
|
6
|
+
begin
|
7
|
+
Mysql.real_connect('localhost','root', '', 'faketable')
|
8
|
+
rescue Mysql::Error
|
9
|
+
end
|
10
|
+
begin
|
11
|
+
Mysql.real_connect('localhost', 'root', 'pass', 'db', 3307)# bad port
|
12
|
+
rescue Mysql::Error
|
13
|
+
end
|
14
|
+
|
15
|
+
print "pass"
|
16
|
+
|
17
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# To run first execute:
|
2
|
+
=begin
|
3
|
+
create database local_test_db;
|
4
|
+
use local_test_db;
|
5
|
+
CREATE TABLE test_table (
|
6
|
+
c1 INT,
|
7
|
+
c2 VARCHAR(20)
|
8
|
+
);
|
9
|
+
=end
|
10
|
+
# This script shows the effect of using .all_hashes instead of looping on each hash
|
11
|
+
# run it by substiting in a 'long' [many row] query for the query variable and toggling use_all_hashes here at the top
|
12
|
+
# note that we load all the rows first, then run .all_hashes on the result [to see more easily the effect of all hashes]
|
13
|
+
# on my machine and a 200_000 row table, it took 3.38s versus 3.65s for the old .each_hash way [note also that .each_hash is
|
14
|
+
# almost as fast, now, as .all_hashes--they've both been optimized]
|
15
|
+
require 'mysqlplus'
|
16
|
+
|
17
|
+
puts 'initing db'
|
18
|
+
# init the DB
|
19
|
+
conn = Mysql.real_connect('localhost', 'root', '', 'local_test_db')
|
20
|
+
conn.query("delete from test_table")
|
21
|
+
200_000.times {conn.query(" insert into test_table (c1, c2) values (3, 'ABCDEFG')")}
|
22
|
+
puts 'connection pool ready'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
EventedMysqlTest.new( 10, "Evented, very small overhead" ) do |test|
|
4
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
5
|
+
test.per_query_overhead = 0.005
|
6
|
+
test.run!
|
7
|
+
end
|
8
|
+
|
9
|
+
EventedMysqlTest.new( 10, "Evented, small overhead" ) do |test|
|
10
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
11
|
+
test.per_query_overhead = 0.1
|
12
|
+
test.run!
|
13
|
+
end
|
14
|
+
|
15
|
+
EventedMysqlTest.new( 10, "Evented, medium overhead" ) do |test|
|
16
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
17
|
+
test.per_query_overhead = 1
|
18
|
+
test.run!
|
19
|
+
end
|
20
|
+
|
21
|
+
EventedMysqlTest.new( 10, "Evented, large overhead" ) do |test|
|
22
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
23
|
+
test.per_query_overhead = 3
|
24
|
+
test.run!
|
25
|
+
end
|
26
|
+
|
27
|
+
EventedMysqlTest.new( 10, "Evented, random overhead" ) do |test|
|
28
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
29
|
+
test.per_query_overhead = :random
|
30
|
+
test.run!
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'mysqlplus'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
with_gc = Mysql.real_connect('localhost','root','','mysql')
|
5
|
+
without_gc = Mysql.real_connect('localhost','root','','mysql')
|
6
|
+
without_gc.disable_gc = true
|
7
|
+
|
8
|
+
$gc_stats = []
|
9
|
+
|
10
|
+
def countable_gc?
|
11
|
+
GC.respond_to? :count
|
12
|
+
end
|
13
|
+
|
14
|
+
def gc_counts( label, scope )
|
15
|
+
$gc_stats << "Objects #{scope} ( #{label} ) #{GC.count}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_gc_counts( label )
|
19
|
+
gc_counts( label, 'before' ) if countable_gc?
|
20
|
+
yield
|
21
|
+
gc_counts( label, 'after' ) if countable_gc?
|
22
|
+
end
|
23
|
+
|
24
|
+
n = 1000
|
25
|
+
|
26
|
+
Benchmark.bmbm do |x|
|
27
|
+
x.report( 'With GC' ) do
|
28
|
+
with_gc_counts( 'With GC' ) do
|
29
|
+
n.times{ with_gc.c_async_query( 'SELECT * FROM user' ) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
GC.start
|
33
|
+
x.report( 'Without GC' ) do
|
34
|
+
with_gc_counts( 'Without GC' ) do
|
35
|
+
n.times{ without_gc.c_async_query( 'SELECT * FROM user' ) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
puts $gc_stats.join( ' | ' )
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
ThreadedMysqlTest.new( 10, "Threaded, native Ruby, very small overhead" ) do |test|
|
4
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
5
|
+
test.per_query_overhead = 0.005
|
6
|
+
test.query_with = :async_query
|
7
|
+
test.run!
|
8
|
+
end
|
9
|
+
|
10
|
+
ThreadedMysqlTest.new( 10, "Threaded, native Ruby, small overhead" ) do |test|
|
11
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
12
|
+
test.per_query_overhead = 0.1
|
13
|
+
test.query_with = :async_query
|
14
|
+
test.run!
|
15
|
+
end
|
16
|
+
|
17
|
+
ThreadedMysqlTest.new( 10, "Threaded, native Ruby, medium overhead" ) do |test|
|
18
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
19
|
+
test.per_query_overhead = 1
|
20
|
+
test.query_with = :async_query
|
21
|
+
test.run!
|
22
|
+
end
|
23
|
+
|
24
|
+
ThreadedMysqlTest.new( 10, "Threaded, native Ruby, large overhead" ) do |test|
|
25
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
26
|
+
test.per_query_overhead = 3
|
27
|
+
test.query_with = :async_query
|
28
|
+
test.run!
|
29
|
+
end
|
30
|
+
|
31
|
+
ThreadedMysqlTest.new( 10, "Threaded, native Ruby, random overhead" ) do |test|
|
32
|
+
test.setup{ Mysql.real_connect('localhost','root') }
|
33
|
+
test.per_query_overhead = :random
|
34
|
+
test.query_with = :async_query
|
35
|
+
test.run!
|
36
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
m = Mysql.real_connect('localhost','root')
|
4
|
+
m.reconnect = true
|
5
|
+
$count = 0
|
6
|
+
class << m
|
7
|
+
def safe_query( query )
|
8
|
+
begin
|
9
|
+
send_query( query )
|
10
|
+
rescue => e
|
11
|
+
$count += 1
|
12
|
+
puts e.message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
m.safe_query( 'select sleep(1)' )
|
19
|
+
m.safe_query( 'select sleep(1)' )#raises
|
20
|
+
m.simulate_disconnect #fires mysql_library_end
|
21
|
+
m.safe_query( 'select sleep(1)' )
|
22
|
+
m.safe_query( 'select sleep(1)' )#raises
|
23
|
+
m.close
|
24
|
+
m.connect('localhost','root')
|
25
|
+
m.safe_query( 'select sleep(1)' )
|
26
|
+
m.safe_query( 'select sleep(1)' )#raises
|
27
|
+
m.simulate_disconnect
|
28
|
+
raise unless $count == 3
|
29
|
+
m.safe_query( 'BEGIN' )
|
30
|
+
m.safe_query( 'select sleep(1)' ) # raises
|
31
|
+
m.get_result()
|
32
|
+
m.safe_query( 'COMMIT' )
|
33
|
+
m.get_result
|
34
|
+
raise unless $count == 4
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# This is an example of using Mysql::ResultSet#use_result [see docs for what that does]
|
2
|
+
# this function is useful for those who have large query results and want to be able to parse them
|
3
|
+
# as they come in, instead of having to wait for the query to finish before doing parsing
|
4
|
+
# for me, running this on a query with 200_000 lines decreases total time to create an array of results
|
5
|
+
# from .82s to .62s
|
6
|
+
# you can experiment with it by changing the query here to be a long one, and toggling the do_the_use_query_optimization variable
|
7
|
+
# this also has the interesting property of 'freeing' Ruby to do thread changes mid-query.
|
8
|
+
require 'create_test_db'
|
9
|
+
|
10
|
+
do_the_use_query_optimization = true
|
11
|
+
|
12
|
+
$count = 5
|
13
|
+
|
14
|
+
$start = Time.now
|
15
|
+
|
16
|
+
$connections = []
|
17
|
+
$count.times do
|
18
|
+
$connections << Mysql.real_connect('localhost','root', '', 'local_test_db')
|
19
|
+
end
|
20
|
+
|
21
|
+
puts 'connection pool ready'
|
22
|
+
|
23
|
+
$threads = []
|
24
|
+
$count.times do |i|
|
25
|
+
$threads << Thread.new do
|
26
|
+
|
27
|
+
puts "sending query on connection #{i}"
|
28
|
+
conn = $connections[i]
|
29
|
+
saved = []
|
30
|
+
query = "select * from test_table"
|
31
|
+
if do_the_use_query_optimization
|
32
|
+
conn.query_with_result=false
|
33
|
+
result = conn.async_query(query)
|
34
|
+
res = result.use_result
|
35
|
+
res.each_hash { |h| saved << h }
|
36
|
+
res.free
|
37
|
+
else
|
38
|
+
conn.async_query(query).each_hash {|h| saved << h }
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
puts 'waiting on threads'
|
45
|
+
$threads.each{|t| t.join }
|
46
|
+
|
47
|
+
puts Time.now - $start
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
$m = Mysql.real_connect('localhost','root')
|
4
|
+
#$m.reconnect = true
|
5
|
+
|
6
|
+
def assert_reconnected
|
7
|
+
puts $m.reconnected?().inspect
|
8
|
+
sleep 1
|
9
|
+
yield
|
10
|
+
puts $m.reconnected?().inspect
|
11
|
+
end
|
12
|
+
|
13
|
+
assert_reconnected do
|
14
|
+
$m.simulate_disconnect
|
15
|
+
end
|
16
|
+
assert_reconnected do
|
17
|
+
$m.close
|
18
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'mysqlplus'
|
2
|
+
|
3
|
+
class MysqlTest
|
4
|
+
|
5
|
+
class NotImplemented < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_accessor :queries,
|
9
|
+
:context,
|
10
|
+
:connections,
|
11
|
+
:connection_signature,
|
12
|
+
:start,
|
13
|
+
:done,
|
14
|
+
:query_with,
|
15
|
+
:per_query_overhead,
|
16
|
+
:timeout
|
17
|
+
|
18
|
+
def initialize( queries, context = '' )
|
19
|
+
@queries = queries
|
20
|
+
@context = context
|
21
|
+
@done = []
|
22
|
+
@query_with = :async_query
|
23
|
+
@per_query_overhead = 3
|
24
|
+
@timeout = 20
|
25
|
+
yield self if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
def setup( &block )
|
29
|
+
@start = Time.now
|
30
|
+
@connection_signature = block
|
31
|
+
end
|
32
|
+
|
33
|
+
def run!
|
34
|
+
c_or_native_ruby_async_query do
|
35
|
+
present_context if context?
|
36
|
+
prepare
|
37
|
+
yield
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def per_query_overhead=( overhead )
|
42
|
+
@per_query_overhead = ( overhead == :random ) ? rand() : overhead
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def prepare
|
48
|
+
raise NotImplemented
|
49
|
+
end
|
50
|
+
|
51
|
+
def teardown
|
52
|
+
raise NotImplemented
|
53
|
+
end
|
54
|
+
|
55
|
+
def log( message, prefix = '' )
|
56
|
+
puts "[#{timestamp}] #{prefix} #{message}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def with_logging( message )
|
60
|
+
log( message, 'Start' )
|
61
|
+
yield
|
62
|
+
log( message, 'End' )
|
63
|
+
end
|
64
|
+
|
65
|
+
def timestamp
|
66
|
+
Time.now - @start
|
67
|
+
end
|
68
|
+
|
69
|
+
def context?
|
70
|
+
@context != ''
|
71
|
+
end
|
72
|
+
|
73
|
+
def present_context
|
74
|
+
log "#############################################"
|
75
|
+
log "# #{@context}"
|
76
|
+
log "#############################################"
|
77
|
+
end
|
78
|
+
|
79
|
+
def c_or_native_ruby_async_query
|
80
|
+
if @query_with == :c_async_query
|
81
|
+
log "** using C based async_query"
|
82
|
+
else
|
83
|
+
log "** using native Ruby async_query"
|
84
|
+
end
|
85
|
+
yield
|
86
|
+
end
|
87
|
+
|
88
|
+
def dispatch_query( connection, sql, timeout = nil )
|
89
|
+
connection.send( @query_with, sql, timeout )
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
class EventedMysqlTest < MysqlTest
|
95
|
+
|
96
|
+
attr_accessor :sockets
|
97
|
+
|
98
|
+
def initialize( queries, context = '' )
|
99
|
+
@sockets = []
|
100
|
+
@connections = {}
|
101
|
+
super( queries, context )
|
102
|
+
end
|
103
|
+
|
104
|
+
def setup( &block )
|
105
|
+
super( &block )
|
106
|
+
with_logging 'Setup connection pool' do
|
107
|
+
@queries.times do
|
108
|
+
connection = @connection_signature.call
|
109
|
+
@connections[ IO.new(connection.socket) ] = connection
|
110
|
+
@sockets = @connections.keys
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def run!
|
116
|
+
super do
|
117
|
+
catch :END_EVENT_LOOP do
|
118
|
+
loop do
|
119
|
+
result = select( @sockets,nil,nil,nil )
|
120
|
+
if result
|
121
|
+
result.first.each do |conn|
|
122
|
+
@connections[conn].get_result.each{|res| log( "Result for socket #{conn.fileno} : #{res}" ) }
|
123
|
+
@done << nil
|
124
|
+
if done?
|
125
|
+
teardown
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
protected
|
135
|
+
|
136
|
+
def prepare
|
137
|
+
@connections.each_value do |conn|
|
138
|
+
conn.send_query( "select sleep(#{@per_query_overhead})" )
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def teardown
|
143
|
+
log "done"
|
144
|
+
throw :END_EVENT_LOOP
|
145
|
+
end
|
146
|
+
|
147
|
+
def done?
|
148
|
+
@done.size == @queries
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
class ThreadedMysqlTest < MysqlTest
|
154
|
+
|
155
|
+
attr_accessor :threads
|
156
|
+
|
157
|
+
def initialize( queries, context = '' )
|
158
|
+
@connections = []
|
159
|
+
@threads = []
|
160
|
+
super( queries, context )
|
161
|
+
end
|
162
|
+
|
163
|
+
def setup( &block )
|
164
|
+
super( &block )
|
165
|
+
with_logging "Setup connection pool" do
|
166
|
+
@queries.times do
|
167
|
+
@connections << @connection_signature.call
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def run!
|
173
|
+
super do
|
174
|
+
with_logging "waiting on threads" do
|
175
|
+
@threads.each{|t| t.join }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
protected
|
181
|
+
|
182
|
+
def prepare
|
183
|
+
with_logging "prepare" do
|
184
|
+
@queries.times do |conn|
|
185
|
+
@threads << Thread.new do
|
186
|
+
|
187
|
+
log "sending query on connection #{conn}"
|
188
|
+
|
189
|
+
dispatch_query( @connections[conn], "select sleep(#{@per_query_overhead})", @timeout ).each do |result|
|
190
|
+
log "connection #{conn} done"
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'mysqlplus'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'sequel'
|
4
|
+
|
5
|
+
class Mysql
|
6
|
+
unless method_defined? :sync_query
|
7
|
+
alias :sync_query :query
|
8
|
+
alias :query :async_query
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
DB = Sequel.connect('mysql://root@localhost', :max_connections => 20)
|
13
|
+
|
14
|
+
start = Time.now
|
15
|
+
|
16
|
+
(0..10).map do
|
17
|
+
Thread.new do
|
18
|
+
|
19
|
+
p DB['select sleep(2)'].all
|
20
|
+
|
21
|
+
end
|
22
|
+
end.map{|t| t.join }
|
23
|
+
|
24
|
+
p (Time.now - start)
|