em-mysqlplus 0.1.3 → 0.1.4
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.md +13 -12
- data/VERSION +1 -1
- data/em-mysqlplus.gemspec +59 -0
- data/lib/active_record/connection_adapters/em_mysqlplus_adapter.rb +50 -0
- data/lib/active_record/patches.rb +102 -0
- data/lib/em-activerecord.rb +4 -0
- data/spec/activerecord_spec.rb +51 -0
- data/spec/database.yml +5 -0
- metadata +10 -3
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
EventMachine wrapper for the C-based MySQL / MySQLPlus Ruby gems, which provides
|
4
4
|
callbacks, errbacks and all other niceties of EventMachine while keeping the API
|
5
|
-
of the original C-based MySQL gem.
|
5
|
+
of the original C-based MySQL gem.
|
6
6
|
|
7
7
|
Features:
|
8
8
|
|
@@ -25,19 +25,20 @@ Features:
|
|
25
25
|
|
26
26
|
## Query queueing:
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
EventMachine.run {
|
29
|
+
conn = EventMachine::MySQL.new(:host => 'localhost')
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
results = []
|
32
|
+
conn.query("select 1") {|res| results.push res.fetch_row.first.to_i}
|
33
|
+
conn.query("select 2") {|res| results.push res.fetch_row.first.to_i}
|
34
|
+
conn.query("select 3") {|res| results.push res.fetch_row.first.to_i}
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
EventMachine.add_timer(0.05) {
|
37
|
+
p results # => [1,2,3]
|
38
|
+
}
|
39
|
+
}
|
40
40
|
|
41
41
|
# Credits
|
42
42
|
|
43
|
-
Original Async MySQL driver for Ruby/EventMachine - (c) 2008 Aman Gupta (tmm1)
|
43
|
+
* Original Async MySQL driver for Ruby/EventMachine - (c) 2008 Aman Gupta (tmm1)
|
44
|
+
* ActiveRecord fiber patches - Mike Perham (mperham)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.4
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{em-mysqlplus}
|
8
|
+
s.version = "0.1.4"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ilya Grigorik", "Aman Gupta"]
|
12
|
+
s.date = %q{2010-07-06}
|
13
|
+
s.description = %q{Async MySQL driver for Ruby/Eventmachine}
|
14
|
+
s.email = %q{ilya@igvita.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"README.md",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION",
|
22
|
+
"em-mysqlplus.gemspec",
|
23
|
+
"lib/active_record/connection_adapters/em_mysqlplus_adapter.rb",
|
24
|
+
"lib/active_record/patches.rb",
|
25
|
+
"lib/em-activerecord.rb",
|
26
|
+
"lib/em-mysqlplus.rb",
|
27
|
+
"lib/em-mysqlplus/connection.rb",
|
28
|
+
"lib/em-mysqlplus/mysql.rb",
|
29
|
+
"spec/activerecord_spec.rb",
|
30
|
+
"spec/database.yml",
|
31
|
+
"spec/helper.rb",
|
32
|
+
"spec/mysql_spec.rb"
|
33
|
+
]
|
34
|
+
s.homepage = %q{http://github.com/igrigorik/em-mysql}
|
35
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubyforge_project = %q{em-mysqlplus}
|
38
|
+
s.rubygems_version = %q{1.3.6}
|
39
|
+
s.summary = %q{Async MySQL driver for Ruby/Eventmachine}
|
40
|
+
s.test_files = [
|
41
|
+
"spec/activerecord_spec.rb",
|
42
|
+
"spec/helper.rb",
|
43
|
+
"spec/mysql_spec.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.9"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
|
54
|
+
end
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'em-synchrony'
|
2
|
+
require 'em-synchrony/em-mysqlplus'
|
3
|
+
|
4
|
+
require 'active_record/connection_adapters/mysql_adapter'
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module ConnectionAdapters
|
8
|
+
|
9
|
+
class EmMysqlAdapter < MysqlAdapter
|
10
|
+
def initialize(connection, logger, host_parameters, connection_parameters, config)
|
11
|
+
@hostname = host_parameters[0]
|
12
|
+
@port = host_parameters[1]
|
13
|
+
@connect_parameters, @config = connection_parameters, config
|
14
|
+
super(connection, logger, nil, config)
|
15
|
+
end
|
16
|
+
|
17
|
+
def connect
|
18
|
+
@connection = EventMachine::MySQL.new({
|
19
|
+
:host => @hostname,
|
20
|
+
:port => @port,
|
21
|
+
:database => @config[:database],
|
22
|
+
:password => @config[:password],
|
23
|
+
:socket => @config[:socket]
|
24
|
+
})
|
25
|
+
|
26
|
+
configure_connection
|
27
|
+
@connection
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Base
|
34
|
+
def self.em_mysqlplus_connection(config) # :nodoc:
|
35
|
+
config = config.symbolize_keys
|
36
|
+
host = config[:host]
|
37
|
+
port = config[:port]
|
38
|
+
username = config[:username].to_s if config[:username]
|
39
|
+
password = config[:password].to_s if config[:password]
|
40
|
+
|
41
|
+
if config.has_key?(:database)
|
42
|
+
database = config[:database]
|
43
|
+
else
|
44
|
+
raise ArgumentError, "No database specified. Missing argument: database."
|
45
|
+
end
|
46
|
+
|
47
|
+
ConnectionAdapters::EmMysqlAdapter.new(nil, logger, [host, port], [database, username, password], config)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
|
4
|
+
def self.fiber_pools
|
5
|
+
@fiber_pools ||= []
|
6
|
+
end
|
7
|
+
def self.register_fiber_pool(fp)
|
8
|
+
fiber_pools << fp
|
9
|
+
end
|
10
|
+
|
11
|
+
class FiberedMonitor
|
12
|
+
class Queue
|
13
|
+
def initialize
|
14
|
+
@queue = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def wait(timeout)
|
18
|
+
t = timeout || 5
|
19
|
+
fiber = Fiber.current
|
20
|
+
x = EM::Timer.new(t) do
|
21
|
+
@queue.delete(fiber)
|
22
|
+
fiber.resume(false)
|
23
|
+
end
|
24
|
+
@queue << fiber
|
25
|
+
returning Fiber.yield do
|
26
|
+
x.cancel
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def signal
|
31
|
+
fiber = @queue.pop
|
32
|
+
fiber.resume(true) if fiber
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def synchronize
|
37
|
+
yield
|
38
|
+
end
|
39
|
+
|
40
|
+
def new_cond
|
41
|
+
Queue.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# ActiveRecord's connection pool is based on threads. Since we are working
|
46
|
+
# with EM and a single thread, multiple fiber design, we need to provide
|
47
|
+
# our own connection pool that keys off of Fiber.current so that different
|
48
|
+
# fibers running in the same thread don't try to use the same connection.
|
49
|
+
class ConnectionPool
|
50
|
+
def initialize(spec)
|
51
|
+
@spec = spec
|
52
|
+
|
53
|
+
# The cache of reserved connections mapped to threads
|
54
|
+
@reserved_connections = {}
|
55
|
+
|
56
|
+
# The mutex used to synchronize pool access
|
57
|
+
@connection_mutex = FiberedMonitor.new
|
58
|
+
@queue = @connection_mutex.new_cond
|
59
|
+
|
60
|
+
# default 5 second timeout unless on ruby 1.9
|
61
|
+
@timeout = spec.config[:wait_timeout] || 5
|
62
|
+
|
63
|
+
# default max pool size to 5
|
64
|
+
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
|
65
|
+
|
66
|
+
@connections = []
|
67
|
+
@checked_out = []
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def current_connection_id #:nodoc:
|
73
|
+
Fiber.current.object_id
|
74
|
+
end
|
75
|
+
|
76
|
+
# Remove stale fibers from the cache.
|
77
|
+
def remove_stale_cached_threads!(cache, &block)
|
78
|
+
keys = Set.new(cache.keys)
|
79
|
+
|
80
|
+
ActiveRecord::ConnectionAdapters.fiber_pools.each do |pool|
|
81
|
+
pool.busy_fibers.each_pair do |object_id, fiber|
|
82
|
+
keys.delete(object_id)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
keys.each do |key|
|
87
|
+
next unless cache.has_key?(key)
|
88
|
+
block.call(key, cache[key])
|
89
|
+
cache.delete(key)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def checkout_and_verify(c)
|
94
|
+
@checked_out << c
|
95
|
+
c.run_callbacks :checkout
|
96
|
+
c.verify!
|
97
|
+
c
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'logger'
|
3
|
+
require 'yaml'
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
require 'active_record'
|
7
|
+
require 'lib/em-activerecord'
|
8
|
+
|
9
|
+
RAILS_ENV='test'
|
10
|
+
|
11
|
+
ActiveRecord::Base.configurations = YAML::load(ERB.new(File.read(File.join(File.dirname(__FILE__), 'database.yml'))).result)
|
12
|
+
ActiveRecord::Base.default_timezone = :utc
|
13
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
14
|
+
ActiveRecord::Base.logger.level = Logger::INFO
|
15
|
+
ActiveRecord::Base.pluralize_table_names = true
|
16
|
+
ActiveRecord::Base.time_zone_aware_attributes = true
|
17
|
+
Time.zone = 'UTC'
|
18
|
+
|
19
|
+
class Widget < ActiveRecord::Base; end;
|
20
|
+
|
21
|
+
describe "ActiveRecord Driver for EM-MySQLPlus" do
|
22
|
+
it "should establish AR connection" do
|
23
|
+
EventMachine.run {
|
24
|
+
Fiber.new {
|
25
|
+
ActiveRecord::Base.establish_connection
|
26
|
+
result = Widget.find_by_sql("select sleep(1)")
|
27
|
+
result.size.should == 1
|
28
|
+
|
29
|
+
EventMachine.stop
|
30
|
+
}.resume
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should use fiber aware ConnectionPool" do
|
35
|
+
EventMachine.run {
|
36
|
+
results = []
|
37
|
+
|
38
|
+
3.times do |n|
|
39
|
+
Fiber.new {
|
40
|
+
ActiveRecord::Base.establish_connection
|
41
|
+
results.push Widget.find_by_sql("select sleep(1)")
|
42
|
+
}.resume
|
43
|
+
end
|
44
|
+
|
45
|
+
EM.add_timer(1.5) {
|
46
|
+
results.size.should == 3
|
47
|
+
EventMachine.stop
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
data/spec/database.yml
ADDED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 4
|
9
|
+
version: 0.1.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ilya Grigorik
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-07-06 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -44,9 +44,15 @@ files:
|
|
44
44
|
- README.md
|
45
45
|
- Rakefile
|
46
46
|
- VERSION
|
47
|
+
- em-mysqlplus.gemspec
|
48
|
+
- lib/active_record/connection_adapters/em_mysqlplus_adapter.rb
|
49
|
+
- lib/active_record/patches.rb
|
50
|
+
- lib/em-activerecord.rb
|
47
51
|
- lib/em-mysqlplus.rb
|
48
52
|
- lib/em-mysqlplus/connection.rb
|
49
53
|
- lib/em-mysqlplus/mysql.rb
|
54
|
+
- spec/activerecord_spec.rb
|
55
|
+
- spec/database.yml
|
50
56
|
- spec/helper.rb
|
51
57
|
- spec/mysql_spec.rb
|
52
58
|
has_rdoc: true
|
@@ -80,5 +86,6 @@ signing_key:
|
|
80
86
|
specification_version: 3
|
81
87
|
summary: Async MySQL driver for Ruby/Eventmachine
|
82
88
|
test_files:
|
89
|
+
- spec/activerecord_spec.rb
|
83
90
|
- spec/helper.rb
|
84
91
|
- spec/mysql_spec.rb
|