SlideShare a Scribd company logo
1 of 46
Beyond 'gem install MySQL’ in Ruby alternative drivers & architecture Ilya Grigorik @igrigorik
and dozens of others… The slides… Twitter My blog
Internals of Ruby VM Ruby MySQL Drivers Looking into the future… Rails Async
vs. vs.
Global Interpreter Lock is a mutual exclusion lock held by a programming language interpreter thread to avoid sharing code that is not thread-safe with other threads.  There is always one GIL for one interpreter process. Concurrency is a myth in Ruby (with a few caveats, of course) http://bit.ly/ruby-gil
N-M thread pool in Ruby 1.9… Better but still the same problem! Concurrency is a myth in Ruby still no concurrency in Ruby 1.9 http://bit.ly/ruby-gil
RTM, your mileage will vary. Concurrency is a myth in Ruby still no concurrency in Ruby 1.9 http://bit.ly/ruby-gil
Blocks entire Ruby VM Not as bad, but avoid it still.. 1. Avoid locking interpreter threads at all costs still no concurrency in Ruby 1.9
require 'rubygems’ require 'sequel'DB = Sequel.connect('mysql://root@localhost/test')while trueDB['select sleep(1)'].select.firstend Blocking 1s call! ltrace –ttTg -xmysql_real_query -p [pid of script above] mysql.gem under the hood 22:10:00.218438 mysql_real_query(0x02740000, "select sleep(1)", 15) = 0 <1.001100>22:10:01.241679 mysql_real_query(0x02740000, "select sleep(1)", 15) = 0 <1.000812> http://bit.ly/c3Pt3f
Blocking calls to mysql_real_query mysql_real_query requires an OS thread Blocking on mysql_real_query blocks the Ruby VM Aka, “select sleep(1)” blocks the entire Ruby runtime for 1s (ouch) gem install mysqlwhat you didn’t know…
gem install mysqlplus An enhanced mysql driver with an ‘async’ interface and threaded access support
select ([] …) classMysql defruby_async_query(sql, timeout =nil) send_query(sql) select [(@sockets ||= {})[socket] ||=IO.new(socket)],nil,nil,nil get_result end begin alias_method:async_query, :c_async_query rescueNameError => e raiseLoadError.new("error loading mysqlplus") end end mysqlplus.gem under the hood gem install mysqlplus
spinning in select ,[object Object]
Currently executing thread is put into WAIT_SELECT
 Allows multiple threads to execute queries
Yay?mysqlplus.gem + ruby_async_query
static VALUE async_query(intargc, VALUE* argv, VALUE obj) {   ... send_query( obj, sql );   ... schedule_query( obj, timeout);   ... returnget_result(obj);  } staticvoidschedule_query(VALUEobj, VALUE timeout) {   ... structtimevaltv = { tv_sec: timeout, tv_usec: 0 }; for(;;){ FD_ZERO(&read); FD_SET(m->net.fd, &read);     ret = rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv);     ... if (m->status == MYSQL_STATUS_READY) break;  } } send query and block Ruby: select() = C: rb_thread_select() mysqlplus.gem + C API
Ruby: ruby select() alias :query, :async_query Native: rb_thread_select ruby_async_queryvs.c_async_query use it, if you can.
Non VM-blocking database calls (win) But there is no pipelining! You can’t re-use same connection. You will need a pool of DB connections You will need to manage the database pool You need to watch out for other blocking calls / gems! Requires threaded execution / framework for parallelism mysqlplusgotchaswhat you need to know…
max concurrency = 5 require'rubygems' require'mysqlplus' require'db_pool' pool =DatabasePool.new(:size => 5) do puts "Connecting to database…" db =Mysql.init db.options(Mysql::SET_CHARSET_NAME, "UTF8") db.real_connect(hostname, username, password,                   database, nil, sock) db.reconnect=true db end pool.query("select sleep 1") 5 shared connections Managing your own DB Pool is easy enough…
MVM   (innovation bait) JVM   (RTM) Threading Multi-Process ,[object Object]
 Green threads…
 Threaded servers (Mongrel)
 Coordination + Locks
 Single core, no matter what
 Multiple cores!
 Avoid blocking extensions
 Green threads…
 Multi-proc + Threads?Concurrency in Ruby 50,000-foot view
Rails 2.2 RC1: i18n, thread safety… Chief inclusions are an internationalization framework, thread safety (including a connection pool for Active Record)… http://bit.ly/br8Nkh  (Oct 24, 2008)
require"active_record” ActiveRecord::Base.establish_connection(   :adapter => "mysql",   :username => "root",   :database => "database",   :pool => 5 ) threads = [] 10.times do |n|   threads <<Thread.new { ActiveRecord::Base.connection_pool.with_connectiondo |conn| res =conn.execute("select sleep(1)") end } end threads.each { |t| t.join } 5 shared connections # time ruby activerecord-pool.rb # # real    0m10.663s # user    0m0.405s # sys     0m0.201s Scaling ActiveRecord with mysqlplus http://bit.ly/bDtFiy
require"active_record" require "mysqlplus" class Mysql; alias :query :async_query; end ActiveRecord::Base.establish_connection(   :adapter => "mysql",   :username => "root",   :database => "database",   :pool => 5 ) threads = [] 10.times do |n|   threads <<Thread.new { ActiveRecord::Base.connection_pool.with_connectiondo |conn| res =conn.execute("select sleep(1)") end } end threads.each { |t| t.join } Parallel execution! # time ruby activerecord-pool.rb # # real    0m2.463s # user    0m0.405s # sys     0m0.201s Scaling ActiveRecord with mysqlplus http://bit.ly/bDtFiy
config.threadsafe! require'mysqlplus’ classMysql; alias :query :async_query; end In your environtments/production.rb Concurrency in Rails? Not so fast… :-( Scaling ActiveRecord with mysqlplus http://bit.ly/bDtFiy
Global dispatcher lock  Random locks in your web-server (like Mongrel) Gratuitous locking in libraries, plugins, etc.  In reality, you still need process parallelism in Rails. But, we’re moving in the right direction.  JRuby? Rails + MySQL = Concurrency?almost, but not quite
gem install activerecord-jdbcmysql-adapter development:    adapter: jdbcmysql    encoding: utf8    database: myapp_development    username: root    password: my_password Subject to all the same Rails restrictions (locks, etc) JRuby: RTM, your mileage will vary all depends on the container
GlasshFish will reuse your database connections via its internal database connection pooling mechanism. http://wiki.glassfish.java.net/Wiki.jsp?page=JRuby JRuby: RTM, your mileage will vary all depends on the container
Non-blocking IO in Ruby: EventMachine for real heavy-lifting, you have to go async…
p "Starting"EM.run dop"Running in EM reactor"endp ”won’t get here" whiletruedo        timersnetwork_ioother_io end EventMachine Reactor concurrency without threads
p "Starting"EM.rundop"Running in EM reactor"endp”won’t get here" whiletruedo        timersnetwork_ioother_io end EventMachine Reactor concurrency without threads
C++ core     Easy concurrency without threading EventMachine Reactor concurrency without threads
Non-blocking IO requires non-blocking drivers: AMQPhttp://github.com/tmm1/amqp MySQLPlushttp://github.com/igrigorik/em-mysqlplus Memcachedhttp://github.com/astro/remcached DNShttp://github.com/astro/em-dns Redishttp://github.com/madsimian/em-redis MongoDBhttp://github.com/tmm1/rmongo HTTPRequesthttp://github.com/igrigorik/em-http-request WebSockethttp://github.com/igrigorik/em-websocket Amazon S3               http://github.com/peritor/happening And many others:  http://wiki.github.com/eventmachine/eventmachine/protocol-implementations
gem install em-mysqlplus EventMachine.rundo conn=EventMachine::MySQL.new(:host => 'localhost') query =conn.query("select sleep(1)") query.callback{ |res| pres.all_hashes } query.errback{ |res| pres.all_hashes } puts ”executing…” end # > ruby em-mysql-test.rb # # executing… # [{"sleep(1)"=>"0"}] callback fired 1s after “executing” em-mysqlplus: example asyncMySQL driver
non-blocking driver require'mysqlplus' defconnect(opts) conn=connect_socket(opts) EM.watch(conn.socket, EventMachine::MySQLConnection, conn, opts, self) end defconnect_socket(opts) conn=Mysql.init conn.real_connect(host, user, pass, db, port, socket, ...) conn.reconnect=false conn end EM.watch:  reactor will poll & notify em-mysqlplus: under the hood mysqlplus + reactor loop
Features: ,[object Object]
Deferrables for every query with callback & errback
 Connection query queue - pile 'em up!

More Related Content

Viewers also liked

Scott Bennett - Shell Game - Whistleblowing Report
Scott Bennett - Shell Game - Whistleblowing ReportScott Bennett - Shell Game - Whistleblowing Report
Scott Bennett - Shell Game - Whistleblowing ReportExopolitics Hungary
 
KoprowskiT_SQLSatMoscow_WASDforBeginners
KoprowskiT_SQLSatMoscow_WASDforBeginnersKoprowskiT_SQLSatMoscow_WASDforBeginners
KoprowskiT_SQLSatMoscow_WASDforBeginnersTobias Koprowski
 
Wysoka Dostępność SQL Server 2008 w kontekscie umów SLA
Wysoka Dostępność SQL Server 2008 w kontekscie umów SLAWysoka Dostępność SQL Server 2008 w kontekscie umów SLA
Wysoka Dostępność SQL Server 2008 w kontekscie umów SLATobias Koprowski
 
Introduction to SQL Server Analysis services 2008
Introduction to SQL Server Analysis services 2008Introduction to SQL Server Analysis services 2008
Introduction to SQL Server Analysis services 2008Tobias Koprowski
 
Eventuosity For Event Producers and Service Providers
Eventuosity For Event Producers and Service ProvidersEventuosity For Event Producers and Service Providers
Eventuosity For Event Producers and Service ProvidersJustin Panzer
 
Презентация стратегической игры MatriX Urban
Презентация стратегической игры MatriX UrbanПрезентация стратегической игры MatriX Urban
Презентация стратегической игры MatriX UrbanАндрей Донских
 
Virtual Study Beta Exam 71-663 Exchange 2010 Designing And Deploying Messagin...
Virtual Study Beta Exam 71-663 Exchange 2010 Designing And Deploying Messagin...Virtual Study Beta Exam 71-663 Exchange 2010 Designing And Deploying Messagin...
Virtual Study Beta Exam 71-663 Exchange 2010 Designing And Deploying Messagin...Tobias Koprowski
 
Can We Automate Predictive Analytics
Can We Automate Predictive AnalyticsCan We Automate Predictive Analytics
Can We Automate Predictive Analyticsodsc
 
Cabs, Cassandra, and Hailo
Cabs, Cassandra, and HailoCabs, Cassandra, and Hailo
Cabs, Cassandra, and HailoDave Gardner
 
ERISA Expert Advocates for 401(k) Loan Default Protection
ERISA Expert Advocates for 401(k) Loan Default ProtectionERISA Expert Advocates for 401(k) Loan Default Protection
ERISA Expert Advocates for 401(k) Loan Default ProtectionRetirement Loan Eraser
 
Slideburst #7 - Next Best Action in All Digital Channels
Slideburst #7 - Next Best Action in All Digital ChannelsSlideburst #7 - Next Best Action in All Digital Channels
Slideburst #7 - Next Best Action in All Digital ChannelsPatrik Svensson
 

Viewers also liked (13)

Scott Bennett - Shell Game - Whistleblowing Report
Scott Bennett - Shell Game - Whistleblowing ReportScott Bennett - Shell Game - Whistleblowing Report
Scott Bennett - Shell Game - Whistleblowing Report
 
KoprowskiT_SQLSatMoscow_WASDforBeginners
KoprowskiT_SQLSatMoscow_WASDforBeginnersKoprowskiT_SQLSatMoscow_WASDforBeginners
KoprowskiT_SQLSatMoscow_WASDforBeginners
 
Wysoka Dostępność SQL Server 2008 w kontekscie umów SLA
Wysoka Dostępność SQL Server 2008 w kontekscie umów SLAWysoka Dostępność SQL Server 2008 w kontekscie umów SLA
Wysoka Dostępność SQL Server 2008 w kontekscie umów SLA
 
Introduction to SQL Server Analysis services 2008
Introduction to SQL Server Analysis services 2008Introduction to SQL Server Analysis services 2008
Introduction to SQL Server Analysis services 2008
 
Eventuosity For Event Producers and Service Providers
Eventuosity For Event Producers and Service ProvidersEventuosity For Event Producers and Service Providers
Eventuosity For Event Producers and Service Providers
 
Презентация стратегической игры MatriX Urban
Презентация стратегической игры MatriX UrbanПрезентация стратегической игры MatriX Urban
Презентация стратегической игры MatriX Urban
 
Virtual Study Beta Exam 71-663 Exchange 2010 Designing And Deploying Messagin...
Virtual Study Beta Exam 71-663 Exchange 2010 Designing And Deploying Messagin...Virtual Study Beta Exam 71-663 Exchange 2010 Designing And Deploying Messagin...
Virtual Study Beta Exam 71-663 Exchange 2010 Designing And Deploying Messagin...
 
Can We Automate Predictive Analytics
Can We Automate Predictive AnalyticsCan We Automate Predictive Analytics
Can We Automate Predictive Analytics
 
Das Next Best Offer-Konzept
Das Next Best Offer-KonzeptDas Next Best Offer-Konzept
Das Next Best Offer-Konzept
 
Cabs, Cassandra, and Hailo
Cabs, Cassandra, and HailoCabs, Cassandra, and Hailo
Cabs, Cassandra, and Hailo
 
ERISA Expert Advocates for 401(k) Loan Default Protection
ERISA Expert Advocates for 401(k) Loan Default ProtectionERISA Expert Advocates for 401(k) Loan Default Protection
ERISA Expert Advocates for 401(k) Loan Default Protection
 
Slideburst #7 - Next Best Action in All Digital Channels
Slideburst #7 - Next Best Action in All Digital ChannelsSlideburst #7 - Next Best Action in All Digital Channels
Slideburst #7 - Next Best Action in All Digital Channels
 
Water Filtration and Food Quality: Why You Should Take Water Seriously
Water Filtration and Food Quality: Why You Should Take Water SeriouslyWater Filtration and Food Quality: Why You Should Take Water Seriously
Water Filtration and Food Quality: Why You Should Take Water Seriously
 

More from Ilya Grigorik

Pagespeed what, why, and how it works
Pagespeed   what, why, and how it worksPagespeed   what, why, and how it works
Pagespeed what, why, and how it worksIlya Grigorik
 
Making the web fast(er) - RailsConf 2012
Making the web fast(er) - RailsConf 2012Making the web fast(er) - RailsConf 2012
Making the web fast(er) - RailsConf 2012Ilya Grigorik
 
0-60 with Goliath: High performance web services
0-60 with Goliath: High performance web services0-60 with Goliath: High performance web services
0-60 with Goliath: High performance web servicesIlya Grigorik
 
0-60 with Goliath: Building High Performance Ruby Web-Services
0-60 with Goliath: Building High Performance Ruby Web-Services0-60 with Goliath: Building High Performance Ruby Web-Services
0-60 with Goliath: Building High Performance Ruby Web-ServicesIlya Grigorik
 
Ruby in the Browser - RubyConf 2011
Ruby in the Browser - RubyConf 2011Ruby in the Browser - RubyConf 2011
Ruby in the Browser - RubyConf 2011Ilya Grigorik
 
Intelligent Ruby + Machine Learning
Intelligent Ruby + Machine LearningIntelligent Ruby + Machine Learning
Intelligent Ruby + Machine LearningIlya Grigorik
 
No callbacks, No Threads - Cooperative web servers in Ruby 1.9
No callbacks, No Threads - Cooperative web servers in Ruby 1.9No callbacks, No Threads - Cooperative web servers in Ruby 1.9
No callbacks, No Threads - Cooperative web servers in Ruby 1.9Ilya Grigorik
 
No Callbacks, No Threads - RailsConf 2010
No Callbacks, No Threads - RailsConf 2010No Callbacks, No Threads - RailsConf 2010
No Callbacks, No Threads - RailsConf 2010Ilya Grigorik
 
Real-time Ruby for the Real-time Web
Real-time Ruby for the Real-time WebReal-time Ruby for the Real-time Web
Real-time Ruby for the Real-time WebIlya Grigorik
 
Ruby C10K: High Performance Networking - RubyKaigi '09
Ruby C10K: High Performance Networking - RubyKaigi '09Ruby C10K: High Performance Networking - RubyKaigi '09
Ruby C10K: High Performance Networking - RubyKaigi '09Ilya Grigorik
 
Lean & Mean Tokyo Cabinet Recipes (with Lua) - FutureRuby '09
Lean & Mean Tokyo Cabinet Recipes (with Lua) - FutureRuby '09Lean & Mean Tokyo Cabinet Recipes (with Lua) - FutureRuby '09
Lean & Mean Tokyo Cabinet Recipes (with Lua) - FutureRuby '09Ilya Grigorik
 
Leveraging Social Media - Strategies & Tactics - PostRank
Leveraging Social Media - Strategies & Tactics - PostRankLeveraging Social Media - Strategies & Tactics - PostRank
Leveraging Social Media - Strategies & Tactics - PostRankIlya Grigorik
 
Ruby Proxies for Scale, Performance, and Monitoring
Ruby Proxies for Scale, Performance, and MonitoringRuby Proxies for Scale, Performance, and Monitoring
Ruby Proxies for Scale, Performance, and MonitoringIlya Grigorik
 
Building Mini Google in Ruby
Building Mini Google in RubyBuilding Mini Google in Ruby
Building Mini Google in RubyIlya Grigorik
 
Ruby Proxies for Scale, Performance, and Monitoring - GoGaRuCo - igvita.com
Ruby Proxies for Scale, Performance, and Monitoring - GoGaRuCo - igvita.comRuby Proxies for Scale, Performance, and Monitoring - GoGaRuCo - igvita.com
Ruby Proxies for Scale, Performance, and Monitoring - GoGaRuCo - igvita.comIlya Grigorik
 
Event Driven Architecture - MeshU - Ilya Grigorik
Event Driven Architecture - MeshU - Ilya GrigorikEvent Driven Architecture - MeshU - Ilya Grigorik
Event Driven Architecture - MeshU - Ilya GrigorikIlya Grigorik
 
Taming The RSS Beast
Taming The  RSS  BeastTaming The  RSS  Beast
Taming The RSS BeastIlya Grigorik
 

More from Ilya Grigorik (17)

Pagespeed what, why, and how it works
Pagespeed   what, why, and how it worksPagespeed   what, why, and how it works
Pagespeed what, why, and how it works
 
Making the web fast(er) - RailsConf 2012
Making the web fast(er) - RailsConf 2012Making the web fast(er) - RailsConf 2012
Making the web fast(er) - RailsConf 2012
 
0-60 with Goliath: High performance web services
0-60 with Goliath: High performance web services0-60 with Goliath: High performance web services
0-60 with Goliath: High performance web services
 
0-60 with Goliath: Building High Performance Ruby Web-Services
0-60 with Goliath: Building High Performance Ruby Web-Services0-60 with Goliath: Building High Performance Ruby Web-Services
0-60 with Goliath: Building High Performance Ruby Web-Services
 
Ruby in the Browser - RubyConf 2011
Ruby in the Browser - RubyConf 2011Ruby in the Browser - RubyConf 2011
Ruby in the Browser - RubyConf 2011
 
Intelligent Ruby + Machine Learning
Intelligent Ruby + Machine LearningIntelligent Ruby + Machine Learning
Intelligent Ruby + Machine Learning
 
No callbacks, No Threads - Cooperative web servers in Ruby 1.9
No callbacks, No Threads - Cooperative web servers in Ruby 1.9No callbacks, No Threads - Cooperative web servers in Ruby 1.9
No callbacks, No Threads - Cooperative web servers in Ruby 1.9
 
No Callbacks, No Threads - RailsConf 2010
No Callbacks, No Threads - RailsConf 2010No Callbacks, No Threads - RailsConf 2010
No Callbacks, No Threads - RailsConf 2010
 
Real-time Ruby for the Real-time Web
Real-time Ruby for the Real-time WebReal-time Ruby for the Real-time Web
Real-time Ruby for the Real-time Web
 
Ruby C10K: High Performance Networking - RubyKaigi '09
Ruby C10K: High Performance Networking - RubyKaigi '09Ruby C10K: High Performance Networking - RubyKaigi '09
Ruby C10K: High Performance Networking - RubyKaigi '09
 
Lean & Mean Tokyo Cabinet Recipes (with Lua) - FutureRuby '09
Lean & Mean Tokyo Cabinet Recipes (with Lua) - FutureRuby '09Lean & Mean Tokyo Cabinet Recipes (with Lua) - FutureRuby '09
Lean & Mean Tokyo Cabinet Recipes (with Lua) - FutureRuby '09
 
Leveraging Social Media - Strategies & Tactics - PostRank
Leveraging Social Media - Strategies & Tactics - PostRankLeveraging Social Media - Strategies & Tactics - PostRank
Leveraging Social Media - Strategies & Tactics - PostRank
 
Ruby Proxies for Scale, Performance, and Monitoring
Ruby Proxies for Scale, Performance, and MonitoringRuby Proxies for Scale, Performance, and Monitoring
Ruby Proxies for Scale, Performance, and Monitoring
 
Building Mini Google in Ruby
Building Mini Google in RubyBuilding Mini Google in Ruby
Building Mini Google in Ruby
 
Ruby Proxies for Scale, Performance, and Monitoring - GoGaRuCo - igvita.com
Ruby Proxies for Scale, Performance, and Monitoring - GoGaRuCo - igvita.comRuby Proxies for Scale, Performance, and Monitoring - GoGaRuCo - igvita.com
Ruby Proxies for Scale, Performance, and Monitoring - GoGaRuCo - igvita.com
 
Event Driven Architecture - MeshU - Ilya Grigorik
Event Driven Architecture - MeshU - Ilya GrigorikEvent Driven Architecture - MeshU - Ilya Grigorik
Event Driven Architecture - MeshU - Ilya Grigorik
 
Taming The RSS Beast
Taming The  RSS  BeastTaming The  RSS  Beast
Taming The RSS Beast
 

Beyond 'gem install MySQL’ in Ruby

  • 1. Beyond 'gem install MySQL’ in Ruby alternative drivers & architecture Ilya Grigorik @igrigorik
  • 2. and dozens of others… The slides… Twitter My blog
  • 3. Internals of Ruby VM Ruby MySQL Drivers Looking into the future… Rails Async
  • 5. Global Interpreter Lock is a mutual exclusion lock held by a programming language interpreter thread to avoid sharing code that is not thread-safe with other threads. There is always one GIL for one interpreter process. Concurrency is a myth in Ruby (with a few caveats, of course) http://bit.ly/ruby-gil
  • 6. N-M thread pool in Ruby 1.9… Better but still the same problem! Concurrency is a myth in Ruby still no concurrency in Ruby 1.9 http://bit.ly/ruby-gil
  • 7. RTM, your mileage will vary. Concurrency is a myth in Ruby still no concurrency in Ruby 1.9 http://bit.ly/ruby-gil
  • 8. Blocks entire Ruby VM Not as bad, but avoid it still.. 1. Avoid locking interpreter threads at all costs still no concurrency in Ruby 1.9
  • 9. require 'rubygems’ require 'sequel'DB = Sequel.connect('mysql://root@localhost/test')while trueDB['select sleep(1)'].select.firstend Blocking 1s call! ltrace –ttTg -xmysql_real_query -p [pid of script above] mysql.gem under the hood 22:10:00.218438 mysql_real_query(0x02740000, "select sleep(1)", 15) = 0 <1.001100>22:10:01.241679 mysql_real_query(0x02740000, "select sleep(1)", 15) = 0 <1.000812> http://bit.ly/c3Pt3f
  • 10. Blocking calls to mysql_real_query mysql_real_query requires an OS thread Blocking on mysql_real_query blocks the Ruby VM Aka, “select sleep(1)” blocks the entire Ruby runtime for 1s (ouch) gem install mysqlwhat you didn’t know…
  • 11. gem install mysqlplus An enhanced mysql driver with an ‘async’ interface and threaded access support
  • 12. select ([] …) classMysql defruby_async_query(sql, timeout =nil) send_query(sql) select [(@sockets ||= {})[socket] ||=IO.new(socket)],nil,nil,nil get_result end begin alias_method:async_query, :c_async_query rescueNameError => e raiseLoadError.new("error loading mysqlplus") end end mysqlplus.gem under the hood gem install mysqlplus
  • 13.
  • 14. Currently executing thread is put into WAIT_SELECT
  • 15. Allows multiple threads to execute queries
  • 17. static VALUE async_query(intargc, VALUE* argv, VALUE obj) { ... send_query( obj, sql ); ... schedule_query( obj, timeout); ... returnget_result(obj); } staticvoidschedule_query(VALUEobj, VALUE timeout) { ... structtimevaltv = { tv_sec: timeout, tv_usec: 0 }; for(;;){ FD_ZERO(&read); FD_SET(m->net.fd, &read); ret = rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv); ... if (m->status == MYSQL_STATUS_READY) break; } } send query and block Ruby: select() = C: rb_thread_select() mysqlplus.gem + C API
  • 18. Ruby: ruby select() alias :query, :async_query Native: rb_thread_select ruby_async_queryvs.c_async_query use it, if you can.
  • 19. Non VM-blocking database calls (win) But there is no pipelining! You can’t re-use same connection. You will need a pool of DB connections You will need to manage the database pool You need to watch out for other blocking calls / gems! Requires threaded execution / framework for parallelism mysqlplusgotchaswhat you need to know…
  • 20. max concurrency = 5 require'rubygems' require'mysqlplus' require'db_pool' pool =DatabasePool.new(:size => 5) do puts "Connecting to database…" db =Mysql.init db.options(Mysql::SET_CHARSET_NAME, "UTF8") db.real_connect(hostname, username, password, database, nil, sock) db.reconnect=true db end pool.query("select sleep 1") 5 shared connections Managing your own DB Pool is easy enough…
  • 21.
  • 23. Threaded servers (Mongrel)
  • 25. Single core, no matter what
  • 27. Avoid blocking extensions
  • 29. Multi-proc + Threads?Concurrency in Ruby 50,000-foot view
  • 30. Rails 2.2 RC1: i18n, thread safety… Chief inclusions are an internationalization framework, thread safety (including a connection pool for Active Record)… http://bit.ly/br8Nkh (Oct 24, 2008)
  • 31. require"active_record” ActiveRecord::Base.establish_connection( :adapter => "mysql", :username => "root", :database => "database", :pool => 5 ) threads = [] 10.times do |n| threads <<Thread.new { ActiveRecord::Base.connection_pool.with_connectiondo |conn| res =conn.execute("select sleep(1)") end } end threads.each { |t| t.join } 5 shared connections # time ruby activerecord-pool.rb # # real 0m10.663s # user 0m0.405s # sys 0m0.201s Scaling ActiveRecord with mysqlplus http://bit.ly/bDtFiy
  • 32. require"active_record" require "mysqlplus" class Mysql; alias :query :async_query; end ActiveRecord::Base.establish_connection( :adapter => "mysql", :username => "root", :database => "database", :pool => 5 ) threads = [] 10.times do |n| threads <<Thread.new { ActiveRecord::Base.connection_pool.with_connectiondo |conn| res =conn.execute("select sleep(1)") end } end threads.each { |t| t.join } Parallel execution! # time ruby activerecord-pool.rb # # real 0m2.463s # user 0m0.405s # sys 0m0.201s Scaling ActiveRecord with mysqlplus http://bit.ly/bDtFiy
  • 33. config.threadsafe! require'mysqlplus’ classMysql; alias :query :async_query; end In your environtments/production.rb Concurrency in Rails? Not so fast… :-( Scaling ActiveRecord with mysqlplus http://bit.ly/bDtFiy
  • 34. Global dispatcher lock Random locks in your web-server (like Mongrel) Gratuitous locking in libraries, plugins, etc. In reality, you still need process parallelism in Rails. But, we’re moving in the right direction. JRuby? Rails + MySQL = Concurrency?almost, but not quite
  • 35. gem install activerecord-jdbcmysql-adapter development: adapter: jdbcmysql encoding: utf8 database: myapp_development username: root password: my_password Subject to all the same Rails restrictions (locks, etc) JRuby: RTM, your mileage will vary all depends on the container
  • 36. GlasshFish will reuse your database connections via its internal database connection pooling mechanism. http://wiki.glassfish.java.net/Wiki.jsp?page=JRuby JRuby: RTM, your mileage will vary all depends on the container
  • 37. Non-blocking IO in Ruby: EventMachine for real heavy-lifting, you have to go async…
  • 38. p "Starting"EM.run dop"Running in EM reactor"endp ”won’t get here" whiletruedo timersnetwork_ioother_io end EventMachine Reactor concurrency without threads
  • 39. p "Starting"EM.rundop"Running in EM reactor"endp”won’t get here" whiletruedo timersnetwork_ioother_io end EventMachine Reactor concurrency without threads
  • 40. C++ core Easy concurrency without threading EventMachine Reactor concurrency without threads
  • 41. Non-blocking IO requires non-blocking drivers: AMQPhttp://github.com/tmm1/amqp MySQLPlushttp://github.com/igrigorik/em-mysqlplus Memcachedhttp://github.com/astro/remcached DNShttp://github.com/astro/em-dns Redishttp://github.com/madsimian/em-redis MongoDBhttp://github.com/tmm1/rmongo HTTPRequesthttp://github.com/igrigorik/em-http-request WebSockethttp://github.com/igrigorik/em-websocket Amazon S3 http://github.com/peritor/happening And many others: http://wiki.github.com/eventmachine/eventmachine/protocol-implementations
  • 42. gem install em-mysqlplus EventMachine.rundo conn=EventMachine::MySQL.new(:host => 'localhost') query =conn.query("select sleep(1)") query.callback{ |res| pres.all_hashes } query.errback{ |res| pres.all_hashes } puts ”executing…” end # > ruby em-mysql-test.rb # # executing… # [{"sleep(1)"=>"0"}] callback fired 1s after “executing” em-mysqlplus: example asyncMySQL driver
  • 43. non-blocking driver require'mysqlplus' defconnect(opts) conn=connect_socket(opts) EM.watch(conn.socket, EventMachine::MySQLConnection, conn, opts, self) end defconnect_socket(opts) conn=Mysql.init conn.real_connect(host, user, pass, db, port, socket, ...) conn.reconnect=false conn end EM.watch: reactor will poll & notify em-mysqlplus: under the hood mysqlplus + reactor loop
  • 44.
  • 45. Deferrables for every query with callback & errback
  • 46. Connection query queue - pile 'em up!
  • 47. Auto-reconnect on disconnects
  • 48. Auto-retry on deadlockshttp://github.com/igrigorik/em-mysqlplus em-mysqlplus mysqlplus + reactor loop
  • 49. EventMachine.rundo conn=EventMachine::MySQL.new(:host => 'localhost') results = [] conn.query("select sleep(1)") {|res| results.push 1 } conn.query("selectsleep(1)") {|res| results.push 2 } conn.query("select sleep(1)") {|res| results.push 3 } EventMachine.add_timer(1.5) { presults # => [1] } end Still need DB pooling, etc. No magic pipelining! em-mysqlplus: under the hood mysqlplus + reactor loop
  • 50. Stargazing with Ruby 1.9 & Fibers the future is here! Well, almost…
  • 51. Ruby 1.9 Fibers are a means of creating code blocks which can be paused and resumed by our application (think lightweight threads, minus the thread scheduler and less overhead). f=Fiber.new{ whiletruedo Fiber.yield"Hi” end } pf.resume# => Hi pf.resume# => Hi pf.resume# => Hi Manual / cooperative scheduling! Ruby 1.9 Fibers and cooperative scheduling http://bit.ly/d2hYw0
  • 52. Fibers vs Threads: creation time much lower Fibers vs Threads: memory usage is much lower Ruby 1.9 Fibers and cooperative scheduling http://bit.ly/aesXy5
  • 53. defquery(sql) f=Fiber.current conn=EventMachine::MySQL.new(:host => 'localhost') q= conn.query(sql) # resume fiber once query call is done c.callback{ f.resume(conn) } c.errback{ f.resume(conn) } returnFiber.yield end EventMachine.rundo Fiber.new{ res =query('select sleep(1)') puts "Results: #{res.fetch_row.first}" }.resume end async query, sync execution! Untangling Evented Code with Fibers http://bit.ly/d2hYw0
  • 54.
  • 55. Multi request interface which accepts any callback enabled client
  • 56. Fibered iterator to allow concurrency control & mixing of sync / async
  • 57. em-http-request: .get, etc are synchronous, while .aget, etc are async
  • 58. em-mysqlplus: .query is synchronous, while .aquery is async
  • 59. remcached: .get, etc, and .multi_* methods are synchronousem-synchrony: simple evented programming best of both worlds…
  • 60. EventMachine.synchronydo db =EventMachine::Synchrony::ConnectionPool.new(size: 2) do EventMachine::MySQL.new(host: "localhost") end multi =EventMachine::Synchrony::Multi.new multi.add:a, db.aquery("select sleep(1)") multi.add:b, db.aquery("select sleep(1)") res =multi.perform p"Look ma, no callbacks, and parallel MySQL requests!” p res EventMachine.stop end Fiber-aware connection pool Parallel queries, synchronous API, no threads! em-synchrony: MySQL example async queries with sync execution
  • 61. Fibers & Cooperative Scheduling in Ruby: http://www.igvita.com/2009/05/13/fibers-cooperative-scheduling-in-ruby/ Untangling Evented Code with Ruby Fibers: http://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/ EM-Synchrony: http://github.com/igrigorik/em-synchrony em-synchrony: more info check it out, it’s the future!
  • 62. Non-blocking Rails??? Mike Perham did it with EM PG driver + Ruby 1.9 & Fibers: http://bit.ly/9qGC00 We can do it with MySQL too…
  • 63. gitclone git://github.com/igrigorik/em-mysqlplus.git git checkout activerecord rake install database.yml development: adapter:em_mysqlplus database:widgets pool: 5 timeout: 5000 environment.rb require 'em-activerecord’ require 'rack/fiber_pool' # Run each request in a Fiber config.middleware.useRack::FiberPool config.threadsafe! Async Rails with EventMachine & MySQL
  • 64. classWidgetsController< ApplicationController defindex Widget.find_by_sql("select sleep(1)") render:text=> "Oh hai” end end ab –c 5 –n 10 http://127.0.0.1:3000/widgets Server Software: thin Server Hostname: 127.0.0.1 Server Port: 3000 Document Path: /widgets/ Document Length: 6 bytes Concurrency Level: 5 Time taken for tests: 2.210 seconds Complete requests: 10 Failed requests: 0 Requests per second: 4.53 [#/sec] (mean) woot! Fiber DB pool at work. Async Rails with EventMachine & MySQL
  • 65. git clone git://…./igrigorik/mysqlplus git checkout activerecord rake install One app server, 5 parallel DB requests!
  • 66. Blog post & slides: http://bit.ly/gem-mysql Code: http://github.com/igrigorik/presentations Twitter: @igrigorik Questions?

Editor's Notes

  1. To understand what&apos;s going on, we need to take a closer look at the Ruby runtime. Whenever you launch a Ruby application, an instance of a Ruby interpreter is launched to parse your code, build an AST tree, and then execute the application you&apos;ve requested - thankfully, all of this is transparent to the user. However, as part of this runtime, the interpreter also instantiates an instance of a Global Interpreter Lock (or more affectionately known as GIL), which is the culprit of our lack of concurrency:
  2. Thread non-blocking region in Ruby 1.9With right driver architecture can block OS thread but VM will continue
  3. rb_thread_select() on the mysql connection&apos;s file descriptor, effectively putting that thread in a WAIT_SELECT and letting other threads run until the query&apos;s results are available.
  4. https://gist.github.com/raw/61762/4d0ba698aa868a7dbd04678c0fa37a7a60201dbf/gistfile1.txt
  5. rb_thread_select() on the mysql connection&apos;s file descriptor, effectively putting that thread in a WAIT_SELECT and letting other threads run until the query&apos;s results are available.
  6. While jruby is able to take advantage of Java&apos;s native threading, if you are running Rails ver &lt; 2.2 which is not thread-safe, and thus cannot benefit from it. Glassfish provides a jruby runtime pool to allow servicing of multiple concurrent requests. Each runtime runs a single instance of Rails, and requests are handed off to whichever one happens to be available at the time of the request.The dynamic pool will maintain itself with the minimum number of runtimes possible to allow consistent, fast runtime access for the requesting application between its min and max. It also may take an initial number of runtimes, but that value is not used after pool creation in any way.
  7. The reactor design pattern is a concurrent programming pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.
  8. coroutines
  9. coroutines
  10. coroutines
  11. coroutines
  12. coroutines
  13. coroutines