jrmey-mysqlplus 0.1.3
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 +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)
|