{"id":3368,"date":"2012-12-19T14:24:34","date_gmt":"2012-12-19T11:24:34","guid":{"rendered":"http:\/\/railsware.com\/blog\/?p=3368"},"modified":"2021-08-12T17:59:53","modified_gmt":"2021-08-12T14:59:53","slug":"fixing-multithreading-for-rails-in-development-mode","status":"publish","type":"post","link":"https:\/\/railsware.com\/blog\/fixing-multithreading-for-rails-in-development-mode\/","title":{"rendered":"Fixing multithreading for Rails in development mode"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Preface<\/h2>\n\n\n\n<p>In Rails, automatic code reloading in development mode is based on <a title=\"autoload\" href=\"http:\/\/www.rubyinside.com\/ruby-techniques-revealed-autoload-1652.html\" target=\"_blank\" rel=\"noopener noreferrer\">autoload<\/a>,<br>which is not threadsafe. It means you can&#8217;t use it while developing multithreaded Rails applications(especially for JRuby). But several monkeypatches can do the trick.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Workaround<\/h2>\n\n\n\n<p>When we discovered this limitation almost a year ago, we had next options:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>development without code reloading and restart server after every change<\/li><li>fix autoload threadsafe issue<\/li><li>create some workaround<\/li><\/ul>\n\n\n\n<p>As any RoR developer, I can&#8217;t imagine my life without automatic code<br>reloading, so server restart was not an option.<br>Fixing autoload is a nontrivial task. Corresponded bug was reported 4<br>years ago, and still wasn&#8217;t fixed.<\/p>\n\n\n\n<p>So, the only choice we had is to create some workaround. Solution<br>quickly came to mind &#8211; simply don&#8217;t use multithreading in development mode. We created fallback which made our application logic work in single-threaded mode for development, and in multithreaded mode for production.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fix of autoload<\/h2>\n\n\n\n<p>After 4 years of struggling, autoload was <a title=\"autoload fix discussion\" href=\"http:\/\/bugs.ruby-lang.org\/issues\/921\" target=\"_blank\" rel=\"noopener noreferrer\">fixed in ruby-trunk<\/a>. It&#8217;s<br>scheduled to be delivered with Ruby 1.9.4 at the beginning of 2013. Luckily for us, patch was ported into JRuby 1.6.6, so we don&#8217;t have to wait till Christmas.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">rvm install jruby-1.6.7\n<\/pre>\n\n\n\n<p>Then I disabled our fallback for development mode, and &#8230; found out that server crashed repeatedly across different places. Switching to production mode instantly fixed all exceptions. It looks like threadsafety of autoload was not the only issue.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Digging into Rails guts<\/h2>\n\n\n\n<p>Every exception was different, but in most cases it was database<br>related issue. E.g. some objects in database were missing, some were not updated, deadlocking of SQL-queries and so on.<\/p>\n\n\n\n<p>I started with grep-ing database query logs. Very soon quite weird behavior was observed: bunch of SQL-queries from one controller action were handled by different database connections. But they all should be issued by only one AR-connection!<\/p>\n\n\n\n<p>After this finding, the main suspect became infamous ActiveRecord::ConnectionAdapters::ConnectionPool. Again! In case you<br>don&#8217;t know, ConnectionPool is not threadsafe in Rails &lt; 3.2.4, so we already had<br><a title=\"monkeypatch\" href=\"https:\/\/gist.github.com\/2658680\" target=\"_blank\" rel=\"noopener noreferrer\">monkeypatch<\/a> for it.<br>After adding some traces to ConnectionPool <em>.checkin<\/em> and <em>.checkout<\/em><br>methods, we got next flow:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"319\" height=\"252\" src=\"\/\/railsware.com\/blog\/wp-content\/uploads\/2012\/11\/code_reload_blog_pic1_v21.png\" alt=\"\" class=\"wp-image-3411\" title=\"code_reload_blog_pic1_v2\" srcset=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2012\/11\/code_reload_blog_pic1_v21.png 319w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2012\/11\/code_reload_blog_pic1_v21-300x236.png 300w\" sizes=\"auto, (max-width: 319px) 100vw, 319px\" \/><\/figure>\n\n\n\n<p>But why was connection checked-in in the middle of job processing?<br><em>ConnectionPool<\/em> has <a title=\"ConnectionPool\" href=\"http:\/\/api.rubyonrails.org\/classes\/ActiveRecord\/ConnectionAdapters\/ConnectionPool.html\" target=\"_blank\" rel=\"noopener noreferrer\">3 working strategies<\/a>, and<br>we used the simplest one. Connection is checked-out on demand, and when controller action is done, all connections used by<br>current thread are checked-in by <em>clear_active_connections!<\/em> method.<\/p>\n\n\n\n<p>But in development mode something checks-in connections while they are actively used. Such behavior may not affect some applications, but with active usage of database transactions, which are bound to db-connections, disaster is guaranteed.<\/p>\n\n\n\n<p>There are just a few methods in ActiveRecord, which checkin\/checkout connections. Putting several additional traces I got next flow:<br><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-3394\" title=\"code_reload_blog_pic2_v2\" src=\"\/\/railsware.com\/blog\/wp-content\/uploads\/2012\/11\/code_reload_blog_pic2_v2.png\" alt=\"\" width=\"773\" height=\"375\" srcset=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2012\/11\/code_reload_blog_pic2_v2.png 773w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2012\/11\/code_reload_blog_pic2_v2-300x145.png 300w\" sizes=\"auto, (max-width: 773px) 100vw, 773px\" \/><br>Connections were checked-in by clear_reloadable_connections! which is<br>called after processing of every request in development mode. Here is how it looks:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">def clear_reloadable_connections!\n  #this part checkin all checked-out connections even used by other threads!\n  @reserved_connections.each do |name, conn|\n    checkin conn\n  end\n  @reserved_connections = {}\n  #this part disconnects all conections which requires reloading.\n  @connections.each do |conn|\n    conn.disconnect! if conn.requires_reloading?\n  end\n  @connections.delete_if do |conn|\n    conn.requires_reloading?\n  end\nend\n<\/pre>\n\n\n\n<p>It can be split in two parts. First one is the root of our issue: it simply checks-in all active connections from all threads, no matter they are used or not. So why is it so lame? Git-blame shows, that this method was <a href=\"https:\/\/github.com\/rails\/rails\/commit\/0a86122a248020081722bf0c12de70c929cbcbd9\" target=\"_blank\" rel=\"noopener noreferrer\">added 6 years ago<\/a>. Commit message says &#8220;Only reload connections in development mode that supports (and requires that) &#8212; in other words, only do it for SQLite&#8221;.<br>And here is how it looked 6 years ago:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">def clear_reloadable_connections!\n  @@active_connections.each do |name, conn|\n    conn.disconnect! if conn.supports_reloading?\n    @@active_connections.delete(name)\n  end\nend\n<\/pre>\n\n\n\n<p>A bit simplier, but it does two things:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>disconnects SQLite connections(.support_reloading? method returns true only for SQLite connection)<\/li><li>checks-in ALL connections without any respect to other threads<\/li><\/ul>\n\n\n\n<p>Before this commit, instead of <em>clear_reloadable_connections!<\/em> another<br>method was called: <em>clear_active_connections!<\/em>, which was:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">def clear_active_connections!\n  clear_cache!(@@active_connections) do |name, conn|\n    conn.disconnect!\n  end\nend\n\ndef clear_cache!(cache, thread_id = nil, &amp;block)\n  if cache\n    if @@allow_concurrency\n      thread_id ||= Thread.current.object_id \n        #in multithreaded environment it will work only with current thread,\n        #leaving others threads cache intact\n      thread_cache, cache = cache, cache[thread_id]\n      return unless cache\n    end\n\n    cache.each(&amp;block) if block_given?\n    cache.clear\n  end\nensure\n  if thread_cache &amp;&amp; @@allow_concurrency\n    thread_cache.delete(thread_id)\n  end\nend\n<\/pre>\n\n\n\n<p>Unlike <em>clear_reloadable_connections!<\/em>, this one respects other threads and clears connections used by current thread only. Looks like <em>clear_reloadable_connections!<\/em> was de-threadsafed unintentionally and this should be fixed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Solution<\/h2>\n\n\n\n<p>If we don&#8217;t use SQLite why bother ourselves with fix for <em>clear_reloadable_connections!<\/em>? Instead we can just disable it :). Here is a dumbest monkey-patch:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">class ActiveRecord::ConnectionAdapters::ConnectionPool\n  def clear_reloadable_connections!;end \n  # disable it, since it checks-in ALL connections from ALL threads after every request\n  # in development mode\nend\n<\/pre>\n\n\n\n<p>We have like 5 minutes to celebrate the victory, but ActiveRecord::ConnectionTimeoutError, &#8220;could not obtain a database connection within 5 seconds&#8221; brings us back to earth. Looks like database connections leak from ConnectionPool.<\/p>\n\n\n\n<p>Further digging showed that WEBrick leaks connections when exception is raised (like RoutingError). Nobody noticed this because <em>clear_reloadable_connections!<\/em> checks-out all connections after every request.<\/p>\n\n\n\n<p>ActiveRecord has special method &#8211; <a href=\"http:\/\/api.rubyonrails.org\/classes\/ActiveRecord\/ConnectionAdapters\/ConnectionPool.html#method-i-clear_stale_cached_connections-21\" target=\"_blank\" rel=\"noopener noreferrer\">clear_stale_cached_connections!<\/a>.<br>It checks-in all leaked connections from dead threads, leaving active intact. Easiest place to call it is at the beginning of the <em>checkout<\/em> method:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">class ActiveRecord::ConnectionAdapters::ConnectionPool\n  def checkout\n    clear_stale_cached_connections! if Rails.env.development? #hack for WEBrick\n    ...\n  end\nend\n<\/pre>\n\n\n\n<p>Voila! We have multithreaded application running in development mode with code reload!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>To make code reloading work in multithreaded mode you need:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>JRuby &gt;= 1.6.6 or MRI &gt;= 1.9.4, where autoload threadsafety is fixed<\/li><li>monkeypatch ConnectionPool to disable clear_reloadable_connections!<\/li><li>monkeypatch ConnectionPool to deal with connections leakage in WEBrick<\/li><li>update to Rails &gt;= 3.2.4, where <a href=\"http:\/\/bibwild.wordpress.com\/2012\/03\/15\/activerecord-concurrency-currently-good-news-and-bad\/\" target=\"_blank\" rel=\"noopener noreferrer\">threadsafety issues<\/a> with ConnectionPool is fixed<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Preface In Rails, automatic code reloading in development mode is based on autoload,which is not threadsafe. It means you can&#8217;t use it while developing multithreaded Rails applications(especially for JRuby). But several monkeypatches can do the trick. Workaround When we discovered this limitation almost a year ago, we had next options: development without code reloading and&#8230;<\/p>\n","protected":false},"author":43,"featured_media":9452,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[],"coauthors":["Oleksandr Bondar"],"class_list":["post-3368","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"aioseo_notices":[],"categories_data":[{"name":"Engineering","link":"https:\/\/railsware.com\/blog?category=development"}],"post_thumbnails":"\/\/railsware.com\/blog\/wp-content\/uploads\/2012\/11\/code_reload_blog_pic1_v21.png","amp_enabled":true,"_links":{"self":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/3368","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/users\/43"}],"replies":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/comments?post=3368"}],"version-history":[{"count":110,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/3368\/revisions"}],"predecessor-version":[{"id":14019,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/3368\/revisions\/14019"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media\/9452"}],"wp:attachment":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media?parent=3368"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/categories?post=3368"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/tags?post=3368"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/coauthors?post=3368"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}