<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xml:lang="en-US">
  <title>BigBinary Blog: Ruby</title>
  <subtitle>Deep technical writing on Ruby on Rails, performance, scaling, monitoring and engineering.</subtitle>
  <link rel="self" type="application/atom+xml" href="https://www.bigbinary.com/blog/categories/ruby/feed.xml"/>
  <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/categories/ruby"/>
  <id>https://www.bigbinary.com/blog/categories/ruby</id>
  <updated>2025-11-25T00:00:00.000Z</updated>
  <author>
    <name>BigBinary</name>
    <uri>https://www.bigbinary.com/</uri>
  </author>
  <icon>https://www.bigbinary.com/favicon.ico</icon>
  <logo>https://www.bigbinary.com/images/bigbinary_square_big_logo.png</logo>
  <rights>Copyright 2025 Big Binary LLC</rights>
  <generator uri="https://nextjs.org">Next.js</generator>
  <entry>
      <title type="text"><![CDATA[Debugging a Stack Overflow in Rails 7.2.1.1]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/debugging-stack-overflow-in-rails"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/debugging_stack_overflow_in_rails.jpg"/>
      <id>https://www.bigbinary.com/blog/debugging-stack-overflow-in-rails</id>
      <published>2025-11-25T00:00:00.000Z</published>
      <updated>2025-11-25T00:00:00.000Z</updated>
      <author>
        <name>Vishnu M</name>
        <uri>https://www.bigbinary.com/blog/authors/vishnu-m</uri>
      </author>
      <category term="Rails"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/debugging_stack_overflow_in_rails.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[A few weeks ago, after upgrading NeetoCal from Rails 7.1.5.2 to Rails 7.2.1.1, we started seeing mysterious crashes in production with the error message SystemStackError: stack level too deep....]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/debugging-stack-overflow-in-rails"><![CDATA[<p>A few weeks ago, after upgrading <a href="https://www.neeto.com/neetocal">NeetoCal</a> from
Rails 7.1.5.2 to Rails 7.2.1.1, we started seeing mysterious crashes in
production with the error message <code>SystemStackError: stack level too deep</code>.</p>
<h2>Identifying the problem</h2>
<p>The crash happened inside <code>Slots::SyncAllCalendarsService</code>, a service that syncs
multiple calendars concurrently using the <code>Async</code> gem. What made it particularly
puzzling was that switching from <code>Async::Barrier</code> to <code>Thread.new</code> made the error
disappear. This led us down a rabbit hole thinking it was an Async-specific
issue.</p>
<p>The stack trace pointed to a strange location:</p>
<pre><code>activerecord (7.2.1.1) lib/arel/visitors/to_sql.rb:898:in `each'
  from lib/arel/visitors/to_sql.rb:898:in `each_with_index'
  from lib/arel/visitors/to_sql.rb:898:in `inject_join'
  from lib/arel/visitors/to_sql.rb:627:in `visit_Arel_Nodes_Or'
  ... 1132 levels...
</code></pre>
<p>Over a thousand levels deep into <code>visit_Arel_Nodes_Or</code>. That's unusual. Why
would visiting OR nodes in a SQL query cause such deep recursion?</p>
<p>The stack trace mentioned <code>Slots::SyncAllCalendarsService</code>, but that service
itself had nothing obviously wrong. It was just iterating through calendars and
syncing them. We traced deeper into the code and found the actual culprit in
Neeto's <code>Integrations::Icloud::SyncEventsService</code>:</p>
<pre><code class="language-ruby">def abandoned_events
  pairs = event_params.map { |e| [e[:url], e[:recurrence_id]] }

  @_abandoned_events = calendar.events
    .where(&quot;start_time BETWEEN ? AND ?&quot;, start_time, end_time)
    .where.not([:url, :recurrence_id] =&gt; pairs)
end
</code></pre>
<p>This code finds &quot;abandoned&quot; events by excluding events that match specific URL
and recurrence ID combinations. In production, <code>pairs</code> contained 233 entries.
Nothing immediately suspicious, right?</p>
<p>To understand what was happening, we needed to see what Rails was actually doing
with this query. The <code>where.not([:url, :recurrence_id] =&gt; pairs)</code> syntax is
Rails' composite key feature. With 233 pairs, Rails generates SQL like this:</p>
<pre><code class="language-sql">WHERE NOT (
  (url = 'url1' AND recurrence_id = 'rec1') OR
  (url = 'url2' AND recurrence_id = 'rec2') OR
  (url = 'url3' AND recurrence_id = 'rec3') OR
  -- ... 230 more OR clauses
)
</code></pre>
<p>233 OR clauses. That's a lot, but databases can handle it. The problem wasn't
the SQL itself - it was how Rails built the internal representation of that
query.</p>
<h2>Going into Rails Internals</h2>
<p>To really understand what changed, we used <code>bundle open activerecord</code> to look at
the Rails source code. We compared the same code in Rails 7.1.5.2 and 7.2.1.1.</p>
<h3>What We Found in Rails 7.1.5.2</h3>
<p>In Rails 7.1.5.2, the code that visits OR nodes looked like this:</p>
<pre><code class="language-ruby">def visit_Arel_Nodes_Or(o, collector)
  stack = [o.right, o.left]

  while o = stack.pop
    if o.is_a?(Arel::Nodes::Or)
      stack.push o.right, o.left
    else
      visit o, collector
      collector &lt;&lt; &quot; OR &quot; unless stack.empty?
    end
  end

  collector
end
</code></pre>
<p>This is an iterative approach using a manual stack. No matter how deeply nested
the OR conditions are, this code only uses a single stack frame for the
<code>visit_Arel_Nodes_Or</code> method. It's stack-safe.</p>
<h3>What Changed in Rails 7.2.1.1</h3>
<p>In Rails 7.2.1.1, the same method became much simpler:</p>
<pre><code class="language-ruby">def visit_Arel_Nodes_Or(o, collector)
  inject_join o.children, collector, &quot; OR &quot;
end
</code></pre>
<p>Where <code>inject_join</code> does:</p>
<pre><code class="language-ruby">def inject_join(list, collector, join_str)
  list.each_with_index do |x, i|
    collector &lt;&lt; join_str unless i == 0
    collector = visit(x, collector)  # Recursive call
  end
  collector
end
</code></pre>
<p>This is a recursive approach. If an OR node contains another OR node, it calls
<code>visit</code>, which calls <code>visit_Arel_Nodes_Or</code>, which calls <code>inject_join</code> again. The
recursion depth grows with the nesting depth of the OR tree.</p>
<h2>The Tree Structure Problem</h2>
<p>But why is the OR tree so deeply nested? We found the answer in Active Record's
<code>PredicateBuilder#grouping_queries</code>:</p>
<pre><code class="language-ruby">def grouping_queries(queries)
  if queries.one?
    queries.first
  else
    queries.map! { |query| query.reduce(&amp;:and) }
    queries = queries.reduce { |result, query| Arel::Nodes::Or.new([result, query]) }
    Arel::Nodes::Grouping.new(queries)
  end
end
</code></pre>
<p>That <code>reduce</code> call is the key. It builds a left-deep nested tree:</p>
<pre><code>Level 1:  Query1 OR Query2 = OR_Node_1
Level 2:  OR_Node_1 OR Query3 = OR_Node_2
Level 3:  OR_Node_2 OR Query4 = OR_Node_3
...
Level 232: OR_Node_231 OR Query233 = OR_Node_232
</code></pre>
<p>With 233 pairs, this creates 232 levels of nesting. Each level adds
approximately 5 stack frames when Rails traverses it recursively. That's 1,160
stack frames just for the query building.</p>
<h2>The Async Connection</h2>
<p>Why did <code>Thread.new</code> work but <code>Async::Barrier</code> didn't?</p>
<p>We wrote a test script to measure the actual stack depth limits in different
contexts:</p>
<pre><code class="language-ruby">def test_recursion_depth(context_name)
  depth = 0

  recurse = lambda do |n|
    depth = n
    recurse.call(n + 1)
  end

  begin
    recurse.call(0)
  rescue SystemStackError
    return depth
  end
end

thread_depth = nil
Thread.new do
  thread_depth = test_recursion_depth(&quot;thread&quot;)
end.join
puts &quot;Thread recursion limit: ~#{thread_depth} calls&quot;

async_depth = nil
Async do
  async_depth = test_recursion_depth(&quot;async&quot;)
end
puts &quot;Async fiber recursion limit: ~#{async_depth} calls&quot;
</code></pre>
<p>When we ran this, the results were revealing:</p>
<pre><code>Thread recursion limit:      ~11910 calls
Async fiber recursion limit: ~1482 calls
</code></pre>
<p>Threads have roughly 8x more stack space than Async fibers. This is because
threads use larger stacks (1MB-8MB) while fibers are designed to be lightweight
with smaller stacks (512KB-1MB). This difference in stack allocation is why
threads can handle deeper recursion before hitting the overflow limit.</p>
<p>With 233 pairs requiring ~1,160 stack frames, we were right at the edge of the
Async fiber limit. But we were still well within the thread limit, which is why
switching to <code>Thread.new</code> seemed to fix it.</p>
<p>When we tested with 500 pairs (which some of our larger calendars had), even
<code>Thread.new</code> failed with a stack overflow. So it wasn't really a fix, it just
pushed the problem further down the road.</p>
<h2>The GitHub Trail</h2>
<p>After we understood the problem, we searched the Rails repository and found the
exact history:</p>
<ol>
<li>
<p><strong>April 4, 2024 - <a href="https://github.com/rails/rails/pull/51492">PR #51492</a></strong>:
This PR changed OR nodes from <strong>binary</strong> to <strong>n-ary</strong> to handle multiple
children in a single node.</p>
</li>
<li>
<p><strong>September 24, 2024 -
<a href="https://github.com/rails/rails/issues/53031">Issue #53031</a></strong>: Someone
reported the exact issue we were seeing. Queries with 800+ OR conditions that
worked in Rails 7.1 now crashed in Rails 7.2.</p>
</li>
<li>
<p><strong>September 25, 2024 -
<a href="https://github.com/rails/rails/pull/53032">PR #53032</a></strong>:
<a href="https://github.com/fatkodima">fatkodima</a> fixed it with a one-line change in
<code>PredicateBuilder#grouping_queries</code>:</p>
</li>
</ol>
<p><strong>Before:</strong></p>
<pre><code class="language-ruby">queries = queries.reduce { |result, query| Arel::Nodes::Or.new([result, query]) }
</code></pre>
<p><strong>After:</strong></p>
<pre><code class="language-ruby">queries = Arel::Nodes::Or.new(queries)
</code></pre>
<p>The difference is profound. The old code using <code>reduce</code> created a deeply nested
structure like Russian nesting dolls:</p>
<pre><code>Or([Or([Or([Query1, Query2]), Query3]), Query4])  # 232 levels with 233 pairs
</code></pre>
<p>The new code creates a flat structure:</p>
<pre><code>Or([Query1, Query2, Query3, Query4, ...Query233])  # Just 1 level
</code></pre>
<p>With a flat structure, the recursive visitor only traverses one level instead of
232, eliminating the stack overflow. The fix is available in Rails 7.2.2+.</p>
<h2>Our Solution</h2>
<p>We're currently on Rails 7.2.1.1 and upgrading immediately would require
extensive testing. So we implemented a workaround in our code:</p>
<pre><code class="language-ruby">def abandoned_events
  return @_abandoned_events if defined?(@_abandoned_events)

  pairs = event_params.map { |e| [e[:url], e[:recurrence_id]] }
  return if pairs.empty?

  events_in_time_range = calendar.events
    .where(&quot;start_time BETWEEN ? AND ?&quot;, start_time, end_time)

  # Get candidate events using simple IN clauses
  urls = pairs.map(&amp;:first).uniq
  recurrence_ids = pairs.map(&amp;:last).uniq

  candidate_events = events_in_time_range
    .where(url: urls)
    .where(recurrence_id: recurrence_ids)

  # Filter to exact pairs in memory
  pairs_set = pairs.to_set
  ids_to_exclude = candidate_events.select { |event|
    pairs_set.include?([event.url, event.recurrence_id])
  }.map(&amp;:id)

  @_abandoned_events = if ids_to_exclude.empty?
    events_in_time_range
  else
    events_in_time_range.where.not(id: ids_to_exclude)
  end
end
</code></pre>
<p>This approach:</p>
<ul>
<li>Uses simple IN clauses instead of complex OR conditions</li>
<li>Filters the exact pairs in memory (fast with a Set)</li>
<li>Excludes by ID, which is a flat list</li>
<li>Never builds deeply nested Arel nodes</li>
</ul>
<p>The trade-off is we run 2 queries instead of 1, but the queries are simpler and
more efficient. The in-memory filtering is negligible since we're already
constrained by time range.</p>
<h2>Measuring and Verifying</h2>
<p>To prove our hypothesis, we wrote verification scripts that patched Rails
internals to measure what was actually happening.</p>
<h3>Counting Arel Visitor Calls</h3>
<p>We created a script that patched <code>visit_Arel_Nodes_Or</code> to count how many times
it was called and how deep the recursion went:</p>
<pre><code class="language-ruby">module Arel
  module Visitors
    class ToSql
      alias_method :original_visit_Arel_Nodes_Or, :visit_Arel_Nodes_Or

      def visit_Arel_Nodes_Or(o, collector)
        @or_call_count ||= 0
        @or_call_count += 1
        @max_depth ||= 0
        @current_depth ||= 0
        @current_depth += 1
        @max_depth = [@max_depth, @current_depth].max

        result = original_visit_Arel_Nodes_Or(o, collector)

        @current_depth -= 1
        result
      end
    end
  end
end
</code></pre>
<p>Results with 50 test pairs:</p>
<ul>
<li><strong>Rails 7.1.5.2</strong>: <code>visit_Arel_Nodes_Or</code> was called 1 time and max recursion
depth was 0.</li>
<li><strong>Rails 7.2.1.1</strong>: <code>visit_Arel_Nodes_Or</code> was called 49 times and max recursion
depth was 100.</li>
</ul>
<p>With 233 pairs in production:</p>
<ul>
<li><strong>Rails 7.1.5.2</strong>: Still called 1 time, depth 0 (iterative approach)</li>
<li><strong>Rails 7.2.1.1</strong>: Called 232 times, depth ~466 (exceeds Async fiber limit)</li>
</ul>
<h3>Testing the Actual Breaking Points</h3>
<p>We ran the problematic query with different pair counts to find exactly where it
breaks:</p>
<pre><code class="language-ruby"># Test with Async
Async do
  pairs = calendar.events.limit(count).pluck(:url, :recurrence_id)
  calendar.events.where.not([:url, :recurrence_id] =&gt; pairs).count
end.wait

# Test with Thread
Thread.new do
  pairs = calendar.events.limit(count).pluck(:url, :recurrence_id)
  calendar.events.where.not([:url, :recurrence_id] =&gt; pairs).count
end.join
</code></pre>
<p>Results:</p>
<ul>
<li><strong>Async Fiber</strong>: Breaks at approximately 233 pairs</li>
<li><strong>Thread.new</strong>: Breaks at approximately 500 pairs</li>
</ul>
<p>This confirmed that Thread.new wasn't a real solution - it just had more
headroom before hitting the same problem.</p>
<p>For now, we're sticking with our workaround. It works reliably, performs well,
and doesn't require upgrading Rails immediately. When we do upgrade to Rails
7.2.2+, we can revert to the original clean syntax, knowing that the Rails team
has fixed the underlying issue.</p>
<p>If you're on Rails 7.2.0 through 7.2.1.x and use composite key queries with
large datasets, watch out for this issue. The fix is in Rails 7.2.2+, or you can
work around it like we did.</p>
<h2>References</h2>
<ul>
<li><a href="https://github.com/rails/rails/pull/51492">PR #51492 - Bug introduced</a></li>
<li><a href="https://github.com/rails/rails/issues/53031">Issue #53031 - Bug reported</a></li>
<li><a href="https://github.com/rails/rails/pull/53032">PR #53032 - Bug fixed</a></li>
</ul>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Active Record adds support for deprecating associations]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/active-record-deprecated-associations"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/active_record_adds_support_for_deprecating_associations.jpg"/>
      <id>https://www.bigbinary.com/blog/active-record-deprecated-associations</id>
      <published>2025-07-03T00:00:00.000Z</published>
      <updated>2025-07-03T00:00:00.000Z</updated>
      <author>
        <name>Chirag Shah</name>
        <uri>https://www.bigbinary.com/blog/authors/chiraggshah</uri>
      </author>
      <category term="Rails"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/active_record_adds_support_for_deprecating_associations.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[Active Record adds the ability to safely remove associations by first deprecating them]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/active-record-deprecated-associations"><![CDATA[<p>In Rails 8, we can now mark Active Record associations as deprecated. This makes
it easy to phase out old associations from our codebase, while still keeping
them around to safely remove their usage. Whenever a deprecated association is
used, whether by calling the association, executing a query that references it,
or triggering a side‑effect like <code>:dependent</code> or <code>:touch</code>, Rails will alert us
according to our chosen reporting mode.</p>
<h3>Marking an association as deprecated</h3>
<p>Simply pass the <code>deprecated: true</code> option when declaring an association.</p>
<pre><code class="language-ruby">class User &lt; ApplicationRecord
  has_many :meetings, deprecated: true
end
</code></pre>
<p>Now, every time the meeting association is invoked, we’ll get a deprecation
warning in our logs.</p>
<pre><code class="language-bash">&gt; user.meetings
The association User#meetings is deprecated, the method meetings was invoked ((calendar):4:in '&lt;main&gt;')

&gt; User.includes(:meetings).where(id: 1)
The association User#meetings is deprecated, referenced in query to preload records ()

&gt; User.joins(:meetings).where(id: 1)
The association User#meetings is deprecated, referenced in query to join its table ()
</code></pre>
<h3>Working with different association types</h3>
<p>We can deprecate any association type.</p>
<pre><code class="language-ruby">class Order &lt; ApplicationRecord
  # has_many
  has_many :line_items, deprecated: true

  # belongs_to
  belongs_to :customer, deprecated: true

  # has_one
  has_one :profile, deprecated: true

  # has_many through
  has_many :archived_comments, through: :comments, deprecated: true
end
</code></pre>
<h3>Reporting modes and backtrace support</h3>
<p>This feature supports three deprecation modes:</p>
<ul>
<li><code>:warn</code> (default) Logs a warning to our Active Record logger.</li>
<li><code>:raise</code> Raises an exception when the deprecated association is used.</li>
<li><code>:notify</code> Emits an Active Support notification event with the key
<code>deprecated_association.active_record</code>. This can be used to send notifications
to external services like Honeybadger etc. We can check the details about its
payload
<a href="https://edgeguides.rubyonrails.org/active_support_instrumentation.html#deprecated-association-active-record">here</a>.</li>
</ul>
<p>Backtraces are disabled by default. If <code>:backtrace</code> is true, <code>:warn</code> mode will
include a clean backtrace in the message, and <code>:notify</code> mode will have a
<code>backtrace</code> key in the payload. Exceptions raised via <code>:raise</code> mode will always
have a clean stack trace.</p>
<p>We can change the global default mode in an initializer.</p>
<pre><code class="language-ruby"># config/initializers/deprecated_associations.rb
ActiveRecord.deprecated_associations_options = {
  mode: :warn,      # :warn | :raise | :notify
  backtrace: true   # whether to include a cleaned backtrace
}
</code></pre>
<p>It can also be set at an environment level.</p>
<pre><code class="language-ruby"># config/environments/development.rb
Rails.application.configure do
  config.active_record.deprecated_associations_options = { mode: :raise, backtrace: true }
end

# config/environments/production.rb
Rails.application.configure do
  config.active_record.deprecated_associations_options = { mode: :warn, backtrace: true }
end
</code></pre>
<h3>Why deprecate rather than remove?</h3>
<p>In large applications, it’s often hard to guarantee complete test coverage. Some
association usages may only surface in production.</p>
<p>Deprecating an association first, lets us:</p>
<ul>
<li>Identify every code path (tests, console, background jobs) that relies on it.</li>
<li>Gradually refactor those references before removal.</li>
<li>Ensure confidence that deleting the association won’t break anything
unexpectedly.</li>
</ul>
<p>This new feature in Rails provides a clean and intuitive way to phase out our
associations with deprecation warnings, making it easier to maintain and
refactor large codebases.</p>
<p><em>This feature <a href="https://github.com/rails/rails/pull/55285">was merged</a> recently,
and will be released in the next Rails minor/patch version.</em></p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Active Job Continuations]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/active-jobs-continuations"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/active_job_continuations.jpg"/>
      <id>https://www.bigbinary.com/blog/active-jobs-continuations</id>
      <published>2025-06-09T00:00:00.000Z</published>
      <updated>2025-06-09T00:00:00.000Z</updated>
      <author>
        <name>Vishnu M</name>
        <uri>https://www.bigbinary.com/blog/authors/vishnu-m</uri>
      </author>
      <category term="Rails"/>
      <category term="Sidekiq"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/active_job_continuations.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[Active job feature that allows us to describe jobs in an interruptible manner]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/active-jobs-continuations"><![CDATA[<p>Active Job Continuations was recently merged into Rails. We recommend that you go
through the description in the
<a href="https://github.com/rails/rails/issues/55127">pull request</a> since they are so
well written.</p>
<p>If you prefer watching a video to learn about Active Job Continuations, then we
made a video for you.</p>
<p><a href="https://www.youtube.com/watch?v=r4uuQh1Zog0"><img src="https://img.youtube.com/vi/r4uuQh1Zog0/hqdefault.jpg" alt="Watch on YouTube" width="480" height="360"></a><br><a href="https://www.youtube.com/watch?v=r4uuQh1Zog0">Watch on YouTube</a></p>
<p>In short, this feature allows you to configure your jobs in such a manner that
the job can be interrupted and next time when the job starts, it'll start from a
particular point so that the work done so far is not totally wasted.</p>
<p>This work is highly inspired by Shopify's
<a href="https://github.com/Shopify/job-iteration">job-iteration</a> gem.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Understanding Queueing Theory]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/understanding-queueing-theory"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/understanding_queueing_theory.jpg"/>
      <id>https://www.bigbinary.com/blog/understanding-queueing-theory</id>
      <published>2025-06-03T00:00:00.000Z</published>
      <updated>2025-06-03T00:00:00.000Z</updated>
      <author>
        <name>Vishnu M</name>
        <uri>https://www.bigbinary.com/blog/authors/vishnu-m</uri>
      </author>
      <category term="Rails"/>
      <category term="Sidekiq"/>
      <category term="Ruby"/>
      <category term="Solid Queue"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/understanding_queueing_theory.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[This is Part 6 of our blog series on scaling Rails applications. --- Queueing Systems In web applications, not every task needs to be processed immediately. When you upload a large video file, send a...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/understanding-queueing-theory"><![CDATA[<p><em>This is Part 6 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>.</em></p>
<hr>
<h2>Queueing Systems</h2>
<p>In web applications, not every task needs to be processed immediately. When you
upload a large video file, send a bulk email campaign, or generate a complex
report, these time-consuming operations are often handled in the background.
This is where queueing systems like <a href="https://sidekiq.org/">Sidekiq</a> or
<a href="https://github.com/rails/solid_queue">Solid Queue</a> comes into play.</p>
<p>Queueing theory helps us understand how these systems behave under different
conditions - from quiet periods to peak load times.</p>
<p>Let's understand the fundamentals of queueing theory.</p>
<h2>Basic Terminology in Queueing Theory</h2>
<ol>
<li>
<p><strong>Unit of Work</strong>: This is the individual item needing service - a job.</p>
</li>
<li>
<p><strong>Server</strong>: This is one &quot;unit of parallel processing capacity.&quot; In queuing
theory, this doesn't necessarily mean a physical server. It refers to the
ability to process one unit of work at a time. For JRuby or TruffleRuby, each
thread can be considered a separate &quot;server&quot; since they can execute in
parallel. For CRuby/MRI, because of the GVL, the concept of a &quot;server&quot; is
different. We'll discuss it later.</p>
</li>
<li>
<p><strong>Queue Discipline</strong>: This is the rule determining which unit of work is
selected next from the queue. For Sidekiq and Solid Queue, it is FCFS(First
Come First Serve). If there are multiple queues, which job is selected
depends on the priority of the queue.</p>
</li>
<li>
<p><strong>Service Time</strong>: The actual time it takes to process a unit of work (how
long a job takes to execute).</p>
</li>
<li>
<p><strong>Latency/Wait Time</strong>: How long jobs spend waiting in the queue before being
processed.</p>
</li>
<li>
<p><strong>Total Time</strong>: The sum of service time and wait time. It's the complete
duration from when a job is enqueued until it finishes executing.</p>
</li>
</ol>
<h2>Little's law</h2>
<p>Little's law is a theorem in queuing theory that states that the average number
of jobs in a system is equal to the average arrival rate of new jobs multiplied
by the average time a job spends in the system.</p>
<pre><code class="language-ruby">L = λW
</code></pre>
<p>L = Average number of jobs in the system <br>
λ = Average arrival rate of new jobs <br>
W = Average time a job spends in the system</p>
<p>For example, if jobs arrive at a rate of 10 per minute (λ), and each job takes
30 seconds (W) to complete:</p>
<p>Average number of jobs in system = 10 jobs/minute * 0.5 minutes = 5 jobs</p>
<p>This helps us understand the current state of our system and that there are 5
jobs in the system on average at any given moment.</p>
<p><code>L</code> is also called <strong>offered traffic</strong>.</p>
<p>Note: Little's Law assumes the arrival rate is consistent over time.</p>
<h3>Managing Utilization</h3>
<p>Utilization measures how busy our processing capacity is.</p>
<p>Mathematically, it is the ratio of how much processing capacity we're using to
the processing capacity we have.</p>
<pre><code class="language-ruby">utilization = (average number of jobs in the system/capacity to handle jobs) * 100
</code></pre>
<p>In other words, it could be written as follows.</p>
<pre><code class="language-ruby">utilization = (offered_traffic / parallelism) * 100
</code></pre>
<p>For example, if we are using Sidekiq to manage our background jobs then in a
single-threaded case, parallelism is equal to the number of Sidekiq processes.</p>
<p>Let's look at a practical case with numbers:</p>
<ul>
<li>We have 30 jobs arriving every minute</li>
<li>It takes 0.5 minutes to process a job</li>
<li>We have 20 Sidekiq processes</li>
</ul>
<p>In this case, the utilization will be:</p>
<pre><code class="language-ruby">utilization = ((30 jobs/minute * 0.5 minutes) / 20 processes) * 100 = 75%
</code></pre>
<h3>High utilization is bad for performance</h3>
<p>Let's assume we maintain 100% utilization in our system. It means that if, on
average, we get 30 jobs per minute, then we have just enough capacity to handle
30 jobs per minute.</p>
<p>One day, we started getting 45 jobs per minute. Since utilization is at 100%,
there is no extra room to accommodate the additional load. This leads to higher
latency.</p>
<p>Hence, having a high utilization rate may result in low performance, as it can
lead to higher latency for specific jobs.</p>
<h3>The knee curve</h3>
<p>Mathematically, it would seem that only when the utilization rate hits 100%
should the latency spike up. However, in the real world, it has been found that
latency begins to increase dramatically when utilization reaches around 70-75%.</p>
<p>If we draw a graph between utilization and performance then the graph would look
something like this.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-queueing-theory/knee-curve.png" alt="The knee curve"></p>
<p>The point at which the curve bends sharply upwards is called &quot;Knee&quot; in the
performance curve. At this point, the exponential effects predicted by queueing
theory become pronounced, causing the queue latency to climb up quickly.</p>
<p>Running any system consistently above 70-75% utilization significantly increases
the risk of spiking the latency, as jobs spend more and more time waiting.</p>
<p>This would directly impact the customer experience, as it could result in delays
in sending emails or making calls to Twilio to send SMS messages, etc.</p>
<p>Tracking this latency will be covered in the upcoming blogs. The tracking of
metrics depends on the queueing backend used (Sidekiq or Solid Queue).</p>
<h2>Concurrency and theoretical parallelism</h2>
<p>In Sidekiq, a process is the primary unit of parallelism. However, concurrency
(threads per process) significantly impacts a process's effective throughput.
Because of GVL we need to take into account how long the job is waiting for I/O.</p>
<p>The more time a job spends waiting on external resources (like databases or
APIs) rather than executing Ruby code, the more other threads within the same
process can run Ruby code while the first thread waits.</p>
<p>We learned about Amdahl's law in
<a href="https://www.bigbinary.com/blog/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency">Part 3</a>
of this series.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-queueing-theory/amdahls-law.png" alt="Amdahl's law"></p>
<p>Where:</p>
<p><code>p</code> is the portion that can be parallelized (the I/O percentage)</p>
<p><code>n</code> is the number of threads (concurrency)</p>
<p>Speedup is equivalent to theoretical parallelism in this context. In queueing
theory, parallelism refers to how many units of work can be processed
simultaneously. When we calculate speedup using Amdahl's Law, we're essentially
determining how much faster a multi-threaded system can handle work compared to
a single-threaded system.</p>
<p>Let's assume that a system has an I/O of 50% and a concurrency of 10. Then
Speedup will be:</p>
<pre><code class="language-ruby">Speedup = 1 / ((1 - 0.5) + 0.5 / 10) = 1 / 0.55 = 1.82 ≈ 2
</code></pre>
<p>This means one Sidekiq process with 10 threads will handle jobs twice as fast as
Sidekiq with a single process with a single thread.</p>
<p>Let's recap what we are saying here. We are assuming that the system has I/O of
50%. System is using a single Sidekiq process with 10 threads(concurrency). Then
because of 10 threads the system has a speed gain of 2x compared to system
having just a single thread. In other words, just because we have 10 threads
running we are not going to gain 10X performance improvement. What those 10
threads is getting us is what is called &quot;theoretical parallelism&quot;.</p>
<p>Similarly, for other values of I/O and Concurrency, we can get the theoretical
parallelism.</p>
<table>
<thead>
<tr>
<th>I/O</th>
<th>Concurrency</th>
<th>Theoretical parallelism</th>
</tr>
</thead>
<tbody>
<tr>
<td>5%</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>25%</td>
<td>5</td>
<td>1.25</td>
</tr>
<tr>
<td>50%</td>
<td>10</td>
<td>2</td>
</tr>
<tr>
<td>75%</td>
<td>16</td>
<td>3</td>
</tr>
<tr>
<td>90%</td>
<td>32</td>
<td>8</td>
</tr>
<tr>
<td>95%</td>
<td>64</td>
<td>16</td>
</tr>
</tbody>
</table>
<p>Let's go over one more time. In the last example, what we are stating is that if
a system has 95% I/O and if the system has 64 threads running, then that will
give 16x performance improvement over the same system running on a single
thread.</p>
<p>Here is the graph for this data.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-queueing-theory/concurrency-vs-effective-parallelism.png" alt="Theoretical Parallelism"></p>
<p>As shown in the graph, a Sidekiq process with 16 threads handling jobs that are
75% I/O-bound achieves a theoretical parallelism of approximately 3. In other
words, 3x performance improvement is there over a single-threaded system.</p>
<h2>Calculating the number of processes required</h2>
<p>At the beginning of this article, we discussed &quot;Little's law&quot; and we discussed
that <code>L</code> is also called &quot;offered traffic,&quot; which depicts the &quot;average number of
jobs in the system&quot;.</p>
<p>If &quot;offered traffic&quot; is 5, then it means we have 5 units of work arriving on
average that requires processing simultaneously.</p>
<p>We just learned that if the utilization is greater than 75% then it can cause
problems as there is a risk of latency to spike.</p>
<p>For queues with low latency requirements(eg. <code>urgent</code>), we need to target a
lower utilization rate. Let's say we want utilization to be around 50% to be on
the safe side.</p>
<p>Now we know the utilization rate that we need to target and we know the &quot;offered
traffic&quot;. So now we can calculate the &quot;parallelism&quot;.</p>
<pre><code class="language-ruby">utilization = offered_traffic / parallelism
=&gt; 0.50 = 5 / parallelism
=&gt; parallelism = 5 / 0.50 = 10
</code></pre>
<p>This means we need a theoretical parallelism of 10 to ensure that the
utilization is 50% at max.</p>
<p>Let's assume the jobs in this queue have an average of <code>50%</code> I/O. Based on the
above mentioned graph, we can see that if the concurrency is 10 then we get
parallelism of 2. However, increasing the concurrency doesn't increase the
parallelism. It means if we want 10 parallelism, then we can't just switch to
concurrency of 50. Even the concurrency of 50 (or 50 threads) will only yield a
parallelism of 2.</p>
<p>So we have no choice but to add more processes. Since one process with 10
concurrency is yielding a parallelism of 2 we need to add 5 processes to get 10
parallelism.</p>
<p><em>To get the I/O wait percentage, we can make use of perfm.
<a href="https://github.com/bigbinary/perfm?tab=readme-ov-file#sidekiq-gvl-instrumentation">Here</a>
is the documentation on how it can be done.</em></p>
<pre><code class="language-ruby">Total number of Sidekiq processes required = 10 / 2 = 5
</code></pre>
<p>Here we're talking about Sidekiq free version, where we'll only be able to run a
single process per dyno. If we're using Sidekiq Pro, we can run multiple
processes per dyno via Sidekiq Swarm.</p>
<p>We can provision 5 dynos for the urgent queue. But we should always have a queue
time based autoscaler like Judoscale enabled to handle spikes.</p>
<h2>Sources of Saturation</h2>
<p>We discussed earlier that, in the context of queueing theory, the saturation
point is typically reached at around 70-75% utilization. This is from the point
of view of further gains by adding more threads.</p>
<p>However saturation can occur in other parts of the system.</p>
<h3>CPU</h3>
<p>The servers running your Sidekiq processes have finite CPU and memory. While CPU
usage is a metric we can track for Sidekiq, it's generally not the only one we
need to focus on for scaling decisions.</p>
<p>CPU utilization can be misleading. If our jobs spend most of their time doing
I/O (like making API calls or database queries), in which case CPU usage will be
very low, even when our Sidekiq system is at capacity.</p>
<h3>Memory</h3>
<p>Memory utilization impacts performance very differently from CPU utilization.
Memory utilization generally exhibits minimal changes in latency or throughput
from 0% to 100% utilization. However, after 100% utilization, things will start
to deteriorate significantly. The system will start using the swap memory, which
can be very slow and thereby increase the job service times.</p>
<h3>Redis</h3>
<p>Another place where saturation can occur is in our datastore i.e Redis in case
of Sidekiq. We have to make sure that we provision a separate Redis instance for
Sidekiq and also make sure to set the eviction policy to <code>noeviction</code>. This
ensures that Redis will reject new data when the memory limit is reached,
resulting in an explicit failure rather than silently dropping important jobs.</p>
<p><em>This was Part 6 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>. If any part of the
blog is not clear to you then please write to us at
<a href="https://www.linkedin.com/company/bigbinary">LinkedIn</a>,
<a href="https://twitter.com/bigbinary">Twitter</a> or
<a href="https://bigbinary.com/contact">BigBinary website</a>.</em></p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Understanding Active Record Connection Pooling]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/understanding-active-record-connection-pooling"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/understanding_active_record_connection_pooling.jpg"/>
      <id>https://www.bigbinary.com/blog/understanding-active-record-connection-pooling</id>
      <published>2025-05-13T00:00:00.000Z</published>
      <updated>2025-05-13T00:00:00.000Z</updated>
      <author>
        <name>Vishnu M</name>
        <uri>https://www.bigbinary.com/blog/authors/vishnu-m</uri>
      </author>
      <category term="Rails"/>
      <category term="Puma"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/understanding_active_record_connection_pooling.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[This is Part 5 of our blog series on scaling Rails applications. Database Connection Pooling When a Rails application needs to interact with a database, it establishes a connection, which is a...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/understanding-active-record-connection-pooling"><![CDATA[<p><em>This is Part 5 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>.</em></p>
<h2>Database Connection Pooling</h2>
<p>When a Rails application needs to interact with a database, it establishes a
connection, which is a dedicated communication channel between the application
and the database server. When a new request comes to Rails, the operation can be
handled like this.</p>
<ol>
<li>Create a connection</li>
<li>Do database operation</li>
<li>Close the connection</li>
</ol>
<p>When the next request comes, then repeat the above process.</p>
<p>Creating new database connections is an expensive operation - it takes time to
establish the connection, authenticate, and set up the communication channel. It
means that every single time a request comes, we are spending time in setting up
the connection.</p>
<p>Wouldn't it be better to store the established connection to somewhere and when
a new request comes, then get the pre-established connection from this pool.
This expedites the process since we don't need to create a connection and close
a connection in every single request.</p>
<p>The new process might look like this.</p>
<ol>
<li>Create a connection</li>
<li>Do database operation</li>
<li>Put the connection in a pool</li>
</ol>
<p>Now, when a new request comes, the operation will look like this.</p>
<ol>
<li>Get the connection from the pool</li>
<li>Do database operation</li>
<li>Return the connection to the pool</li>
</ol>
<p>Database connection pooling is a performance optimization technique that
maintains a set of reusable database connections.</p>
<h2>Active Record Connection Pool Implementation</h2>
<p>Active Record manages a pool of database connections for each web and background
process. Each process will have a connection pool of its own, which means a
Rails application running with multiple processes (like Puma processes, Sidekiq
processes) will have multiple independent connection pools. The pool is a set of
database connections that are shared among threads in the <strong>same process</strong>.</p>
<p>Note that the pooling happens at the process level. A thread from process A
can't get a connection from process B.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-active-record-connection-pooling/without-pg-bouncer.png" alt="Connection pooling"></p>
<p>When a connection is needed, the thread checks out a connection from the pool,
perform operations, and then returns the connection to the pool. This is done at
a query level now. For each individual query, a connection is leased, used, and
then returned back to the pool.</p>
<p>Pre Rails 7.2, the connection used to be leased and held till the end of the
request if it is a web request and till the job is done if it is a background
job. This was a problem for applications that spent a lot of time doing I/O. The
thread will hog the connection for the entire duration of the I/O operation
limiting the number of queries that can be executed concurrently. To facilitate
this change and make query caching work, the query cache has been
<a href="https://github.com/rails/rails/pull/50938/">updated</a> to be owned by the pool.</p>
<p>This means that the query cache is now shared across all connections in the
pool. Previously, each connection had its own query cache. As the whole request
used the same connection, this was fine. But now, as the connection is leased
for each query, the query cache needs to be shared across all connections in the
pool.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-active-record-connection-pooling/connection-leasing-comparison.png" alt="Connection-leasing-comparison"></p>
<h2>Connection Pool Configuration Options</h2>
<p>Active Record's connection pool behavior can be customized through several
configuration options in the database.yml file:</p>
<ul>
<li><a href="https://api.rubyonrails.org/classes/ActiveRecord/DatabaseConfigurations/HashConfig.html#method-i-pool">pool</a>:
Sets the maximum number of connections the pool will maintain. The default is
tied to <code>RAILS_MAX_THREADS</code>, but you can set it to any value. There is a small
problem when you set it to <code>RAILS_MAX_THREADS</code> which we'll discuss later.</li>
<li><a href="https://api.rubyonrails.org/classes/ActiveRecord/DatabaseConfigurations/HashConfig.html#method-i-checkout_timeout">checkout timeout</a>:
Determines how long a thread will wait to get a connection before timing out.
The default is 5 seconds. If all connections are in use and a thread waits
longer than this value, an <code>ActiveRecord::ConnectionTimeoutError</code> exception
will be raised.</li>
<li><a href="https://api.rubyonrails.org/classes/ActiveRecord/DatabaseConfigurations/HashConfig.html#method-i-idle_timeout">idle timeout</a>:
Specifies how long a connection can remain idle before it's removed from the
pool. The default is 300 seconds. This helps reclaim resources from
connections that aren't being used.</li>
<li><a href="https://api.rubyonrails.org/classes/ActiveRecord/DatabaseConfigurations/HashConfig.html#method-i-reaping_frequency">reaping frequency</a>:
Controls how often the Reaper(which we'll discuss shortly) process runs to
remove dead or idle connections. The default is 60 seconds.</li>
</ul>
<h2>Active Record Connection Pool Reaper</h2>
<p>Database connections can sometimes become &quot;dead&quot; due to issues like database
restarts, network problems etc. Active Record provides Reaper to handle this.</p>
<p>The Reaper periodically checks connections in the pool and removes dead
connections as well as idle connections that have been checked out for a long
time.</p>
<p>It acts somewhat like a garbage collector for database connections. The Reaper
uses the <code>idle_timeout</code> setting to determine how long a connection can remain
idle before being removed, tracking idle time based on when connections were
last used.</p>
<p>There is another configuration option called <code>reaping_frequency</code> that controls
how often the Reaper runs to remove dead or idle connections from the pool. By
default, this is set to 60 seconds. It means the Reaper will wake up once every
minute to perform its maintenance tasks.</p>
<p>If your application is spiky and receives a lot of traffic in surges, then set
the reaping frequency and idle timeout to a lower value. This will ensure that
the reaper runs more frequently and removes idle connections more quickly,
helping to keep the connection pool healthy and responsive.</p>
<h2>Why are idle connections bad?</h2>
<p>Idle database connections can significantly impact database performance for
several interconnected reasons:</p>
<p><strong>Memory Consumption</strong>: Each database connection, even when idle, maintains its
own memory allocation. The database must reserve memory for session state,
buffers, user context, and transaction workspace. This memory remains allocated
even when the connection isn't doing any work. For example, if each connection
uses 10 MB of memory, 100 idle connections would unnecessarily consume 1 GB of
your database's memory that could otherwise be used for active queries, caching,
or other productive work.</p>
<p><strong>CPU overhead</strong>: While &quot;idle&quot; suggests no activity, the database still performs
regular maintenance work for each connection. It must monitor connection health
via keepalive checks, manage process tables etc.</p>
<p>The crucial issue is that the overhead of having idle connections scales
non-linearly. As we add more idle connections, the database spends an increasing
proportion of its CPU time just managing these connections rather than
processing actual queries. Thankfully, the reaper handles this for us.</p>
<h2>How many database connections will the web and background processes utilize at maximum?</h2>
<p>As we learned, the connection pool is managed at the process level. Each Rails
process maintains its own pool.</p>
<ol>
<li><strong>In web processes (Puma)</strong>:</li>
</ol>
<p>Each Puma process is a separate process with its own connection pool. In a
process, each thread can check out one connection. Therefore, maximum
connections needed per process equals <code>max_threads</code> setting in Puma.</p>
<ol start="2">
<li><strong>In background processes (Sidekiq)</strong>:</li>
</ol>
<p>Sidekiq runs as a separate process with its own connection pool. The Sidekiq
<code>concurrency</code> setting determines the number of threads and therefore the maximum
connections needed equals the <code>concurrency</code> value.</p>
<p><em>Note: If you're using Sidekiq swarm and running multiple Sidekiq processes,
then take that it into account.</em></p>
<p>We can calculate the total potential connections for a typical application as
shown below.</p>
<pre><code>Web connections = Number of web dynos * Number of Puma processes
                  * `max_threads` value

Background connections = Number of worker dynos * Number of Sidekiq processes
                         * Threads per process

Total number of connections = Web connections + Background connections
</code></pre>
<p>The key thing to note here is that the database needs to support at least these
many simultaneous connections.</p>
<p><em>Note that if preboot is enabled, then the maximum number of connections will be
<strong>double</strong> the above value. This is because during the release phase, there is a
small window in which both the old dynos and new dynos are running.</em></p>
<p>In Rails 7,
<a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-load_async"><code>load_async</code></a>
was introduced which allows us to run database queries asynchronously in a
background thread. When <code>load_async</code> is in used, the calculation for maximum
number of connections needed changes a bit. First, let's understand how
<code>load_async</code> works.</p>
<h2>How <code>load_async</code> works</h2>
<p><code>load_async</code> allows Rails to execute database queries asynchronously in
background threads. Unlike regular ActiveRecord queries which are lazily loaded,
<code>load_async</code> queries are always executed immediately in background threads and
joined to the main thread when results are needed.</p>
<p>The async executor is configured through the
<code>config.active_record.async_query_executor</code> setting. There are three possible
configurations:</p>
<ol>
<li><code>nil</code> (default): Async queries are disabled, and <code>load_async</code> will execute
queries synchronously.</li>
<li><code>:global_thread_pool</code>: Uses a single thread pool for all database
connections.</li>
<li><code>:multi_thread_pool</code>: Uses separate thread pools for each database
connection.</li>
</ol>
<p>Rails provides a configuration option named
<a href="https://guides.rubyonrails.org/configuring.html#config-active-record-global-executor-concurrency">global_executor_concurrency</a>
(default: 4) that controls how many concurrent async queries can run per
process. So, the maximum number of connections per process when <code>load_async</code> is
used.</p>
<pre><code class="language-ruby">Maximum connections per process = Process level concurrency
                                  + global_executor_concurrency + 1
</code></pre>
<p>Here Process level concurrency means <code>max_threads</code> for the Puma process and
<code>sidekiq_conurrency</code> for the Sidekiq process.</p>
<p>The &quot;+1&quot; accounts for the main control thread, which may occasionally need a
connection (e.g., during model introspection at class load time).</p>
<p>There is a nice
<a href="https://judoscale.com/tools/heroku-postgresql-connection-calculator">calculator</a>
created by the folks at <a href="https://judoscale.com/">Judoscale</a> which can be used to
calculate the maximum number of connections needed for your application.</p>
<h2>Setting Database Pool Size Configuration</h2>
<p>Our <code>database.yml</code> file has the following line.</p>
<pre><code class="language-yaml">pool: &lt;%%= ENV.fetch(&quot;RAILS_MAX_THREADS&quot;) { 5 } %&gt;
</code></pre>
<p>We know that a thread doesn't take more than one DB connection. So the maximum
number of connections needed per pool is equal to the total number of threads.
So the above configuration looks fine.</p>
<p>However this doesn't take into account whether we use <code>load_async</code> or not. If we
use <code>load_async</code>, then the number of connections needed per process will be
<code>RAILS_MAX_THREADS + global_executor_concurrency + 1</code>.</p>
<p>Do we really need to go into this much detail to determine the pool size? Turns
out there is a much easier answer.</p>
<p>Almost all database hosting providers mention the maximum number of connections
allowed in their plan. We can just set the pool config to the maximum number of
connections supported by our database plan. Let us say we have a Standard-0
database on Heroku. It supports up to 120 connections. So we can set the pool
config to 120.</p>
<pre><code class="language-yaml">pool: 120
</code></pre>
<p>We can do this because the database connections are lazily initialized in the
pool. The application doesn't create more database connections than it needs. So
we needn't be conservative here.</p>
<p>The only thing we need to ensure is that the maximum connection utilization
doesn't exceed the database plan limit. If that happens, then we have another
solution - PgBouncer.</p>
<h2>PgBouncer</h2>
<p>PgBouncer is a lightweight connection pooler for PostgreSQL. It sits between our
application(s) and our PostgreSQL database and manages a pool of database
connections.</p>
<p>While both PgBouncer and Active Record provide connection pooling, they operate
at different levels and serve different purposes.</p>
<p>Active Record connection pool operates within a single Ruby process and manages
connections for threads within the process, whereas PgBouncer is an external
connection pooler that sits between the application and the database and manages
connections across all the application processes.
<img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-active-record-connection-pooling/with-pg-bouncer.png" alt="With PgBouncer"></p>
<h2>The dreaded ActiveRecord::ConnectionTimeoutError</h2>
<p>This error comes up when a thread waits more than <code>checkout_timeout</code> seconds to
acquire a connection. This usually happens when the <code>pool</code> size is set to a
value less than the concurrency.</p>
<p>For example, lets say we have set the Sidekiq concurrency to 10 and pool size
to 5. If we have more than 5 threads wanting a connection at any point of time,
the threads will have to wait.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-active-record-connection-pooling/threads-waiting-for-connection.png" alt="Connection pooling"></p>
<p>What's the solution? As we discussed earlier setting the <code>pool</code> to a really high
value should fix the error in most cases.</p>
<p>Even after setting the config correctly, <code>ActiveRecord::ConnectionTimeoutError</code>
can still happen and it could be puzzling. Let is discuss a few scenarios where
this can happen.</p>
<h2>Custom code spinning up new threads and taking up connections</h2>
<pre><code class="language-ruby">class SomeService
  def process
    threads = []

    5.times do |index|
      threads &lt;&lt; Thread.new do
        ActiveRecord::Base.connection.execute(&quot;select pg_sleep(5);&quot;)
      end
    end

    threads.each(&amp;:join)
  end
end
</code></pre>
<p>Here 5 threads are spun up. Note that these threads also take up connections
from the same pool allotted to the process.</p>
<h3>Active Storage proxy mode</h3>
<p>Even if our application code is not spinning up new threads, Rails itself can
sometimes spin up additional threads. For example Active Storage configured in
<a href="https://edgeguides.rubyonrails.org/active_storage_overview.html#proxy-mode">proxy mode</a>.</p>
<p>Active Storage’s proxy controllers
<a href="https://github.com/rails/rails/blob/b97a7625970c74f2273211ccb17046049f409110/activestorage/app/controllers/active_storage/blobs/proxy_controller.rb">1</a>,
<a href="https://github.com/rails/rails/blob/b97a7625970c74f2273211ccb17046049f409110/activestorage/app/controllers/active_storage/representations/proxy_controller.rb">2</a>
generate responses as streams, which require dedicated threads for processing.</p>
<p>This means that when serving an Active Storage file through one of these proxy
controllers, Rails actually utilizes two separate threads - one for the main
request and another for the streaming process. Each of these threads requires
its own separate database connection from the ActiveRecord connection pool.</p>
<h3>Rack timeouts</h3>
<p><a href="https://github.com/zombocom/rack-timeout">rack-timeout</a> is commonly used across
Rails applications to automatically terminate long-running requests. While it
helps prevent server resources from being tied up by slow requests, it can also
cause a few issues.</p>
<p>Rack timeout uses Ruby's
<a href="https://rubyapi.org/3.4/o/thread#method-i-raise">Thread#raise</a> API to terminate
requests that exceed the configured timeout. When a timeout occurs, rack-timeout
raises a <code>Rack::Timeout::RequestTimeoutException</code> from another thread. If this
exception is raised while a thread is in the middle of database operations, it
can prevent proper cleanup of database connections.</p>
<h2>Tracking down ActiveRecord::ConnectionTimeoutErrors</h2>
<p>If we still frequently see <code>ActiveRecord::ConnectionTimeoutError</code> exceptions in
our application, we can get additional context by logging the connection pool
info to our error monitoring service. This can help identify which all threads
were holding onto the connections when the error occurred.</p>
<pre><code class="language-ruby">config.before_notify do |notice|
  if notice.error_class == &quot;ActiveRecord::ConnectionTimeoutError&quot;
    notice.context = { connection_pool_info: detailed_connection_pool_info }
  end
end

def detailed_connection_pool_info
  connection_info = {}

  ActiveRecord::Base.connection_pool.connections.each_with_index do |conn, index|
    connection_info[&quot;connection_#{index + 1}&quot;] = conn.owner ? conn.owner.inspect : &quot;[UNUSED]&quot;
  end

  connection_info[&quot;current_thread&quot;] = Thread.current.inspect
  connection_info
end
</code></pre>
<p><code>&lt;thread_obj&gt;.inspect</code> gives us the name, id and status of the thread. For
example, if one entry in the hash looks like
<code>#&lt;Thread:0x00006a42eca73ba0@puma srv tp 002 /app/.../gems/puma-6.2.2/lib/puma/thread_pool.rb:106 sleep_forever&gt;</code>
then it means that the connection is taken up by a Puma thread.</p>
<h2>Monitoring Active Record Connection Pool Stats</h2>
<p>If we want to monitor Active Record Connection Pool stats, then periodically we
need to send the stats to a service provider which can display the data
graphically. For periodically checking the stat, we are using
<a href="https://github.com/jmettraux/rufus-scheduler">rufus-scheduler</a> gem. For
collecting the data and showing the data we are using NewRelic but you can use
any APM of your choice. We have configured to send the pool stat every 15
seconds.</p>
<p><a href="https://gist.github.com/vishnu-m/8cfae21cac385aa07819c8805e491872">Here</a> is the
gist which collects and sends data.</p>
<p><em>This was Part 5 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>. If any part of the
blog is not clear to you then please write to us at
<a href="https://www.linkedin.com/company/bigbinary">LinkedIn</a>,
<a href="https://twitter.com/bigbinary">Twitter</a> or
<a href="https://bigbinary.com/contact">BigBinary website</a>.</em></p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Finding the ideal threads per process with GVL tooling]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/tuning-puma-max-threads-configuration-with-gvl-instrumentation"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/tuning_puma_max_threads_configuration_with_gvl_instrumentation.jpg"/>
      <id>https://www.bigbinary.com/blog/tuning-puma-max-threads-configuration-with-gvl-instrumentation</id>
      <published>2025-05-06T00:00:00.000Z</published>
      <updated>2025-05-06T00:00:00.000Z</updated>
      <author>
        <name>Vishnu M</name>
        <uri>https://www.bigbinary.com/blog/authors/vishnu-m</uri>
      </author>
      <category term="Rails"/>
      <category term="Puma"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/tuning_puma_max_threads_configuration_with_gvl_instrumentation.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[This is Part 4 of our blog series on scaling Rails applications. In part 1 we saw how to find ideal number of processes for our Rails application. In part 3, we learned about Amdahl's law, which...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/tuning-puma-max-threads-configuration-with-gvl-instrumentation"><![CDATA[<p><em>This is Part 4 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>.</em></p>
<p>In
<a href="https://bigbinary.com/blog/understanding-puma-concurrency-and-the-effect-of-the-gvl-on-performance">part 1</a>
we saw how to find ideal number of processes for our Rails application.</p>
<p>In
<a href="https://bigbinary.com/blog/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency">part 3</a>,
we learned about Amdahl's law, which helps us find the ideal number of threads
theoretically.</p>
<p>In this blog, we'll run a bunch of tests on our real production application to
see what the actual number of threads should be for each process.</p>
<p>In
<a href="https://bigbinary.com/blog/understanding-puma-concurrency-and-the-effect-of-the-gvl-on-performance">part 1</a>
we discussed the presence of GVL and the concept of thread switching. Based on
the GVL's interaction, a thread can be in one of these three states.</p>
<ol>
<li><strong>Running</strong>: The thread has the GVL and is executing Ruby code.</li>
<li><strong>Idle</strong>: The thread doesn't want the GVL because it is performing I/O
operations.</li>
<li><strong>Stalled</strong>: The thread wants the GVL and is waiting for it in the GVL wait
queue.</li>
</ol>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/tuning-puma-max-threads-configuration-with-gvl-instrumentation/thread-states.png" alt="Thread states"></p>
<p>Based on the above diagram, we can approximately equate <code>idle time</code> to
<code>I/O time</code>.</p>
<h2>GVL instrumentation using perfm</h2>
<p>Thanks to Jean Boussier's work on the
<a href="https://bugs.ruby-lang.org/issues/18339">GVL instrumentation API</a> and John
Hawthorn's work on <a href="https://github.com/jhawthorn/gvl_timing">gvl_timing</a>, we can
now measure the time a thread spends in each of these states for apps running on
Ruby 3.2 or higher.</p>
<p>Using the great work done by these folks, we have created
<a href="https://github.com/bigbinary/perfm">perfm</a>, to help us figure out the ideal
number of Puma threads based on the application's workload.</p>
<p>Perfm inserts a Rack middleware to our Rails application. This middleware
instruments the GVL, collects the required metrics and stores them in a table.
It also has a <code>Perfm::GvlMetricsAnalyzer</code> class which can be used to generate a
report on the data collected.</p>
<h3>Using perfm to measure the application's I/O percentage</h3>
<p>To use perfm, we need to add the following line to our Gemfile.</p>
<pre><code class="language-ruby">gem 'perfm'
</code></pre>
<p>We'll run <code>bin/rails generate perfm:install</code>. This will generate the migration
to create <code>perfm_gvl_metrics</code> which will be used to store request-level metrics.</p>
<p>Now we'll create an initializer <code>config/initializers/perfm.rb</code>.</p>
<pre><code class="language-ruby">Perfm.configure do |config|
  config.enabled = true
  config.monitor_gvl = true
  config.storage = :local
end

Perfm.setup!
</code></pre>
<p>After deploying the code to production, we need to collect around 20K requests
as that will give us a fair number of data points to analyze. The GVL monitoring
can be disabled after that by setting <code>config.monitor_gvl</code> to <code>false</code> so that
the table doesn't keep growing.</p>
<p>After collecting the request data, now it's time to analyze it.</p>
<p>Run the following code in the Rails console.</p>
<pre><code class="language-ruby">irb(main):001* gvl_metrics_analyzer = Perfm::GvlMetricsAnalyzer.new(
irb(main):002*   start_time: 2.days.ago, # configure this
irb(main):003*   end_time: Time.current
irb(main):004&gt; )
irb(main):005&gt;
irb(main):006&gt; results = gvl_metrics_analyzer.analyze
irb(main):007&gt; io_percentage = results[:summary][:total_io_percentage]
=&gt; 45.09
</code></pre>
<p>This will give us the percentage of time spent doing I/O. We ran it in our
<a href="https://neeto.com/cal">NeetoCal</a> production application and we got a value of
45%.</p>
<p>As we discussed in
<a href="https://www.bigbinary.com/blog/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency">part 3</a>,
Amdahl's law gives us a theoretical maximum speedup based on the parallelizable
portion of our workload. The formula is given below.
<img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/tuning-puma-max-threads-configuration-with-gvl-instrumentation/amdahls-law.png" alt="Amdahl's law formula"></p>
<p>Where:</p>
<ul>
<li><code>p</code> is the portion that can be parallelized (in our case, it's 0.45)</li>
<li><code>N</code> is the number of threads</li>
<li><code>(1 - p)</code> is the portion that must run sequentially (in our case, it's 0.55)</li>
</ul>
<p>Let's calculate the theoretical speedup for different numbers of threads with p
= 0.45:</p>
<table>
<thead>
<tr>
<th>Thread Count (N)</th>
<th>Speedup</th>
<th>% Improvement from previous run</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>1.00</td>
<td>-</td>
</tr>
<tr>
<td>2</td>
<td>1.29</td>
<td>29%</td>
</tr>
<tr>
<td>3</td>
<td>1.43</td>
<td>11%</td>
</tr>
<tr>
<td>4</td>
<td>1.52</td>
<td>6%</td>
</tr>
<tr>
<td>5</td>
<td>1.57</td>
<td>3%</td>
</tr>
<tr>
<td>6</td>
<td>1.60</td>
<td>2%</td>
</tr>
<tr>
<td>8</td>
<td>1.64</td>
<td>&lt;2%</td>
</tr>
<tr>
<td>16</td>
<td>1.69</td>
<td>&lt;1%</td>
</tr>
<tr>
<td>∞</td>
<td>1.82</td>
<td>-</td>
</tr>
</tbody>
</table>
<p>We can see that after 4 threads, the percentage improvement drops below 5%. This
means that 4 is a reasonable value for <code>max_threads</code>. We can set the value of
<code>RAILS_MAX_THREADS</code> to 4.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/tuning-puma-max-threads-configuration-with-gvl-instrumentation/speedup-vs-thread-count.png" alt="Speedup V/S Number of threads"></p>
<p>Looking at the table, adding a 5th thread would only give us a 3% performance
improvement, which may not justify the additional memory usage and potential GVL
contention.</p>
<p>We have also created a small application to help visualize and find the ideal
number of threads when the I/O percentage is provided as input.
<a href="https://v0-single-page-application-lake.vercel.app/">Here</a> is the link to the
app.</p>
<h2>Validate thread count using stall time</h2>
<p>This value of <code>4</code> we got theoretically by using Amdahl's law. Now it's time to
put this law to test. Let's see in the real world if the value of <code>4</code> is the
correct value or not.</p>
<p>What we need to do is start with <code>RAILS_MAX_THREADS</code> env variable (Puma
<code>max_threads</code>) set to <code>4</code> and then check if this value provides minimal GVL
contention. By GVL contention, we mean the amount of time a thread spends
waiting for the GVL i.e the stall time.</p>
<p>If the stall time is high, that means the set thread count is high. We don't
want our request threads to spend their time doing nothing causing latency
spikes.<code>75ms</code> is an acceptable value for stall time. The lesser the better of
course.</p>
<p>The average stall time can be found in the perfm analyzer results. As we
mentioned earlier, we had collected data for <a href="https://neeto.com/cal">NeetoCal</a>.
Now let's find the average stall time.</p>
<pre><code class="language-ruby">irb(main):001* gvl_metrics_analyzer = Perfm::GvlMetricsAnalyzer.new(
irb(main):002*   start_time: 2.days.ago,
irb(main):003*   end_time: Time.current,
irb(main):004*   puma_max_threads: 4
irb(main):005&gt; )
irb(main):006&gt; results = gvl_metrics_analyzer.analyze
irb(main):007&gt; avg_stall_ms = results[:summary][:average_stall_ms]
=&gt; 110.24
</code></pre>
<p>The stall time seems a bit high. Let us decrease the <code>RAILS_MAX_THREADS</code> value
by 1 and collect a few data points(i.e around 20K requests). Now the value of
<code>RAILS_MAX_THREADS</code> will be <code>3</code>. This process has to be repeated until we find
the value for which the average stall time is less than <code>75ms</code>.</p>
<pre><code class="language-ruby">irb(main):001* gvl_metrics_analyzer = Perfm::GvlMetricsAnalyzer.new(
irb(main):002*   start_time: 2.days.ago,
irb(main):003*   end_time: Time.current,
irb(main):004*   puma_max_threads: 3
irb(main):005&gt; )
irb(main):006&gt; results = gvl_metrics_analyzer.analyze
irb(main):007&gt; avg_stall_ms = results[:summary][:average_stall_ms]
=&gt; 79.38
</code></pre>
<p>Now the output is closer to <code>75 ms</code>.</p>
<p>Hence we can finalize on the value 3 as the value for <code>RAILS_MAX_THREADS</code>. If we
decrease the value again by one i.e set it to 2, the stall time will decrease
but we're limiting the concurrency of our application. It is a trade-off.</p>
<p>Remember that our goal is to maximize concurrency while minimizing GVL
contention. But if our app spends a lot of time doing I/O - for instance, if we
have a proxy application that makes a lot of external API calls directly from
the controller, then we can switch the app server to
<a href="https://github.com/socketry/falcon">Falcon</a>. Falcon is tailor-made for such use
cases.</p>
<p>Broadly speaking, one should take care of the following items to ensure that the
time spent by the request doing I/O is minimal.</p>
<ul>
<li>Remove N+1 queries</li>
<li>Remove long-running queries</li>
<li>Move inline third party API calls to background job processor</li>
<li>Move heavy computational stuff to background job processor</li>
</ul>
<p>For a finely optimized Rails application, the <code>max_threads</code> value will be
around 3. That's why the default value of <code>max_threads</code> for Rails applications
is <code>3</code> now. This has been decided after a lot of discussion
<a href="https://github.com/rails/rails/issues/50450">here</a>. We recommend you read the
whole discussion. It is very interesting.</p>
<p><em>This was Part 4 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>. If any part of the
blog is not clear to you then please write to us at
<a href="https://www.linkedin.com/company/bigbinary">LinkedIn</a>,
<a href="https://twitter.com/bigbinary">Twitter</a> or
<a href="https://bigbinary.com/contact">BigBinary website</a>.</em></p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Amdahl's Law: the relationship of speedup and concurrency]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/amdahls_law_the_theoretical_relationship_between_speedup_and_concurrency.jpg"/>
      <id>https://www.bigbinary.com/blog/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency</id>
      <published>2025-04-29T00:00:00.000Z</published>
      <updated>2025-04-29T00:00:00.000Z</updated>
      <author>
        <name>Vishnu M</name>
        <uri>https://www.bigbinary.com/blog/authors/vishnu-m</uri>
      </author>
      <category term="Rails"/>
      <category term="Puma"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/amdahls_law_the_theoretical_relationship_between_speedup_and_concurrency.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[This is Part 3 of our blog series on scaling Rails applications. We have only two parameters to work with if we want to fine-tune our Puma configuration. 1. The number of processes. 2. The number of...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency"><![CDATA[<p><em>This is Part 3 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>.</em></p>
<p>We have only two parameters to work with if we want to fine-tune our Puma
configuration.</p>
<ol>
<li>The number of processes.</li>
<li>The number of threads each process can have.</li>
</ol>
<p>In
<a href="https://bigbinary.com/blog/understanding-puma-concurrency-and-the-effect-of-the-gvl-on-performance">part 1</a>
of <a href="https://www.bigbinary.com/blog/scaling-rails-series">Scaling Rails series</a>,
we saw what the number of processes should be. Now let's look at what the number
of threads in each process should be.</p>
<h2>Amdahl's law</h2>
<p>Each application has a few things which must be performed in &quot;serial order&quot; and
a few things which can be &quot;parallelized&quot;. If we draw a diagram, then this is
what it will look like.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency/amdahls-gantt-chart1.png" alt="Amdahl's law Gantt chart"></p>
<p>Let's say that <code>T1'</code> and <code>T2'</code> are the enhanced times. These are the times the
application would take after the enhancement has been applied. In this case the
enhancement will come in the form of increasing the threads in a process.</p>
<p><code>T1'</code> will be same as <code>T1</code> since it's the serial part. <code>T2'</code> will be lower than
<code>T2</code> since we will parallelize some of the code. After the parallelization is
done, the enhanced version would look something like this.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency/amdahls-gantt-chart.png" alt="Amdahl's law Gantt chart"></p>
<p>It's clear that the serial part (T1) will limit how much speedup we can get no
matter how much we parallelize <code>T2</code>.</p>
<p>Computer scientist <a href="https://en.wikipedia.org/wiki/Gene_Amdahl">Gene Amdahl</a> came
up with <a href="https://en.wikipedia.org/wiki/Amdahl%27s_law">Amdahl's law</a> which gives
the mathematical value for the overall speedup that can be achieved.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency/amdahls-law.png" alt="Amdahl's law picture"></p>
<p>I made a video explaining how this formula came about.</p>
<p><a href="https://www.youtube.com/watch?v=2hYs2X6Fb1M"><img src="https://img.youtube.com/vi/2hYs2X6Fb1M/hqdefault.jpg" alt="Watch on YouTube" width="480" height="360"></a><br><a href="https://www.youtube.com/watch?v=2hYs2X6Fb1M">Watch on YouTube</a></p>
<p><em>Amdahl's law states that the theoretical speedup gained from parallelization is
directly determined by the fraction of sequential code in the program.</em></p>
<p>Now, let's see how we can use Amdahl's law to determine the ideal number of
threads.</p>
<p>The parallelizable portion in this case is the portion of the application that
spends time doing I/O. The non-parallelizable portion is the time spent by the
application executing Ruby code. Remember that because of GVL, within a process,
only one thread has access to CPU at any point of time.</p>
<p>Now we need to know what percentage of time our app spends doing I/O. This will
be the value <code>p</code> as per the video.</p>
<p>Later in this series, we'll show you how to calculate what percentage of the
time the Rails application is spending doing I/O. For this blog let's assume
that our application spends 37% of the time doing I/O i.e the value of <code>p</code> is
<strong>0.37</strong>.</p>
<p>Let's calculate how much speedup we will get if we use one thread(n is 1). Now
let's change n to 2 and get the speedup value. Similarly, we bump up n all the
way to 15 and we record the speedup.</p>
<p>Now let's draw a graph between the overall speedup and the number of threads.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/amdahls-law-the-theoretical-relationship-between-speedup-and-concurrency/speedup-vs-thread-count.png" alt="Speedup V/S Number of threads"></p>
<p>From the graph, it can be seen that the speedup increases as the number of
threads increases, but the rate of increase diminishes as more threads are
added. This is because the serial portion remains constant and is unaffected by
the increase in threads.</p>
<table>
<thead>
<tr>
<th>Threads(N)</th>
<th>Speedup(S)</th>
<th>% Improvement from previous run</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>1.000</td>
<td>-</td>
</tr>
<tr>
<td>2</td>
<td>1.227</td>
<td>22.7%</td>
</tr>
<tr>
<td>3</td>
<td>1.366</td>
<td>11.3%</td>
</tr>
<tr>
<td>4</td>
<td>1.456</td>
<td>6.6%</td>
</tr>
<tr>
<td>5</td>
<td>1.518</td>
<td>4.2%</td>
</tr>
<tr>
<td>6</td>
<td>1.562</td>
<td>2.9%</td>
</tr>
<tr>
<td>7</td>
<td>1.594</td>
<td>2.0%</td>
</tr>
<tr>
<td>8</td>
<td>1.619</td>
<td>1.6%</td>
</tr>
</tbody>
</table>
<p>By examining the graph we can observe that the speedup gain from increasing
threads seem significant up to 4 threads, after which the incremental gain in
speedup starts to plateau.</p>
<p>Remember that these are theoretical maximums based on Amdahl's law. In practice,
we need to use fewer threads as adding more threads can cause an increase in
memory usage and GVL contention, thereby causing latency spikes.</p>
<p>It's obvious that if we add more threads then more requests can be handled by
Puma concurrently. What it means is that requests will be waiting for lesser
time at the load balancer layer as there are more Puma threads waiting to pick
up the request for processing. But in part 1, we saw that just because we have
more threads, it doesn't mean things will move faster. More threads might cause
other threads to wait for the GVL.</p>
<p>There is no point in accepting requests if our web server can't respond to it
promptly. Whereas, if the <code>max_threads</code> value is set to a lower value, requests
will queue up at the Load Balancer layer which is better than overwhelming the
application server.</p>
<p>If more and more requests are waiting at the load balancer level, then the
request queue time will shoot up. The right way to solve this problem is to add
more Puma processes. It is advised to increase the capacity of the Puma server
by adding more processes rather than increasing the number of threads.</p>
<p><a href="https://gist.github.com/neerajsingh0101/35e5307fb197b08ac6a62aa725cafec6">Here</a>
is a middleware that can be used to track the request queue time. This code is
taken from
<a href="https://github.com/judoscale/judoscale-ruby/blob/15a4e9bd59734defb76656b59cba067b60aed473/judoscale-ruby/lib/judoscale/request_metrics.rb">judoscale</a>.</p>
<p>Note that <strong>Request Queue Time</strong> is the time spent waiting before the request is
picked up for processing.</p>
<p>This middleware will only work if the load balancer is adding the
<code>HTTP_REQUEST_START</code> header. Heroku automatically adds this header.</p>
<p>Now we need to use this middleware and for that open <code>config/application.rb</code>
file and we need to add the following line.</p>
<pre><code class="language-ruby">config.middleware.use RequestQueueTimeMiddleware
</code></pre>
<p><em>This was Part 3 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>. If any part of the
blog is not clear to you then please write to us at
<a href="https://www.linkedin.com/company/bigbinary">LinkedIn</a>,
<a href="https://twitter.com/bigbinary">Twitter</a> or
<a href="https://bigbinary.com/contact">BigBinary website</a>.</em></p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[The GVL in Ruby and its impact on scaling Rails]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/gvl-in-ruby-and-its-impact-in-scaling-rails-applications"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/gvl_in_ruby_and_its_impact_in_scaling_rails_applications.jpg"/>
      <id>https://www.bigbinary.com/blog/gvl-in-ruby-and-its-impact-in-scaling-rails-applications</id>
      <published>2025-04-24T00:00:00.000Z</published>
      <updated>2025-04-24T00:00:00.000Z</updated>
      <author>
        <name>Vishnu M</name>
        <uri>https://www.bigbinary.com/blog/authors/vishnu-m</uri>
      </author>
      <category term="Rails"/>
      <category term="Puma"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/gvl_in_ruby_and_its_impact_in_scaling_rails_applications.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[This is Part 2 of our blog series on scaling Rails applications. Let's start from the basics. Let's see how a standard web application mostly behaves. Web applications and CPU usage Code in a web...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/gvl-in-ruby-and-its-impact-in-scaling-rails-applications"><![CDATA[<p><em>This is Part 2 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>.</em></p>
<p>Let's start from the basics. Let's see how a standard web application mostly
behaves.</p>
<h2>Web applications and CPU usage</h2>
<p>Code in a web application typically works like this.</p>
<ul>
<li>Do some data manipulation.</li>
<li>Make a few database calls.</li>
<li>Do more calculations.</li>
<li>Make some network calls.</li>
<li>Do more calculations.</li>
</ul>
<p>Visually, it'll look something like this.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/work-done-in-processing-web-request.png" alt="Three threads 1 process &amp; 2 cores"></p>
<p>CPU work includes operations like view rendering, string manipulation, any kind
of business logic processing etc. In short, anything that involves Ruby code
execution can be considered CPU work. For the rest of the work, like database
calls, network call etc. CPU is idle. Another way of looking at when the CPU is
working and when it's idle is this picture.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/cpu-working-idle.png" alt="CPU sometimes working &amp; sometimes idle"></p>
<p>When a program is using CPU, then that portion of the code is called <strong>CPU
bound</strong> and when program is not using CPU, then that portion of the code is
called <strong>IO bound</strong>.</p>
<h3>CPU bound or IO bound</h3>
<p>Let us understand what <strong>CPU bound</strong> truly means. Consider the following piece
of code.</p>
<pre><code class="language-ruby">10.times do
  Net::HTTP.get(URI.parse(&quot;https://bigbinary.com&quot;))
end
</code></pre>
<p>In the above code, we are hitting the BigBinary website 10 times sequentially.
Running the above code takes time because making a network connection is a
time-consuming process.</p>
<p>Let's assume the code above takes 10 seconds to finish. We want the code to run
faster. So we bought a better CPU for the server. Do you think now the code will
run faster?</p>
<p>It will not. That's because the above code is <strong>not</strong> CPU bound. CPU is not the
limiting factor in this case. This code is I/O bound.</p>
<p>A program is <strong>CPU bound</strong> if the program will run faster if the CPU were
faster.</p>
<p>A program is <strong>I/O bound</strong> if the program will run faster if the I/O operations
were faster.</p>
<p>Some of the examples of I/O bound operations are:</p>
<ul>
<li><strong>making database calls</strong>: reading data from tables, creating new tables etc.</li>
<li><strong>making network calls</strong>: reading data from a website, sending emails etc.</li>
<li><strong>dealing with file systems</strong>: reading files from the file system.</li>
</ul>
<p>Previously, we saw that our CPU was idle sometimes. Now we know that the
technical term for that idleness is <strong>IO bound</strong> let's update the picture.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/cpu-bound-io-bound.png" alt="CPU bound and IO bound"></p>
<p>When a program is I/O bound, the CPU is not doing anything. We don't want
precious CPU cycles to be wasted. So what can we do so that the CPU is fully
utilized?</p>
<p>So far we have been dealing with only one thread. We can increase the number of
threads in the process. In this way, whenever the CPU is done executing CPU
bound code of one thread and that thread is doing an I/O bound operation, then
the CPU can switch and handle the work from another thread. This will ensure
that the CPU is efficiently utilized. We will look at how the switching between
threads works a bit later in the article.</p>
<h2>Concurrency vs Parallelism</h2>
<p>Concurrency and parallelism sound similar, and in your daily life, you can
substitute one for another, and you will be fine. However, from the computer
engineering point of view, there is a difference between work happening
concurrently and work happening in parallel.</p>
<p>Imagine a person who has to respond to 100 emails and 100 Twitter messages. The
person can reply to an email and then reply to a Twitter message, then do the
same all over again: reply to an email and reply to a Twitter message.</p>
<p>The boss will see the count of pending emails and Twitter messages go down from
100 to 99 to 98. The boss might think that the work is happening in &quot;parallel.&quot;
But that's not true.</p>
<p>Technically, the work is happening concurrently. For a system to be parallel, it
should have two or more actions executed simultaneously. In this case, at any
given moment, the person was either responding to email or responding to
Twitter.</p>
<p>Another way to look at it is that <strong>Concurrency is about dealing</strong> with lots of
things at the same time. <strong>Parallelism is about doing</strong> lots of things at the
same time.</p>
<p>If you find it hard to remember which one is which then remember that
<strong>concurrency</strong> starts with the word <strong>con</strong>. Concurrency is the <em>conman</em>. It's
pretending to be doing things &quot;in parallel,&quot; but it's only doing things
concurrently.</p>
<h2>Understanding GVL in Ruby</h2>
<p>GVL (Global VM Lock) in Ruby is a mechanism that prevents multiple threads from
executing Ruby code simultaneously. The GVL acts like a traffic light in a
one-lane bridge. Even if multiple cars (threads) want to cross the bridge at the
same time, the traffic light (GVL) allows only one car to pass at a time. Only
when one car has made it safely to the other end, the second car is allowed by
the traffic light(GVL) to start.</p>
<p>Ruby's memory management(like garbage collection) and some other parts of Ruby
are not thread-safe. Hence, GVL ensures that only one thread runs Ruby code at a
time to avoid any data corruption.</p>
<p>When a thread &quot;holds the GVL&quot;, it has exclusive access to modify the VM
structures.</p>
<p>It's important to note that GVL is there to protect how Ruby works and manages
Ruby's internal VM state. GVL is not there to protect our application code. It's
worth repeating. The presence of GVL doesn't mean that we can write our code in
a thread unsafe manner and expect Ruby to take care of all threading issues in
our code.</p>
<p>Ruby offers tools like <a href="https://ruby-doc.com/3.3.6/Thread/Mutex.html">Mutex</a> and
<a href="https://github.com/ruby-concurrency/concurrent-ruby">concurrent-ruby</a> gem to
manage concurrent code. For example, the following
code(<a href="https://www.youtube.com/watch?v=rI4XlFvMNEw&amp;t=575s">source</a>) is not thread
safe and the GVL will not protect our code from race conditions.</p>
<pre><code class="language-ruby">from = 100_000_000
to = 0

50.times.map do
  Thread.new do
    while from &gt; 0
      from -= 1
      to += 1
    end
  end
end.map(&amp;:join)

puts &quot;to = #{to}&quot;
</code></pre>
<p>When we run this code, we might expect the result to always equal 100,000,000
since we're just moving numbers from <code>from</code> to <code>to</code>. However, if we run it
multiple times, we'll get different results.</p>
<p>This happens because multiple threads are trying to modify the same variables
(from and to) simultaneously without any synchronization. This is called race
condition and it happens because the operation <code>to += 1</code> and <code>from -= 1</code> are
non-atomic at the CPU-level. In simpler terms operation <code>to += 1</code> can be written
as three CPU-level operations.</p>
<ol>
<li>Read current value of <code>to</code>.</li>
<li>Add 1 to it.</li>
<li>Store the result back to <code>to</code>.</li>
</ol>
<p>To fix this race condition the above code can be re-written using a
<a href="https://docs.ruby-lang.org/en/master/Thread/Mutex.html">Mutex</a>.</p>
<pre><code class="language-rb">from = 100_000_000
to = 0
lock = Mutex.new

50.times.map do
  Thread.new do
    while from &gt; 0
      lock.synchronize do
        if from &gt; 0
          from -= 1
          to += 1
        end
      end
    end
  end
end.map(&amp;:join)

puts &quot;to = #{to}&quot;
</code></pre>
<p>It's worth nothing that Ruby implementations like JRuby and TruffleRuby don't
have a GVL.</p>
<h2>GVL dictates how many processes we will need</h2>
<p>Let's say that we deploy the production app to AWS's EC2's <code>t2.medium</code> machine.
This machine has 2 vCPU as we can see from this chart.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/t2-medium.png" alt="T2 medium"></p>
<p>Without going into CPU vs vCPU discussion, let's keep things simple and assume
that the AWS machine has two cores. So we have deployed our code on a machine
with two cores but we have only one process running in production. No worries.
We have three threads. So three threads can share two cores. You would think
that something like this should be possible.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/puma-one-process-2-cores.png" alt="Three threads 1 process &amp; 2 cores"></p>
<p>But it's not possible. Ruby doesn't allow it.</p>
<p>Currently, it's not possible because Thread 1 and Thread 2 belong to the same
process. This is because of the <strong>Global VM Lock (GVL)</strong>.</p>
<p>The GVL ensures that only one thread can execute CPU bound code at a time within
a single Ruby process. The important thing to note here is that this lock is
<strong>only for the CPU bound code</strong> and <strong>only for the same process</strong>.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/gvl-lock.png" alt="Single Process Multi Core"></p>
<p>In the above case, all three threads can do DB operations in parallel. But two
threads of the same process can't be doing CPU operations in parallel.</p>
<p>We can see that &quot;Thread 1&quot; is using Core 1. Core 2 is available but &quot;Thread 2&quot;
can't use Core 2. GVL won't allow it.</p>
<p>Again, let's revisit what GVL does. For the CPU bound code GVL will ensure that
only one thread from a process can access CPU.</p>
<p>So now the question is how do we utilize Core 2. Well, the GVL is applied at a
process level. Threads of the same process are not allowed to do CPU operations
in parallel. Hence, the solution is to have more processes.</p>
<p>To have two Puma processes we need to set the value of env variable
<code>WEB_CONCURRENCY</code> to 2 and reboot Puma.</p>
<pre><code class="language-ruby">WEB_CONCURRENCY=2 bundle exec rails s
</code></pre>
<p>Now we have two processes. Now both Core 1 and Core 2 are being utilized.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/gvl-two-process.png" alt="Multi Process Multi Core"></p>
<p>What if the machine has 5 cores. Do we need 5 processes?</p>
<p>Yes. In that case, we will need to have 5 processes to utilize all the cores.</p>
<p>Therefore for achieving maximum utilization, the rule of thumb is that the
number of processes i.e <code>WEB_CONCURRENCY</code> should be set to the number of cores
available in the machine.</p>
<h2>Thread switching</h2>
<p>Now let's see how switching between threads happens in a multi-threaded
environment. Note that the number of threads is 2 in this case.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/thread1-thread2.png" alt="CPU bound and IO bound"></p>
<p>As we can see, the CPU switches between Thread 1 and Thread 2 whenever it's
idle. This is great. We don't waste CPU cycles now, as we saw in the
single-threaded case. But the switching logic is much more nuanced than what is
shown in the picture.</p>
<p>Ruby manages multiple threads at two levels: the operating system level and the
Ruby level. When we create threads in Ruby, they are &quot;native threads&quot; - meaning
they are real threads that the operating system (OS) can see and manage.</p>
<p>All operating systems have a component called the scheduler. In Linux, it's
called the
<a href="https://en.wikipedia.org/wiki/Completely_Fair_Scheduler">Completely Fair Scheduler</a>
or CFS. This scheduler decides which thread gets to use the CPU and for how
long. However, Ruby adds its own layer of control through the Global VM Lock
(GVL).</p>
<p>In Ruby a thread can execute CPU bound code only if it holds the GVL. The Ruby
VM makes sure that a thread can hold the GVL for up to 100 milliseconds. After
that the thread will be forced to release GVL to another thread if there is
another thread waiting to execute CPU bound code. This ensures that the waiting
Ruby threads are not
<a href="https://en.wikipedia.org/wiki/Starvation_(computer_science)">starved</a>.</p>
<p>When a thread is executing CPU-bound code, it will continue until either:</p>
<ol>
<li>It completes its CPU-bound work.</li>
<li>It hits an I/O operation (which automatically releases the GVL).</li>
<li>It reaches the limit of 100ms.</li>
</ol>
<p>When a thread starts running, the Ruby VM uses a background timer thread at the
VM level that checks every 10ms how long the current Ruby thread has been
running. If the thread has been running longer than the thread quantum (100ms by
default), the Ruby VM takes back the GVL from the active thread and gives it to
the next thread waiting in the queue. When a thread gives up the GVL (either
voluntarily or is forced to give up), the thread goes to the back of the queue.</p>
<p>The default thread quantum is 100ms and starting from Ruby 3.3, it can be
configured using the <code>RUBY_THREAD_TIMESLICE</code> environment variable.
<a href="https://bugs.ruby-lang.org/issues/20861">Here</a> is the link to the discussion.
This environment variable allows fine-tuning of thread scheduling behavior - a
smaller quantum means more frequent thread switches, while a larger quantum
means fewer switches.</p>
<p>Let's see what happens when we have two threads.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/multi-threaded.png" alt="CPU bound and IO bound"></p>
<ol>
<li>T1 completes quantum limit of 100ms and gives up the GVL to T2.</li>
<li>T2 completes 50ms of CPU work and voluntarily gives up the GVL to do I/O.</li>
<li>T1 completes 75 ms of CPU work and voluntarily gives the GVL to do I/O.</li>
<li>Both T1 and T2 are doing I/O and doesn't want the GVL.</li>
</ol>
<p>It means that Thread 2 would be a lot faster if it had more access to CPU. To
make CPU instantly available, we can have lesser number of threads CPU has to
handle. But we need to play a balancing game. If the CPU is idle then we are
paying for the processing cost for no reason. If the CPU is extremely busy then
that means requests will take longer to process.</p>
<h2>Thread switching can lead to misleading data</h2>
<p>Let's take a look at a simple code given below. This code is taken from
<a href="https://byroot.github.io/ruby/performance/2025/01/23/the-mythical-io-bound-rails-app.html">a blog</a>
by <a href="https://x.com/_byroot">Jean Boussier</a>.</p>
<pre><code>start = Time.now
database_connection.execute(&quot;SELECT ...&quot;)
query_duration = (Time.now - start) * 1000.0
puts &quot;Query took: #{query_duration.round(2)}ms&quot;
</code></pre>
<p>The code looks simple. If the result is say <code>Query took: 80ms</code> then you would
think that the query actually took <code>80ms</code>. But now we know two things</p>
<ul>
<li>Executing database query is an IO operation (IO bound)</li>
<li>Once the IO bound operation is done then the thread might not immediately get
hold of the GVL to execute CPU bound code.</li>
</ul>
<p>Think about it. What if the query took only <code>10ms</code> and the rest of the <code>70ms</code>
The thread was waiting for the CPU because of the GVL. The only way to know
which portion took how much time is by instrumenting the GVL.</p>
<h2>Visualizing the effect of the GVL</h2>
<p>To better understand the effect of multiple threads when it comes to Ruby's
performance, let's do a quick test. We'll start with a <strong>cpu_intensive</strong> method
that performs pure arithmetic operations in nested loops, creating a workload
that is heavily CPU dependent.</p>
<p><a href="https://gist.github.com/neerajsingh0101/de84bf200fae4e2003205ed81fcd9d7f">Here</a>
is the code.</p>
<p>Running this script produced the following output:</p>
<pre><code class="language-ruby">Running demonstrations with GVL tracing...

Starting demo with 1 threads doing CPU-bound work
Time elapsed: 7.4921 seconds

Starting demo with 3 threads doing CPU-bound work
Time elapsed: 7.8146 seconds
</code></pre>
<p>From the output, we can see that for CPU-bound work, a single thread performed
better. Why? Let's visualize the result with the help of the traces generated in
the above script using the <a href="https://github.com/ivoanjo/gvl-tracing">gvl-tracing</a>
gem. The trace files can be visualized using
<a href="https://ui.perfetto.dev/">Perfetto</a>, which provides a timeline view showing how
threads interact with the GVL.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/cpu-single-multi.webp" alt="Single and multi threads CPU bound"></p>
<p>We can see above that in the case of CPU-bound work if we have a single thread
then it's not waiting for GVL. However, if we have three threads then each
thread is waiting for GVL multiple times.</p>
<h3>Understanding the advantage of multiple threads in mixed workloads</h3>
<p>Now let's look at mixed workloads in single-threaded and multi-threaded
environment. We'll use a separate script with a <strong>mixed_workload</strong> method that
combines CPU-bound work with I/O operations. We use <code>IO.select</code> with blocking
behavior to simulate I/O operations. This creates actual I/O blocking that
releases the GVL and shows as &quot;waiting&quot; in the GVL trace, accurately
representing real-world I/O operations like database queries.</p>
<p><a href="https://gist.github.com/neerajsingh0101/af1eb90a79c7da429d4287528d7bb788">Here</a>
is the code for the mixed workload test.</p>
<p>Running this script with 1 thread and 3 threads produced the following output:</p>
<pre><code class="language-ruby">Running demonstrations with GVL tracing...

Starting demo with 1 thread doing Mixed I/O and CPU work
Time elapsed: 9.32 seconds

Starting demo with 3 threads doing Mixed I/O and CPU work
Time elapsed: 6.1344 seconds
</code></pre>
<p>The key advantage of multiple threads in mixed workloads lies in how the GVL is
managed during I/O operations. When a thread encounters an I/O operation (like a
database query, network call, or file read), it voluntarily releases the GVL.
This is fundamentally different from CPU-bound work, where threads compete for
the GVL and one thread must wait for another to finish or reach the 100ms
quantum limit.</p>
<p>During I/O operations, the thread is essentially blocked waiting for an external
resource (database, network, disk). While waiting, the thread doesn't need the
GVL because it's not executing Ruby code. This creates an opportunity for other
threads to acquire the GVL and do useful CPU work. The result is that CPU cycles
that would otherwise be wasted during I/O waits are now being utilized
productively by other threads.</p>
<p>Let's visualize this with the single-threaded case first:</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/mixed-single.png" alt="Single thread mixed workload"></p>
<p>In the single-threaded case, the threads wait for I/O operations to complete.
During these I/O waits, the CPU sits idle. The thread performs some CPU work,
then waits for I/O, then does more CPU work, then waits for I/O again. During
all the I/O wait periods, no productive work is happening. The CPU is available
but there's no other thread to utilize it.</p>
<p>Now let's look at the multi-threaded case with three threads:</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/mixed-multi.png" alt="Multi threaded mixed workload"></p>
<p>When there are three threads, the situation changes a bit. Threads now
occasionally spend time waiting for the GVL, but the overall throughput is
significantly better.</p>
<p>When Thread 1 releases the GVL to perform I/O, Thread 2 can immediately acquire
it and start executing CPU-bound work. While Thread 2 is working, Thread 1 might
still be waiting for its I/O operation to complete. Then when Thread 2 releases
the GVL for its own I/O operation, Thread 3 can acquire it. This creates a
pipeline effect where threads are constantly handing off the GVL to each other
ensuring that the CPU is almost always doing useful work.</p>
<p>The small amount of GVL contention we see in the multi-threaded case (threads
waiting for GVL) is more than compensated for by the elimination of idle CPU
time. Instead of the CPU sitting idle during I/O operations, other threads keep
it busy.</p>
<p>This is why Rails applications with typical workloads (lots of database queries,
API calls, and other I/O operations) benefit significantly from having multiple
threads.</p>
<h2>Why can't we increase the thread count to a really high value?</h2>
<p>In the previous section, we saw that increasing the number of threads can help
in utilizing the CPU better. So why can't we increase the number of threads to a
really high value? Let us visualize it.</p>
<p>In the hope of increasing performance, let us bump up the number of threads in
the previous code snippet to <code>20</code> and see the gvl-tracing result.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/gvl-in-ruby-and-its-impact-in-scaling-rails-applications/20-threads.png" alt="20 threads"></p>
<p>As we can see in the above picture, the amount of GVL contention is massive
here. Threads are waiting to get a hold of the GVL. Same will happen inside a
Puma process if we increase the number of threads to a very high value. As we
know, each request is handled by a thread. GVL contention therefore, means that
the requests keep waiting, thereby increasing latency.</p>
<h2>What's next</h2>
<p>In the coming blogs, we'll see how we can figure out the ideal value for
<code>max_threads</code>, both theoretically and empirically, based on our application's
workload.</p>
<p><em>This was Part 2 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>. If any part of the
blog is not clear to you then please write to us at
<a href="https://www.linkedin.com/company/bigbinary">LinkedIn</a>,
<a href="https://twitter.com/bigbinary">Twitter</a> or
<a href="https://bigbinary.com/contact">BigBinary website</a>.</em></p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Understanding how Puma handles requests]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/understanding-puma-concurrency-and-the-effect-of-the-gvl-on-performance"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/understanding_puma_concurrency_effect_of_gvl.jpg"/>
      <id>https://www.bigbinary.com/blog/understanding-puma-concurrency-and-the-effect-of-the-gvl-on-performance</id>
      <published>2025-04-23T00:00:00.000Z</published>
      <updated>2025-04-23T00:00:00.000Z</updated>
      <author>
        <name>Vishnu M</name>
        <uri>https://www.bigbinary.com/blog/authors/vishnu-m</uri>
      </author>
      <category term="Rails"/>
      <category term="Puma"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/understanding_puma_concurrency_effect_of_gvl.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[This is Part 1 of our blog series on scaling Rails applications. If we do rails new to create a new Rails application, Puma will be the default web server. Let's start by explaining how Puma handles...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/understanding-puma-concurrency-and-the-effect-of-the-gvl-on-performance"><![CDATA[<p><em>This is Part 1 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>.</em></p>
<p>If we do <code>rails new</code> to create a new Rails
application, <a href="https://puma.io">Puma</a> will be the default web server. Let's start
by explaining how Puma handles requests.</p>
<h2>How does Puma handle requests?</h2>
<p>Puma listens to incoming requests on a TCP socket. When a request comes in, then
that request is queued up in the socket. The request is then picked up by a Puma
process. In Puma, a process is a separate OS process that runs an instance of
the Rails application.</p>
<p><em>Note that Puma official documentation calls a Puma process a Puma worker. Since
the term &quot;worker&quot; might confuse people with background workers like Sidekiq or
SolidQueue, in this article, we have used the word Puma process at a few places
to remove any ambiguity</em>.</p>
<p>Now, let's look at how a request is processed by Puma step-by-step.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-puma-concurrency-and-the-effect-of-the-gvl-on-performance/puma-internals.png" alt="Puma internals"></p>
<ol>
<li>
<p>All the incoming connections are added to the socket backlog, which is an OS
level queue that holds pending connections.</p>
</li>
<li>
<p>A separate thread (created by the
<a href="https://msp-greg.github.io/puma/Puma/Reactor.html">Reactor</a> class) reads the
connection from the socket backlog. As the name suggests, this Reactor class
implements the
<a href="https://en.wikipedia.org/wiki/Reactor_pattern">reactor pattern</a>. The reactor
can manage multiple connections at a time thanks to non-blocking I/O and an
event-driven architecture.</p>
</li>
<li>
<p>Once the incoming request is fully buffered in memory, the request is passed
to the thread pool where the request resides in the <code>@todo</code> array.</p>
</li>
<li>
<p>A thread in the thread pool pulls a request from the <code>@todo</code> array and
processes it. The thread calls the Rack application, which, in our case is a
Rails application, and generates a response.</p>
</li>
<li>
<p>The response is then sent back to the client via the same connection. Once
this is complete, the thread is released back to the thread pool to handle
the next item from the <code>@todo</code> array.</p>
</li>
</ol>
<h2>Modes in Puma</h2>
<ol>
<li>
<p><strong>Single Mode</strong>: In single mode, only a single Puma process boots and does
not have any additional child processes. It is suitable only for applications
with low traffic.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-puma-concurrency-and-the-effect-of-the-gvl-on-performance/single-mode.png" alt="Single mode"></p>
</li>
<li>
<p><strong>Cluster Mode</strong>: In cluster mode, Puma boots up a master process, which
prepares the application and then invokes the
<a href="https://en.wikipedia.org/wiki/Fork_(system_call)">fork()</a> system call to
create one or more child processes. These processes are the ones that are
responsible for handling requests. The master process monitors and manages
these child processes.
<img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2025/understanding-puma-concurrency-and-the-effect-of-the-gvl-on-performance/cluster-mode.png" alt="Cluster mode"></p>
</li>
</ol>
<h2>Default Puma configuration in a new Rails application</h2>
<p>When we create a new Rails 8 or higher application, the default Puma
<code>config/puma.rb</code> will have the following code.</p>
<p><em>Please note that we are mentioning Rails 8 here because the Puma configuration
is different in prior versions of Rails.</em></p>
<pre><code class="language-ruby">threads_count = ENV.fetch(&quot;RAILS_MAX_THREADS&quot;, 3)
threads threads_count, threads_count

rails_env = ENV.fetch(&quot;RAILS_ENV&quot;, &quot;development&quot;)
environment rails_env

case rails_env
when &quot;production&quot;
  workers_count = Integer(ENV.fetch(&quot;WEB_CONCURRENCY&quot;, 1))
  workers workers_count if workers_count &gt; 1

  preload_app!
when &quot;development&quot;
  worker_timeout 3600
end
</code></pre>
<p>For a brand new Rails application, the env variables <code>RAILS_MAX_THREADS</code> and
<code>WEB_CONCURRENCY</code> won't be set. This means <code>threads_count</code> will be set to 3 and
<code>workers_count</code> will be 1.</p>
<p>Now let's look at the second line from the above mentioned code.</p>
<pre><code class="language-ruby">threads threads_count, threads_count
</code></pre>
<p>In the above code, <code>threads</code> is a method to which we are passing two arguments.
The default value of <code>threads_count</code> is 3. So effectively, we are calling method
<code>threads</code> like this.</p>
<pre><code>threads(3, 3)
</code></pre>
<p>The threads method in Puma takes two arguments: <code>min</code> and <code>max</code>. These arguments
specify the minimum and maximum number of threads that each Puma process will
use to handle requests. In this case Puma will initialize 3 threads in the
thread pool.</p>
<p>Now let's look at the following line from the above mentioned code.</p>
<pre><code class="language-ruby">workers workers_count if workers_count &gt; 1
</code></pre>
<p>The value of <code>workers_count</code> in this case is <code>1</code>, so Puma will run in <strong>single</strong>
mode. As mentioned earlier in Puma a worker is basically a process. It's not
background job worker.</p>
<p>What we have seen is that if we don't specify <code>RAILS_MAX_THREADS</code> or
<code>WEB_CONCURRENCY</code> then, by default, Puma will boot a single process and that
process will have three threads. In other words Rails will boot with the ability
to handle 3 requests concurrently.</p>
<p>This is the default value for Puma for Rails booting in development or in
production mode - a single process with three threads.</p>
<h2>Configuring Puma's concurrency and parallelism</h2>
<p>When it comes to concurrency and parallelism in Puma, there are two primary
parameters we can configure: the number of threads each process will have and
the number of processes we need.</p>
<p>To figure out the right value for each of these parameters, we need to know how
Ruby works. Specifically, we need to know how GVL in Ruby works and how it
impacts the performance of Rails applications.</p>
<p>We also need to know what kind of Rails application it is. Is it a CPU intensive
application, IO intensive or somewhere in between.</p>
<p>Don't worry in the next blog, we will start from the basics and will discuss all
this and much more.</p>
<p><em>This was Part 1 of our blog series on
<a href="https://www.bigbinary.com/blog/scaling-rails-series">scaling Rails applications</a>. If any part of the
blog is not clear to you then please write to us at
<a href="https://www.linkedin.com/company/bigbinary">LinkedIn</a>,
<a href="https://twitter.com/bigbinary">Twitter</a> or
<a href="https://bigbinary.com/contact">BigBinary website</a>.</em></p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Upgrade Ruby using dual boot]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-upgrade-using-dual-boot"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/upgrade_ruby_using_dual_boot.jpg"/>
      <id>https://www.bigbinary.com/blog/ruby-upgrade-using-dual-boot</id>
      <published>2024-09-04T00:00:00.000Z</published>
      <updated>2024-09-04T00:00:00.000Z</updated>
      <author>
        <name>Vijay Vinod</name>
        <uri>https://www.bigbinary.com/blog/authors/vijay2136</uri>
      </author>
      <category term="Ruby"/>
      <category term="Rails"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/upgrade_ruby_using_dual_boot.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[Recently, we upgraded all neeto product's Ruby version from 3.2.2 to 3.3.0 using "dual-booting". Dual-booting is a process that allows you to run your application with different sets of dependencies,...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-upgrade-using-dual-boot"><![CDATA[<p>Recently, we upgraded all <a href="https://www.neeto.com/">neeto</a> product's Ruby version
from 3.2.2 to 3.3.0 using &quot;dual-booting&quot;.</p>
<p>Dual-booting is a process that allows you to run your application with different
sets of dependencies, making it easy to switch between them. This approach
enables you to quickly test your code with both the current and the newer
version of what you are upgrading, ensuring that everything works smoothly
before fully committing to the upgrade.</p>
<h4>How to dual-boot Ruby?</h4>
<p>The dual-boot technique involves maintaining two separate <code>Gemfile.lock</code> files
within your Rails project: one for the current version of Ruby and another for
the next version you're upgrading to.</p>
<p>To get started, first create a symbolic link for <code>Gemfile.next</code> with the
following command:</p>
<pre><code class="language-bash">ln -s Gemfile Gemfile.next
</code></pre>
<p>This command creates <code>Gemfile.next</code> file, which you'll use for the new Ruby
version. This file isn't a separate copy but rather a pointer to your original
<code>Gemfile</code> in the Rails project directory.</p>
<p>Next, add the following snippet at the top of your <code>Gemfile</code>:</p>
<pre><code class="language-ruby">def next?
  File.basename(__FILE__) == &quot;Gemfile.next&quot;
end
</code></pre>
<p>This code snippet helps determine which <code>Gemfile</code> is in use, whether it's the
standard one or the <code>Gemfile.next</code> for the new Ruby version.</p>
<p>Now, let's utilize the <code>next?</code> method to dynamically set the Ruby version in the
<code>Gemfile</code> using a conditional operator:</p>
<pre><code class="language-ruby">ruby_version = next? ? &quot;3.3.0&quot; : &quot;3.2.2&quot;
ruby ruby_version
</code></pre>
<p>This code snippet helps determine the Ruby version based on the <code>next?</code> method.
If <code>next?</code> returns true, meaning you're operating within <code>Gemfile.next</code>, it sets
the Ruby version to &quot;3.3.0&quot;. Otherwise, if the standard <code>Gemfile</code> is being
processed, it defaults to &quot;3.2.2&quot;.</p>
<h4>How to install dependencies?</h4>
<p>With the dual-boot setup in place, you can effectively manage dependencies for
both your current and next versions of the application.</p>
<p>Installing current dependencies: To install dependencies for your current
version, simply run:</p>
<pre><code class="language-bash">bundle install
</code></pre>
<p>This command uses the standard <code>Gemfile</code> to resolve and install dependencies for
your current application.</p>
<p>Installing next dependencies: To install dependencies for the next version of
your application, use the following command:</p>
<pre><code class="language-bash">BUNDLE_GEMFILE=Gemfile.next bundle install
</code></pre>
<p>This command specifies <code>Gemfile.next</code> as the <code>Gemfile</code>, allowing you to install
dependencies specifically for the next version.</p>
<p>Managing commands for the next version: To perform various operations for the
next version, you can use the syntax <code>BUNDLE_GEMFILE=Gemfile.next &lt;command&gt;</code>.
For example, to start the Rails server with the next version's dependencies, you
would use:</p>
<pre><code class="language-bash">BUNDLE_GEMFILE=Gemfile.next rails s
</code></pre>
<p>This approach ensures that <code>Gemfile.next</code> is explicitly used for the specified
command, allowing you to work with the next version of your application while
keeping dependencies and configurations correctly managed.</p>
<h4>Considerations for dual-boot setup</h4>
<p>When implementing a dual-boot setup, it's crucial to consider where both Ruby
and gem versions are defined. While gem versions are usually specified only in
the <code>Gemfile</code>, the Ruby version can be defined in several locations. The most
common are:</p>
<ul>
<li><code>.ruby-version</code> File: Used by version managers like rbenv to specify the Ruby
version.</li>
<li><code>Gemfile</code>: This is where Bundler, RVM, Heroku, and similar tools reference the
Ruby version to manage the Ruby environment.</li>
<li><code>Gemfile.lock</code>: Although the Ruby version in this file is mostly informative,
it's worth noting.</li>
<li><code>Dockerfile</code>: Defines the Ruby version for Docker containers if you're using
Docker.</li>
<li>CI setup steps and configurations.</li>
<li>Other less common locations within applications.</li>
</ul>
<p>We need to make sure that dual-boot setup is compatible with all of them to
ensure a smooth transition and consistency across different environments.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Using enable-load-relative flag in building Ruby binaries]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/use-of-enable-load-relative-flag-in-building-ruby-binaries"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/using_enable_load_relative_flag_ruby_binaries.jpg"/>
      <id>https://www.bigbinary.com/blog/use-of-enable-load-relative-flag-in-building-ruby-binaries</id>
      <published>2023-06-20T00:00:00.000Z</published>
      <updated>2023-06-20T00:00:00.000Z</updated>
      <author>
        <name>Vishal Yadav</name>
        <uri>https://www.bigbinary.com/blog/authors/vishal76342</uri>
      </author>
      <category term="NeetoCI"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/using_enable_load_relative_flag_ruby_binaries.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[I'm working on building NeetoCI, which is a CI/CD solution. While building precompiled Ruby binaries, we encountered some challenges. This blog post explores the problems we faced and how we solved...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/use-of-enable-load-relative-flag-in-building-ruby-binaries"><![CDATA[<p>I'm working on building <a href="https://neeto.com/neetoci">NeetoCI</a>, which is a CI/CD
solution. While building precompiled Ruby binaries, we encountered some
challenges. This blog post explores the problems we faced and how we solved
them.</p>
<h2>Pre-compiled Ruby binaries</h2>
<p>Pre-compiled Ruby binaries are distribution-ready versions of Ruby that include
optimized features for specific systems. These Ruby binaries save time by
eliminating the need to compile Ruby source code manually. Pre-compiled Ruby
binaries help users quickly deploy applications that use different versions of
Ruby on multiple machines.</p>
<p><a href="https://rvm.io/">RVM</a> (Ruby Version Manager) is widely used for managing Ruby
installations on Unix-like systems. RVM provides customized pre-compiled Ruby
binaries tailored for various CPU architectures. These binaries offer additional
features like readline support and SSL/TLS support. You can find them at
<a href="https://rvm.io/binaries/">RVM binaries</a>.</p>
<h2>The Need for pre-compiled Ruby Binaries</h2>
<p><a href="https://www.neeto.com/neetoci">NeetoCI</a> must execute user code in a
containerized environment. A Ruby environment is essential for running Ruby on
Rails applications. However, relying on the system's Ruby version is impractical
since it may differ from the user's required version. Although rbenv or rvm can
be used to install the necessary Ruby version, this approach could be slow. To
save time, we chose to leverage pre-compiled Ruby binaries.</p>
<p>As a CI/CD system, NeetoCI must ensure that all versions of Ruby that an
application requires are always available. Hence, we decided to build our
binaries instead of relying on binaries provided by RVM. Also, this would allow
us to do more system-specific optimizations to the Ruby binary at build time.</p>
<h2>Building pre-compiled Ruby binaries</h2>
<p>We built a Ruby binary following the
<a href="https://github.com/ruby/ruby/blob/master/doc/contributing/building_ruby.md">official documentation</a>
. We were able to execute it on our local development machines. But the same
binary ran into an error in our CI/CD environment.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2023/use-of-enable-load-relative-flag-in-building-ruby-binaries/failing-binary-error-image.png" alt="Bad Interpreter"></p>
<pre><code class="language-bash">$ bundle config path vendor/bundle
./ruby: bad interpreter: No such file or directory
</code></pre>
<p>To debug the issue, we initially focused on <code>$PATH</code>. However, even after
resolving the <code>$PATH</code> issues, the problem persisted. We conducted a thorough
investigation to identify the root cause. Unfortunately, not much was written on
the Internet about this error. There was no mention of it in the official
<a href="https://github.com/ruby/ruby/blob/master/doc/contributing/building_ruby.md">Ruby documentation</a>.</p>
<p>As the next step, we decided to download the binary for version 3.2.2 from
<a href="https://rvm.io/binaries/">RVM</a>. While examining the configuration file, we
noticed that the following arguments were used with the configure command during
the Ruby binary build process:</p>
<pre><code class="language-bash">configure_args=&quot;'--prefix=/usr/share/rvm/rubies/ruby-3.2.2' '--enable-load-relative' '--sysconfdir=/etc' '--disable-install-doc' '--enable-shared'&quot;
</code></pre>
<p>Here are the explanations of the configuration arguments:</p>
<ol>
<li>
<p><code>--prefix=/usr/share/rvm/rubies/ruby-3.2.2</code>: This specifies the directory
where the Ruby binaries, libraries and other files will be kept after the
installation is done.</p>
</li>
<li>
<p><code>--enable-load-relative</code>: This specifies that Ruby can load relative paths
for dynamically linked libraries. It allows the usage of relative paths
instead of absolute paths when loading shared libraries. This feature can be
beneficial in specific deployment scenarios.</p>
</li>
<li>
<p><code>--sysconfdir=/etc</code>: This argument sets the directory where Ruby's system
configuration files will be installed. In this case, it specifies the <code>/etc</code>
directory as the location for these files.</p>
</li>
<li>
<p><code>--disable-install-doc</code>: When this option is enabled, the installation of
documentation files during the build process is disabled. This can help speed
up the build process and save disk space, especially if you do not require
the documentation files.</p>
</li>
<li>
<p><code>--enable-shared</code>: Enabling this option allows the building of shared
libraries for Ruby. Shared libraries enable Ruby to dynamically link and load
specific functionality at runtime, leading to potential performance
improvements and reduced memory usage.</p>
</li>
</ol>
<p>In simpler terms, when the <code>--enable-load-relative</code> flag is enabled, the
compiled Ruby binary can search for shared libraries in its own directory using
the <code>$ORIGIN</code> variable.</p>
<p>When I built the binary on the Docker registry, then the passed <code>--prefix</code> was
something like <code>/usr/share/neetoci</code>. When the binary is built, then binary had
<code>/usr/share/neetoci</code> is hard-coded at various places. When we download this
binary and use in CI then in the CI environment, Ruby is looking for
<code>/user/share/neetoci</code> to load dependencies.</p>
<p>By enabling <code>--enable-load-relative</code> flag while building the binary Ruby will
not use the hard coded value. Rather Ruby will use <code>$ORIGIN</code> variable and will
search for the dependencies in the directory mentioned in <code>$ORIGIN</code>.</p>
<p>This is particularly helpful when the Ruby binary is relocated to a different
directory or system. By using relative paths with <code>$ORIGIN</code>, the binary can find
its shared libraries regardless of its new location. Without this flag, shared
libraries are loaded using absolute paths, which can cause issues if the binary
is moved to a different location and cannot locate its shared libraries.</p>
<p>In our specific use case, where we create and download binaries in separate
containers, we encountered an error due to the absolute paths. To overcome this,
we enabled the <code>--enable-load-relative</code> flag. This allowed the binary to find
its shared libraries successfully, and it worked as expected in our CI/CD
environment.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2023/use-of-enable-load-relative-flag-in-building-ruby-binaries/passing-ruby-binary-image.webp" alt="Successful Build"></p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Improving application performance with the ancestry gem]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/how-neetotestify-improved-test-suites-performance-utilizing-ancestry-gem"/>
      <link rel="enclosure" type="image/jpeg" href="https://www.bigbinary.com/blog/images/social_preview_images/improving_the_application_performance_using_ancestry_gem.jpg"/>
      <id>https://www.bigbinary.com/blog/how-neetoTestify-improved-test-suites-performance-utilizing-ancestry-gem</id>
      <published>2023-05-23T00:00:00.000Z</published>
      <updated>2023-05-23T00:00:00.000Z</updated>
      <author>
        <name>Shemin Anto</name>
        <uri>https://www.bigbinary.com/blog/authors/sheminanto</uri>
      </author>
      <category term="NeetoTestify"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/improving_the_application_performance_using_ancestry_gem.jpg" width="1200" height="627"/>
      <summary type="text"><![CDATA[How NeetoTestify improved test suites performance by utilizing Ancestry Gem, including migration to materialized_path2, adding collation, and optimizing tree structure creation with the "arrange" method.]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/how-neetotestify-improved-test-suites-performance-utilizing-ancestry-gem"><![CDATA[<p><a href="https://www.neeto.com/neetotestify">NeetoTestify</a> is a test management platform
for manual and automated QA testing. It allows us to organize test cases into
logical groups called test suites. A single suite can contain multiple test
cases and multiple suites. The image below shows how the suites are displayed in
the UI in a hierarchical order. The arrangement in which the suites are
displayed resembles a tree data structure.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2023/how-neetoTestify-improved-test-suites-performance-utilizing-ancestry-gem/neetoTestify-test-cases-page.png" alt="Suites displayed with their hierarchial structure in NeetoTestify"></p>
<p>To display test suites in a tree structure, we need to store some information
about the parent-child relationship in the database. This is where
<a href="https://github.com/stefankroes/ancestry">Ancestry</a> comes in. Ancestry is a gem
that allows Rails Active Record models to be organized as a tree structure.</p>
<p>Normally, web applications implement pagination to show a list of records. But,
implementing pagination for a tree-structured data can be challenging and can
make the application more complex. To avoid pagination, the application must
have enough performance to display an entire tree having a large number of
nodes/records without significant delays.</p>
<p>In this blog, we will discuss on how we leveraged the full potential of the
Ancestry gem to address the performance issues encountered while listing suites.</p>
<h3>1. Migration to materialized_path2</h3>
<p>There are several ways to store hierarchical data in a relational database, such
as materialized paths, closure tree tables, adjacency lists, and nested sets.
Ancestry Gem uses the materialized path pattern to store hierarchical data.</p>
<p>The materialized path pattern is a technique in which a single node is stored in
the database as a record and it has an additional column to store the
hierarchical information. In the case of the Ancestry gem, this additional
column is named as <code>ancestry</code>. The <code>ancestry</code> column is used to store IDs of the
ancestors of a node as a single string, separated by a delimiter.</p>
<p>In order to understand how Ancestry gem uses materialized path pattern, first
let's look at the nodes we have in our example. In the screenshot posted above,
we see the following four nodes:</p>
<table>
<thead>
<tr>
<th>Test suite name</th>
<th>Node ID</th>
</tr>
</thead>
<tbody>
<tr>
<td>Suite 1</td>
<td>s1</td>
</tr>
<tr>
<td>Suite 1.1</td>
<td>s11</td>
</tr>
<tr>
<td>Suite 1.1.1</td>
<td>s111</td>
</tr>
<tr>
<td>Suite 1.2</td>
<td>s12</td>
</tr>
</tbody>
</table>
<p>In our example, the s1 is a root node, s11 and s12 are the children of node s1,
and s111 is the child of s11.</p>
<p>In order to store the hierarchical data, the gem offers two types of ancestry
formats, <code>materialized_path</code> and <code>materialized_path2</code>. In these techniques, each
node is represented by a record in the database. Our example consists of four
nodes, so there will be four records in the database. The only difference
between <code>materialized_path</code> and <code>materialized_path2</code> lies in the format in
which, the IDs are stored in the <code>ancestry</code> column.</p>
<h4>materialized_path</h4>
<p>Here the IDs of ancestors are stored in the format &quot;id-1/id-2/id-3&quot;, were
<code>id-1</code>, <code>id-2</code> and <code>id-3</code> are the IDs of three nodes with <code>/</code> as the delimiter.
The <code>id-1</code> is the root node, <code>id-2</code> is the child of <code>id-1</code> and <code>id-3</code> is the
child of <code>id-2</code>. In case of a root node, the <code>ancestry</code> will be <code>null</code>.</p>
<p>The table below shows how the suites in our example are stored in the database
using <code>materialized_path</code>:</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>ancestry</th>
</tr>
</thead>
<tbody>
<tr>
<td>s1</td>
<td>null</td>
</tr>
<tr>
<td>s11</td>
<td>s1</td>
</tr>
<tr>
<td>s111</td>
<td>s1/s11</td>
</tr>
<tr>
<td>s12</td>
<td>s1</td>
</tr>
</tbody>
</table>
<p>This arrangement of node IDs as a single string makes it easier to query for all
descendants of a node, as we use SQL string functions to match the <code>ancestry</code>
column. Here is the SQL statement to get descendants of suite s1:</p>
<pre><code class="language-sql">SELECT &quot;suites&quot;.* FROM &quot;suites&quot; WHERE (&quot;suites&quot;.&quot;ancestry&quot; LIKE 's1/%' OR &quot;suites&quot;.&quot;ancestry&quot; = 's1')
</code></pre>
<p>The result of the above query is:</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>ancestry</th>
</tr>
</thead>
<tbody>
<tr>
<td>s11</td>
<td>s1</td>
</tr>
<tr>
<td>s111</td>
<td>s1/s11</td>
</tr>
<tr>
<td>s12</td>
<td>s1</td>
</tr>
</tbody>
</table>
<h4>materialized_path2</h4>
<p><code>materialized_path2</code> stores ancestors in the format &quot;/id-1/id-2/id-3/&quot;, were
<code>id-1</code> is the root node, <code>id-2</code> is the child of <code>id-1</code>, and <code>id-3</code> is the child
of <code>id-2</code>. Here the delimiter is <code>/</code> as same as <code>materialized_path</code>, but the
<code>ancestry</code> will be starting with a <code>/</code> and ending with a <code>/</code>. For a root node
the <code>ancestry</code> will be <code>/</code>.</p>
<p>The table below shows how the suites in our example are stored in the database
using <code>materialized_path2</code>:</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>ancestry</th>
</tr>
</thead>
<tbody>
<tr>
<td>s1</td>
<td>/</td>
</tr>
<tr>
<td>s11</td>
<td>/s1/</td>
</tr>
<tr>
<td>s111</td>
<td>/s1/s11/</td>
</tr>
<tr>
<td>s12</td>
<td>/s1/</td>
</tr>
</tbody>
</table>
<p>The SQL statement to get the descendants of suite s1 is:</p>
<pre><code class="language-sql">SELECT &quot;suites&quot;.* FROM &quot;suites&quot; WHERE &quot;suites&quot;.&quot;ancestry&quot; LIKE '/s1/%'
</code></pre>
<p>The result of above query is:</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>ancestry</th>
</tr>
</thead>
<tbody>
<tr>
<td>s11</td>
<td>/s1/</td>
</tr>
<tr>
<td>s111</td>
<td>/s1/s11/</td>
</tr>
<tr>
<td>s12</td>
<td>/s1/</td>
</tr>
</tbody>
</table>
<p>If we compare the 2 SQL queries, we can see that <code>materialized_path2</code> has one
less &quot;OR&quot; statement. This gives <code>materialized_path2</code> a slight advantage in
performance.</p>
<p>In NeetoTestify, we previously used the <code>materialized_path</code> format, but now we
have migrated to <code>materialized_path2</code> for improved performance.</p>
<h3>2. Added collation to the ancestry column</h3>
<p><a href="https://www.npgsql.org/efcore/misc/collations-and-case-sensitivity.html?tabs=data-annotations">Collation</a>
In database systems, the index specifies how data is sorted and compared in a
database. Collation provides the sorting rules, case sensitivity, and accent
sensitivity properties for the data in the database.</p>
<p>As mentioned above, our resulting query for fetching the descendants of a node
would be</p>
<pre><code class="language-sql">SELECT &quot;suites&quot;.* FROM &quot;suites&quot; WHERE &quot;suites&quot;.&quot;ancestry&quot; LIKE '/s1/%'
</code></pre>
<p>It uses a <code>LIKE</code> query for comparison with a wildcard character(%). In general,
when using the <code>LIKE</code> query with a wildcard character (%) on the right-hand side
of the pattern, the database can utilize an index and potentially optimize the
query performance. This optimization holds true for ASCII strings.</p>
<p>However, it's important to note that this optimization may not necessarily hold
true for Unicode strings, as Unicode characters can have varying lengths and
different sorting rules compared to ASCII characters.</p>
<p>In our case, the <code>ancestry</code> column contains only ASCII strings. If we let the
database know about this constraint, we can optimize the database's query
performance. To do that, we need to specify the collation type of the <code>ancestry</code>
column.</p>
<p>From
<a href="https://www.postgresql.org/docs/current/collation.html">Postgres's documentation</a>:</p>
<blockquote>
<p>The C and POSIX collations both specify “traditional C” behavior, in which
only the ASCII letters “A” through “Z” are treated as letters, and sorting is
done strictly by character code byte values.</p>
</blockquote>
<p>Since we are using Postgres in NeetoTestify, we use collation <code>C</code>. Instead if we
use MySQL, then the ancestry suggests using collation <code>utf8mb4_bin</code>.</p>
<pre><code class="language-rb">class AddAncestryToTable &lt; ActiveRecord::Migration[6.1]
  def change
    change_table(:table) do |t|
      # postgres
      t.string &quot;ancestry&quot;, collation: 'C', null: false
      t.index &quot;ancestry&quot;
      # mysql
      t.string &quot;ancestry&quot;, collation: 'utf8mb4_bin', null: false
      t.index &quot;ancestry&quot;
    end
  end
end
</code></pre>
<h3>3. Usage of arrange method</h3>
<p>Previously, we were constructing the tree structure of the suites by fetching
the children of each node individually from the database. For this, we first
fetched the suites whose <code>ancestry</code> is <code>/</code> (root nodes). Then, for each of these
suites, we fetched their children, and repeated this process until we reached
leaf-level suites.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2023/how-neetoTestify-improved-test-suites-performance-utilizing-ancestry-gem/previous-approach-to-list-suites.png" alt="Previous approach to list suites"></p>
<p>This recursive approach results in a large number of database queries, causing
performance issues as the tree size increases. Constructing a tree with n(4)
nodes required n+1(5) database queries, adding to the complexity of the process.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2023/how-neetoTestify-improved-test-suites-performance-utilizing-ancestry-gem/current-approach-to-list-suites.png" alt="Current approach to list suites"></p>
<p>The <code>arrange</code> method provided by Ancestry gem, converts the array of nodes into
nested hashes, utilizing the <code>ancestry</code> column information. Also by using this
method the number of database queries will remain 1, even if the number of
suites and nested suites increases.</p>
<pre><code class="language-rb">suites = project.suites
# SELECT &quot;suites&quot;.* FROM &quot;suites&quot; WHERE &quot;suites&quot;.&quot;project_id&quot; = &quot;p1&quot;
# [
#   &lt;Suite id: &quot;s1&quot;, project_id: &quot;p1&quot;, name: &quot;Suite 1&quot;, ancestry: &quot;/&quot;&gt;,
#   &lt;Suite id: &quot;s11&quot;, project_id: &quot;p1&quot;, name: &quot;Suite 1.1&quot;, ancestry: &quot;/s1/&quot;&gt;,
#   &lt;Suite id: &quot;s111&quot;, project_id: &quot;p1&quot;, name: &quot;Suite 1.1.1&quot;, ancestry: &quot;/s1/s11/&quot;&gt;,
#   &lt;Suite id: &quot;s12&quot;, project_id: &quot;p1&quot;, name: &quot;Suite 1.2&quot;, ancestry: &quot;/s1/&quot;&gt;
# ]

suites.arrange
# {
#   &lt;Suite id: s1, project_id: &quot;p1&quot;, name: &quot;Suite 1&quot;, ancestry: &quot;/&quot;&gt; =&gt; {
#     &lt;Suite id: s11, project_id: &quot;p1&quot;, name: &quot;Suite 1.1&quot;, ancestry: &quot;/s1/&quot;&gt; =&gt; {
#       &lt;Suite id: s111, project_id: &quot;p1&quot;, name: &quot;Suite 1.1.1&quot;, ancestry: &quot;/s1/s11/&quot;&gt; =&gt; {}
#     },
#     &lt;Suite id: s12, project_id: &quot;p1&quot;, name: &quot;Suite 1.2&quot;, ancestry: &quot;/s1/&quot;&gt; =&gt; {}
#   }
# }
</code></pre>
<p>The recursive approach took 5.72 seconds to retrieve 170 suites, but the array
conversion approach of <code>arrange</code> method, retrieved the same number of suites in
728.72 ms.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2023/how-neetoTestify-improved-test-suites-performance-utilizing-ancestry-gem/performance-comparison.png" alt="Performance comparison"></p>
<p>The above image shows the advantage in performance while using the <code>arrange</code>
method over the recursive approach. For the comparison of two approaches 170
suites and 1650 test cases were considered.</p>
<p>By implementing the above 3 best practices, we were able to considerably improve
the overall performance of the application.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Three case studies of debugging redis running out of memory]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/debugging-redis-memory-issue"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/debugging-redis-memory-issue.png"/>
      <id>https://www.bigbinary.com/blog/debugging-redis-memory-issue</id>
      <published>2022-09-12T00:00:00.000Z</published>
      <updated>2022-09-12T00:00:00.000Z</updated>
      <author>
        <name>Unnikrishnan KP</name>
        <uri>https://www.bigbinary.com/blog/authors/unni</uri>
      </author>
      <category term="Ruby"/>
      <category term="Rails"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/debugging-redis-memory-issue.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[In this blog, we will discuss three separate case studies of Redis running out of memory. All three case studies have videos demonstrating how the debugging was done. All three videos were prepared...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/debugging-redis-memory-issue"><![CDATA[<p>In this blog, we will discuss three separate case studies of Redis running
out of memory. All three case studies have videos demonstrating how the
debugging was done.</p>
<p>All three videos were prepared for my team members to show how to go about
debugging. The videos are being presented &quot;as it was recorded&quot;.</p>
<h2>First Case Study</h2>
<p>When a job fails in <a href="https://sidekiq.org/">Sidekiq</a>, Sidekiq puts that job in
<a href="https://github.com/mperham/sidekiq/wiki/API#retries">RetrySet</a> and retries that
job until the job succeeds or the job reaches the maximum number of retries. By
default the maximum number of retries is 25. If a job fails 25 times, then that
job is moved to the <a href="https://github.com/mperham/sidekiq/wiki/API#dead">DeadSet</a>.
By default, Sidekiq will store up to 10,000 jobs in the deadset.</p>
<p>We had a situation where Redis was running out of memory. Here is how the
debugging was done.</p>
<p><a href="https://www.youtube.com/watch?v=dg-K_IoT-x0"><img src="https://img.youtube.com/vi/dg-K_IoT-x0/hqdefault.jpg" alt="Watch on YouTube" width="480" height="360"></a><br><a href="https://www.youtube.com/watch?v=dg-K_IoT-x0">Watch on YouTube</a></p>
<h3>How to inspect the deadset</h3>
<pre><code class="language-ruby">ds = Sidekiq::DeadSet.new
ds.each do |job|
  puts &quot;Job #{job['jid']}: #{job['class']} failed at #{job['failed_at']}&quot;
end
</code></pre>
<p>Running the following to view the latest entry to the dataset:</p>
<pre><code class="language-ruby">ds.first
ds.count
</code></pre>
<p>To see the memory usage following commands were executed in the Redis console.</p>
<pre><code>&gt; memory usage dead
30042467

&gt; type dead
zset
</code></pre>
<p>As discussed in the video large amount of payload was being sent. This is not
the right way to send data to the worker. Ideally, some sort of <code>id</code> should be
sent to the worker and the worker should be able to get the necessary data from
the database based on the received <code>id</code>.</p>
<h4>References</h4>
<ol>
<li><a href="https://github.com/mperham/sidekiq/discussions/5011">How to increase the number of jobs in the Sidekiq deadset or disable deadset</a></li>
<li><a href="https://github.com/mperham/sidekiq/blob/main/lib/sidekiq/job_retry.rb#L71">Maximum number of job retries in Sidekiq</a></li>
<li><a href="https://github.com/mperham/sidekiq/blob/a89d84509c569a78882e24e0e28913a22c9311f5/lib/sidekiq.rb#L38">Maximum number of jobs in Sidekiq Deadset</a></li>
</ol>
<h2>Second case study</h2>
<p>In this case, the Redis instance of <a href="https://www.neeto.com/neetochat/">neetochat</a>
was running out of memory. The Redis instance had 50MB capacity, but we were
getting the following error.</p>
<pre><code>ERROR: heartbeat: OOM command not allowed when used memory &gt; 'maxmemory'.
</code></pre>
<p>We were pushing too many geo info records to Redis and that caused the memory to
fill up. Here is the video capturing the debugging session.</p>
<p><a href="https://www.youtube.com/watch?v=oz7Pcbc_zxM"><img src="https://img.youtube.com/vi/oz7Pcbc_zxM/hqdefault.jpg" alt="Watch on YouTube" width="480" height="360"></a><br><a href="https://www.youtube.com/watch?v=oz7Pcbc_zxM">Watch on YouTube</a></p>
<p>The following are the commands executed while debugging.</p>
<pre><code>&gt; ping
PONG

&gt; info

&gt; info memory

&gt; info keyspace

&gt; keys *failed*

&gt; keys *process*

&gt; keys *geocoder*

&gt; get getocoder:http://ipinfo.io/41.174.30.55/geo?
</code></pre>
<h2>Third Case Study</h2>
<p>In this case, the authentication service of <a href="https://www.neeto.com/">Neeto</a> was
failing because of memory exhaustion.</p>
<p>Here the number of keys was limited, but the payload data was huge and all that
payload data was hogging the memory. Here is the video capturing the debugging
session.</p>
<p><a href="https://www.youtube.com/watch?v=a_Ygbcreokw"><img src="https://img.youtube.com/vi/a_Ygbcreokw/hqdefault.jpg" alt="Watch on YouTube" width="480" height="360"></a><br><a href="https://www.youtube.com/watch?v=a_Ygbcreokw">Watch on YouTube</a></p>
<p>The following are the commands executed while debugging.</p>
<pre><code>&gt; ping

&gt; info keyspace
db0:keys=106, expires=86,avg_ttl=1233332728573

&gt; key * (to see all the keys)
</code></pre>
<p>The last command listed all 106 keys. Next, we needed to find how much memory
each of these keys are using. For that, the following commands were executed.</p>
<pre><code>&gt; memory usage organizations/subdomains/bigbinary/neeto_app_links
736 bytes

&gt; memory usage failed
10316224 (10MB)

&gt; memory usage dead
29871174 (29MB)
</code></pre>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3.1 adds Class#subclasses]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-1-adds-class-subclasses"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-adds-class-subclasses.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-1-adds-class-subclasses</id>
      <published>2021-12-27T00:00:00.000Z</published>
      <updated>2021-12-27T00:00:00.000Z</updated>
      <author>
        <name>Ashik Salman</name>
        <uri>https://www.bigbinary.com/blog/authors/ashiksp</uri>
      </author>
      <category term="Ruby 3.1"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-adds-class-subclasses.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 3.1 introduces the Class#subclasses method, which returns all classes directly inheriting from the receiver without including singleton classes. We can see many implementations for calculating...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-1-adds-class-subclasses"><![CDATA[<p>Ruby 3.1 introduces the <code>Class#subclasses</code> method, which returns all classes
directly inheriting from the receiver without including singleton classes.</p>
<p>We can see many implementations for calculating all subclasses of a particular
class from the Ruby community with different gems. The
<a href="https://api.rubyonrails.org/classes/ActiveSupport/DescendantsTracker.html#method-c-subclasses">ActiveSupport::DescendantsTracker</a>
is one of such implementations used in Rails framework. Finally, Ruby has added
the <code>Class#subclasses</code> native implementation for it's 3.1 version release.</p>
<h2>After Ruby 3.1</h2>
<pre><code class="language-ruby">=&gt; class User; end
=&gt; class Employee &lt; User; end
=&gt; class Client &lt; User; end

=&gt; class Manager &lt; Employee; end
=&gt; class Developer &lt; Employee; end

=&gt; User.subclasses
=&gt; [Employee, Client]

=&gt; Employee.subclasses
=&gt; [Manager, Developer]

=&gt; Developer.subclasses
=&gt; []
</code></pre>
<p>Here's the relevant <a href="https://github.com/ruby/ruby/pull/5045">pull request</a> and
<a href="https://bugs.ruby-lang.org/issues/18273">feature discussion</a> for this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3.1 adds MatchData#match & MatchData#match_length]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-1-adds-match-data-methods"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-adds-match-data-methods.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-1-adds-match-data-methods</id>
      <published>2021-11-09T00:00:00.000Z</published>
      <updated>2021-11-09T00:00:00.000Z</updated>
      <author>
        <name>Ashik Salman</name>
        <uri>https://www.bigbinary.com/blog/authors/ashiksp</uri>
      </author>
      <category term="Ruby 3.1"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-adds-match-data-methods.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 3.1 introduces the MatchData#match & MatchData#matchlength methods which returns the substring matched against a regular expression & it's length respectively. Before Ruby 3.1 We have...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-1-adds-match-data-methods"><![CDATA[<p>Ruby 3.1 introduces the <code>MatchData#match</code> &amp; <code>MatchData#match_length</code> methods
which returns the substring matched against a regular expression &amp; it's length
respectively.</p>
<pre><code class="language-ruby">=&gt; str = &quot;Ruby 3.1 introduces MatchData#match method&quot;
=&gt;  m = /(?&lt;lang&gt;\S+)(\s)(?&lt;version&gt;[\d\.]+)/.match(str)
=&gt; #&lt;MatchData &quot;Ruby 3.1&quot; lang:&quot;Ruby&quot; version:&quot;3.1&quot;&gt;
</code></pre>
<h3>Before Ruby 3.1</h3>
<p>We have <code>MatchData#[]</code> method to access all the substring groups that are
matched. We can access either a single match or multiple matches by giving a
single index or a range of indexes, respectively, using <code>MatchData#[]</code> method.</p>
<pre><code class="language-ruby">=&gt; m[:lang]
=&gt; &quot;Ruby&quot;

=&gt; m[1]
=&gt; &quot;Ruby&quot;
=&gt; m[:version]
=&gt; &quot;3.1&quot;

=&gt; m[0..2]
=&gt; [&quot;Ruby 3.1&quot;, &quot;Ruby&quot;, &quot;3.1&quot;]

=&gt; m[1..2]
=&gt; [&quot;Ruby&quot;, &quot;3.1&quot;]


=&gt; m[0].length
=&gt; 8
</code></pre>
<h3>After Ruby 3.1</h3>
<p>We have two new methods now to access the match data &amp; its length. The
<code>MatchData#match</code> &amp; <code>MatchData#match_length</code> methods accepts either an index or
a symbol as an argument to return the match data &amp; its length.</p>
<pre><code class="language-ruby">=&gt; m.match(0)
=&gt; &quot;Ruby 3.1&quot;

=&gt; m.match(:version)
=&gt; &quot;3.1&quot;

=&gt; m.match(3)
=&gt; nil

=&gt; m.match_length(0)
=&gt; 8

=&gt; m.match_length(:version)
=&gt; 3

=&gt; m.match_length(3)
=&gt; nil
</code></pre>
<p>Please note that <code>MatchData#match</code> is not same as <code>MatchData#[]</code> method. The
later accepts an optional length or range as an argument to return the matched
data but the former doesn't accept it, instead it allows only a single index or
symbol as an argument.</p>
<p>Here's the relevant <a href="https://github.com/ruby/ruby/pull/4851">pull request</a> and
<a href="https://bugs.ruby-lang.org/issues/18172">feature discussion</a> for this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3.1 adds Array#intersect?]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-1-adds-array-intersect"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-adds-array-intersect.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-1-adds-array-intersect</id>
      <published>2021-05-11T00:00:00.000Z</published>
      <updated>2021-05-11T00:00:00.000Z</updated>
      <author>
        <name>Ashik Salman</name>
        <uri>https://www.bigbinary.com/blog/authors/ashiksp</uri>
      </author>
      <category term="Ruby 3.1"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-adds-array-intersect.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 3.1 introduces the Array#intersect? method which returns boolean value true or false based on the given input arrays have common elements in it. We already know Array#intersection or Array#&...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-1-adds-array-intersect"><![CDATA[<p>Ruby 3.1 introduces the <code>Array#intersect?</code> method which returns boolean value
<code>true</code> or <code>false</code> based on the given input arrays have common elements in it.</p>
<p>We already know
<a href="https://ruby-doc.org/core-3.0.1/Array.html#method-i-intersection">Array#intersection or Array#&amp;</a>
methods which are used to find the common elements between arrays.</p>
<pre><code class="language-ruby">=&gt; x = [1, 2, 5, 8]
=&gt; y = [2, 4, 5, 9]
=&gt; z = [3, 7]

=&gt; x.intersection(y) # x &amp; y
=&gt; [2, 5]

=&gt; x.intersection(z) # x &amp; z
=&gt; []
</code></pre>
<p>The <code>intersection</code> or <code>&amp;</code> methods return an empty array or array having the
common elements in it as result. We have to further call <code>empty?</code>, <code>any?</code> or
<code>blank?</code> like methods to check whether two arrays intersect each other or not.</p>
<h2>Before Ruby 3.1</h2>
<pre><code class="language-ruby">=&gt; x.intersection(y).empty?
=&gt; false

=&gt; (x &amp; z).empty?
=&gt; true

=&gt; (y &amp; z).any?
=&gt; false
</code></pre>
<h2>After Ruby 3.1</h2>
<pre><code class="language-ruby">=&gt; x.intersect?(y)
=&gt; true

=&gt; y.intersect?(z)
=&gt; false
</code></pre>
<p>The <code>Array#intersect?</code> method accepts only single array as argument, but
<code>Array#intersection</code> method can accept multiple arrays as arguments.</p>
<pre><code class="language-ruby">=&gt; x.intersection(y, z) # x &amp; y &amp; z
=&gt; []
</code></pre>
<p>The newly introduced <code>intersect?</code> method is faster than the above described
checks using <code>intersection</code> or <code>&amp;</code> since the new method avoids creating an
intermediate array while evaluating for common elements. Also new method returns
<code>true</code> as soon as it finds a common element between arrays.</p>
<p>Here's the relevant <a href="https://github.com/ruby/ruby/pull/1972">pull request</a> and
<a href="https://bugs.ruby-lang.org/issues/15198">feature discussion</a> for this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3.1 accumulates Enumerable#tally results]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-1-accumulates-enumerable-tally-results"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-accumulates-enumerable-tally-results.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-1-accumulates-enumerable-tally-results</id>
      <published>2021-04-20T00:00:00.000Z</published>
      <updated>2021-04-20T00:00:00.000Z</updated>
      <author>
        <name>Ashik Salman</name>
        <uri>https://www.bigbinary.com/blog/authors/ashiksp</uri>
      </author>
      <category term="Ruby 3.1"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-accumulates-enumerable-tally-results.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[We already know the Enumerable#tally method is used to count the occurrences of each element in an Enumerable collection. The #tally method was introduced in ruby 2.7.0. Please check our blog for...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-1-accumulates-enumerable-tally-results"><![CDATA[<p>We already know the
<a href="https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-tally">Enumerable#tally</a>
method is used to count the occurrences of each element in an <code>Enumerable</code>
collection. The <code>#tally</code> method was introduced in ruby 2.7.0. Please check
<a href="https://bigbinary.com/blog/ruby-2-7-adds-enumerable-tally">our blog</a> for more
details on it.</p>
<p>Ruby 3.1 introduces an optional hash argument for the <code>Enumerable#tally</code> method
to count. If a hash is given, the total number of occurrences of each element is
added to the hash values and the final hash is returned.</p>
<h2>Ruby 2.7.0+</h2>
<pre><code class="language-ruby">=&gt; letters = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;a&quot;, &quot;d&quot;, &quot;c&quot;, &quot;a&quot;, &quot;c&quot;, &quot;a&quot;]
=&gt; result = letters.tally
=&gt; {&quot;a&quot;=&gt;4, &quot;b&quot;=&gt;1, &quot;c&quot;=&gt;3, &quot;d&quot;=&gt;1}
</code></pre>
<h2>Before Ruby 3.1</h2>
<pre><code class="language-ruby">=&gt; new_letters = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;a&quot;, &quot;c&quot;, &quot;a&quot;]
=&gt; new_letters.tally(result)
=&gt; ArgumentError (wrong number of arguments (given 1, expected 0))
</code></pre>
<h2>After Ruby 3.1</h2>
<pre><code class="language-ruby">=&gt; new_letters = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;a&quot;, &quot;c&quot;, &quot;a&quot;]
=&gt; new_letters.tally(result)
=&gt; {&quot;a&quot;=&gt;7, &quot;b&quot;=&gt;2, &quot;c&quot;=&gt;5, &quot;d&quot;=&gt;1}
</code></pre>
<p>The value corresponding to each element in the hash must be an integer.
Otherwise, the method raises <code>TypeError</code> on execution.</p>
<p>If the default value is defined for the given hash, it will be ignored and the
count of occurrences will be added in the returned hash.</p>
<pre><code class="language-ruby">=&gt; letters = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;a&quot;]
=&gt; letters.tally(Hash.new(10))
=&gt; {&quot;a&quot;=&gt;2, &quot;b&quot;=&gt;1, &quot;c&quot;=&gt;1}
</code></pre>
<p>Here's the relevant <a href="https://github.com/ruby/ruby/pull/4318">pull request</a> and
<a href="https://bugs.ruby-lang.org/issues/17744">feature discussion</a> for this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3.1 adds Enumerable#compact and Lazy#compact]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-1-adds-enumerable-compact"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-adds-enumerable-compact.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-1-adds-enumerable-compact</id>
      <published>2021-04-06T00:00:00.000Z</published>
      <updated>2021-04-06T00:00:00.000Z</updated>
      <author>
        <name>Ashik Salman</name>
        <uri>https://www.bigbinary.com/blog/authors/ashiksp</uri>
      </author>
      <category term="Ruby 3.1"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-1-adds-enumerable-compact.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 3.1 adds Enumerable#compact and Enumerator::Lazy#compact]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-1-adds-enumerable-compact"><![CDATA[<p>We are familiar with the
<a href="https://apidock.com/ruby/v1_9_3_392/Array/compact">compact</a> method associated
with arrays. The <code>compact</code> method returns a copy of the array after removing all
<code>nil</code> elements.</p>
<p>Ruby 3.1 introduces the <code>compact</code> method in the <code>Enumerable</code> module. Now we can
use the <code>compact</code> method along with the <code>Enumerator</code> and <code>Enumerator::Lazy</code>
classes which include the <code>Enumerable</code> module.</p>
<h2>Before Ruby 3.1</h2>
<pre><code class="language-ruby">=&gt; enum = [1, nil, 3, nil, 5].to_enum
=&gt; #&lt;Enumerator: ...&gt;

=&gt; enum.compact
=&gt; NoMethodError (undefined method `compact' for #&lt;Enumerator: [1, nil, 3, nil, 5]:each&gt;)

=&gt;  enum.reject { |x| x.nil? }
=&gt; [1, 3, 5]
</code></pre>
<h2>After Ruby 3.1</h2>
<pre><code class="language-ruby">=&gt; enum = [1, nil, 3, nil, 5].to_enum
=&gt; #&lt;Enumerator: ...&gt;

=&gt; enum.compact
=&gt; [1, 3, 5]
</code></pre>
<p>We can access the <code>compact</code> method to remove all <code>nil</code> occurrences from any
classes where we include the <code>Enumerable</code> module.</p>
<pre><code class="language-ruby">class Person
  include Enumerable

  attr_accessor :names

  def initialize(names = [])
    @names = names
  end

  def each &amp;block
    @names.each(&amp;block)
  end
end

=&gt; list = Person.new([&quot;John&quot;, nil, &quot;James&quot;, nil])
=&gt; #&lt;Person:0x0000000101cd3de8 @names=[&quot;John&quot;, nil, &quot;James&quot;, nil]&gt;

=&gt; list.compact
=&gt; [&quot;John&quot;, &quot;James&quot;]
</code></pre>
<p>Similarly, lazy evaluation can be chained with the <code>compact</code> method to remove
all <code>nil</code> entries from the <code>Enumerator</code> collection.</p>
<pre><code class="language-ruby">=&gt; enum = [1, nil, 3, nil, 5].to_enum.lazy.compact
=&gt; #&lt;Enumerator::Lazy: ...&gt;

=&gt; enum.force
=&gt; [1, 3, 5]


=&gt; list = Person.new([&quot;John&quot;, nil, &quot;James&quot;, nil]).lazy.compact
=&gt; #&lt;Enumerator::Lazy: ...&gt;

=&gt; list.force
=&gt; [&quot;John&quot;, &quot;James&quot;]
</code></pre>
<p>Here's the relevant <a href="https://github.com/ruby/ruby/pull/3851">pull request</a> and
<a href="https://bugs.ruby-lang.org/issues/17312">feature discussion</a> for this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3 is released - The list of Ruby 3 features]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-features"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-features.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-features</id>
      <published>2020-12-25T00:00:00.000Z</published>
      <updated>2020-12-25T00:00:00.000Z</updated>
      <author>
        <name>Datt Dongare</name>
        <uri>https://www.bigbinary.com/blog/authors/datt</uri>
      </author>
      <category term="Ruby"/>
      <category term="Ruby 3"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-features.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 3.0 is released on the Xmas 2020. Ruby 3 comes with many new features and optimizations, check the list of all the features and their importance]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-features"><![CDATA[<p>For all Rubyists, <strong>2020</strong> was a special year. Why wouldn't it be? Ruby 2 was
released in 2013. We have been using Ruby 2.x for almost 7 years and we have
been waiting to see Ruby 3 get released. Finally, the wait is over now.
<a href="https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/">Ruby 3.0.0 has been released</a>.
It's time to unwrap the gift box and see all the Ruby 3 features we got.</p>
<h2>Ruby 3 major updates</h2>
<p>The number <strong>3</strong> is very significant in the Ruby 3 release. Be it release
version number, making performance 3x faster, or the trio of core
contributors(Matz, TenderLove, Koichi). Similarly, there were 3 major goals of
Ruby 3: being faster, having better concurrency, and ensuring correctness.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2020/ruby-3-features/ruby-3-features.jpg" alt="Ruby 3 features"></p>
<h3>1. Ruby 3 performance</h3>
<p>One of the major focuses for Ruby 3 was the performance. In fact, the initial
discussion for Ruby 3 was started around it. Matz had set a very ambitious goal
of making Ruby 3 times faster.</p>
<h4>What is Ruby 3x3?</h4>
<p>Before discussing this, let's revisit Ruby's core philosophy.</p>
<blockquote>
<p>&quot;I hope to see Ruby help every programmer in the world to be productive, and
to enjoy programming, and to be happy.&quot; - Matz</p>
</blockquote>
<p>About Ruby 3x3, some asked whether the goal was to make Ruby the fastest
language? The answer is no. The main goal of Ruby 3x3 was to make Ruby 3 times
faster than Ruby 2.</p>
<blockquote>
<p>“No language is fast enough.” - Matz</p>
</blockquote>
<p>Ruby was not designed to be fastest and if it would have been the goal, Ruby
wouldn't be the same as it is today. As Ruby language gets performance boost, it
definitely helps our application be faster and scalable.</p>
<blockquote>
<p>&quot;In the design of the Ruby language we have been primarily focused on
productivity and the joy of programming. As a result, Ruby was too slow.&quot; -
Matz</p>
</blockquote>
<p>There are two areas where performance can be measured: memory and CPU.</p>
<h4>CPU optimization</h4>
<p>Some enhancements in Ruby internals have been made to improve the speed. The
Ruby team has optimized the JIT(Just In Time) compiler from previous versions.
<a href="https://bigbinary.com/blog/mjit-support-in-ruby-2-6">Ruby MJIT compiler</a> was
first introduced in Ruby 2.6. Ruby 3 MJIT comes with better security and seems
to improve web apps’ performance to a greater extent.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2020/ruby-3-features/machine-performance.jpg" alt="CPU optimization"></p>
<p>MJIT implementation is different from the usual JIT. When methods get called
repeatedly e.g. 10000 times, MJIT will pick such methods which can be compiled
into native code and put them into a queue. Later MJIT will fetch the queue and
convert them to native code.</p>
<p>Please check
<a href="https://engineering.appfolio.com/appfolio-engineering/2019/7/18/jit-and-rubys-mjit">JIT vs MJIT</a>
for more details.</p>
<h4>Memory optimization</h4>
<p>Ruby 3 comes with an enhanced garbage collector. It has python's buffer-like API
which helps in better memory utilization. Since Ruby 1.8, Ruby has continuously
evolved in
<a href="https://scoutapm.com/blog/ruby-garbage-collection">Garbage collection</a>
algorithms.</p>
<h5>Automatic Garbage Compaction</h5>
<p>The latest change in garbage collection is
<a href="https://engineering.appfolio.com/appfolio-engineering/2019/3/22/ruby-27-and-the-compacting-garbage-collector">Garbage Compaction</a>.
It was introduced in Ruby 2.7 where the process was a bit manual. But in version
3 it is fully automatic. The compactor is invoked aptly to make sure proper
memory utilization.</p>
<h5>Objects Grouping</h5>
<p>The garbage compactor moves objects in the heap. It groups dispersed objects
together at a single place in the memory so that this memory can be used even by
heavier objects later.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2020/ruby-3-features/binary-background.jpg" alt="Memory optimization"></p>
<h3>2. Parallelism and Concurrency in Ruby 3</h3>
<p>Concurrency is one of the important aspects of any programming language. Matz
feels that Threads are not the right level of abstraction for Ruby programmers
to use.</p>
<blockquote>
<p>“I regret adding Threads.” - Matz</p>
</blockquote>
<p>Ruby 3 makes it a lot easier to make applications where concurrency is a major
focus. There are several features and improvements added in Ruby 3 related to
concurrency.</p>
<h4>Fibers</h4>
<p>Fibers are considered a disruptive addition in Ruby 3. Fibers are light-weight
workers which appear like Threads but have some advantages. It consumes less
memory than Threads. It gives greater control to the programmer to define code
segments that can be paused or resumed resulting in better I/O handling.</p>
<p><a href="https://github.com/socketry/falcon">Falcon Rack web server</a> uses Async Fibers
internally. This allows Falcon to not block on I/O. Asynchronously managing I/O
gives a great uplift to the Falcon server to serve requests concurrently.</p>
<h5>Fiber Scheduler</h5>
<p><a href="https://bugs.ruby-lang.org/issues/16786">Fiber Scheduler</a> is an experimental
feature added in Ruby 3. It was introduced to intercept blocking operations such
as I/O. The best thing is that it allows lightweight concurrency and can easily
integrate into the existing codebase without changing the original logic. It's
an interface and that can be implemented by creating a wrapper for a gem like
<code>EventMachine</code> or <code>Async</code>. This interface design allows separation of concerns
between the event loop implementation and the application code.</p>
<p>Following is an example to send multiple <code>HTTP</code> requests concurrently using
<code>Async</code>.</p>
<pre><code class="language-ruby">require 'async'
require 'net/http'
require 'uri'

LINKS = [
  'https://bigbinary.com',
  'https://basecamp.com'
]

Async do
  LINKS.each do |link|
    Async do
      Net::HTTP.get(URI(link))
    end
  end
end
</code></pre>
<p>Please check <a href="https://github.com/ruby/ruby/blob/master/doc/fiber.md">fibers</a> for
more details.</p>
<h4>Ractors (Guilds)</h4>
<p>As we know Ruby’s global <code>VM lock (GVL)</code> prevents most Ruby Threads from
computing in parallel.
<a href="https://github.com/ruby/ruby/blob/master/doc/ractor.md">Ractors</a> work around
the <code>GVL</code> to offer better parallelism. Ractor is an Actor-Model like concurrent
abstraction designed to provide a parallel execution without thread-safety
concerns.</p>
<p>Ractors allows Threads in different Ractors to compute at the same time. Each
Ractor has at least one thread, which may contain multiple fibers. In a Ractor,
only a single thread is allowed to execute at a given time.</p>
<p>The following program returns the square root of a really large number. It
calculates the result for both numbers in parallel.</p>
<pre><code class="language-ruby"># Math.sqrt(number) in ractor1, ractor2 run in parallel

ractor1, ractor2 = *(1..2).map do
  Ractor.new do
    number = Ractor.recv
    Math.sqrt(number)
  end
end

# send parameters
ractor1.send 3**71
ractor2.send 4**51

p ractor1.take #=&gt; 8.665717809264115e+16
p ractor2.take #=&gt; 2.251799813685248e+15
</code></pre>
<h3>3. Static Analysis</h3>
<p>We need tests to ensure correctness of our program. However by its very nature
tests could mean code duplication.</p>
<blockquote>
<p>“I hate tests because they aren't DRY.” - Matz</p>
</blockquote>
<p>To ensure the correctness of a program, static analysis can be a great tool in
addition to tests.</p>
<p>The static analysis relies on inline type annotations which aren't DRY. The
solution to address this challenge is having <code>.rbs</code> files parallel to our <code>.rb</code>
files.</p>
<h4>RBS</h4>
<p>RBS is a language to describe the structure of a Ruby program. It provides us an
overview of the program and how overall classes, methods, etc. are defined.
Using RBS, we can write the definition of Ruby classes, modules, methods,
instance variables, variable types, and inheritance. It supports commonly used
patterns in Ruby code, and advanced types like unions and duck typing.</p>
<p>The <code>.rbs</code> files are something similar to <code>.d.ts</code> files in TypeScript. Following
is a small example of how a <code>.rbs</code> file looks like. The advantage of having a
type definition is that it can be validated against both implementation and
execution.</p>
<p>The below example is pretty self-explanatory. One thing we can note here though,
<code>each_post</code> accepts a block or returns an enumerator.</p>
<pre><code class="language-ruby"># user.rbs

class User
  attr_reader name: String
  attr_reader email: String
  attr_reader age: Integer
  attr_reader posts: Array[Post]

  def initialize: (name: String,
                   email: String,
                   age: Integer) -&gt; void

  def each_post: () { (Post) -&gt; void } -&gt; void
                   | () -&gt; Enumerator[Post, void]
end
</code></pre>
<p>Please check <a href="https://github.com/ruby/rbs">RBS gem documentation</a> for more
details.</p>
<h4>Typeprof</h4>
<p>Introducing type definition was a challenge because there is already a lot of
existing Ruby code around and we need a tool that could automatically generate
the type signature. Typeprof is a type analysis tool that reads plain Ruby code
and generates a prototype of type signature in RBS format by analyzing the
methods, and its usage. Typeprof is an experimental feature. Right now only
small subset of ruby is supported.</p>
<blockquote>
<p>“Ruby is simple in appearance, but is very complex inside, just like our human
body.” - Matz</p>
</blockquote>
<p>Let's see an example.</p>
<pre><code class="language-ruby"># user.rb
class User

  def initialize(name:, email:, age:)
    @name, @email, @age = name, email, age
  end

  attr_reader :name, :email, :age
end

User.new(name: &quot;John Doe&quot;, email: 'john@example.com', age: 23)
</code></pre>
<p>Output</p>
<pre><code class="language-ruby">$ typeprof user.rb

# Classes
class User

  attr_reader name : String
  attr_reader email : String
  attr_reader age : Integer

  def initialize : (name: String,
                    email: String,
                    age: Integer) -&gt; [String, String, Integer]

end
</code></pre>
<h2>Other Ruby 3 features and changes</h2>
<p>In the 7-year period, the Ruby community has seen significant improvement in
performance and other aspects. Apart from major goals, Ruby 3 is an exciting
update with lots of new features, handy syntactic changes, and new enhancements.
In this section, we will discuss some notable features.</p>
<blockquote>
<p>“We are making Ruby even better.” - Matz</p>
</blockquote>
<h3>One-line pattern matching syntax change</h3>
<p>Previously one-line pattern matching used the keyword <code>in</code>. Now it's replaced
with <code>=&gt;</code>.</p>
<h6>Ruby 2.7</h6>
<pre><code class="language-ruby">  { name: 'John', role: 'CTO' } in {name:}
  p name # =&gt; 'John'
</code></pre>
<h6>Ruby 3.0</h6>
<pre><code class="language-ruby">  { name: 'John', role: 'CTO' } =&gt; {name:}
  p name # =&gt; 'John'
</code></pre>
<h3>Find pattern</h3>
<p>The
<a href="https://github.com/ruby/ruby/blob/9738f96fcfe50b2a605e350bdd40bd7a85665f54/test/ruby/test_pattern_matching.rb">find pattern</a>
was introduced in <code>Ruby 2.7</code> as an experimental feature. This is now part of
<code>Ruby 3.0</code>. It is similar to pattern matching in <code>Elixir</code> or <code>Haskell</code>.</p>
<pre><code class="language-ruby">users = [
  { name: 'Oliver', role: 'CTO' },
  { name: 'Sam', role: 'Manager' },
  { role: 'customer' },
  { name: 'Eve', city: 'New York' },
  { name: 'Peter' },
  { city: 'Chicago' }
]

users.each do |person|
  case person
  in { name:, role: 'CTO' }
    p &quot;#{name} is the Founder.&quot;
  in { name:, role: designation }
    p &quot;#{name} is a #{designation}.&quot;
  in { name:, city: 'New York' }
    p &quot;#{name} lives in New York.&quot;
  in {role: designation}
    p &quot;Unknown is a #{designation}.&quot;
  in { name: }
    p &quot;#{name}'s designation is unknown.&quot;
  else
    p &quot;Pattern not found.&quot;
  end
end

&quot;Oliver is the Founder.&quot;
&quot;Sam is a Manager.&quot;
&quot;Unknown is a customer.&quot;
&quot;Eve lives in New York.&quot;
&quot;Peter's designation is unknown.&quot;
&quot;Pattern not found.&quot;
</code></pre>
<h3>Endless Method definition</h3>
<p>This is another syntax enhancement that is optional to use. It enables us to
create method definitions
<a href="https://bigbinary.com/blog/ruby-3-adds-endless-method-definition">without end keyword</a>.</p>
<pre><code class="language-ruby">def: increment(x) = x + 1

p increment(42) #=&gt; 43
</code></pre>
<h3>Except method in Hash</h3>
<p>Sometimes while working on a non Rails app I get <code>undefined method except</code>. The
<code>except</code> method was available only in Rails. In Ruby 3 <code>Hash#except</code> was
<a href="https://bigbinary.com/blog/ruby-3-adds-new-method-hash-except">added to Ruby</a>
itself.</p>
<pre><code class="language-ruby">user = { name: 'Oliver', age: 29, role: 'CTO' }

user.except(:role) #=&gt; {:name=&gt; &quot;Oliver&quot;, :age=&gt; 29}
</code></pre>
<h3>Memory View</h3>
<p>This is again an experimental feature. This is a C-API that will allow extension
libraries to exchange raw memory area. Extension libraries can also share
metadata of memory area that consists of shape and element format. It was
inspired by
<a href="https://docs.python.org/3/c-api/buffer.html">Python’s buffer protocol</a>.</p>
<h3>Arguments forwarding</h3>
<p>Arguments forwarding <code>(...)</code> now supports leading arguments.</p>
<p>It is helpful in <code>method_missing</code>, where we need method name as well.</p>
<pre><code class="language-ruby">def method_missing(name, ...)
  if name.to_s.end_with?('?')
    self[name]
  else
    fallback(name, ...)
  end
end
</code></pre>
<h3>Other Notable changes</h3>
<ul>
<li>Pasting in IRB is much faster.</li>
<li>The order of backtrace had been
<a href="https://bigbinary.com/blog/ruby-2-5-prints-backstrace-and-error-message-in-reverse-order">reversed</a>.
The error message and line number are printed first, rest of the backtrace is
printed later.</li>
<li><a href="https://bigbinary.com/blog/ruby-3-supports-transforming-hash-keys-using-a-hash-argument">Hash#transform_keys</a>
accepts a hash that maps old keys with new keys.</li>
<li>Interpolated String literals are no longer frozen when
<code># frozen-string-literal: true</code> is used.</li>
<li>Symbol#to_proc now returns a lambda Proc.</li>
<li><a href="https://bigbinary.com/blog/ruby-3-adds-symbol-name">Symbol#name</a> has been
added, which returns the symbol's name as a frozen string.</li>
</ul>
<p>Many other changes can be checked at
<a href="https://github.com/ruby/ruby/blob/v3_0_0_preview2/NEWS.md">Ruby 3 News</a> for
more details.</p>
<h2>Transition</h2>
<p>A lot of core libraries have been modified to fit the Ruby 3 goal needs. But
this doesn't mean that our old applications will suddenly stop working. The Ruby
team has made sure that these changes are backward compatible. We might see some
deprecation warnings in our existing code. The developers can fix these warnings
to smoothly transition from an old version to the new version. We are all set to
use new features and get all new performance improvements.</p>
<h2>Conclusion</h2>
<p>With great improvements in performance, memory utilization, static analysis, and
new features like Ractors and schedulers, we have great confidence in the future
of Ruby. With Ruby 3, the applications can be more scalable and more enjoyable
to work with. The coming 2021 is not just a new year but rather a new era for
all Rubyists. We at BigBinary thank everyone who contributed towards the Ruby 3
release directly or indirectly.</p>
<p>Happy Holidays and Happy New Year folks!!!</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3 supports transforming hash keys using a hash argument]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-supports-transforming-hash-keys-using-a-hash-argument"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-supports-transforming-hash-keys-using-a-hash-argument.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-supports-transforming-hash-keys-using-a-hash-argument</id>
      <published>2020-12-15T00:00:00.000Z</published>
      <updated>2020-12-15T00:00:00.000Z</updated>
      <author>
        <name>Yedhin Kizhakkethara</name>
        <uri>https://www.bigbinary.com/blog/authors/yedhink</uri>
      </author>
      <category term="Ruby"/>
      <category term="Ruby 3"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-supports-transforming-hash-keys-using-a-hash-argument.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby Hash#transform_keys now accepts a hash for transforming existing keys]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-supports-transforming-hash-keys-using-a-hash-argument"><![CDATA[<p>From Ruby 3 onwards, the <code>Hash#transform_keys</code> method
accepts a hash argument for transforming
existing keys to new keys as specified in the argument.</p>
<h5>Usage before Ruby 3</h5>
<p>The following example shows how we used to apply <code>transform_keys</code>:</p>
<pre><code class="language-ruby"># 1. Declare address hash
irb(main)&gt; address = {House: 'Kizhakkethara', house_no: 123, locality: 'India'}
=&gt; {:House=&gt;&quot;Kizhakkethara&quot;, :house_no=&gt;123, :locality=&gt;&quot;India&quot;}

# 2. Lowercase all the keys
irb(main)&gt; address.transform_keys(&amp;:downcase)
=&gt; {:house=&gt;&quot;Kizhakkethara&quot;, :house_no=&gt;123, :locality=&gt;&quot;India&quot;}

# 3. Replace a particular key with a new key along with lowercasing
irb(main)* address.transform_keys do |key|
irb(main)*   new_key = key
irb(main)*   if key == :locality
irb(main)*     new_key = :country
irb(main)*   end
irb(main)*   new_key.to_s.downcase.to_sym
irb(main)&gt; end
=&gt; {:house=&gt;&quot;Kizhakkethara&quot;, :house_no=&gt;123, :country=&gt;&quot;India&quot;}
</code></pre>
<p>Although the changes required are trivial,
we ended up writing a block to do the job.
But what happens when the number of keys
that needs to be transformed increases?
Do we need to write n-number of conditions within a block?
Not anymore!</p>
<h5>Introducing Hash#transform_keys with hash argument</h5>
<p>Let's take the same example and provide a hash,
which will be used for the transformation:</p>
<pre><code class="language-ruby"># 1. Declare address hash
irb(main)&gt; address = {House: 'Kizhakkethara', house_no: 123, locality: 'India'}
=&gt; {:House=&gt;&quot;Kizhakkethara&quot;, :house_no=&gt;123, :locality=&gt;&quot;India&quot;}

# 2. Provide hash with transform_keys
irb(main)&gt; address.transform_keys({House: :house, locality: :country})
=&gt; {:house=&gt;&quot;Kizhakkethara&quot;, :house_no=&gt;123, :country=&gt;&quot;India&quot;}
</code></pre>
<p>That does the job.
But let's try to improve this code.
Ultimately what happens when we invoke that method
is that it goes through
each of the keys in our variable
and
maps the existing keys to the new keys.
The <code>transform_keys</code> method accepts a block as a parameter.
Thus let's pass in the <code>downcase</code> method as a <code>Proc</code> argument:</p>
<pre><code class="language-ruby"># 1. Passing in block parameters
irb(main)&gt; address.transform_keys({locality: :country}, &amp;:downcase)
=&gt; {:house=&gt;&quot;Kizhakkethara&quot;, :house_no=&gt;123, :country=&gt;&quot;India&quot;}
</code></pre>
<p>An important point to be noted about the block parameter is that,
<strong>it's only applied to keys which are not specified in the hash argument</strong>.</p>
<h3>Other common use cases</h3>
<h5>Transforming params received in the Rails controller</h5>
<pre><code class="language-ruby"># 1. Declare params
irb(rails)&gt; params = ActionController::Parameters.new({&quot;firstName&quot;=&gt;&quot;oliver&quot;, &quot;lastName&quot;=&gt;&quot;smith&quot;, &quot;email&quot;=&gt;&quot;oliver@bigbinary.com&quot;})
=&gt; &lt;ActionController::Parameters {&quot;firstName&quot;=&gt;&quot;oliver&quot;, &quot;lastName&quot;=&gt;&quot;smith&quot;, &quot;email&quot;=&gt;&quot;oliver@bigbinary.com&quot;} permitted: false&gt;

# 2. Convert camelCase to snake_case using block parameter
irb(rails)&gt; params.permit(:firstName, :lastName, :email).transform_keys(&amp;:underscore)
=&gt; &lt;ActionController::Parameters {&quot;first_name&quot;=&gt;&quot;oliver&quot;, &quot;last_name&quot;=&gt;&quot;smith&quot;, &quot;email&quot;=&gt;&quot;oliver@bigbinary.com&quot;} permitted: true&gt;

# 3. Or using hash argument
irb(rails)&gt; params.permit(:firstName, :lastName, :email).transform_keys({firstName: 'first_name', lastName: 'last_name'})
=&gt; &lt;ActionController::Parameters {&quot;first_name&quot;=&gt;&quot;oliver&quot;, &quot;last_name&quot;=&gt;&quot;smith&quot;, &quot;email&quot;=&gt;&quot;oliver@bigbinary.com&quot;} permitted: true&gt;
</code></pre>
<h5>Slicing hash along with key transformation</h5>
<pre><code class="language-ruby">irb(main)&gt; address.transform_keys({locality: :country}).slice(:house_no, :country)
=&gt; {:house_no=&gt;123, :country=&gt;&quot;India&quot;}
</code></pre>
<h5>Transforming keys in place using bang counterpart</h5>
<pre><code class="language-ruby">irb(main)&gt; address.transform_keys!({locality: :country}, &amp;:downcase)
irb(main)&gt; address
=&gt; {:house=&gt;&quot;Kizhakkethara&quot;, :house_no=&gt;123, :country=&gt;&quot;India&quot;}
</code></pre>
<h5>References</h5>
<ul>
<li>Discussions regarding this feature can be found
<a href="https://bugs.ruby-lang.org/issues/16274?tab=history">here</a>.</li>
<li>Commit for this feature can be found
<a href="https://github.com/ruby/ruby/commit/b25e27277dc39f25cfca4db8452d254f6cc8046e">here</a>.</li>
</ul>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3 adds new method Hash#except]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-adds-new-method-hash-except"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-adds-new-method-hash-except.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-adds-new-method-hash-except</id>
      <published>2020-11-11T00:00:00.000Z</published>
      <updated>2020-11-11T00:00:00.000Z</updated>
      <author>
        <name>Akhil Gautam</name>
        <uri>https://www.bigbinary.com/blog/authors/akhilgautam</uri>
      </author>
      <category term="Ruby"/>
      <category term="Ruby 3"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-adds-new-method-hash-except.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 3 adds new method except to Hash class]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-adds-new-method-hash-except"><![CDATA[<p>Ruby 3 adds
a new method, <code>except</code>, to the Hash class.
<code>Hash#except</code> returns a hash
excluding the given keys
and
their values.</p>
<h3>Why do we need Hash#except?</h3>
<p>At times,
we need
to print or log everything
except some sensitive data.
Let's say
we want to print
user details
in the logs
but not passwords.</p>
<p>Before Ruby 3 we could have achieved
it in the following ways:</p>
<pre><code class="language-ruby">
irb(main):001:0&gt; user_details = { name: 'Akhil', age: 25, address: 'India', password: 'T:%g6R' }

# 1. Reject with a block and include?
irb(main):003:0&gt; puts user_details.reject { |key, _| key == :password }
=&gt; { name: 'Akhil', age: 25, address: 'India' }

# 2. Clone the hash with dup, tap into it and delete that key/value from the clone
irb(main):005:0&gt; puts user_details.dup.tap { |hash| hash.delete(:password) }
=&gt; { name: 'Akhil', age: 25, address: 'India' }

</code></pre>
<p>We know that ActiveSupport already
comes with <code>Hash#except</code>
but for a simple Ruby application
using ActiveSupport would
be overkill.</p>
<h3>Ruby 3</h3>
<p>To make the above task
easier and more explicit,
Ruby 3 adds <code>Hash#except</code>
to return a hash
excluding the given keys and their values:</p>
<pre><code class="language-ruby">
irb(main):001:0&gt; user_details = { name: 'Akhil', age: 25, address: 'India', password: 'T:%g6R' }
irb(main):002:0&gt; puts user_details.except(:password)
=&gt; { name: 'Akhil', age: 25, address: 'India' }

irb(main):003:0&gt; db_info = YAML.safe_load(File.read('./database.yml'))
irb(main):004:0&gt; puts db_info.except(:username, :password)
=&gt; { port: 5432, database_name: 'example_db_production' }

</code></pre>
<p>Check out the
<a href="https://github.com/ruby/ruby/commit/82ca8c73034b0a522fd2970ea39edfcd801955fe">commit</a>
for more details.
Discussion around it can be found <a href="https://bugs.ruby-lang.org/issues/15822">here</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3 adds Symbol#name]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-adds-symbol-name"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-adds-symbol-name.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-adds-symbol-name</id>
      <published>2020-10-12T00:00:00.000Z</published>
      <updated>2020-10-12T00:00:00.000Z</updated>
      <author>
        <name>Datt Dongare</name>
        <uri>https://www.bigbinary.com/blog/authors/datt</uri>
      </author>
      <category term="Ruby"/>
      <category term="Ruby 3"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-adds-symbol-name.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 3 adds an instance method 'name' to a Symbol class. Check what the 'name' method does and why it was introduced]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-adds-symbol-name"><![CDATA[<p>All are excited about
what <code>Ruby 3.0</code> has
to offer to the Ruby developers.
There is already a lot of buzz
that the feature set of <code>Ruby 3.0</code>
will change the perspective of developers
how they look at <code>Ruby</code>.</p>
<p>One of the important aspects of <code>Ruby 3.0</code> is optimization.
The part of that optimization is
the introduction of <code>name</code> method for <code>Symbol</code>.
In this blog,
we will take a look
at what <code>name</code> method of class <code>Symbol</code> does
and
why it was introduced.
The new <code>name</code> method is introduced on <code>Symbol</code>
to simply convert a symbol into a string.
<code>Symbol#name</code> returns a string.
Let's see how it works.</p>
<pre><code class="language-ruby">
irb(main):001:0&gt; :simba.name
=&gt; 'simba'
irb(main):002:0&gt; :simba.name.class
=&gt; String
irb(main):003:0&gt; :simba.name === :simba.name
=&gt; true

</code></pre>
<p>Wait what?
Don't we have <code>to_s</code>
to convert a symbol into a string.
Most of us have used <code>to_s</code> method on a <code>Symbol</code>.
The <code>to_s</code> method returns a <code>String</code> object
and
we can simply use it.
But why <code>name</code>?</p>
<p>Using <code>to_s</code> is okay in most cases.
But the problem with <code>to_s</code> is
that it creates a new <code>String</code> object
every time we call it on a symbol.
We can verify this in <code>irb</code>.</p>
<pre><code class="language-ruby">
irb(main):023:0&gt; :simba.to_s.object_id
=&gt; 260
irb(main):024:0&gt; :simba.to_s.object_id
=&gt; 280

</code></pre>
<p>Creating a new object for every <code>symbol</code> to a <code>string</code> conversion
allocates new memory which increases overhead.
The light was thrown on this issue by
<a href="https://bugs.ruby-lang.org/users/6346">schneems (Richard Schneeman)</a>
in a talk at RubyConf Thailand
where he showed how <code>Symbol#to_s</code> allocation
causes significant overhead in <code>ActiveRecord</code>.
This inspired <code>Ruby</code> community
to have a new method <code>name</code> on <code>Symbol</code>
which returns a <code>frozen string</code> object.
This reduces the string allocations dramatically
which results in reducing overhead.</p>
<pre><code class="language-ruby">
irb(main):001:0&gt; :simba.name.frozen?
=&gt; true
irb(main):002:0&gt; :simba.name.object_id
=&gt; 200
irb(main):003:0&gt; :simba.name.object_id
=&gt; 200

</code></pre>
<p>The reason
to bring this feature was
that most of the times
we want a simple string representation
for displaying purpose
or
to interpolate into another string.
The result of <code>to_s</code> is rarely mutated directly.
By introducing this method
we save a lot of objects
which helps in optimization.
Now we know the benefits of <code>name</code>,
we should prefer using <code>name</code> over <code>to_s</code>
when we don't want to mutate a string.</p>
<p>For more information on discussion,
official documentation,
please head on to <a href="https://bugs.ruby-lang.org/issues/16150">Feature #16150</a> discussion,
<a href="https://github.com/ruby/ruby/pull/3514">Pull request</a>
and
<a href="https://www.ruby-lang.org/en/news/2020/09/25/ruby-3-0-0-preview1-released/">Ruby 3.0 official release preview</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 3 adds endless method definition]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-3-adds-endless-method-definition"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-adds-endless-method-definition.png"/>
      <id>https://www.bigbinary.com/blog/ruby-3-adds-endless-method-definition</id>
      <published>2020-09-15T00:00:00.000Z</published>
      <updated>2020-09-15T00:00:00.000Z</updated>
      <author>
        <name>Akhil Gautam</name>
        <uri>https://www.bigbinary.com/blog/authors/akhilgautam</uri>
      </author>
      <category term="Ruby"/>
      <category term="Ruby 3"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-3-adds-endless-method-definition.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 3.0 adds endless method definition. It enables us to create method definitions without the need of end keyword. It is marked as an experimental feature. The discussion around it can be found...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-3-adds-endless-method-definition"><![CDATA[<p>Ruby 3.0
adds endless method definition.
It enables
us to create
method definitions
without the
need of <code>end</code> keyword.
It is marked as an experimental feature.</p>
<pre><code class="language-ruby"># endless method definition
&gt;&gt; def raise_to_power(number, power) = number ** power

&gt;&gt; raise_to_power(2, 5)

=&gt; 32

</code></pre>
<p>The discussion around
it can be found <a href="https://bugs.ruby-lang.org/issues/16746">here</a>.
Check out the
<a href="https://github.com/ruby/ruby/pull/2996/files">pull request</a>
for more details on this.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.7 adds Beginless Range]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-7-adds-beginless-range"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-adds-beginless-range.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-7-adds-beginless-range</id>
      <published>2020-08-25T00:00:00.000Z</published>
      <updated>2020-08-25T00:00:00.000Z</updated>
      <author>
        <name>Ashwath Biradar</name>
        <uri>https://www.bigbinary.com/blog/authors/ashwath</uri>
      </author>
      <category term="Ruby 2.7"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-adds-beginless-range.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.7 added support for Beginless Range which makes the start of range an optional parameter. (..100) is a Beginless Range and it is equivalent to (nil..100). Let's see how Beginless Range could...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-7-adds-beginless-range"><![CDATA[<p>Ruby 2.7 added support for
<a href="https://ruby-doc.org/core-2.7.0/Range.html#class-Range-label-Beginless-2FEndless+Ranges">Beginless Range</a>
which makes the start of range an optional parameter.</p>
<p><code>(..100)</code> is a Beginless Range and it is equivalent to <code>(nil..100)</code>.</p>
<p>Let's see how Beginless Range could be used.</p>
<pre><code class="language-ruby">&gt; array = (1..10).to_a

# Select first 6 elements
&gt; array[..5]
=&gt; [1, 2, 3, 4, 5, 6]

# Select first 5 elements
&gt; array[...5]
=&gt; [1, 2, 3, 4, 5]

# grep (INFINITY..5) in (1..5)
&gt; (1..10).grep(..5)
=&gt; [1, 2, 3, 4, 5]

# (..100) is equivalent to (nil..100)
&gt; (..100) == (nil..100)
=&gt; true
</code></pre>
<p>Here is another example where in the <code>case</code> statement the condition can be read
as <code>below the specified level</code>.</p>
<pre><code class="language-ruby">case temperature
when ..-15
  puts &quot;Deep Freeze&quot;
when -15..8
  puts &quot;Refrigerator&quot;
when 8..15
  puts &quot;Cold&quot;
when 15..25
  puts &quot;Room Temperature&quot;
when (25..)   # Kindly notice the brackets here
  puts &quot;Hot&quot;
end
</code></pre>
<p>It can also be used for defining constants for ranges.</p>
<pre><code class="language-ruby">TEMPERATURE = {
  ..-15  =&gt; :deep_freeze,
  -15..8 =&gt; :refrigerator,
  8..15  =&gt; :cold,
  15..25 =&gt; :room_temperature,
  25..   =&gt; :hot
end
</code></pre>
<p>Using Beginless Range in DSL makes it easier to write conditions and it looks
more natural.</p>
<pre><code class="language-ruby">
# In Rails
User.where(created_at: (..DateTime.now))
# User Load (2.2ms)  SELECT &quot;users&quot;.* FROM &quot;users&quot; WHERE &quot;users&quot;.&quot;created_at&quot; &lt;= $1 LIMIT $2  [[&quot;created_at&quot;, &quot;2020-08-05 15:00:19.111217&quot;], [&quot;LIMIT&quot;, 11]]


# In RubySpec
ruby_version(..'1.9') do
# Tests for old Ruby
end
</code></pre>
<p>Here is the relevant
<a href="https://github.com/ruby/ruby/commit/95f7992b89efd35de6b28ac095c4d3477019c583">commit</a>
and <a href="https://bugs.ruby-lang.org/issues/14799">discussion</a> regarding this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.7 adds Enumerable#filter_map]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-7-adds-enumerable-filter-map"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-adds-enumerable-filter-map.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-7-adds-enumerable-filter-map</id>
      <published>2020-05-08T00:00:00.000Z</published>
      <updated>2020-05-08T00:00:00.000Z</updated>
      <author>
        <name>Ashik Salman</name>
        <uri>https://www.bigbinary.com/blog/authors/ashiksp</uri>
      </author>
      <category term="Ruby 2.7"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-adds-enumerable-filter-map.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.7 adds Enumerable#filtermap which is a combination of filter + map as the name indicates. The 'filtermap' method filters and map the enumerable elements within a single iteration. Before Ruby...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-7-adds-enumerable-filter-map"><![CDATA[<p>Ruby 2.7 adds
<a href="https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-filter_map">Enumerable#filter_map</a>
which is a combination of filter + map as the name indicates. The 'filter_map'
method filters and map the enumerable elements within a single iteration.</p>
<p>Before Ruby 2.7, we could have achieved the same with 2 iterations using
<code>select</code> &amp; <code>map</code> combination or <code>map</code> &amp; <code>compact</code> combination.</p>
<pre><code class="language-ruby">
irb&gt; numbers = [3, 6, 7, 9, 5, 4]

# we can use select &amp; map to find square of odd numbers

irb&gt; numbers.select { |x| x.odd? }.map { |x| x**2 }
=&gt; [9, 49, 81, 25]

# or we can use map &amp; compact to find square of odd numbers

irb&gt; numbers.map { |x| x**2 if x.odd? }.compact
=&gt; [9, 49, 81, 25]

</code></pre>
<h4>Ruby 2.7</h4>
<p>Ruby 2.7 adds <code>Enumerable#filter_map</code> which can be used to filter &amp; map the
elements in a single iteration and which is more faster when compared to other
options described above.</p>
<pre><code class="language-ruby">irb&gt; numbers = [3, 6, 7, 9, 5, 4]
irb&gt; numbers.filter_map { |x| x**2 if x.odd? }
=&gt; [9, 49, 81, 25]

</code></pre>
<p>The original discussion had started
<a href="https://bugs.ruby-lang.org/issues/5663">8 years back</a>. Here is the latest
<a href="https://bugs.ruby-lang.org/issues/15323">thread</a> and
<a href="https://github.com/ruby/ruby/pull/2017/commits/38e04e15bf1d4e67c52630fa3fca1d4a056ea768">github commit</a>
for reference.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.7 deprecates conversion of keyword arguments]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-7-deprecates-conversion-of-keyword-arguments"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-deprecates-conversion-of-keyword-arguments.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-7-deprecates-conversion-of-keyword-arguments</id>
      <published>2020-04-14T00:00:00.000Z</published>
      <updated>2020-04-14T00:00:00.000Z</updated>
      <author>
        <name>Taha Husain</name>
        <uri>https://www.bigbinary.com/blog/authors/taha</uri>
      </author>
      <category term="Ruby 2.7"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-deprecates-conversion-of-keyword-arguments.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.7 deprecates conversion of keyword arguments and positional arguments]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-7-deprecates-conversion-of-keyword-arguments"><![CDATA[<p>A notable change has been announced for Ruby 3 for which deprecation warning has
been added in Ruby 2.7. Ruby 2.7 deprecated automatic conversion of keyword
arguments and positional arguments. This conversion will be completely removed
in Ruby 3.</p>
<p><a href="https://github.com/ruby/ruby/blob/4643bf5d55af6f79266dd67b69bb6eb4ff82029a/doc/NEWS-2.7.0#the-spec-of-keyword-arguments-is-changed-towards-30-">Ruby 2.7 NEWS</a>
has listed the spec of keyword arguments for Ruby 3.0. We will take the examples
mentioned there and for each scenario we will look into how we can fix them in
the existing codebase.</p>
<h4>Scenario 1</h4>
<p><em>When method definition accepts keyword arguments as the last argument.</em></p>
<pre><code class="language-ruby">def sum(a: 0, b: 0)
  a + b
end
</code></pre>
<p>Passing exact keyword arguments in a method call is acceptable, but in
applications we usually pass a hash to a method call.</p>
<pre><code class="language-ruby">sum(a: 2, b: 4) # OK

sum({ a: 2, b: 4 }) # Warned
</code></pre>
<p>In this case, we can add a double splat operator to the hash to avoid
deprecation warning.</p>
<pre><code class="language-ruby">sum(**{ a: 2, b: 4 }) # OK
</code></pre>
<h4>Scenario 2</h4>
<p><em>When method call passes keyword arguments but does not pass enough required
positional arguments.</em></p>
<p>If the number of positional arguments doesn't match with method definition, then
keyword arguments passed in method call will be considered as the last
positional argument to the method.</p>
<pre><code class="language-ruby">def sum(num, x: 0)
  num.values.sum + x
end
</code></pre>
<pre><code class="language-ruby">sum(a: 2, b: 4) # Warned

sum(a: 2, b: 4, x: 6) # Warned
</code></pre>
<p>To avoid deprecation warning and for code to be compatible with Ruby 3, we
should pass hash instead of keyword arguments in method call.</p>
<pre><code class="language-ruby">sum({ a: 2, b: 4 }) # OK

sum({ a: 2, b: 4}, x: 6) # OK
</code></pre>
<h4>Scenario 3</h4>
<p><em>When a method accepts a hash and keyword arguments but method call passes only
hash or keyword arguments.</em></p>
<p>If a method arguments are a mix of symbol keys and non-symbol keys, and the
method definition accepts either one of them then Ruby splits the keyword
arguments but also raises a warning.</p>
<pre><code class="language-ruby">def sum(num={}, x: 0)
  num.values.sum + x
end
</code></pre>
<pre><code class="language-ruby">sum(&quot;x&quot; =&gt; 2, x: 4) # Warned

sum(x: 2, &quot;x&quot; =&gt; 4) # Warned
</code></pre>
<p>To fix this warning, we should pass hash separately as defined in the method
definition.</p>
<pre><code class="language-ruby">sum({ &quot;x&quot; =&gt; 4 }, x: 2) # OK
</code></pre>
<h4>Scenario 4</h4>
<p><em>When an empty hash with double splat operator is passed to a method that
doesn't accept keyword arguments.</em></p>
<p>Passing keyword arguments using double splat operator to a method that doesn't
accept keyword argument will send empty hash similar to earlier version of Ruby
but will raise a warning.</p>
<pre><code class="language-ruby">def sum(num)
  num.values.sum
end
</code></pre>
<pre><code class="language-ruby">numbers = {}
sum(**numbers) # Warned
</code></pre>
<p>To avoid this warning, we should change method call to pass hash instead of
using double splat operator.</p>
<pre><code class="language-ruby">numbers = {}
sum(numbers) # OK
</code></pre>
<hr>
<h3>Added support for non-symbol keys</h3>
<p>In Ruby 2.6.0, support for non-symbol keys in method call was removed. It is
added back in Ruby 2.7. When method accepts arbitrary keyword arguments using
double splat operator then non-symbol keys can also be passed.</p>
<pre><code class="language-ruby">def sum(**num)
  num.values.sum
end
</code></pre>
<h5>ruby 2.6.5</h5>
<pre><code class="language-ruby">sum(&quot;x&quot; =&gt; 4, &quot;y&quot; =&gt; 3)
=&gt; ArgumentError (wrong number of arguments (given 1, expected 0))

sum(x: 4, y: 3)
=&gt; 7
</code></pre>
<h5>ruby 2.7.0</h5>
<pre><code class="language-ruby">sum(&quot;x&quot; =&gt; 4, &quot;y&quot; =&gt; 3)
=&gt; 7

sum(x: 4, y: 3)
=&gt; 7
</code></pre>
<hr>
<h3>Added support for <code>**nil</code></h3>
<p>Ruby 2.7 added support for <code>**nil</code> to explicitly mention if a method doesn't
accept any keyword arguments in method call.</p>
<pre><code class="language-ruby">def sum(a, b, **nil)
  a + b
end

sum(2, 3, x: 4)
=&gt; ArgumentError (no keywords accepted)
</code></pre>
<hr>
<p>To suppress above deprecation warnings we can use
<a href="https://github.com/ruby/ruby/blob/4643bf5d55af6f79266dd67b69bb6eb4ff82029a/doc/NEWS-2.7.0#warning-option-"><code>-W:no-deprecated option</code></a>.</p>
<p>In conclusion, Ruby 2.7 has worked big steps towards changing specification of
keyword arguments which will be completely changed in Ruby 3.</p>
<p>For more information on discussion, code changes and official documentation,
please head to <a href="https://bugs.ruby-lang.org/issues/14183">Feature #14183</a>
discussion, <a href="https://github.com/ruby/ruby/pull/2395">pull request</a> and
<a href="https://github.com/ruby/ruby/blob/4643bf5d55af6f79266dd67b69bb6eb4ff82029a/doc/NEWS-2.7.0#the-spec-of-keyword-arguments-is-changed-towards-30-">NEWS release</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.7 adds Enumerator::Lazy#eager]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-7-adds-enumerator-lazy-eager"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-adds-enumerator-lazy-eager.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-7-adds-enumerator-lazy-eager</id>
      <published>2020-03-18T00:00:00.000Z</published>
      <updated>2020-03-18T00:00:00.000Z</updated>
      <author>
        <name>Ashik Salman</name>
        <uri>https://www.bigbinary.com/blog/authors/ashiksp</uri>
      </author>
      <category term="Ruby 2.7"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-adds-enumerator-lazy-eager.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.0 introduced Enumerator::Lazy, a special type of enumerator which helps us in processing chains of operations on a collection without actually executing it instantly. By applying...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-7-adds-enumerator-lazy-eager"><![CDATA[<p>Ruby 2.0 introduced
<a href="https://ruby-doc.org/core-2.7.0/Enumerator/Lazy.html"><code>Enumerator::Lazy</code></a>, a
special type of enumerator which helps us in processing chains of operations on
a collection without actually executing it instantly.</p>
<p>By applying
<a href="https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-lazy"><code>Enumerable#lazy</code></a>
method on any enumerable object, we can convert that object into
<code>Enumerator::Lazy</code> object. The chains of actions on this lazy enumerator will be
evaluated only when it is needed. It helps us in processing operations on large
collections, files and infinite sequences seamlessly.</p>
<pre><code class="language-ruby"># This line of code will hang and you will have to quit the console by Ctrl+C.
irb&gt; list = (1..Float::INFINITY).select { |i| i%3 == 0 }.reject(&amp;:even?)

# Just adding `lazy`, the above line of code now executes properly
# and returns result without going to infinite loop. Here the chains of
# operations are performed as and when it is needed.
irb&gt; lazy_list = (1..Float::INFINITY).lazy.select { |i| i%3 == 0 }.reject(&amp;:even?)
=&gt; #&lt;Enumerator::Lazy: ...&gt;

irb&gt; lazy_list.first(5)
=&gt; [3, 9, 15, 21, 27]

</code></pre>
<p>When we chain more operations on <code>Enumerable#lazy</code> object, it again returns lazy
object without executing it. So, when we pass lazy objects to any method which
expects a normal enumerable object as an argument, we have to force evaluation
on lazy object by calling
<a href="https://ruby-doc.org/core-2.7.0/Enumerator/Lazy.html#method-i-to_a"><code>to_a</code></a>
method or it's alias
<a href="https://ruby-doc.org/core-2.7.0/Enumerator/Lazy.html#method-i-force"><code>force</code></a>.</p>
<pre><code class="language-ruby">
# Define a lazy enumerator object.
irb&gt; list = (1..30).lazy.select { |i| i%3 == 0 }.reject(&amp;:even?)
=&gt; #&lt;Enumerator::Lazy: #&lt;Enumerator::Lazy: ... 1..30&gt;:select&gt;:reject&gt;

# The chains of operations will return again a lazy enumerator.
irb&gt; result = list.select { |x| x if x &lt;= 15 }
=&gt; #&lt;Enumerator::Lazy: #&lt;Enumerator::Lazy: ... 1..30&gt;:select&gt;:reject&gt;:map&gt;

# It returns error when we call usual array methods on result.
irb&gt; result.sample
irb&gt; NoMethodError (undefined method `sample'
irb&gt; for #&lt;Enumerator::Lazy:0x00007faab182a5d8&gt;)

irb&gt; result.length
irb&gt; NoMethodError (undefined method `length'
irb&gt; for #&lt;Enumerator::Lazy:0x00007faab182a5d8&gt;)

# We can call the normal array methods on lazy object after forcing
# its actual execution with methods as mentioned above.
irb&gt; result.force.sample
=&gt; 9

irb&gt; result.to_a.length
=&gt; 3

</code></pre>
<p>The
<a href="https://ruby-doc.org/core-2.7.0/Enumerator/Lazy.html#method-i-eager"><code>Enumerable#eager</code></a>
method returns a normal enumerator from a lazy enumerator, so that lazy
enumerator object can be passed to any methods which expects a normal enumerable
object as an argument. Also, we can call other usual array methods on the
collection to get desired results.</p>
<pre><code class="language-ruby">
# By adding eager on lazy object, the chains of operations would return
# actual result here. If lazy object is passed to any method, the
# processed result will be received as an argument.
irb&gt; eager_list = (1..30).lazy.select { |i| i%3 == 0 }.reject(&amp;:even?).eager
=&gt; #&lt;Enumerator: #&lt;Enumerator::Lazy: ... 1..30&gt;:select&gt;:reject&gt;:each&gt;

irb&gt; result = eager_list.select { |x| x if x &lt;= 15 }
irb&gt; result.sample
=&gt; 9

irb&gt; result.length
=&gt; 3

</code></pre>
<p>The same way, we can use <code>eager</code> method when we pass lazy enumerator as an
argument to any method which expects a normal enumerator.</p>
<pre><code class="language-ruby">irb&gt; list = (1..10).lazy.select { |i| i%3 == 0 }.reject(&amp;:even?)
irb&gt; def display(enum)
irb&gt;   enum.map { |x| p x }
irb&gt; end

irb&gt;  display(list)
=&gt; #&lt;Enumerator::Lazy: #&lt;Enumerator::Lazy: ... 1..30&gt;:select&gt;:reject&gt;:map&gt;

irb&gt; eager_list = (1..10).lazy.select { |i| i%3 == 0 }.reject(&amp;:even?).eager
irb&gt; display(eager_list)
=&gt; 3
=&gt; 9

</code></pre>
<p>Here's the relevant
<a href="https://github.com/ruby/ruby/commit/1d4bd229b898671328c2a942b04f08065c640c28">commit</a>
and <a href="https://bugs.ruby-lang.org/issues/15901">feature discussion</a> for this
change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.7 adds numbered parameters for blocks]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-7-introduces-numbered-parameters-as-default-block-parameters"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-introduces-numbered-parameters-as-default-block-parameters.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-7-introduces-numbered-parameters-as-default-block-parameters</id>
      <published>2020-03-03T00:00:00.000Z</published>
      <updated>2020-03-03T00:00:00.000Z</updated>
      <author>
        <name>Taha Husain</name>
        <uri>https://www.bigbinary.com/blog/authors/taha</uri>
      </author>
      <category term="Ruby 2.7"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-introduces-numbered-parameters-as-default-block-parameters.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.7 introduces numbered parameters as default block parameters]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-7-introduces-numbered-parameters-as-default-block-parameters"><![CDATA[<p>At some point, all of us have used names like <code>a</code>, <code>n</code>, <code>i</code> etc for block
parameters. Below are few examples where numbered parameters can come in handy.</p>
<pre><code class="language-ruby">
&gt; (1..10).each { |n| p n * 3 }

&gt; { a: [1, 2, 3], b: [2, 4, 6], c: [3, 6, 9] }.each { |_k, v| p v }

&gt; [10, 100, 1000].each_with_index { |n, i| p n, i }

</code></pre>
<p>Ruby 2.7 introduces a new way to access block parameters. Ruby 2.7 onwards, if
block parameters are obvious and we wish to not use absurd names like <code>n</code> or <code>i</code>
etc, we can use numbered parameters which are available inside a block by
default.</p>
<p>We can use **1_ for first parameter, **2_ for second parameter and so on.</p>
<p>Here's how Ruby 2.7 provides numbered parameters inside a block. Below shown are
the examples from above, only this time using numbered parameters.</p>
<pre><code class="language-ruby">
&gt; (1..10).each { p _1 * 3 }

&gt; { a: [1, 2, 3], b: [2, 4, 6], c: [3, 6, 9] }.each { p _2 }

&gt; [10, 100, 1000].each_with_index { p _1, _2 }

</code></pre>
<p>Like mentioned in
<a href="https://github.com/ruby/ruby/blob/7f6bd6bb1c2220d2d7c17b77abf52fb4af548001/doc/NEWS-2.7.0#numbered-parameters">News-2.7.0 docs</a>,
Ruby now raises a warning if we try to define local variable in the format <code>_1</code>.
Local variable will have precedence over numbered parameter inside the block.</p>
<pre><code class="language-ruby">
&gt; _1 = 0
&gt; =&gt; warning: `_1' is reserved for numbered parameter; consider another name

&gt; [10].each { p _1 }
&gt; =&gt; 0

</code></pre>
<p>Numbered parameters are not accessible inside the block if we define ordinary
parameters. If we try to access <code>_1</code> when ordinary parameters are defined, then
ruby raises <code>SyntaxError</code> like shown below.</p>
<pre><code class="language-ruby">
&gt; [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;].each_with_index { |alphabet, index| p _1, _2}

=&gt; SyntaxError ((irb):1: ordinary parameter is defined)

</code></pre>
<p>This feature was suggested 9 years back and came back in discussion last year.
After many suggestions community agreed to use <code>_1</code> syntax.</p>
<p>Head to following links to read the discussion behind numbered parameters,
<a href="https://bugs.ruby-lang.org/issues/4475">Feature #4475</a> and
<a href="https://bugs.ruby-lang.org/issues/15723">Discussion #15723</a>.</p>
<p>Here's relevant
<a href="https://github.com/ruby/ruby/commit/12acc751e3e7fd6f8aec33abf661724ad76c862a">commit</a>
for this feature.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.7 adds Enumerable#tally]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-7-adds-enumerable-tally"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-adds-enumerable-tally.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-7-adds-enumerable-tally</id>
      <published>2020-02-18T00:00:00.000Z</published>
      <updated>2020-02-18T00:00:00.000Z</updated>
      <author>
        <name>Akhil Gautam</name>
        <uri>https://www.bigbinary.com/blog/authors/akhilgautam</uri>
      </author>
      <category term="Ruby 2.7"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-7-adds-enumerable-tally.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Let's say that we have to find the frequency of each element of an array. Before Ruby 2.7, we could have achieved it using groupby or inject. Ruby 2.7 Ruby 2.7 adds Enumerable#tally which can be used...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-7-adds-enumerable-tally"><![CDATA[<p>Let's say that we have to find the frequency of each element of an array.</p>
<p>Before Ruby 2.7, we could have achieved it using <code>group_by</code> or <code>inject</code>.</p>
<pre><code class="language-ruby">irb&gt; scores = [100, 35, 70, 100, 70, 30, 35, 100, 45, 30]

# we can use group_by to group the scores

irb&gt; scores.group_by { |v| v }.map { |k, v| [k, v.size] }.to_h
=&gt; {100=&gt;3, 35=&gt;2, 70=&gt;2, 30=&gt;2, 45=&gt;1}

# or we can use inject to group the scores

irb&gt; scores.inject(Hash.new(0)) {|hash, score| hash[score] += 1; hash }
=&gt; {100=&gt;3, 35=&gt;2, 70=&gt;2, 30=&gt;2, 45=&gt;1}

</code></pre>
<h4>Ruby 2.7</h4>
<p>Ruby 2.7 adds <code>Enumerable#tally</code> which can be used to find the frequency.
<code>Tally</code> makes the code more readable and intuitive. It returns a hash where keys
are the unique elements and values are its corresponding frequency.</p>
<pre><code class="language-ruby">
irb&gt; scores = [100, 35, 70, 100, 70, 30, 35, 100, 45, 30]
irb&gt; scores.tally
=&gt; {100=&gt;3, 35=&gt;2, 70=&gt;2, 30=&gt;2, 45=&gt;1}

</code></pre>
<p>Check out the
<a href="https://github.com/ruby/ruby/commit/673dc51c251588be3c9f4b5b5486cd80d46dfeee">github commit</a>
for more details on this.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[MJIT Support in Ruby 2.6]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/mjit-support-in-ruby-2-6"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/mjit-support-in-ruby-2-6.png"/>
      <id>https://www.bigbinary.com/blog/mjit-support-in-ruby-2-6</id>
      <published>2019-03-05T00:00:00.000Z</published>
      <updated>2019-03-05T00:00:00.000Z</updated>
      <author>
        <name>Sudeep Tarlekar</name>
        <uri>https://www.bigbinary.com/blog/authors/sudeeptarlekar</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/mjit-support-in-ruby-2-6.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[What is JIT? JIT stands for Just-In-Time compiler. JIT converts repetitive code into bytecode which can then be sent to the processor directly, hence, saving time by not compiling the same piece of...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/mjit-support-in-ruby-2-6"><![CDATA[<h3>What is JIT?</h3>
<p>JIT stands for Just-In-Time compiler. JIT converts repetitive code into bytecode
which can then be sent to the processor directly, hence, saving time by not
compiling the same piece of code over and over.</p>
<h3>Ruby 2.6</h3>
<p>MJIT is introduced in Ruby 2.6. It is most commonly known as MRI JIT or Method
Based JIT.</p>
<p>It is a part of the Ruby 3x3 project started by Matz. The name &quot;Ruby 3x3&quot;
signifies Ruby 3.0 will be 3 times faster than Ruby 2.0 and it will focus mainly
on performance. In addition to performance, it also aims for the following
things:</p>
<ol>
<li>Portability</li>
<li>Stability</li>
<li>Security</li>
</ol>
<p>MJIT is still in development, therefore, MJIT is optional in Ruby 2.6. If you
are running Ruby 2.6, then you can execute the following command.</p>
<pre><code class="language-shell">ruby --help
</code></pre>
<p>You will see following options.</p>
<pre><code class="language-shell">--Jit-wait # Wait program execution until code compiles.
--jit-verbose=num # Level information MJIT compiler prints for Ruby program.
--jit-min-calls=num # Minimum count in loops for which MJIT should work.
--jit-max-cache
--jit-save-temps # Save compiled library to the file.
</code></pre>
<p>Vladimir Makarov proposed improving performance by replacing VM instructions
with RTL(Register Transfer Language) and introducing the Method based JIT
compiler.</p>
<p>Vladimir explained MJIT architecture in his
<a href="https://youtu.be/qpZDw-p9yag?t=1655">RubyKaigi 2017 conference keynote</a>.</p>
<p>Ruby's compiler converts the code to YARV(Yet Another Ruby VM) instructions and
then these instructions are run by the Ruby Virtual Machine. Code that is
executed too often is converted to RTL instructions, which runs faster.</p>
<p>Let's take a look at how MJIT works.</p>
<pre><code class="language-ruby">
# mjit.rb

require 'benchmark'

puts Benchmark.measure {
def test_while
start_time = Time.now
i = 0

    while i &lt; 4
      i += 1
    end

    i
    puts Time.now - start_time

end

4.times { test_while }
}
</code></pre>
<p>Let's run this code with MJIT options and check what we got.</p>
<pre><code class="language-shell">ruby --jit --jit-verbose=1 --jit-wait --disable-gems mjit.rb
</code></pre>
<pre><code class="language-shell">Time taken is 4.0e-06
Time taken is 0.0
Time taken is 0.0
Time taken is 0.0
0.000082 0.000032 0.000114 ( 0.000105)
Successful MJIT finish
</code></pre>
<p>Nothing interesting right? And why is that? because we are iterating the loop
for 4 times and default value for MJIT to work is 5. We can always decide after
how many calls MJIT should work by providing <code>--jit-min-calls=#number</code> option.</p>
<p>Let's tweak the program a bit so MJIT gets to work.</p>
<pre><code class="language-ruby">require 'benchmark'

puts Benchmark.measure {
def test_while
start_time = Time.now
i = 0

    while i &lt; 4_00_00_000
      i += 1
    end

    puts &quot;Time taken is #{Time.now - start_time}&quot;

end

10.times { test_while }
}
</code></pre>
<p>After running the above code we can see some work done by MJIT.</p>
<pre><code class="language-shell">Time taken is 0.457916
Time taken is 0.455921
Time taken is 0.454672
Time taken is 0.452823
JIT success (72.5ms): block (2 levels) in &lt;main&gt;@mjit.rb:15 -&gt; /var/folders/v6/\_6sh53vn5gl3lct18w533gr80000gn/T//\_ruby_mjit_p66220u0.c
JIT success (140.9ms): test_while@mjit.rb:4 -&gt; /var/folders/v6/\_6sh53vn5gl3lct18w533gr80000gn/T//\_ruby_mjit_p66220u1.c
JIT compaction (23.0ms): Compacted 2 methods -&gt; /var/folders/v6/\_6sh53vn5gl3lct18w533gr80000gn/T//\_ruby_mjit_p66220u2.bundle
Time taken is 0.463703
Time taken is 0.102852
Time taken is 0.103335
Time taken is 0.103299
Time taken is 0.103252
Time taken is 0.103261
2.797843 0.005357 3.141944 ( 2.801391)
Successful MJIT finish
</code></pre>
<p>Here's what's happening. Method ran 4 times and on the 5th call it found it is
running same code again. So MJIT started a separate thread to convert the code
into RTL instructions, which created a shared object library. Next, threads took
that shared code and executed directly. As we passed option <code>--jit-verbose=1</code> we
can see what MJIT did.</p>
<p>What we are seeing in output is the following:</p>
<ol>
<li>Time taken to compile.</li>
<li>What block of code is compiled by JIT.</li>
<li>Location of compiled code.</li>
</ol>
<p>We can open the file and see how MJIT converted the piece of code to binary
instructions but for that we need to pass another option which is
<code>--jit-save-temps</code> and then just inspect those files.</p>
<p>After compiling the code to RTL instructions, take a look at the execution time.
It dropped down to 0.10 ms from 0.46 ms. That's a neat speed bump.</p>
<p>Here is a comparison across some of the Ruby versions for some basic operations.</p>
<p>![Ruby time comparison in different versions](/blog/images/images_used_in_blog/image
ruby_mjit_execution_comparison.png)</p>
<h2>Rails comparison on Ruby 2.5, Ruby 2.6 and Ruby 2.6 with JIT</h2>
<p>Create a rails application with different Ruby versions and start a server. We
can start the rails server with the JIT option, as shown below.</p>
<pre><code class="language-shell">RUBYOPT=&quot;--jit&quot; bundle exec rails s
</code></pre>
<p>Now, we can start testing the performance on servers. We found that Ruby 2.6 is
faster than Ruby 2.5, but enabling JIT in Ruby 2.6 does not add more value to
the Rails application.</p>
<h2>MJIT status and future directions</h2>
<ul>
<li>It is in an early development stage.</li>
<li>Does not work on windows.</li>
<li>Needs more time to mature.</li>
<li>Needs more optimisations.</li>
<li>MJIT can use GCC or LLVM in the future C Compilers.</li>
</ul>
<h2>Further reading</h2>
<ol>
<li><a href="https://developers.redhat.com/blog/2018/03/22/ruby-3x3-performance-goal">Ruby 3x3 Performance Goal</a></li>
<li><a href="https://medium.com/@k0kubun/the-method-jit-compiler-for-ruby-2-6-388ee0989c13">The method JIT compiler for Ruby2.6</a></li>
<li><a href="https://github.com/vnmakarov/ruby/tree/rtl_mjit_branch">Vladimir Makarov's Ruby Edition</a></li>
</ol>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 Range#cover? accepts Range object as argument]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-range-cover-now-accepts-range-object"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-range-cover-now-accepts-range-object.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-range-cover-now-accepts-range-object</id>
      <published>2018-10-24T00:00:00.000Z</published>
      <updated>2018-10-24T00:00:00.000Z</updated>
      <author>
        <name>Abhay Nikam</name>
        <uri>https://www.bigbinary.com/blog/authors/abhay</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-range-cover-now-accepts-range-object.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.6 Range#cover? now accepts Range object as an argument]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-range-cover-now-accepts-range-object"><![CDATA[<p><code>Range#cover?</code> returns true if the object passed as argument is in the range.</p>
<pre><code class="language-ruby">(1..10).cover?(5)
=&gt; true
</code></pre>
<p><code>Range#cover?</code> returns false if the object passed as an argument is
non-comparable or is not in the range.</p>
<p>Before Ruby 2.6, <code>Range#cover?</code> used to return false if a Range object is passed
as an argument.</p>
<pre><code class="language-ruby">&gt;&gt; (1..10).cover?(2..5)
=&gt; false
</code></pre>
<h4>Ruby 2.6</h4>
<p>In Ruby 2.6 <code>Range#cover?</code> can accept a Range object as an argument. It returns
true if the argument Range is equal to or a subset of the Range.</p>
<pre><code class="language-ruby">(1..100).cover?(10..20)
=&gt; true

(1..10).cover?(2..5)
=&gt; true

(5..).cover?(4..)
=&gt; false

(&quot;a&quot;..&quot;d&quot;).cover?(&quot;x&quot;..&quot;z&quot;)
=&gt; false
</code></pre>
<p>Here is relevant
<a href="https://github.com/ruby/ruby/commit/9ca738927293df1c7a2a1ed7e2d6cf89527b5438">commit</a>
and <a href="https://bugs.ruby-lang.org/issues/14473">discussion</a> for this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 adds RubyVM::AST module]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-adds-rubyvm-ast-module"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-rubyvm-ast-module.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-adds-rubyvm-ast-module</id>
      <published>2018-10-02T00:00:00.000Z</published>
      <updated>2018-10-02T00:00:00.000Z</updated>
      <author>
        <name>Amit Choudhary</name>
        <uri>https://www.bigbinary.com/blog/authors/amit</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-rubyvm-ast-module.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.6 added RubyVM::AST to generate the Abstract Syntax Tree of code. Please note, this feature is experimental and under active development. As of now, RubyVM::AST supports two methods: parse and...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-adds-rubyvm-ast-module"><![CDATA[<p>Ruby 2.6 added <code>RubyVM::AST</code> to generate the
<a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">Abstract Syntax Tree</a> of
code. Please note, this feature is experimental and under active development.</p>
<p>As of now, <code>RubyVM::AST</code> supports two methods: <code>parse</code> and <code>parse_file</code>.</p>
<p><code>parse</code> method takes a string as a parameter and returns the root node of the
tree in the form of the object, RubyVM::AST::Node (Link is not available).</p>
<p><code>parse_file</code> method takes the file name as a parameter and returns the root node
of the tree in the form of the object,
<a href="https://ruby-doc.org/core-2.6.0.preview2/RubyVM/AST/Node.html">RubyVM::AST::Node</a>.</p>
<h4>Ruby 2.6.0-preview2</h4>
<pre><code class="language-ruby">irb&gt; RubyVM::AST.parse(&quot;(1..100).select { |num| num % 5 == 0 }&quot;)
=&gt; #&lt;RubyVM::AST::Node(NODE_SCOPE(0) 1:0, 1:38): &gt;

irb&gt; RubyVM::AST.parse_file(&quot;/Users/amit/app.rb&quot;)
=&gt; #&lt;RubyVM::AST::Node(NODE_SCOPE(0) 1:0, 1:38): &gt;
</code></pre>
<p><a href="https://ruby-doc.org/core-2.6.0.preview2/RubyVM/AST/Node.html">RubyVM::AST::Node</a>
has seven public instance methods - <code>children</code>, <code>first_column</code>, <code>first_lineno</code>,
<code>inspect</code>, <code>last_column</code>, <code>last_lineno</code> and <code>type</code>.</p>
<h4>Ruby 2.6.0-preview2</h4>
<pre><code class="language-ruby">irb&gt; ast_node = RubyVM::AST.parse(&quot;(1..100).select { |num| num % 5 == 0 }&quot;)
=&gt; #&lt;RubyVM::AST::Node(NODE_SCOPE(0) 1:0, 1:38): &gt;

irb&gt; ast_node.children
=&gt; [nil, #&lt;RubyVM::AST::Node(NODE_ITER(9) 1:0, 1:38): &gt;]

irb&gt; ast_node.first_column
=&gt; 0

irb&gt; ast_node.first_lineno
=&gt; 1

irb&gt; ast_node.inspect
=&gt; &quot;#&lt;RubyVM::AST::Node(NODE_SCOPE(0) 1:0, 1:38): &gt;&quot;

irb&gt; ast_node.last_column
=&gt; 38

irb&gt; ast_node.last_lineno
=&gt; 1

irb&gt; ast_node.type
=&gt; &quot;NODE_SCOPE&quot;
</code></pre>
<p>This module will majorly help in building a static code analyzer and formatter.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Configuring memory allocation in ImageMagick]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/configuring-memory-allocation-in-imagemagick"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/configuring-memory-allocation-in-imagemagick.png"/>
      <id>https://www.bigbinary.com/blog/configuring-memory-allocation-in-imagemagick</id>
      <published>2018-09-12T00:00:00.000Z</published>
      <updated>2018-09-12T00:00:00.000Z</updated>
      <author>
        <name>Ershad Kunnakkadan</name>
        <uri>https://www.bigbinary.com/blog/authors/ershad</uri>
      </author>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/configuring-memory-allocation-in-imagemagick.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[ImageMagick has a security policy file policy.xml using which we can control and limit the execution of the program. For example, the default memory limit of ImageMagick-6 is 256 MiB. Recently, we...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/configuring-memory-allocation-in-imagemagick"><![CDATA[<p>ImageMagick has a security policy file
<a href="https://www.imagemagick.org/source/policy.xml">policy.xml</a> using which we can
control and limit the execution of the program. For example, the default memory
limit of ImageMagick-6 is 256 MiB.</p>
<p>Recently, we saw following error while processing a gif image.</p>
<pre><code class="language-text">
convert-im6.q16: DistributedPixelCache '127.0.0.1' @ error/distribute-cache.c/ConnectPixelCacheServer/244.
convert-im6.q16: cache resources exhausted `file.gif' @ error/cache.c/OpenPixelCache/3945.

</code></pre>
<p>This happens when ImageMagick cannot allocate enough memory to process the
image. This can be fixed by tweaking memory configuration in <code>policy.xml</code>.</p>
<p>Path of <code>policy.xml</code> can be located as follows.</p>
<pre><code class="language-text">$ identify -list policy

Path: /etc/ImageMagick-6/policy.xml
  Policy: Resource
    name: disk
    value: 1GiB
</code></pre>
<p>Memory limit can be configured in the following line of <code>policy.xml</code>.</p>
<pre><code class="language-xml">&lt;policy domain=&quot;resource&quot; name=&quot;memory&quot; value=&quot;256MiB&quot;/&gt;
</code></pre>
<p>Increasing this value would solve the error if you have a machine with larger a
memory.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 adds Enumerable#filter as alias for #select]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-adds-enumerable-filter-as-an-alias-of-enumerable-select"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-enumerable-filter-as-an-alias-of-enumerable-select.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-adds-enumerable-filter-as-an-alias-of-enumerable-select</id>
      <published>2018-08-28T00:00:00.000Z</published>
      <updated>2018-08-28T00:00:00.000Z</updated>
      <author>
        <name>Amit Choudhary</name>
        <uri>https://www.bigbinary.com/blog/authors/amit</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-enumerable-filter-as-an-alias-of-enumerable-select.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.6 adds Enumerable#filter as an alias of Enumerable#select]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-adds-enumerable-filter-as-an-alias-of-enumerable-select"><![CDATA[<p>Ruby 2.6 has added <code>Enumerable#filter</code> as an alias of <code>Enumerable#select</code>. The
reason for adding <code>Enumerable#filter</code> as an alias is to make it easier for
people coming from other languages to use Ruby. A lot of other languages,
including Java, R, PHP etc., have a filter method to filter/select records based
on a condition.</p>
<p>Let's take an example in which we have to select/filter all numbers which are
divisible by 5 from a range.</p>
<h4>Ruby 2.5</h4>
<pre><code class="language-ruby">irb&gt; (1..100).select { |num| num % 5 == 0 }
=&gt; [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]

irb&gt; (1..100).filter { |num| num % 5 == 0 }
=&gt; Traceback (most recent call last):
2: from /Users/amit/.rvm/rubies/ruby-2.5.1/bin/irb:11:in `&lt;main&gt;' 1: from (irb):2 NoMethodError (undefined method`filter' for 1..100:Range)
</code></pre>
<h4>Ruby 2.6.0-preview2</h4>
<pre><code class="language-ruby">irb&gt; (1..100).select { |num| num % 5 == 0 }
=&gt; [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]

irb&gt; (1..100).filter { |num| num % 5 == 0 }
=&gt; [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
</code></pre>
<p>Also note, along with <code>Enumerable#filter</code>, <code>Enumerable#filter!</code>,
<code>Enumerable#select!</code> was also added as an alias.</p>
<p>Here is the relevant <a href="https://github.com/ruby/ruby/commit/b1a8c64483">commit</a>
and <a href="https://bugs.ruby-lang.org/issues/13784">discussion</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 adds support for non-ASCII capital letter]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2.6-adds-support-for-non-ascii-capital-case-constant"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2.6-adds-support-for-non-ascii-capital-case-constant.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2.6-adds-support-for-non-ascii-capital-case-constant</id>
      <published>2018-08-22T00:00:00.000Z</published>
      <updated>2018-08-22T00:00:00.000Z</updated>
      <author>
        <name>Rohan Pujari</name>
        <uri>https://www.bigbinary.com/blog/authors/rohanpujaris</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2.6-adds-support-for-non-ascii-capital-case-constant.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.6 adds support for non-ASCII capital letter as a first character in constant name]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2.6-adds-support-for-non-ascii-capital-case-constant"><![CDATA[<p>Before Ruby 2.6, constant must have a capital ASCII letter as the first
character. It means class and module name cannot start with non-ASCII capital
character.</p>
<p>Below code will raise <code>class/module name must be CONSTANT</code> exception.</p>
<pre><code class="language-ruby">  class Большойдвоичный
  end
</code></pre>
<p>We can use above non-ASCII character as a method name or variable name though.</p>
<p>Below code will run without any exception</p>
<pre><code class="language-ruby">  class NonAsciiMethodAndVariable
    def Большойдвоичный
      Имя = &quot;BigBinary&quot;
    end
  end
</code></pre>
<p>&quot;Имя&quot; is treated as a variable name in above example, even though first
letter(И) is a capital non-ASCII character.</p>
<h4>Ruby 2.6</h4>
<p>Ruby 2.6 relaxes above mentioned limitation. We can now define constants in
languages other than English. Languages having capital letters like Russian and
Greek can be used to define constant name.</p>
<p>Below code will run without exception in any Ruby 2.6.</p>
<pre><code class="language-ruby">  class Большойдвоичный
  end
</code></pre>
<p>As capital non-Ascii characters are now treated as constant, below code will
raise a warning in Ruby 2.6.</p>
<pre><code class="language-ruby">  irb(main):001:0&gt; Имя = &quot;BigBinary&quot;
  =&gt; &quot;BigBinary&quot;
  irb(main):002:0&gt; Имя = &quot;BigBinary&quot;
  (irb):2: warning: already initialized constant Имя
  (irb):1: warning: previous definition of Имя was here
</code></pre>
<p>Above code will run without any warnings on Ruby versions prior to 2.6</p>
<p>Here is relevant <a href="https://github.com/ruby/ruby/commit/f852af">commit</a> and
<a href="https://bugs.ruby-lang.org/issues/13770">discussion</a> for this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Setting up a high performance Geocoder]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/setting-up-a-high-performance-geocoder"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/setting-up-a-high-performance-geocoder.png"/>
      <id>https://www.bigbinary.com/blog/setting-up-a-high-performance-geocoder</id>
      <published>2018-08-21T00:00:00.000Z</published>
      <updated>2018-08-21T00:00:00.000Z</updated>
      <author>
        <name>Midhun Krishna</name>
        <uri>https://www.bigbinary.com/blog/authors/midhunkrishna</uri>
      </author>
      <category term="Ruby"/>
      <category term="Rails"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/setting-up-a-high-performance-geocoder.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[A setup guide with side-by-side comparison of Nominatim with Google geocoder and how to improve geocoding efficiency]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/setting-up-a-high-performance-geocoder"><![CDATA[<p>One of our applications uses geocoding extensively. When we started the project,
we included the excellent
<a href="https://github.com/alexreisner/geocoder">Geocoder gem</a>, and set
<a href="https://developers.google.com/maps/documentation/geocoding/start">Google</a> as
the geocoding backend. As the application scaled, its geocoding requirements
grew and soon we were looking at geocoding bills worth thousands of dollars.</p>
<h3>An alternative Geocoder</h3>
<p>Our search for an alternative geocoder landed us on Nominatim. Written in C,
with a PHP web interface, Nominatim was performant enough for our requirements.
Once set up, Nominatim required 8GB of RAM to run and this included RAM for the
PostgreSQL (+ PostGIS) as well.</p>
<p>The rest of the blog discusses how to setup Nominatim and the tips and tricks
that we learned along the way and how it compares with the geocoding solution
offered by Google.</p>
<h3>Setting up Nominatim</h3>
<p>We started off by looking for Amazon Machine Images with Nominatim setup and
could only find one which was hosted by
<a href="https://www.openstreetmap.org/">OpenStreetMap</a> but the magnet link was dead.</p>
<p>Next, we went through the
<a href="http://nominatim.org/release-docs/latest/admin/Installation/">official installation document</a>.
We decided to give docker a shot and found that there are many Nominatim docker
builds. We used
<a href="https://github.com/merlinnot/nominatim-docker">https://github.com/merlinnot/nominatim-docker</a>
since it seemed to follow all the steps mentioned in the official installation
guide.</p>
<h2>Issues faced during Setup</h2>
<h4>Out of Memory Errors</h4>
<p>The official documentation recommends using 32GB of RAM for initial import but
we needed to double the memory to 64GB to make it work.</p>
<p>Also any time docker build failed, due to the large amount of data that is
generated on each run, we also ran out of disk space on subsequent docker builds
since docker caches layers across builds.</p>
<h4>Merging Multiple Regions</h4>
<p>We wanted to geocode locations from USA, Mexico, Canada and Sri Lanka. USA,
Mexico and Canada are
<a href="http://download.geofabrik.de/north-america.html#subregions">included by default in North America data extract</a>
but we had to merge data for Sri Lanka with North America to get it in a format
required for initial import.</p>
<p>The following snippet pre-processes map data for North America and Sri Lanka
into a single data.osm.pbf file that can be directly used by Nominatim
installer.</p>
<pre><code class="language-bash">RUN curl -L 'http://download.geofabrik.de/north-america-latest.osm.pbf' \
    --create-dirs -o /srv/nominatim/src/north-america-latest.osm.pbf
RUN curl -L 'http://download.geofabrik.de/asia/sri-lanka-latest.osm.pbf' \
    --create-dirs -o /srv/nominatim/src/sri-lanka-latest.osm.pbf

RUN osmconvert /srv/nominatim/src/north-america-latest.osm.pbf \
    -o=/srv/nominatim/src/north-america-latest.o5m
RUN osmconvert /srv/nominatim/src/sri-lanka-latest.osm.pbf \
    -o=/srv/nominatim/src/sri-lanka-latest.o5m

RUN osmconvert /srv/nominatim/src/north-america-latest.o5m \
    /srv/nominatim/src/sri-lanka-latest.o5m \
    -o=/srv/nominatim/src/data.o5m

RUN osmconvert /srv/nominatim/src/data.o5m \
    -o=/srv/nominatim/src/data.osm.pbf
</code></pre>
<h4>Slow Search times</h4>
<p>Once the installation was done, we tried running simple location
<a href="https://nominatim.openstreetmap.org/search.php?q=New+York&amp;polygon_geojson=1&amp;viewbox=">searches like this one</a>,
but the search timed out. Usually Nominatim can provide a lot of information
from its web-interface by appending <code>&amp;debug=true</code> to the search query.</p>
<pre><code class="language-bash"># from
https://nominatim.openstreetmap.org/search.php?q=New+York&amp;polygon_geojson=1&amp;viewbox=
# to
https://nominatim.openstreetmap.org/search.php?q=New+York&amp;polygon_geojson=1&amp;viewbox=&amp;debug=true
</code></pre>
<p>We created an
<a href="https://github.com/openstreetmap/Nominatim/issues/1023">issue in Nominatim repository</a>
and got very prompt replies from Nominatim maintainers, especially from
<a href="https://github.com/lonvia">Sarah Hoffman</a> .</p>
<pre><code class="language-sql"># runs analyze on the entire nominatim database
psql -d nominatim -c 'ANALYZE VERBOSE'
</code></pre>
<p>PostgreSQL query planner
<a href="https://www.postgresql.org/docs/9.5/static/planner-stats.html">depends on statistics</a>
collected by
<a href="https://www.postgresql.org/docs/9.1/static/monitoring-stats.html">postgres statistics collector</a>
while executing a query. In our case, query planner took an enormous amount of
time to plan queries as there were no stats collected since we had a fresh
installation.</p>
<h3>Comparing Nominatim and Google Geocoder</h3>
<p>We compared 2500 addresses and we found that Google geocoded 99% of those
addresses. In comparison Nominatim could only geocode 47% of the addresses.</p>
<p>It means we still need to geocode ~50% of addresses using Google geocoder. We
found that we could increase geocoding efficiency by normalizing the addresses
we had.</p>
<h3>Address Normalization using libpostal</h3>
<p><a href="https://github.com/openvenues/libpostal">Libpostal</a> is an address normalizer,
which uses
<a href="https://en.wikipedia.org/wiki/Natural-language_processing#Statistical_natural-language_processing_(SNLP)">statistical natural-language processing</a>
to normalize addresses. Libpostal also has
<a href="https://github.com/openvenues/ruby_postal">ruby bindings</a> which made it quite
easy to use it for our test purposes.</p>
<p>Once libpostal and its ruby-bindings were installed (installation is
straightforward and steps are available in
<a href="https://github.com/openvenues/ruby_postal">ruby-postal's github page</a>), we gave
libpostal + Nominatim a go.</p>
<pre><code class="language-ruby">require 'geocoder'
require 'ruby_postal/expand'
require 'ruby_postal/parser'

Geocoder.configure({lookup: :nominatim, nominatim: { host: &quot;nominatim_host:port&quot;}})

full_address = [... address for normalization ...]
expanded_addresses = Postal::Expand.expand_address(full_address)
parsed_addresses = expanded_addresses.map do |address|
  Postal::Parser.parse_address(address)
end

parsed_addresses.each do | address |
  parsed_address = [:house_number, :road, :city, :state, :postcode, :country].inject([]) do |acc, key|
    # address is of format
    # [{label: 'postcode', value: 12345}, {label: 'city', value: 'NY'} .. ]
    key_value = address.detect { |address| address[:label] == key }
    if key_value
        acc &lt;&lt; &quot;#{key_value_pair[:value]}&quot;.titleize
    end
    acc
  end

  coordinates = Geocoder.coordinates(parsed_address.join(&quot;, &quot;))
  if (coordinates.is_a? Array) &amp;&amp; coordinates.present?
    puts &quot;By Libpostal #{coordinates} =&gt; #{parsed_address.join(&quot;, &quot;)}&quot;
    break
  end
end
</code></pre>
<p>With this, we were able to improve our geocoding efficiency by 10% as
Nominatim + Libpostal combination could geocode ~ 59% of addresses.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 adds write_timeout to Net::HTTP]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-adds-write-timeout-to-net-http"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-write-timeout-to-net-http.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-adds-write-timeout-to-net-http</id>
      <published>2018-08-14T00:00:00.000Z</published>
      <updated>2018-08-14T00:00:00.000Z</updated>
      <author>
        <name>Taha Husain</name>
        <uri>https://www.bigbinary.com/blog/authors/taha</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-write-timeout-to-net-http.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[write_timeout is added to Net::HTTP and Net::BufferedIO in Ruby 2.6]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-adds-write-timeout-to-net-http"><![CDATA[<p>Before Ruby 2.6, if we created a large request with <code>Net::HTTP</code>, it would hang
forever until request is interrupted. To fix this issue,
<a href="https://docs.ruby-lang.org/en/trunk/Net/HTTP.html#attribute-i-write_timeout">write_timeout</a>
attribute and
<a href="https://docs.ruby-lang.org/en/trunk/Net/HTTP.html#method-i-write_timeout-3D">write_timeout=</a>
method is added to <a href="https://docs.ruby-lang.org/en/2.6.0/">Net::HTTP</a> in Ruby
2.6. Default value for <code>write_timeout</code> is 60 seconds and can be set to an
integer or a float value.</p>
<p>Similarly, <code>write_timeout</code> attribute and <code>write_timeout=</code> method is added to
<code>Net::BufferedIO</code> class.</p>
<p>If any chunk of response is not written within number of seconds provided to
<code>write_timeout</code>, <a href="https://docs.ruby-lang.org/en/2.6.0/">Net::WriteTimeout</a>
exception is raised. <code>Net::WriteTimeout</code> exception is not raised on Windows
systems.</p>
<h6>Example</h6>
<pre><code class="language-ruby">
# server.rb

require 'socket'

server = TCPServer.new('localhost', 2345)
loop do
socket = server.accept
end

</code></pre>
<h5>Ruby 2.5.1</h5>
<pre><code class="language-ruby">
# client.rb

require 'net/http'

connection = Net::HTTP.new('localhost', 2345)
connection.open_timeout = 1
connection.read_timeout = 3
connection.start

post = Net::HTTP::Post.new('/')
body = (('a' _ 1023) + &quot;\n&quot;) _ 5_000
post.body = body

puts &quot;Sending #{body.bytesize} bytes&quot;
connection.request(post)

</code></pre>
<h6>Output</h6>
<pre><code class="language-ruby">\$ RBENV_VERSION=2.5.1 ruby client.rb

Sending 5120000 bytes
</code></pre>
<p>Ruby 2.5.1 processes request endlessly unless above program is interrupted.</p>
<h5>Ruby 2.6.0-dev</h5>
<p>Add <code>write_timeout</code> attribute to <code>Net::HTTP</code> instance in client.rb program.</p>
<pre><code class="language-ruby">
# client.rb

require 'net/http'

connection = Net::HTTP.new('localhost', 2345)
connection.open_timeout = 1
connection.read_timeout = 3

# set write_timeout to 10 seconds

connection.write_timeout = 10

connection.start

post = Net::HTTP::Post.new('/')
body = (('a' _ 1023) + &quot;\n&quot;) _ 5_000
post.body = body

puts &quot;Sending #{body.bytesize} bytes&quot;
connection.request(post)

</code></pre>
<h6>Output</h6>
<pre><code class="language-ruby">\$ RBENV_VERSION=2.6.0-dev ruby client.rb

Sending 5120000 bytes
Traceback (most recent call last):
13: `from client.rb:17:in `&lt;main&gt;`` 12: `from /net/http.rb:1479:in `request``
11: `from /net/http.rb:1506:in `transport_request`` 10: `from /net/http.rb:1506:in `catch``
9: `from /net/http.rb:1507:in `block in transport_request`` 8: `from /net/http/generic_request.rb:123:in `exec``
7: `from /net/http/generic_request.rb:189:in `send_request_with_body`` 6: `from /net/protocol.rb:221:in `write``
5: `from /net/protocol.rb:239:in `writing`` 4: `from /net/protocol.rb:222:in `block in write``
3: `from /net/protocol.rb:249:in `write0`` 2: `from /net/protocol.rb:249:in `each_with_index``
1: `from /net/protocol.rb:249:in `each`` `/net/protocol.rb:270:in `block in write0`: Net::WriteTimeout (Net::WriteTimeout)`

</code></pre>
<p>In Ruby 2.6.0, above program is terminated raising <code>Net::WriteTimeout</code> exception
after 10 seconds (value set to <code>write_timeout</code> attribute).</p>
<p>Here is relevant <a href="https://github.com/ruby/ruby/commit/bd7c46">commit</a> and
<a href="https://bugs.ruby-lang.org/issues/13396">discussion</a> for this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 adds Dir#each_child & Dir#children instance]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-introduces-dir-each_child-and-dir-children-instance-methods"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-introduces-dir-each_child-and-dir-children-instance-methods.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-introduces-dir-each_child-and-dir-children-instance-methods</id>
      <published>2018-08-07T00:00:00.000Z</published>
      <updated>2018-08-07T00:00:00.000Z</updated>
      <author>
        <name>Tejaswini Chile</name>
        <uri>https://www.bigbinary.com/blog/authors/tejaswini</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-introduces-dir-each_child-and-dir-children-instance-methods.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.6 introduces each_child and children as instance methods to have file names in a directory without `.` and `..`]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-introduces-dir-each_child-and-dir-children-instance-methods"><![CDATA[<p>Ruby 2.5 had introduced class level methods
<a href="https://ruby-doc.org/core-2.5.1/Dir.html#method-c-each_child">Dir::each_child</a>
and <a href="https://ruby-doc.org/core-2.5.1/Dir.html#method-c-children">Dir::children</a>.
We wrote a
<a href="https://blog.bigbinary.com/2017/11/21/ruby-2_5-introduces-dir-children-and-dir-each_child.html">detailed blog</a>
about it.</p>
<p>In Ruby 2.6, same methods are added as instance methods on <code>Dir</code> class.
Dir#children (Link is not available) returns array of all the filenames except
<code>.</code> and <code>..</code> in the directory. Dir#each_child (Link is not available) yields all
the filenames and operates on it.</p>
<p>Let's have a look at examples to understand it better.</p>
<h5>Dir#children</h5>
<pre><code class="language-ruby">directory = Dir.new('/Users/tejaswinichile/workspace')

directory.children
=&gt; [&quot;panda.png&quot;, &quot;apple.png&quot;, &quot;banana.png&quot;, &quot;camera.jpg&quot;]

</code></pre>
<p><code>Dir#each_child</code> iterates and calls block for each file entry in the given
directory. It uses filename as a parameter to the block.</p>
<h5>Dir#each_child</h5>
<pre><code class="language-ruby">directory = Dir.new('/Users/tejaswinichile/workspace')

directory.each_child { |filename| puts &quot;Currently reading: #{filename}&quot;}

Currently reading: panda.png
Currently reading: apple.png
Currently reading: banana.png
Currently reading: camera.jpg
=&gt; #&lt;Dir:/Users/tejaswinichile/Desktop&gt;

</code></pre>
<p>If we don't pass any block to <code>each_child</code>, it returns enumerator instead.</p>
<pre><code class="language-ruby">directory = Dir.new('/Users/tejaswinichile/workspace')

directory.each_child

=&gt; #&lt;Enumerator: #&lt;Dir:/Users/tejaswinichile/Desktop&gt;:each_child&gt;

</code></pre>
<p>Here is relevant
<a href="https://github.com/ruby/ruby/commit/6a3a7e9114c3ede47d15f0d2a73f392cfcdd1ea7">commit</a>
and <a href="https://bugs.ruby-lang.org/issues/13969">discussion</a> for this change.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 adds exception parameter to Integer and Float]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-adds-option-to-not-raise-exception-for-integer-float-methods"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-option-to-not-raise-exception-for-integer-float-methods.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-adds-option-to-not-raise-exception-for-integer-float-methods</id>
      <published>2018-07-31T00:00:00.000Z</published>
      <updated>2018-07-31T00:00:00.000Z</updated>
      <author>
        <name>Prathamesh Sonpatki</name>
        <uri>https://www.bigbinary.com/blog/authors/prathamesh</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-option-to-not-raise-exception-for-integer-float-methods.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Instead of raising exception, we can pass a keyword argument and the type conversion methods will return nil]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-adds-option-to-not-raise-exception-for-integer-float-methods"><![CDATA[<p>We can use <code>Integer</code> and <code>Float</code> methods to convert values to integers and
floats respectively. Ruby also has <code>to_i</code> and <code>to_f</code> methods for same purpose.
Let's see how it differs from the <code>Integer</code> method.</p>
<pre><code class="language-ruby">
&gt; &gt; &quot;1one&quot;.to_i
&gt; &gt; =&gt; 1

&gt; &gt; Integer(&quot;1one&quot;)
&gt; &gt; ArgumentError: invalid value for Integer(): &quot;1one&quot;

    from (irb):2:in `Integer'
    from (irb):2
    from /Users/prathamesh/.rbenv/versions/2.4.0/bin/irb:11:in `&lt;main&gt;'

&gt; &gt;
</code></pre>
<p>The <code>to_i</code> method tries to convert the given input to integer as much as
possible whereas the <code>Integer</code> method throws an <code>ArgumentError</code> if it can't
covert the input to integer. The <code>Integer</code> and <code>Float</code> methods parse more
strictly compared to <code>to_i</code> and <code>to_f</code> respectively.</p>
<p>Some times, we might need the strictness of <code>Integer</code> and <code>Float</code> but ability to
not raise an exception every time the input can't be parsed.</p>
<p>Before Ruby 2.6 it was possible to achieve it in following way.</p>
<pre><code class="language-ruby">
&gt; &gt; Integer(&quot;msg&quot;) rescue nil
&gt; &gt;
</code></pre>
<p>In Ruby 2.6, the
<a href="https://bugs.ruby-lang.org/issues/12732">Integer and Float methods accept a keyword argument exception</a>
which can be either <code>true</code> or <code>false</code>. If it is <code>false</code> then no exception is
raised if the input can't be parsed and <code>nil</code> is returned.</p>
<pre><code class="language-ruby">
&gt; &gt; Float(&quot;foo&quot;, exception: false)
&gt; &gt; =&gt; nil
&gt; &gt; Integer(&quot;foo&quot;, exception: false)
&gt; &gt; =&gt; nil
&gt; &gt;
</code></pre>
<p>This is also faster than rescuing the exception and returning <code>nil</code>.</p>
<pre><code class="language-ruby">
&gt; &gt; Benchmark.ips do |x|
&gt; &gt; ?&gt; x.report(&quot;rescue&quot;) {
&gt; &gt; ?&gt; Integer('foo') rescue nil
&gt; &gt; }
&gt; &gt; x.report(&quot;kwarg&quot;) {
&gt; &gt; ?&gt; Integer('foo', exception: false)
&gt; &gt; }
&gt; &gt; x.compare!
&gt; &gt; end
&gt; &gt; Warming up --------------------------------------

              rescue    41.896k i/100ms
               kwarg    81.459k i/100ms

Calculating -------------------------------------
rescue 488.006k (± 4.5%) i/s - 2.472M in 5.076848s
kwarg 1.024M (±11.8%) i/s - 5.050M in 5.024937s

Comparison:
kwarg: 1023555.3 i/s
rescue: 488006.0 i/s - 2.10x slower


</code></pre>
<p>As we can see, rescuing the exception is twice slower than using the new keyword
argument. We can still use the older technique if we want to return a different
value from <code>nil</code>.</p>
<pre><code class="language-ruby">
&gt; &gt; Integer('foo') rescue 42
&gt; &gt; =&gt; 42
&gt; &gt;
</code></pre>
<p>By default, the keyword argument <code>exception</code> is set to <code>true</code> for backward
compatibility.</p>
<p>The Chinese version of this blog is available
<a href="http://madao.me/yi-ruby-2-6-zeng-jia-liao-integer-he-float-fang-fa-de-yi-chang-can-shu/">here</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 adds Binding#source_location]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-adds-binding-source-location"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-binding-source-location.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-adds-binding-source-location</id>
      <published>2018-07-24T00:00:00.000Z</published>
      <updated>2018-07-24T00:00:00.000Z</updated>
      <author>
        <name>Taha Husain</name>
        <uri>https://www.bigbinary.com/blog/authors/taha</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-binding-source-location.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Binding#source_location is added in Ruby 2.6 which makes fetching filename and line number more readable]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-adds-binding-source-location"><![CDATA[<p>Before Ruby 2.6, if we want to know file name with location and line number of
source code, we would need to use Binding#eval (Link is not available) .</p>
<pre><code class="language-ruby">binding.eval('[__FILE__, __LINE__]')
=&gt; [&quot;/Users/taha/blog/app/controllers/application_controller&quot;, 2]
</code></pre>
<p>Ruby 2.6 adds more readable method Binding#source_location (Link is not
available) to achieve similar result.</p>
<pre><code class="language-ruby">binding.source_location
=&gt; [&quot;/Users/taha/blog/app/controllers/application_controller&quot;, 2]
</code></pre>
<p>Here is relevant <a href="https://github.com/ruby/ruby/commit/571e48">commit</a> and
<a href="https://bugs.ruby-lang.org/issues/14230">discussion</a> for this change.</p>
<p>The Chinese version of this blog is available
<a href="http://madao.me/yi-ruby-2-6-binding-dui-xiang-zeng-jia-source_location-fang-fa/">here</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 adds String#split with block]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-adds-split-with-block"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-split-with-block.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-adds-split-with-block</id>
      <published>2018-07-17T00:00:00.000Z</published>
      <updated>2018-07-17T00:00:00.000Z</updated>
      <author>
        <name>Taha Husain</name>
        <uri>https://www.bigbinary.com/blog/authors/taha</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-split-with-block.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[String#split with block is added in Ruby 2.6. split yields each split substrings if the block is given, instead of returing the array.]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-adds-split-with-block"><![CDATA[<p>Before Ruby 2.6,
<a href="http://ruby-doc.org/core-2.5.1/String.html#method-i-split">String#split</a>
returned array of split strings.</p>
<p>In Ruby 2.6, a block can be passed to String#split (Link is not available) which
yields each split string and operates on it. This avoids creating an array and
thus is memory efficient.</p>
<p>We will add method <code>is_fruit?</code> to understand how to use <code>split</code> with a block.</p>
<pre><code class="language-ruby">def is_fruit?(value)
%w(apple mango banana watermelon grapes guava lychee).include?(value)
end
</code></pre>
<p>Input is a comma separated string with vegetables and fruits names. Goal is to
fetch names of fruits from input string and store it in an array.</p>
<h5>String#split</h5>
<pre><code class="language-ruby">input_str = &quot;apple, mango, potato, banana, cabbage, watermelon, grapes&quot;

splitted_values = input_str.split(&quot;, &quot;)
=&gt; [&quot;apple&quot;, &quot;mango&quot;, &quot;potato&quot;, &quot;banana&quot;, &quot;cabbage&quot;, &quot;watermelon&quot;, &quot;grapes&quot;]

fruits = splitted_values.select { |value| is_fruit?(value) }
=&gt; [&quot;apple&quot;, &quot;mango&quot;, &quot;banana&quot;, &quot;watermelon&quot;, &quot;grapes&quot;]
</code></pre>
<p>Using <code>split</code> an intermediate array is created which contains both fruits and
vegetables names.</p>
<h5>String#split with a block</h5>
<pre><code class="language-ruby">fruits = []

input_str = &quot;apple, mango, potato, banana, cabbage, watermelon, grapes&quot;

input_str.split(&quot;, &quot;) { |value| fruits &lt;&lt; value if is_fruit?(value) }
=&gt; &quot;apple, mango, potato, banana, cabbage, watermelon, grapes&quot;

fruits
=&gt; [&quot;apple&quot;, &quot;mango&quot;, &quot;banana&quot;, &quot;watermelon&quot;, &quot;grapes&quot;]
</code></pre>
<p>When a block is passed to <code>split</code>, it returns the string on which split was
called and does not create an array. <code>String#split</code> yields block on each split
string, which in our case was to push fruit names in a separate array.</p>
<h4>Update</h4>
<h5>Benchmark</h5>
<p>We created a large random string to benchmark performance of <code>split</code> and
<code>split with block</code></p>
<pre><code class="language-ruby">require 'securerandom'

test_string = ''

100_000.times.each do
test_string += SecureRandom.alphanumeric(10)
test_string += ' '
end
</code></pre>
<pre><code class="language-ruby">require 'benchmark'

Benchmark.bmbm do |bench|

bench.report('split') do
arr = test_string.split(' ')
str_starts_with_a = arr.select { |str| str.start_with?('a') }
end

bench.report('split with block') do
str_starts_with_a = []
test_string.split(' ') { |str| str_starts_with_a &lt;&lt; str if str.start_with?('a') }
end

end
</code></pre>
<p>Results</p>
<pre><code class="language-plaintext">Rehearsal ----------------------------------------------------
split              0.023764   0.000911   0.024675 (  0.024686)
split with block   0.012892   0.000553   0.013445 (  0.013486)
------------------------------------------- total: 0.038120sec

                       user     system      total        real
split              0.024107   0.000487   0.024594 (  0.024622)
split with block   0.010613   0.000334   0.010947 (  0.010991)
</code></pre>
<p>We did another iteration of benchmarking using
<a href="https://github.com/evanphx/benchmark-ips">benchmark/ips</a>.</p>
<pre><code class="language-ruby">require 'benchmark/ips'
Benchmark.ips do |bench|

bench.report('split') do
splitted_arr = test_string.split(' ')
str_starts_with_a = splitted_arr.select { |str| str.start_with?('a') }
end

bench.report('split with block') do
str_starts_with_a = []
test_string.split(' ') { |str| str_starts_with_a &lt;&lt; str if str.start_with?('a') }
end

bench.compare!
end
</code></pre>
<p>Results</p>
<pre><code class="language-plaintext">Warming up --------------------------------------
               split     4.000  i/100ms
    split with block    10.000  i/100ms
Calculating -------------------------------------
               split     46.906  (± 2.1%) i/s -    236.000  in   5.033343s
    split with block    107.301  (± 1.9%) i/s -    540.000  in   5.033614s

Comparison:
    split with block:      107.3 i/s
               split:       46.9 i/s - 2.29x  slower
</code></pre>
<p>This benchmark shows that <code>split with block</code> is about 2 times faster than
<code>split</code>.</p>
<p>Here is relevant
<a href="https://github.com/ruby/ruby/commit/2258a97fe2b21da9ec294ccedd00b3bbbc85cb07">commit</a>
and <a href="https://bugs.ruby-lang.org/issues/4780">discussion</a> for this change.</p>
<p>The Chinese version of this blog is available
<a href="http://madao.me/yi-ruby-2-6-stringde-split-fang-fa-zhi-chi-dai-ma-kuai-zhi-xing/">here</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 raises exception for else without rescue]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2.6-raise-exception-for-else-without-rescue"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2.6-raise-exception-for-else-without-rescue.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2.6-raise-exception-for-else-without-rescue</id>
      <published>2018-07-10T00:00:00.000Z</published>
      <updated>2018-07-10T00:00:00.000Z</updated>
      <author>
        <name>Rohan Pujari</name>
        <uri>https://www.bigbinary.com/blog/authors/rohanpujaris</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2.6-raise-exception-for-else-without-rescue.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.6 raises exception when 'else' is used inside 'begin..end' block without 'rescue']]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2.6-raise-exception-for-else-without-rescue"><![CDATA[<h4>Ruby 2.5</h4>
<p>If we use <code>else</code> without <code>rescue</code> inside <code>begin..end</code> block in Ruby 2.5, it
gives a warning.</p>
<pre><code class="language-ruby">irb(main):001:0&gt; begin
irb(main):002:1&gt; puts &quot;Inside begin block&quot;
irb(main):003:1&gt; else
irb(main):004:1&gt; puts &quot;Inside else block&quot;
irb(main):005:1&gt; end
(irb):5: warning: else without rescue is useless
</code></pre>
<p>This warning is present as code inside <code>else</code> block will never get executed</p>
<h4>Ruby 2.6</h4>
<p>In Ruby 2.6 it will raise an exception if we use <code>else</code> without <code>rescue</code> in
<code>begin..end</code> block. This
<a href="https://github.com/ruby/ruby/commit/140512d2225e6fd046ba1bdbcd1a27450f55c233#diff-ff4e2dc4962dc25a1512353299992c8d">commit</a>
changed warning into exception in Ruby 2.6. Changes made in the commit are
experimental.</p>
<pre><code class="language-ruby">irb(main):001:0&gt; begin
irb(main):002:1&gt; puts &quot;Inside begin block&quot;
irb(main):003:1&gt; else
irb(main):004:1&gt; puts &quot;Inside else block&quot;
irb(main):005:1&gt; end
Traceback (most recent call last):
1: from /usr/local/bin/irb:11:in `&lt;main&gt;'
SyntaxError ((irb):3: else without rescue is useless)
</code></pre>
<p>The Chinese version of this blog is available
<a href="http://madao.me/yi-ruby-2-6-hui-zai-begin-end-dai-ma-kuai-zhong-yin-wei-bu-xie-rescue-zhi-xie-else-er-pao-chu-yi-chang-shi-yan-xing-feature">here</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 adds endless range]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-adds-endless-range"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-endless-range.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-adds-endless-range</id>
      <published>2018-07-04T00:00:00.000Z</published>
      <updated>2018-07-04T00:00:00.000Z</updated>
      <author>
        <name>Taha Husain</name>
        <uri>https://www.bigbinary.com/blog/authors/taha</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-adds-endless-range.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Shorter syntax for endless range with index is added in Ruby 2.6]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-adds-endless-range"><![CDATA[<p>Before Ruby 2.6, if we want endless loop with index, we would need to use
<a href="http://ruby-doc.org/core-2.5.1/Float.html#INFINITY">Float::INFINITY</a> with
<a href="http://ruby-doc.org/core-2.5.1/Integer.html#method-i-upto">up to</a> or
<a href="http://ruby-doc.org/core-2.5.1/Range.html">Range</a>, or use
<a href="http://ruby-doc.org/core-2.5.1/Numeric.html#method-i-step">Numeric#step</a>.</p>
<h5>Ruby 2.5.0</h5>
<pre><code class="language-ruby">irb&gt; (1..Float::INFINITY).each do |n|
irb\* # logic goes here
irb&gt; end
</code></pre>
<p>OR</p>
<pre><code class="language-ruby">irb&gt; 1.step.each do |n|
irb\* # logic goes here
irb&gt; end
</code></pre>
<h4>Ruby 2.6.0</h4>
<p>Ruby 2.6 makes infinite loop more readable by changing mandatory second argument
in range to optional. Internally, Ruby changes second argument to <code>nil</code> if
second argument is not provided. So, both <code>(0..)</code> and <code>(0..nil)</code> are same in
Ruby 2.6.</p>
<h6>Using endless loop in Ruby 2.6</h6>
<pre><code class="language-ruby">irb&gt; (0..).each do |n|
irb\* # logic goes here
irb&gt; end
</code></pre>
<pre><code class="language-ruby">irb&gt; (0..nil).size
=&gt; Infinity
irb&gt; (0..).size
=&gt; Infinity
</code></pre>
<p>In Ruby 2.5, <code>nil</code> is not an acceptable argument and <code>(0..nil)</code> would throw
<code>ArgumentError</code>.</p>
<pre><code class="language-ruby">irb&gt; (0..nil)
ArgumentError (bad value for range)
</code></pre>
<p>Here is the relevant
<a href="https://github.com/ruby/ruby/commit/7f95eed19e22cb9a4867819355fe4ab99f85fd16">commit</a>
and <a href="https://bugs.ruby-lang.org/issues/12912">discussion</a> for this change.</p>
<p>The Chinese version of this blog is available
<a href="http://madao.me/yi-ruby-2-6-zeng-jia-wu-qiong-fan-wei/">here</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Using Concurrent Ruby in a Ruby on Rails Application]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/using-concurrent-ruby-in-a-ruby-on-rails-application"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/using-concurrent-ruby-in-a-ruby-on-rails-application.png"/>
      <id>https://www.bigbinary.com/blog/using-concurrent-ruby-in-a-ruby-on-rails-application</id>
      <published>2018-06-05T00:00:00.000Z</published>
      <updated>2018-06-05T00:00:00.000Z</updated>
      <author>
        <name>Midhun Krishna</name>
        <uri>https://www.bigbinary.com/blog/authors/midhunkrishna</uri>
      </author>
      <category term="Ruby"/>
      <category term="Rails"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/using-concurrent-ruby-in-a-ruby-on-rails-application.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[A pratical use case of using Concurrent Ruby in Rails applications without swalloing the exceptions]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/using-concurrent-ruby-in-a-ruby-on-rails-application"><![CDATA[<p><a href="https://github.com/ruby-concurrency/concurrent-ruby">Concurrent Ruby</a> is a
concurrency toolkit that builds on a lot of interesting ideas from many
functional languages and classic concurrency patterns. When it comes to writing
threaded code in Rails applications, look no further since concurrent ruby is
<a href="https://github.com/rails/rails/blob/565ce0eaba233fcdd196c11715a8740bd571fd31/activesupport/activesupport.gemspec#L33">already included in Rails</a>
via Active Support.</p>
<h3>Using Concurrent::Future</h3>
<p>In one of our applications, to improve performance we added threaded code using
<a href="https://github.com/ruby-concurrency/concurrent-ruby">Concurrent::Future</a>. It
worked really well for us until one day it stopped working.</p>
<p>&quot;Why threads?&quot; one might ask. The code in question was a textbook threading use
case. It had a few API calls, some DB requests and finally an action that was
performed on all the data that was aggregated.</p>
<p>Let us look at what this code looks like.</p>
<h4>Non threaded code</h4>
<pre><code class="language-ruby">selected_shipping_companies.each do | carrier |
  # api calls
  distance_in_miles = find_distance_from_origin_to_destination
  historical_average_rate = historical_average_for_this_particular_carrier

  # action performed
  build_price_details_for_this_carrier(distance_in_miles,
                                       historical_average_rate)
end
</code></pre>
<p>Converting the above code to use Concurrent::Future is trivial.</p>
<pre><code class="language-ruby">futures = selected_shipping_companies.map do |carrier|
  Concurrent::Future.execute do
    # api calls
    distance_in_miles = find_distance_from_origin_to_destination
    historical_average_rate = historical_average_for_this_particular_carrier

    # action performed
    build_price_details_for_this_carrier(distance_in_miles,
                                         historical_average_rate)
  end
end

futures.map(&amp;:value)
</code></pre>
<h3>A bit more about Concurrent::Future</h3>
<p>It is often intimidating to work with threads. They can bring in complexity and
can have unpredictable behaviors due to lack of thread-safety. Ruby, being a
language of mutable references, we often find it difficult to write 100%
thread-safe code.</p>
<p>Inspired by
<a href="https://clojuredocs.org/clojure.core/future">Clojure's Future function</a>,
Concurrent::Future is a primitive that guarantees thread safety. It takes a
block of work and performs the work asynchronously using Concurrent Ruby's
global thread-pool. Once a block of work is scheduled, Concurrent Ruby gives us
a handle to this future work, on which when #value (or #deref) is called block's
value is returned.</p>
<h3>The Bug</h3>
<p>Usually, when an exception occurs in the main thread, the interpreter stops and
gathers the exception data. In the case of Ruby Threads, any unhandled
exceptions are reported only when
<a href="https://ruby-doc.org/core-2.2.0/Thread.html#method-i-join">Thread#join</a> is
called. Setting <code>Thread#abort_on_exception</code> to <code>true</code>, is an better alternative
which will cause all threads to exit when an exception is raised in any running
thread. We
<a href="https://blog.bigbinary.com/2018/04/18/ruby-2-5-enables-thread-report_on_exception-by-default.html">published a blog</a>
recently which talks about this in great detail.</p>
<h4>Exception handling in Concurrent Ruby</h4>
<pre><code class="language-ruby">
future = Concurrent::Future.execute {
            raise StandardError.new(&quot;Boom!&quot;)
          }

sleep(0.1) # giving arbitrary time for future to execute

future.value     #=&gt; nil
</code></pre>
<p>Where did the exception go? This code fails silently and swallows the
exceptions. How can we find out if the code executed successfully?</p>
<pre><code class="language-ruby">future = Concurrent::Future.execute {
              raise StandardError.new(&quot;Boom!&quot;)
          }

sleep(0.1) # giving arbitrary time for future to execute

future.value     #=&gt; nil

future.rejected? #=&gt; true
future.reason    #=&gt; &quot;#&lt;StandardError: Boom!&gt;&quot;
</code></pre>
<h3>How we fixed our issue</h3>
<p>We found places in our application where Concurrent::Future was used in a way
that would swallow exceptions. It is also a possibility that people might
overlook the explicit need to manually report exception. We addressed these
concerns with the following wrapper class.</p>
<pre><code class="language-ruby">module ConcurrentExecutor
  class Error &lt; StandardError
    def initialize(exceptions)
      @exceptions = exceptions
      super
    end

    def message
      @exceptions.map { | e | e.message }.join &quot;\n&quot;
    end

    def backtrace
      traces = @exceptions.map { |e| e.backtrace }
      [&quot;ConcurrentExecutor::Error START&quot;, traces, &quot;END&quot;].flatten
    end
  end

  class Future
    def initialize(pool: nil)
      @pool = pool || Concurrent::FixedThreadPool.new(20)
      @exceptions = Concurrent::Array.new
    end

    # Sample Usage
    # executor = ConcurrentExecutor::Future.new(pool: pool)
    # executor.execute(carriers) do | carrier |
    #   ...
    # end
    #
    # values = executor.resolve

    def execute array, &amp;block
      @futures = array.map do | element |
        Concurrent::Future.execute({ executor: @pool }) do
          yield(element)
        end.rescue do | exception |
          @exceptions &lt;&lt; exception
        end
      end

      self
    end

    def resolve
      values = @futures.map(&amp;:value)

      if @exceptions.length &gt; 0
        raise ConcurrentExecutor::Error.new(@exceptions)
      end

      values
    end
  end
end
</code></pre>
<p>Please note that using Concurrent Ruby Futures caused segmentation fault while
running specs in Circle CI. As of this writing, we are using normal looping
instead of Futures in Circle CI until the reason for the segfault is isolated
and fixed.</p>
<h3>Update</h3>
<p>Concurrent::Future also gives us another API which not only returns the value of
the block but also posts/raises any exceptions that occur into the main thread.</p>
<pre><code class="language-ruby">thread_pool = Concurrent::FixedThreadPool.new(20)
executors = [1, 2, 3, 4].map do |random_number|
  Concurrent::Future.execute({ executor: thread_pool }) do
    random_number / (random_number.even? ? 0 : 1)
  end
end

executors.map(&amp;:value)
=&gt; [1, nil, 3, nil]

executors.map(&amp;:value!)

&gt; ZeroDivisionError: divided by 0
&gt; from (pry):4:in `/'

</code></pre>
<p>We thank <a href="https://github.com/jrochkind">Jonathan Rochkind</a> for pointing us to
this undocumented api
<a href="https://www.reddit.com/r/ruby/comments/8pedmm/using_concurrent_ruby_in_a_ruby_on_rails/">in his reddit post</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[RubyKaigi 2018 Day two]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/rubykaigi-2018-day-two"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/rubykaigi-2018-day-two.png"/>
      <id>https://www.bigbinary.com/blog/rubykaigi-2018-day-two</id>
      <published>2018-06-01T00:00:00.000Z</published>
      <updated>2018-06-01T00:00:00.000Z</updated>
      <author>
        <name>Prathamesh Sonpatki</name>
        <uri>https://www.bigbinary.com/blog/authors/prathamesh</uri>
      </author>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/rubykaigi-2018-day-two.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ground report of Day two from Sendai, Japan where RubyKaigi 2018 is happening]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/rubykaigi-2018-day-two"><![CDATA[<p><a href="http://rubykaigi.org/2018">RubyKaigi</a> is happening at Sendai, Japan from 31st
May to 2nd June. It is perhaps the only conference where one can find almost all
the core Ruby team members in attendance.</p>
<p>This is <a href="https://twitter.com/_cha1tanya">Prathamesh</a>. I bring you live details
about what is happening at the Kaigi over the next two days. If you are at the
conference please come and say &quot;Hi&quot; to me.</p>
<p>Check out
<a href="https://blog.bigbinary.com/2018/05/31/rubykaigi-2018-day-one.html">what happened on day 1</a>
.</p>
<h3>Faster Apps, No Memory Thrash: Get Your Memory Config Right by Noah Gibbs</h3>
<p><a href="https://twitter.com/codefolio">Noah</a> gave an awesome talk on techniques to
manage the memory used by Ruby applications. One of the main point while dealing
with GC is to make it run less, which means don't create too many objects. He
also mentioned that if application permits then destructive operations such as
<code>gsub!</code> or <code>concat</code> should be used since they save CPU cycles and memory. Ruby
allows setting up environment variables for managing the heap memory but it is
really hard to choose values for these environment variables blindly.</p>
<p>Noah has built a tool which uses <code>GC.stat</code> results from applications to estimate
the values of the memory related environment variables. Check out the
<a href="https://github.com/noahgibbs/env_mem">EnvMem</a> gem.</p>
<p>In the end, he discussed some advanced debugging methods like checking
fragmentation percentage. The formula is prepared by
<a href="https://twitter.com/nateberkopec/">Nate Berkopec</a>.</p>
<pre><code class="language-ruby">s = GC.stat
used_ratio = s[:heap_live_slots].to_f / (s[:heap_eden_pages] * 408)
fragmentation = 1 - used_ratio
</code></pre>
<p>We can also use <code>GC::Profiler</code> to profile the code in real time to see how GC is
behaving.</p>
<p>Benchmark used for this talk can be found
<a href="https://github.com/noahgibbs/rails_ruby_bench">here</a>. Slides for this talk can
be found
<a href="https://docs.google.com/presentation/d/1-WrYwz-QnSI9yeRZfCCgUno-KOMuggiGHlmOETXZy9c/edit?usp=sharing">here</a>.</p>
<h3>Guild prototype</h3>
<p>Next I attended talk by <a href="https://twitter.com/_ko1">Koichi Sasada</a> on Guild
prototype. He discussed the proposed design spec for Guild with a demo of
Fibonacci number program with 40 core CPU and 40 guilds. One of the interesting
observations is that performance drops as number of guilds increases because of
the global locking.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2018/rubykaigi-2018-day-two/guild.JPG" alt="Guild performance"></p>
<p>He discussed the concept of shareable and non-shareable objects. Shareable
objects can be shared across multiple Guilds whereas non-shareable objects can
only be used one Guild. This also means, you can't make thread unsafe program
with Guilds &quot;by design&quot;. He discussed about the challenges in specifying the
shareable objects for Guilds.</p>
<p>Overall, there is still a lot of work left to be done for Guilds to become a
part of Ruby. It includes defining protocols for shareable and non-shareable
objects, making sure GC runs properly in case of Guilds, synchronization between
different Guilds.</p>
<p>The slides for this talk can be found
<a href="http://www.atdot.net/~ko1/activities/2018_rubykaigi2018.pdf">here</a>.</p>
<h3>Ruby programming with type checking</h3>
<p><a href="https://twitter.com/soutaro">Soutaro</a> from SideCI gave a talk on
<a href="https://github.com/soutaro/steep">Steep</a>, a gradual type checker for Ruby.</p>
<p>In the past, Matz has said that he doesn't like type definitions to be present
in the Ruby code. Steep requires type definitions to be present in separate
files with extension <code>.rbi</code>. The Ruby source code needs little amount of
annotations. Steep also has a scaffold generator to generate the basic type
definitions for existing code.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2018/rubykaigi-2018-day-two/steep.JPG" alt="Steep v/s Sorbet"></p>
<p>As of now, Steep runs slower than <a href="https://sorbet.run">Sorbet</a> which was
discussed yesterday by Stripe team. Soutaro also discussed issues in type
definitions due to meta programming in libraries such as Active Record. That
looks like a challenge for Steep as of now.</p>
<h3>Web console</h3>
<p>After the tea break, I attended talk by Genadi on how
<a href="https://github.com/rails/web-console">web console</a> works.</p>
<p>He discussed the implementation of web-console in detail with references to Ruby
internals related to bindings. He compared the web-console interface with IRB
and pry and explained the difference. As of now, web console has to monkey patch
some of the Rails internals. Genadi has added support for
<a href="https://github.com/rails/rails/pull/23868">registering interceptors</a> which will
prevent this monkey patching in Rails 6. He is also mentoring a Google summer of
code student to work on Actionable errors project where the user can take
actions like running pending migrations via the webpage itself when the error is
shown.</p>
<h3>Ruby committers v/s the World</h3>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2018/rubykaigi-2018-day-two/committers.JPG" alt="Ruby Committers v/s the World"></p>
<p>RubyKaigi offers this unique event where all the Ruby committers come on stage
and face the questions from the audience. This year the format was slightly
different and it was run in the style of the Ruby core developer meeting. The
<a href="https://docs.google.com/document/u/1/d/1Skh54Fq_nkpycZAS4_03tsafrtABDWxIx2-CKc-QIOY/pub">agenda</a>
was predecided with some questions from the people and some tickets to discuss.
The session started with discussion of
<a href="https://www.ruby-lang.org/en/news/2018/05/31/ruby-2-6-0-preview2-released/">features coming up in Ruby 2.6</a>.</p>
<p>After that, the questions and the tickets in the agenda were discussed. It was
good to see how the Ruby team takes decisions about features and suggestions.</p>
<p>Apart from this, there were
<a href="http://rubykaigi.org/2018/schedule#jun01">talks on SciRuby, mRuby, linting, gem upgrades, c extensions and more</a>
which I could not attend.</p>
<p>That's all for the day two. Looking forward to the day three already!</p>
<p>Oh and here is the world map of all the attendees from RubyKaigi.</p>
<p><img src="https://www.bigbinary.com/blog/images/images_used_in_blog/2018/rubykaigi-2018-day-two/world.webp" alt="World map of all attendees"></p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[RubyKaigi 2018 Day one]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/rubykaigi-2018-day-one"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/rubykaigi-2018-day-one.png"/>
      <id>https://www.bigbinary.com/blog/rubykaigi-2018-day-one</id>
      <published>2018-05-31T00:00:00.000Z</published>
      <updated>2018-05-31T00:00:00.000Z</updated>
      <author>
        <name>Prathamesh Sonpatki</name>
        <uri>https://www.bigbinary.com/blog/authors/prathamesh</uri>
      </author>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/rubykaigi-2018-day-one.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ground report of Day one from Sendai, Japan where RubyKaigi 2018 is happening]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/rubykaigi-2018-day-one"><![CDATA[<p><a href="http://rubykaigi.org/2018">RubyKaigi</a> is happening at Sendai, Japan from 31st May to
2nd June. It is perhaps the only conference where one can find almost all
the core Ruby team members in attendance.</p>
<p>This is <a href="https://twitter.com/_cha1tanya">Prathamesh</a>.
I bring you live details about what is happening
at the Kaigi over the next three days.
If you are at the conference please come and say &quot;Hi&quot; to me.</p>
<h3>Matz's keynote</h3>
<p>RubyKaigi started with Matz's keynote. He used lot of proverbs applying
them to the Ruby language and software development.</p>
<p>He talked about one of the hardest problems in programming - naming with an example of
<code>yield_self</code>.
Matz added alias <code>then</code> to the <code>yield_self</code> method <a href="https://github.com/ruby/ruby/commit/d53ee008911b5c3b22cff1566a9ef7e7d4cbe183">yesterday</a>. He also discussed about <code>googlability</code> of the names.
Ironically, Ruby was named in 1993 which was before Google had started.</p>
<p>Matz also touched upon
<a href="https://www.ruby-lang.org/en/news/2018/02/24/ruby-2-6-0-preview1-released/">JIT option being introduced in Ruby 2.6</a>
and guild as the ways the language continues to improve in
performance and concurrency.
There is a talk on Guild by Koichi Sasada on second day of RubyKaigi which
will have further details about it.</p>
<p>Matz ended the keynote talking about the need of maintaining backward compatibility
and not running into the situation like Ruby 1.9 or Python 3 where the compatibility was not
maintained. He also stressed upon the community aspect of the Ruby language and its importance
in the success of Ruby.</p>
<h3>ETL processing in Ruby using Kiba</h3>
<p><a href="https://twitter.com/thibaut_barrere">Thibaut Barrère</a>
gave a talk on
<a href="https://www.kiba-etl.org">Kiba</a> - a data processing ETL framework for Ruby.
He discussed about
the design decisions that went into the version 1 and how it evolved to version 2 which was recently released.</p>
<p>Kiba provides programmatic API which can be used in the background jobs instead of shelling out. It also
has support for multistep batch processing.</p>
<p>Thibaut also explained how it can be used for data migration,
reusing the components and big rewrites. He observed that the performance has been gradually increasing with each Ruby release over the years.</p>
<p>The slides for this talk can be found <a href="https://speakerdeck.com/thbar/kiba-etl-v2-rubykaigi-2018">here</a>.</p>
<h3>Architecture of Hanami applications</h3>
<p>Next I attended talk from
<a href="https://twitter.com/anton_davydov">Anton Davydov</a>
on architecture patterns in Hanami apps.
He discussed about the problems typical Rails applications face
and
how abstractions can address those issues.
He explained how Hanami tries to achieve business logic isolation, avoid global state, sequential logic and test coverage.
Functional callable objects, containers, dry-containers, dry-inject and event sourcing are some of the abstractions that can be used in Hanami apps that help in
achieving this.</p>
<h3>Lightning talks</h3>
<p>The last session of the day was lightning talks.</p>
<p>The talk on
<a href="https://github.com/godfat/rib">Rib</a>(wordplay on IRB) was an interesting one. Rib is yet another interactive Ruby shell but lightweight compared to IRB and pry. It has some nice features like auto indent, multiline history, filtering of callers. It can also <code>beep</code> when the console starts, so you know it is time to get back to work.</p>
<p>I liked another talk where
<a href="https://github.com/Watson1978">Watson</a>
had worked on improving the performance of JSON gem.
He achieved this by using CRuby API wherever applicable and avoiding heavy calls like <code>rbfuncall</code>.
Check these two
<a href="https://github.com/flori/json/pull/346/">pull</a>
<a href="https://github.com/flori/json/pull/345">requests</a> for benchmark and more discussions.</p>
<p>Apart from these talks,
there were lot of other talks as well which I could not attend.
<a href="http://rubykaigi.org/2018/presentations/DarkDimius.html#may31">Stripe team</a>
is building a
<a href="https://sorbet.run">type checker for Ruby</a>
which looks very interesting and is extremely fast.</p>
<p><a href="http://rubykaigi.org/2018/presentations/bbatsov.html#may31">Bozhidar Batsov</a>
gave a talk on Rubocop project and
how it has evolved over the years.
There was also a talk on Karafka - event driven architecture in Ruby.
This talk was a good precursor to the Hanami talk where event driven architecture was mentioned again.</p>
<p><a href="http://rubykaigi.org/2018/schedule#may31">Other talks from day one</a>
ranged from memory management, playing with Ruby syntax, code highlighter, deep learning, C extensions to Rubygems.</p>
<p>That's all for the day one. Looking forward to the day two already!</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.5 added lazy proc allocation for block parameters]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-5-added-lazy-proc-allocation-for-block-parameters"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-5-added-lazy-proc-allocation-for-block-parameters.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-5-added-lazy-proc-allocation-for-block-parameters</id>
      <published>2018-05-22T00:00:00.000Z</published>
      <updated>2018-05-22T00:00:00.000Z</updated>
      <author>
        <name>Amit Choudhary</name>
        <uri>https://www.bigbinary.com/blog/authors/amit</uri>
      </author>
      <category term="Ruby 2.5"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-5-added-lazy-proc-allocation-for-block-parameters.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[In Ruby 2.4 when we pass a block to a method, which further passes to another method, Ruby creates a new Proc object by the given block before passing this proc to the another method. This creates...]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-5-added-lazy-proc-allocation-for-block-parameters"><![CDATA[<pre><code class="language-ruby">irb&gt; def greet
irb&gt;   yield
irb&gt; end
  =&gt; :greet
irb&gt;
irb&gt; def greet_with_welcome(&amp;block)
irb&gt;   puts 'Welcome'
irb&gt;   greet(&amp;block)
irb&gt; end
  =&gt; :greet_with_welcome
irb&gt; greet_with_welcome { p 'BigBinary' }
Welcome
&quot;BigBinary&quot;
  =&gt; &quot;BigBinary&quot;
</code></pre>
<p>In Ruby 2.4 when we pass a block to a method, which further passes to another
method, Ruby creates a new <code>Proc</code> object by the given block before passing this
proc to the another method.</p>
<p>This creates unnecessary objects even when the block parameter is not accessed.
It also creates a chain of <code>Proc</code> objects when the block parameter is passed
through various methods.</p>
<p>Proc creation is one a heavyweight operation because we need to store all local
variables (represented by Env objects in MRI internal) in the heap.</p>
<p>Ruby 2.5 introduced a lazy proc allocation. Ruby 2.5 will not create a Proc
object when passing a block to another method. Instead, it will pass the block
information. If the block is accessed somewhere else, then it creates a <code>Proc</code>
object by the given block.</p>
<p>This results in lesser memory allocation and faster execution.</p>
<h4>Ruby 2.4</h4>
<pre><code class="language-ruby">irb&gt; require 'benchmark'
  =&gt; true
irb&gt; def greet
irb&gt;   yield
irb&gt; end
  =&gt; :greet
irb&gt;
irb&gt; def greet_with_welcome(&amp;block)
irb&gt;   puts 'Welcome'
irb&gt;   greet(&amp;block)
irb&gt; end
  =&gt; :greet_with_welcome
irb&gt;
irb&gt; Benchmark.measure { 1000.times { greet_with_welcome { 'BigBinary' } } }
Welcome
Welcome
...
...
...
  =&gt; #&lt;Benchmark::Tms:0x007fe6ab929de0 @label=&quot;&quot;, @real=0.022295999999187188, @cstime=0.0, @cutime=0.0, @stime=0.01, @utime=0.0, @total=0.01&gt;
</code></pre>
<h4>Ruby 2.5</h4>
<pre><code class="language-ruby">irb&gt; require 'benchmark'
  =&gt; true
irb&gt; def greet
irb&gt;   yield
irb&gt; end
  =&gt; :greet
irb&gt;
irb&gt; def greet_with_welcome(&amp;block)
irb&gt;   puts 'Welcome'
irb&gt;   greet(&amp;block)
irb&gt; end
  =&gt; :greet_with_welcome
irb&gt;
  irb&gt; Benchmark.measure { 1000.times { greet_with_welcome { 'BigBinary' } } }
Welcome
Welcome
...
...
...
  =&gt; #&lt;Benchmark::Tms:0x00007fa4400871b8 @label=&quot;&quot;, @real=0.004612999997334555, @cstime=0.0, @cutime=0.0, @stime=0.001524000000000001, @utime=0.0030690000000000023, @total=0.004593000000000003&gt;
</code></pre>
<p>As we can see, there is considerable improvement in execution time when a block
param is passed in Ruby 2.5.</p>
<p>Here is the relevant <a href="https://github.com/ruby/ruby/commit/5ee9513a71">commit</a>
and <a href="https://bugs.ruby-lang.org/issues/14045">discussion</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Increase reliability using super_fetch of Sidekiq Pro]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/increase-reliability-of-background-job-processing-using-super_fetch-of-sidekiq-pro"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/increase-reliability-of-background-job-processing-using-super_fetch-of-sidekiq-pro.png"/>
      <id>https://www.bigbinary.com/blog/increase-reliability-of-background-job-processing-using-super_fetch-of-sidekiq-pro</id>
      <published>2018-05-08T00:00:00.000Z</published>
      <updated>2018-05-08T00:00:00.000Z</updated>
      <author>
        <name>Vishal Telangre</name>
        <uri>https://www.bigbinary.com/blog/authors/vishaltelangre</uri>
      </author>
      <category term="Ruby"/>
      <category term="Rails"/>
      <category term="Kubernetes"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/increase-reliability-of-background-job-processing-using-super_fetch-of-sidekiq-pro.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Increase reliability of background job processing using super_fetch of Sidekiq Pro]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/increase-reliability-of-background-job-processing-using-super_fetch-of-sidekiq-pro"><![CDATA[<p><a href="https://github.com/mperham/sidekiq">Sidekiq</a>
is a background job processing library for Ruby.
Sidekiq offers three versions: OSS, Pro and Enterprise.</p>
<p>OSS is free and open source and has basic features.
Pro and Enterprise versions are closed source and paid,
thus comes with more advanced features.
To compare the list of features offered by each of these versions,
please visit <a href="https://sidekiq.org">Sidekiq website</a>.</p>
<p>Sidekiq Pro 3.4.0
<a href="https://github.com/mperham/sidekiq/blob/6e79f2a860ae558f2ed52b8917d2fede846c0a50/Pro-Changes.md#340">introduced</a>
<code>super_fetch</code> strategy
to reliably fetch jobs from the queue in Redis.</p>
<p>In this post, we will discuss the benefits of using <code>super_fetch</code> strategy.</p>
<h2>Problem</h2>
<p>Open source version of Sidekiq comes with <code>basic_fetch</code> strategy.
Let's see an example to understand how it works.</p>
<p>Let's add Sidekiq to our <code>Gemfile</code> and run <code>bundle install</code> to install it.</p>
<pre><code class="language-ruby">gem 'sidekiq'
</code></pre>
<p>Add following Sidekiq worker in <code>app/workers/sleep_worker.rb</code>.</p>
<pre><code class="language-ruby">class SleepWorker
  include Sidekiq::Worker

  def perform(name)
    puts &quot;Started #{name}&quot;
    sleep 30
    puts &quot;Finished #{name}&quot;
  end
end
</code></pre>
<p>This worker does nothing great but sleeps for 30 seconds.</p>
<p>Let's open Rails console
and schedule this worker to run as a background job asynchronously.</p>
<pre><code class="language-ruby">&gt;&gt; require &quot;sidekiq/api&quot;
=&gt; true
&gt;&gt; Sidekiq::Queue.new.size
=&gt; 0
&gt;&gt; SleepWorker.perform_async(&quot;A&quot;)
=&gt; &quot;5d8bf898c36a60a1096cf4d3&quot;
&gt;&gt; Sidekiq::Queue.new.size
=&gt; 1
</code></pre>
<p>As we can see, queue now has 1 job scheduled to be processed.</p>
<p>Let's start Sidekiq in another terminal tab.</p>
<pre><code class="language-ruby">$ bundle exec sidekiq

40510 TID-owu1swr1i INFO: Booting Sidekiq 5.1.3 with redis options {:id=&gt;&quot;Sidekiq-server-PID-40510&quot;, :url=&gt;nil}
40510 TID-owu1swr1i INFO: Starting processing, hit Ctrl-C to stop
40510 TID-owu1tr5my SleepWorker JID-5d8bf898c36a60a1096cf4d3 INFO: start
Started A
</code></pre>
<p>As we can see, the job with ID <code>5d8bf898c36a60a1096cf4d3</code>
was picked up by Sidekiq
and it started processing the job.</p>
<p>If we check the Sidekiq queue size in the Rails console, it will be zero now.</p>
<pre><code class="language-ruby">&gt;&gt; Sidekiq::Queue.new.size
=&gt; 0
</code></pre>
<p>Let's shutdown the Sidekiq process gracefully
while Sidekiq is still in the middle of processing our scheduled job.
Press either <code>Ctrl-C</code> or run <code>kill -SIGINT &lt;PID&gt;</code> command.</p>
<pre><code class="language-ruby">$ kill -SIGINT 40510
</code></pre>
<pre><code class="language-ruby">40510 TID-owu1swr1i INFO: Shutting down
40510 TID-owu1swr1i INFO: Terminating quiet workers
40510 TID-owu1x00rm INFO: Scheduler exiting...
40510 TID-owu1swr1i INFO: Pausing to allow workers to finish...
40510 TID-owu1swr1i WARN: Terminating 1 busy worker threads
40510 TID-owu1swr1i WARN: Work still in progress [#&lt;struct Sidekiq::BasicFetch::UnitOfWork queue=&quot;queue:default&quot;, job=&quot;{\&quot;class\&quot;:\&quot;SleepWorker\&quot;,\&quot;args\&quot;:[\&quot;A\&quot;],\&quot;retry\&quot;:true,\&quot;queue\&quot;:\&quot;default\&quot;,\&quot;jid\&quot;:\&quot;5d8bf898c36a60a1096cf4d3\&quot;,\&quot;created_at\&quot;:1525427293.956314,\&quot;enqueued_at\&quot;:1525427293.957355}&quot;&gt;]
40510 TID-owu1swr1i INFO: Pushed 1 jobs back to Redis
40510 TID-owu1tr5my SleepWorker JID-5d8bf898c36a60a1096cf4d3 INFO: fail: 19.576 sec
40510 TID-owu1swr1i INFO: Bye!
</code></pre>
<p>As we can see, Sidekiq pushed back the unfinished job back to Redis queue
when Sidekiq received a <code>SIGINT</code> signal.</p>
<p>Let's verify it.</p>
<pre><code class="language-ruby">&gt;&gt; Sidekiq::Queue.new.size
=&gt; 1
</code></pre>
<p>Before we move on, let's learn some basics about signals such as <code>SIGINT</code>.</p>
<h2>A crash course on POSIX signals</h2>
<p><code>SIGINT</code> is an interrupt signal.
It is an alternative to hitting
<code>Ctrl-C</code> from the keyboard.
When a process is running in foreground,
we can hit <code>Ctrl-C</code> to signal the process to shut down.
When the process is running in background,
we can use <code>kill</code> command to send a <code>SIGINT</code> signal to the process' PID.
A process can optionally catch this signal and shutdown itself gracefully.
If the process does not respect this signal and ignores it,
then nothing really happens and the process keeps running.
Both <code>INT</code> and <code>SIGINT</code> are identical signals.</p>
<p>Another useful signal is <code>SIGTERM</code>.
It is called a termination signal.
A process can either catch it
and perform necessary cleanup or just ignore it.
Similar to a <code>SIGINT</code> signal,
if a process ignores this signal, then the process keeps running.
Note that, if no signal is supplied to the <code>kill</code> command,
<code>SIGTERM</code> is used by default.
Both <code>TERM</code> and <code>SIGTERM</code> are identical signals.</p>
<p><code>SIGTSTP</code> or <code>TSTP</code> is called terminal stop signal.
It is an alternative to hitting <code>Ctrl-Z</code> on the keyboard.
This signal causes a process to suspend further execution.</p>
<p><code>SIGKILL</code> is known as kill signal.
This signal is intended to kill the process immediately and forcefully.
A process cannot catch this signal,
therefore the process cannot perform cleanup or graceful shutdown.
This signal is used
when a process does not respect and respond
to both <code>SIGINT</code> and <code>SIGTERM</code> signals.
<code>KILL</code>, <code>SIGKILL</code> and <code>9</code> are identical signals.</p>
<p>There are a lot of other signals besides these,
but they are not relevant for this post.
Please check them out <a href="https://en.wikipedia.org/wiki/Signal_(IPC)#POSIX_signals">here</a>.</p>
<p>A Sidekiq process pays respect
to all of these signals and behaves as we expect.
When Sidekiq receives a <code>TERM</code> or <code>SIGTERM</code> signal,
Sidekiq terminates itself gracefully.</p>
<h2>Back to our example</h2>
<p>Coming back to our example from above,
we had sent a <code>SIGINT</code> signal to the Sidekiq process.</p>
<pre><code class="language-ruby">$ kill -SIGINT 40510
</code></pre>
<p>On receiving this <code>SIGINT</code> signal,
Sidekiq process having PID 40510 terminated quiet workers,
paused the queue and waited for a while
to let busy workers finish their jobs.
Since our busy SleepWorker did not finish quickly,
Sidekiq terminated that busy worker
and pushed it back to the queue in Redis.
After that, Sidekiq gracefully terminated itself with an exit code 0.
Note that, the default timeout is 8 seconds
until which Sidekiq can wait to let the busy workers finish
otherwise it pushes the unfinished jobs back to the queue in Redis.
This timeout can be changed with <code>-t</code> option
given at the startup of Sidekiq process.</p>
<p>Sidekiq <a href="https://github.com/mperham/sidekiq/wiki/Deployment#overview">recommends</a>
to send a <code>TSTP</code> and a <code>TERM</code> together
to ensure that the Sidekiq process shuts down safely and gracefully.
On receiving a <code>TSTP</code> signal,
Sidekiq stops pulling new work
and
finishes the work which is in-progress.
The idea is to first send a <code>TSTP</code> signal,
wait as much as possible (by default for 8 seconds as discussed above)
to ensure that busy workers finish their jobs
and then send a <code>TERM</code> signal
to shutdown the process.</p>
<p>Sidekiq pushes back the unprocessed job in Redis when terminated gracefully.
It means that Sidekiq pulls the unfinished job and starts processing again when
we restart the Sidekiq process.</p>
<pre><code class="language-ruby">$ bundle exec sidekiq

45916 TID-ovfq8ll0k INFO: Booting Sidekiq 5.1.3 with redis options {:id=&gt;&quot;Sidekiq-server-PID-45916&quot;, :url=&gt;nil}
45916 TID-ovfq8ll0k INFO: Starting processing, hit Ctrl-C to stop
45916 TID-ovfqajol4 SleepWorker JID-5d8bf898c36a60a1096cf4d3 INFO: start
Started A
Finished A
45916 TID-ovfqajol4 SleepWorker JID-5d8bf898c36a60a1096cf4d3 INFO: done: 30.015 sec
</code></pre>
<p>We can see that Sidekiq pulled the previously terminated job
with ID <code>5d8bf898c36a60a1096cf4d3</code> and processed that job again.</p>
<p>So far so good.</p>
<p>This behavior is implemented using
<a href="https://github.com/mperham/sidekiq/blob/6e79f2a860ae558f2ed52b8917d2fede846c0a50/lib/sidekiq/fetch.rb"><code>basic_fetch</code></a>
strategy which is present in the open sourced version of Sidekiq.</p>
<p>Sidekiq uses <a href="https://redis.io/commands/brpop">BRPOP</a> Redis command
to fetch a scheduled job from the queue.
When a job is fetched,
that job gets removed from the queue and
that job no longer exists in Redis.
If this fetched job is processed, then all is good.
Also, if the Sidekiq process is terminated gracefully on
receiving either a <code>SIGINT</code> or a <code>SIGTERM</code> signal,
Sidekiq will push back the unfinished jobs back to the queue in Redis.</p>
<p>But what if the Sidekiq process crashes in the middle
while processing that fetched job?</p>
<p>A process is considered as crashed
if the process does not shutdown gracefully.
As we discussed before,
when we send a <code>SIGKILL</code> signal to a process,
the process cannot receive or catch this signal.
Because the process cannot shutdown gracefully and nicely,
it gets crashed.</p>
<p>When a Sidekiq process is crashed,
the fetched jobs by that Sidekiq process
which are not yet finished get lost
forever.</p>
<p>Let's try to reproduce this scenario.</p>
<p>We will schedule another job.</p>
<pre><code class="language-ruby">&gt;&gt; SleepWorker.perform_async(&quot;B&quot;)
=&gt; &quot;37a5ab4139796c4b9dc1ea6d&quot;

&gt;&gt; Sidekiq::Queue.new.size
=&gt; 1
</code></pre>
<p>Now, let's start Sidekiq process and kill it using <code>SIGKILL</code> or <code>9</code> signal.</p>
<pre><code class="language-ruby">$ bundle exec sidekiq

47395 TID-ow8q4nxzf INFO: Starting processing, hit Ctrl-C to stop
47395 TID-ow8qba0x7 SleepWorker JID-37a5ab4139796c4b9dc1ea6d INFO: start
Started B
[1]    47395 killed     bundle exec sidekiq
</code></pre>
<pre><code class="language-ruby">$ kill -SIGKILL 47395
</code></pre>
<p>Let's check if Sidekiq had pushed the busy (unprocessed) job
back to the queue in Redis before terminating.</p>
<pre><code class="language-ruby">&gt;&gt; Sidekiq::Queue.new.size
=&gt; 0
</code></pre>
<p>No. It does not.</p>
<p>Actually, the Sidekiq process did not get a chance to shutdown gracefully
when it received the <code>SIGKILL</code> signal.</p>
<p>If we restart the Sidekiq process,
it cannot fetch that unprocessed job
since the job was not pushed back to the queue in Redis at all.</p>
<pre><code class="language-ruby">$ bundle exec sidekiq

47733 TID-ox1lau26l INFO: Booting Sidekiq 5.1.3 with redis options {:id=&gt;&quot;Sidekiq-server-PID-47733&quot;, :url=&gt;nil}
47733 TID-ox1lau26l INFO: Starting processing, hit Ctrl-C to stop
</code></pre>
<p>Therefore,
the job having name argument as <code>B</code> or ID as <code>37a5ab4139796c4b9dc1ea6d</code>
is completely lost.
There is no way to get that job back.</p>
<p>Losing job like this may not be a problem for some applications
but for some critical applications this could be a huge issue.</p>
<p>We faced a similar problem like this.
One of our clients' application is deployed on a Kubernetes cluster.
Our Sidekiq process runs in a Docker container
in the Kubernetes
<a href="https://kubernetes.io/docs/concepts/workloads/pods/pod">pods</a>
which we call <code>background</code> pods.</p>
<p>Here's our stripped down version of
<a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/">Kubernetes deployment</a>
manifest which creates a Kubernetes deployment resource.
Our Sidekiq process runs in the pods spawned by that deployment resource.</p>
<pre><code class="language-ruby">---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: background
spec:
  replicas: 2
  template:
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: background
        image: &lt;%= ENV['IMAGE'] %&gt;
        env:
        - name: POD_TYPE
          value: background
        lifecycle:
          preStop:
            exec:
              command:
              - /bin/bash
              - -l
              - -c
              - for pid in tmp/pids/sidekiq*.pid; do bin/bundle exec sidekiqctl stop $pid 60; done
</code></pre>
<p>When we apply an updated version of this manifest
,for say, changing the Docker image, the running pods are terminated
and new pods are created.</p>
<p>Before terminating the only container in the pod,
Kubernetes executes <code>sidekiqctl stop $pid 60</code> command
which we have defined using the
<a href="https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/">preStop</a>
event handler.
Note that, Kubernetes already sends <code>SIGTERM</code> signal
to the container being terminated inside the pod
before invoking the <code>preStop</code> event handler.
The default termination grace period is 30 seconds and it is configurable.
If the container doesn't terminate within the termination grace period,
a <code>SIGKILL</code> signal will be sent to forcefully terminate the container.</p>
<p>The <code>sidekiqctl stop $pid 60</code> command executed in the <code>preStop</code> handler does
three things.</p>
<ol>
<li>Sends a <code>SIGTERM</code> signal to the Sidekiq process running in the container.</li>
<li>Waits for 60 seconds.</li>
<li>Sends a <code>SIGKILL</code> signal to kill the Sidekiq process forcefully
if the process has not terminated gracefully yet.</li>
</ol>
<p>This worked for us when the count of busy jobs was relatively small.</p>
<p>When the number of processing jobs is higher,
Sidekiq does not get enough time
to quiet the busy workers
and fails to push some of them back on the Redis queue.</p>
<p>We found that some of the jobs were getting lost
when our <code>background</code> pod restarted.
We had to restart our background pod for
reasons such as
updating the Kubernetes deployment manifest,
pod being automatically evicted by Kubernetes
due to host node encountering OOM (out of memory) issue, etc.</p>
<p>We tried increasing both
<code>terminationGracePeriodSeconds</code> in the deployment manifest
as well as the <code>sidekiqctl stop</code> command's timeout.
Despite that,
we still kept facing the same issue
of losing jobs whenever pod restarts.</p>
<p>We even tried sending <code>TSTP</code> and then <code>TERM</code> after a timeout
relatively longer than 60 seconds.
But the pod was getting harshly terminated
without gracefully terminating Sidekiq process running inside it.
Therefore we kept losing the busy jobs
which were running during the pod termination.</p>
<h2>Sidekiq Pro's super_fetch</h2>
<p>We were looking for a way to stop losing our Sidekiq jobs
or a way to recover them reliably when our <code>background</code> Kubernetes pod restarts.</p>
<p>We realized that the commercial version of Sidekiq,
Sidekiq Pro offers an additional fetch strategy,
<a href="https://github.com/mperham/sidekiq/wiki/Reliability#using-super_fetch"><code>super_fetch</code></a>,
which seemed more efficient and reliable
compared to <code>basic_fetch</code> strategy.</p>
<p>Let's see what difference <code>super_fetch</code> strategy
makes over <code>basic_fetch</code>.</p>
<p>We will need to use <code>sidekiq-pro</code> gem which needs to be purchased.
Since Sidekiq Pro gem is close sourced, we cannot fetch it
from the default public gem registry,
<a href="https://rubygems.org">https://rubygems.org</a>.
Instead, we will have to fetch it from a private gem registry
which we get after purchasing it.
We add following code to our <code>Gemfile</code> and run <code>bundle install</code>.</p>
<pre><code class="language-ruby">source ENV['SIDEKIQ_PRO_GEM_URL'] do
  gem 'sidekiq-pro'
end
</code></pre>
<p>To enable <code>super_fetch</code>,
we need to add following code
in an initializer <code>config/initializers/sidekiq.rb</code>.</p>
<pre><code class="language-ruby">Sidekiq.configure_server do |config|
  config.super_fetch!
end
</code></pre>
<p>Well, that's it.
Sidekiq will use <code>super_fetch</code> instead of <code>basic_fetch</code> now.</p>
<pre><code class="language-ruby">$ bundle exec sidekiq

75595 TID-owsytgvqj INFO: Sidekiq Pro 4.0.2, commercially licensed.  Thanks for your support!
75595 TID-owsytgvqj INFO: Booting Sidekiq 5.1.3 with redis options {:id=&gt;&quot;Sidekiq-server-PID-75595&quot;, :url=&gt;nil}
75595 TID-owsytgvqj INFO: Starting processing, hit Ctrl-C to stop
75595 TID-owsys5imz INFO: SuperFetch activated
</code></pre>
<p>When <code>super_fetch</code> is activated, Sidekiq process' graceful shutdown behavior
is similar to that of <code>basic_fetch</code>.</p>
<pre><code class="language-ruby">&gt;&gt; SleepWorker.perform_async(&quot;C&quot;)
=&gt; &quot;f002a41393f9a79a4366d2b5&quot;
&gt;&gt; Sidekiq::Queue.new.size
=&gt; 1
</code></pre>
<pre><code class="language-ruby">$ bundle exec sidekiq

76021 TID-ow6kdcca5 INFO: Sidekiq Pro 4.0.2, commercially licensed.  Thanks for your support!
76021 TID-ow6kdcca5 INFO: Booting Sidekiq 5.1.3 with redis options {:id=&gt;&quot;Sidekiq-server-PID-76021&quot;, :url=&gt;nil}
76021 TID-ow6kdcca5 INFO: Starting processing, hit Ctrl-C to stop
76021 TID-ow6klq2cx INFO: SuperFetch activated
76021 TID-ow6kiesnp SleepWorker JID-f002a41393f9a79a4366d2b5 INFO: start
Started C
</code></pre>
<pre><code class="language-ruby">&gt;&gt; Sidekiq::Queue.new.size
=&gt; 0
</code></pre>
<pre><code class="language-ruby">$ kill -SIGTERM 76021
</code></pre>
<pre><code class="language-ruby">76021 TID-ow6kdcca5 INFO: Shutting down
76021 TID-ow6kdcca5 INFO: Terminating quiet workers
76021 TID-ow6kieuwh INFO: Scheduler exiting...
76021 TID-ow6kdcca5 INFO: Pausing to allow workers to finish...
76021 TID-ow6kdcca5 WARN: Terminating 1 busy worker threads
76021 TID-ow6kdcca5 WARN: Work still in progress [#&lt;struct Sidekiq::Pro::SuperFetch::Retriever::UnitOfWork queue=&quot;queue:default&quot;, job=&quot;{\&quot;class\&quot;:\&quot;SleepWorker\&quot;,\&quot;args\&quot;:[\&quot;C\&quot;],\&quot;retry\&quot;:true,\&quot;queue\&quot;:\&quot;default\&quot;,\&quot;jid\&quot;:\&quot;f002a41393f9a79a4366d2b5\&quot;,\&quot;created_at\&quot;:1525500653.404454,\&quot;enqueued_at\&quot;:1525500653.404501}&quot;, local_queue=&quot;queue:sq|vishal.local:76021:3e64c4b08393|default&quot;&gt;]
76021 TID-ow6kdcca5 INFO: SuperFetch: Moving job from queue:sq|vishal.local:76021:3e64c4b08393|default back to queue:default
76021 TID-ow6kiesnp SleepWorker JID-f002a41393f9a79a4366d2b5 INFO: fail: 13.758 sec
76021 TID-ow6kdcca5 INFO: Bye!
</code></pre>
<pre><code class="language-ruby">&gt;&gt; Sidekiq::Queue.new.size
=&gt; 1
</code></pre>
<p>That looks good.
As we can see, Sidekiq moved busy job back from a private queue
to the queue in Redis
when Sidekiq received a <code>SIGTERM</code> signal.</p>
<p>Now, let's try to kill Sidekiq process forcefully
without allowing a graceful shutdown
by sending a <code>SIGKILL</code> signal.</p>
<p>Since Sidekiq was gracefully shutdown before,
if we restart Sidekiq again,
it will re-process the pushed back job having ID <code>f002a41393f9a79a4366d2b5</code>.</p>
<pre><code class="language-ruby">$ bundle exec sidekiq

76890 TID-oxecurbtu INFO: Sidekiq Pro 4.0.2, commercially licensed.  Thanks for your support!
76890 TID-oxecurbtu INFO: Booting Sidekiq 5.1.3 with redis options {:id=&gt;&quot;Sidekiq-server-PID-76890&quot;, :url=&gt;nil}
76890 TID-oxecurbtu INFO: Starting processing, hit Ctrl-C to stop
76890 TID-oxecyhftq INFO: SuperFetch activated
76890 TID-oxecyotvm SleepWorker JID-f002a41393f9a79a4366d2b5 INFO: start
Started C
[1]    76890 killed     bundle exec sidekiq
</code></pre>
<pre><code class="language-ruby">$ kill -SIGKILL 76890
</code></pre>
<pre><code class="language-ruby">&gt;&gt; Sidekiq::Queue.new.size
=&gt; 0
</code></pre>
<p>It appears that Sidekiq didn't get any chance
to push the busy job back to the queue in Redis
on receiving a <code>SIGKILL</code> signal.</p>
<p>So, where is the magic of <code>super_fetch</code>?</p>
<p>Did we lose our job again?</p>
<p>Let's restart Sidekiq and see it ourself.</p>
<pre><code class="language-ruby">$ bundle exec sidekiq

77496 TID-oum04ghgw INFO: Sidekiq Pro 4.0.2, commercially licensed.  Thanks for your support!
77496 TID-oum04ghgw INFO: Booting Sidekiq 5.1.3 with redis options {:id=&gt;&quot;Sidekiq-server-PID-77496&quot;, :url=&gt;nil}
77496 TID-oum04ghgw INFO: Starting processing, hit Ctrl-C to stop
77496 TID-oum086w9s INFO: SuperFetch activated
77496 TID-oum086w9s WARN: SuperFetch: recovered 1 jobs
77496 TID-oum08eu3o SleepWorker JID-f002a41393f9a79a4366d2b5 INFO: start
Started C
Finished C
77496 TID-oum08eu3o SleepWorker JID-f002a41393f9a79a4366d2b5 INFO: done: 30.011 sec
</code></pre>
<p>Whoa, isn't that cool?</p>
<p>See that line where it says <code>SuperFetch: recovered 1 jobs</code>.</p>
<p>Although the job wasn't pushed back to the queue in Redis,
Sidekiq somehow recovered our lost job having ID <code>f002a41393f9a79a4366d2b5</code>
and reprocessed that job again!</p>
<p>Interested to learn about how Sidekiq did that? Keep on reading.</p>
<p>Note that, since Sidekiq Pro is a close sourced and commercial software,
we cannot explain <code>super_fetch</code>'s exact implementation details.</p>
<p>As we discussed in-depth before,
Sidekiq's <code>basic_fetch</code> strategy uses <code>BRPOP</code> Redis command
to fetch a job from the queue in Redis.
It works great to some extent,
but it is prone to losing job
if Sidekiq crashes or does not shutdown gracefully.</p>
<p>On the other hand, Sidekiq Pro offers <code>super_fetch</code> strategy which uses
<a href="http://redis.io/commands/rpoplpush">RPOPLPUSH</a> Redis command to fetch a job.</p>
<p><code>RPOPLPUSH</code> Redis command provides
a unique approach towards implementing a reliable queue.
<code>RPOPLPUSH</code> command accepts two lists
namely a source list and a destination list.
This command atomically
returns and removes the last element from the source list,
and pushes that element as the first element in the destination list.
Atomically means that both pop and push operations
are performed as a single operation at the same time;
i.e. both should succeed, otherwise both are treated as failed.</p>
<p><code>super_fetch</code> registers a private queue in Redis
for each Sidekiq process on start-up.
<code>super_fetch</code> atomically fetches a scheduled job
from the public queue in Redis
and pushes that job into the private queue (or working queue)
using <code>RPOPLPUSH</code> Redis command.
Once the job is finished processing,
Sidekiq removes that job from the private queue.
During a graceful shutdown,
Sidekiq moves back the unfinished jobs
from the private queue to the public queue.
If shutdown of Sidekiq process is not graceful,
the unfinished jobs of that Sidekiq process
remain there in the private queue which are called as orphaned jobs.
On restarting or starting another Sidekiq process,
<code>super_fetch</code> looks for such orphaned jobs in the private queues.
If Sidekiq finds orphaned jobs, Sidekiq re-enqueue them and processes again.</p>
<p>It may happen that
we have multiple Sidekiq processes running at the same time.
If a process dies among them, its unfinished jobs become orphans.
<a href="https://github.com/mperham/sidekiq/wiki/Reliability#recovering-jobs">This Sidekiq wiki</a>
describes in detail the criteria which <code>super_fetch</code> relies upon
for identifying which jobs are orphaned and which jobs are not orphaned.
If we don't restart or start another process,
<code>super_fetch</code> may take 5 minutes or 3 hours to recover such orphaned jobs.
The recommended approach is to restart or start another Sidekiq process
to signal <code>super_fetch</code> to look for orphans.</p>
<p>Interestingly, in the older versions of Sidekiq Pro,
<code>super_fetch</code> performed checks for orphaned jobs and queues
<a href="https://github.com/mperham/sidekiq/issues/3273">every 24 hours</a>
at the Sidekiq process startup.
Due to this, when the Sidekiq process crashes,
the orphaned jobs of that process remain unpicked for up to 24 hours
until the next restart.
This orphan delay check window
had been later lowered to 1 hour in Sidekiq Pro 3.4.1.</p>
<p>Another fun thing to know is that,
there existed two fetch strategies namely
<a href="https://github.com/mperham/sidekiq/wiki/Reliability/_compare/71312b1f3880bcee9ff47f59c7516c15657553d8...15776fd781848a36a0ddb24c3f2315202696e30c"><code>reliable_fetch</code></a>
and <code>timed_fetch</code>
in the older versions of Sidekiq Pro.
Apparently, <code>reliable_fetch</code>
<a href="https://github.com/mperham/sidekiq/wiki/Pro-Reliability-Server#reliable_fetch">did not work with Docker</a>
and <code>timed_fetch</code> had asymptotic computational complexity <code>O(log N)</code>,
comparatively
<a href="https://github.com/mperham/sidekiq/wiki/Pro-Reliability-Server#timed_fetch">less efficient</a>
than <code>super_fetch</code>,
which has asymptotic computational complexity <code>O(1)</code>.
Both of these strategies had been deprecated
in Sidekiq Pro 3.4.0 in favor of <code>super_fetch</code>.
Later, both of these strategies had been
<a href="https://github.com/mperham/sidekiq/blob/6e79f2a860ae558f2ed52b8917d2fede846c0a50/Pro-4.0-Upgrade.md#whats-new">removed</a>
in Sidekiq Pro 4.0
and <a href="https://github.com/mperham/sidekiq/wiki/Reliability#notes">are not documented anywhere</a>.</p>
<h2>Final result</h2>
<p>We have enabled <code>super_fetch</code> in our application and
it seemed to be working without any major issues so far.
Our Kubernetes <code>background</code> pods does not seem to
be loosing any jobs when these pods are restarted.</p>
<p>Update : Mike Pheram, author of Sidekiq, posted following
<a href="https://www.reddit.com/r/ruby/comments/8htnpe/increase_reliability_of_background_job_processing">comment</a>.</p>
<blockquote>
<p>Faktory provides all of the beanstalkd functionality, including the same reliability, with a nicer Web UI. It's free and OSS.
https://github.com/contribsys/faktory http://contribsys.com/faktory/</p>
</blockquote>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.6 added option to raise exception in Kernel#system]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-6-added-option-to-raise-exception-in-kernel-system"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-added-option-to-raise-exception-in-kernel-system.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-6-added-option-to-raise-exception-in-kernel-system</id>
      <published>2018-04-25T00:00:00.000Z</published>
      <updated>2018-04-25T00:00:00.000Z</updated>
      <author>
        <name>Amit Choudhary</name>
        <uri>https://www.bigbinary.com/blog/authors/amit</uri>
      </author>
      <category term="Ruby 2.6"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-6-added-option-to-raise-exception-in-kernel-system.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[The BigBinary team's lesson on Ruby 2.6 adding an option to raise exception in Kernel#system]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-6-added-option-to-raise-exception-in-kernel-system"><![CDATA[<p>We write scripts to automate setup and deployment of Rails applications. In
those scripts, in many places, we need to run system commands like
<code>bundle install</code>, <code>rake db:create</code>, <code>rake db:migrate</code> and many more.</p>
<p>Let's suppose we need to run migrations using <code>rake db:migrate</code> in a Rails
project setup script. We can use the <code>Kernel#system</code> method.</p>
<pre><code class="language-ruby">irb&gt; system('rake db:migrate')
</code></pre>
<h4>Ruby 2.5.0</h4>
<p>Executing <code>system</code> returns <code>true</code> or <code>false</code>. Another feature of <code>system</code> is
that it eats up the exceptions.</p>
<p>Let's suppose our migrations can run successfully. In this case the <code>system</code>
command for running migrations will return true.</p>
<pre><code class="language-ruby">irb&gt; system('rake db:migrate')
 =&gt; true
</code></pre>
<p>Let's suppose we have a migration that is trying to add a column to a table
which does not exist. In this case, the <code>system</code> command for running migrations
will return false.</p>
<pre><code class="language-ruby">irb&gt; system('rake db:migrate')
== 20180311211836 AddFirstNameToAdmins: migrating =============================
-- add_column(:admins, :first_name, :string)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::UndefinedTable: ERROR:  relation &quot;admins&quot; does not exist
: ALTER TABLE &quot;admins&quot; ADD &quot;first_name&quot; character varying
.
.
.
Tasks: TOP =&gt; db:migrate
(See full trace by running task with --trace)
 =&gt; false
</code></pre>
<p>As we can see, even when there is a failure in executing system commands, the
return value is false. Ruby does not raise an exception in those cases.</p>
<p>However, we can use <code>raise</code> explicitly to raise an exception and halt the setup
script execution.</p>
<pre><code class="language-ruby">irb&gt; system('rake db:migrate') || raise('Failed to run migrations')
== 20180311211836 AddFirstNameToAdmins: migrating =============================
-- add_column(:admins, :first_name, :string)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::UndefinedTable: ERROR:  relation &quot;admins&quot; does not exist
: ALTER TABLE &quot;admins&quot; ADD &quot;first_name&quot; character varying
.
.
.
Tasks: TOP =&gt; db:migrate
(See full trace by running task with --trace)
Traceback (most recent call last):
        2: from /Users/amit/.rvm/rubies/ruby-2.5.0/bin/irb:11:in `&lt;main&gt;'
        1: from (irb):4
RuntimeError (Failed to run migrations)
</code></pre>
<h4>Ruby 2.6.0-preview1</h4>
<p>Ruby 2.6 make our lives easier by providing an option <code>exception: true</code> so that
we do not need to use <code>raise</code> explicitly to halt script execution.</p>
<pre><code class="language-ruby">irb&gt; system('rake db:migrate', exception: true)
== 20180311211836 AddFirstNameToAdmins: migrating =============================
-- add_column(:admins, :first_name, :string)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::UndefinedTable: ERROR:  relation &quot;admins&quot; does not exist
: ALTER TABLE &quot;admins&quot; ADD &quot;first_name&quot; character varying
.
.
.
Tasks: TOP =&gt; db:migrate
(See full trace by running task with --trace)
Traceback (most recent call last):
        3: from /Users/amit/.rvm/rubies/ruby-2.6.0-preview1/bin/irb:11:in `&lt;main&gt;'
        2: from (irb):2
        1: from (irb):2:in `system'
RuntimeError (Command failed with exit 1: rake)
</code></pre>
<p>Ruby 2.6 works the same way as previous Ruby versions when used without the
<code>exception</code> option or used with <code>exception</code> set as false.</p>
<pre><code class="language-ruby">irb&gt; system('rake db:migrate', exception: false)
== 20180311211836 AddFirstNameToAdmins: migrating =============================
-- add_column(:admins, :first_name, :string)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::UndefinedTable: ERROR:  relation &quot;admins&quot; does not exist
: ALTER TABLE &quot;admins&quot; ADD &quot;first_name&quot; character varying
.
.
.
Tasks: TOP =&gt; db:migrate
(See full trace by running task with --trace)
 =&gt; false
</code></pre>
<p>Here is the relevant <a href="https://github.com/ruby/ruby/commit/fb29cffab0">commit</a>
and <a href="https://bugs.ruby-lang.org/issues/14386">discussion</a> for this change.</p>
<p><code>system</code> is not the only way to execute scripts like these. We wrote
<a href="https://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html">a blog</a>
6 years ago which discusses the differences between running commands using
<code>backtick</code>, <code>exec</code>, <code>sh</code>, <code>popen3</code>, <code>popen2e</code> and <code>Process.spawn</code>.</p>
<p>The Chinese version of this blog is available
<a href="http://madao.me/yi-ruby-2-6-kernel-de-system-fang-fa-zeng-jia-shi-fou-pao-chu-yi-chang-can-shu/">here</a>.</p>
]]></content>
    </entry>
  <entry>
      <title type="text"><![CDATA[Ruby 2.5 adds Thread.report_on_exception by default]]></title>
      <link rel="alternate" type="text/html" href="https://www.bigbinary.com/blog/ruby-2-5-enables-thread-report_on_exception-by-default"/>
      <link rel="enclosure" type="image/png" href="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-5-enables-thread-report_on_exception-by-default.png"/>
      <id>https://www.bigbinary.com/blog/ruby-2-5-enables-thread-report_on_exception-by-default</id>
      <published>2018-04-18T00:00:00.000Z</published>
      <updated>2018-04-18T00:00:00.000Z</updated>
      <author>
        <name>Vishal Telangre</name>
        <uri>https://www.bigbinary.com/blog/authors/vishaltelangre</uri>
      </author>
      <category term="Ruby 2.5"/>
      <category term="Ruby"/>
      <media:thumbnail url="https://www.bigbinary.com/blog/images/social_preview_images/generated/ruby-2-5-enables-thread-report_on_exception-by-default.png" width="1200" height="630"/>
      <summary type="text"><![CDATA[Ruby 2.5 enables Thread.report_on_exception by default and we get more info when threads die]]></summary>
      <content type="html" xml:base="https://www.bigbinary.com/blog/ruby-2-5-enables-thread-report_on_exception-by-default"><![CDATA[<p>Let's see what happens when an exception is raised inside a thread.</p>
<pre><code class="language-ruby">division_thread = Thread.new do
  puts &quot;Calculating 4/0 in division_thread&quot;
  puts &quot;Result is: #{4/0}&quot;
  puts &quot;Exiting from division_thread&quot;
end

sleep 1

puts &quot;In the main thread&quot;
</code></pre>
<p>Execution of it looks like this.</p>
<pre><code class="language-ruby">$ RBENV_VERSION=2.4.0 ruby thread_example_1.rb

Calculating 4/0 in division_thread

In the main thread
</code></pre>
<p>Note that the last two lines from the block were not printed. Also notice that
after failing in the thread the program continued to run in main thread. That's
why we got the message &quot;In the main thread&quot;.</p>
<p>This is because the default behavior of Ruby is to silently ignore exceptions in
threads and then to continue to execute in the main thread.</p>
<h2>Enabling abort_on_exception to stop on failure</h2>
<p>If we want an exception in a thread to stop further processing both in the
thread and in the main thread then we can enable <code>Thread[.#]abort_on_exception</code>
on that thread to achieve that.</p>
<p>Notice that in the below code we are using <code>Thread.current</code>.</p>
<pre><code class="language-ruby">division_thread = Thread.new do
  Thread.current.abort_on_exception = true

  puts &quot;Calculating 4/0 in division_thread&quot;
  puts &quot;Result is: #{4/0}&quot;
  puts &quot;Exiting from division_thread&quot;
end

sleep 1

puts &quot;In the main thread&quot;
</code></pre>
<pre><code class="language-ruby">$ RBENV_VERSION=2.4.0 ruby thread_example_2.rb

Calculating 4/0 in division_thread

thread_example_2.rb:5:in `/': divided by 0 (ZeroDivisionError)
  from thread_example_2.rb:5:in `block in &lt;main&gt;'
</code></pre>
<p>As we can see once an exception was encountered in the thread then processing
stopped on both in the thread and in the main thread.</p>
<p>Note that <code>Thread.current.abort_on_exception = true</code> activates this behavior
only for the current thread.</p>
<p>If we want this behavior globally for all the threads then we need to use
<code>Thread.abort_on_exception = true</code>.</p>
<h2>Running program with debug flag to stop on failure</h2>
<p>Let's run the original code with <code>--debug</code> option.</p>
<pre><code class="language-ruby">$ RBENV_VERSION=2.4.0 ruby --debug thread_example_1.rb

thread_example_1.rb:1: warning: assigned but unused variable - division_thread

Calculating 4/0 in division_thread

Exception `ZeroDivisionError' at thread_example_1.rb:3 - divided by 0
Exception `ZeroDivisionError' at thread_example_1.rb:7 - divided by 0
thread_example_1.rb:3:in `/': divided by 0 (ZeroDivisionError)
  from thread_example_1.rb:3:in `block in &lt;main&gt;'
</code></pre>
<p>In this case the exception is printed in detail and the code in main thread was
not executed.</p>
<p>Usually when we execute a program with <code>--debug</code> option then the behavior of the
program does not change. We expect the program to print more stuff but we do not
expect behavior to change. However in this case the <code>--debug</code> option changes the
behavior of the program.</p>
<h2>Running program with join on thread to stop on failure</h2>
<p>If a thread raises an exception and <code>abort_on_exception</code> and <code>$DEBUG</code> flags are
not set then that exception will be processed at the time of joining of the
thread.</p>
<pre><code class="language-ruby">division_thread = Thread.new do
  puts &quot;Calculating 4/0 in division_thread&quot;
  puts &quot;Result is: #{4/0}&quot;
  puts &quot;Exiting from division_thread&quot;
end

division_thread.join

puts &quot;In the main thread&quot;
</code></pre>
<pre><code class="language-ruby">$ RBENV_VERSION=2.4.0 ruby thread_example_3.rb

Calculating 4/0 in division_thread

thread_example_3.rb:3:in `/': divided by 0 (ZeroDivisionError)
  from thread_example_3.rb:3:in `block in &lt;main&gt;'
</code></pre>
<p>Both <code>Thread#join</code> and <code>Thread#value</code> will stop processing in the thread and in
the main thread once an exception is encountered.</p>
<h2>Introduction of report_on_exception in Ruby 2.4</h2>
<p>Almost 6 years ago, <a href="https://github.com/headius">Charles Nutter (headius)</a> had
proposed that the exceptions raised in threads should be automatically logged
and reported, by default. To make his point, he explained issues similar to what
we discussed above about the Ruby's behavior of silently ignoring exceptions in
threads. <a href="https://bugs.ruby-lang.org/issues/6647">Here</a> is the relevant
discussion on his proposal.</p>
<p>Following are some of the notable points discussed.</p>
<ul>
<li>Enabling <code>Thread[.#]abort_on_exception</code>, by default, is not always a good
idea.</li>
<li>There should be a flag which, if enabled, would print the thread-killing
exception info.</li>
<li>In many cases, people spawn one-off threads which are not hard-referenced
using <code>Thread#join</code> or <code>Thread#value</code>. Such threads gets garbage collected.
Should it report the thread-killing exception at the time of garbage
collection if such a flag is enabled?</li>
<li>Should it warn using
<a href="https://ruby-doc.org/core-2.4.0/Warning.html#method-i-warn"><code>Warning#warn</code></a>
or redirect to STDERR device while reporting?</li>
</ul>
<p>Charles Nutter suggested that a configurable global flag
<code>Thread.report_on_exception</code> and instance-level flag
<code>Thread#report_on_exception</code> should be implemented having its default value as
<code>true</code>. When set to <code>true</code>, it should report print exception information.</p>
<p>Matz and other core members approved that <code>Thread[.#]report_on_exception</code> can be
implemented having its default value set to <code>false</code>.</p>
<p>Charles Nutter, Benoit Daloze and other people demanded that it should be <code>true</code>
by default so that programmers can be aware of the silently disappearing threads
because of exceptions.</p>
<p>Shyouhei Urabe <a href="https://bugs.ruby-lang.org/issues/6647#note-41">advised</a> that
due to some technical challenges, the default value should be set to <code>false</code> so
as this feature could land in Ruby. Once this feature is in then the default
value can be changed in a later release.</p>
<p><a href="https://github.com/nobu">Nobuyoshi Nakada (nobu)</a> pushed an
<a href="https://github.com/ruby/ruby/commit/2e71c752787e0c7659bd5e89b6c5d433eddfe13a">implementation</a>
for <code>Thread[.#]report_on_exception</code> with a default value set to <code>false</code>. It was
released in Ruby 2.4.0.</p>
<p>Let's try enabling <code>report_on_exception</code> globally using
<code>Thread.report_on_exception</code>.</p>
<pre><code class="language-ruby">Thread.report_on_exception = true

division_thread = Thread.new do
  puts &quot;Calculating 4/0 in division_thread&quot;
  puts &quot;Result is: #{4/0}&quot;
  puts &quot;Exiting from division_thread&quot;
end

addition_thread = Thread.new do
  puts &quot;Calculating nil+4 in addition_thread&quot;
  puts &quot;Result is: #{nil+4}&quot;
  puts &quot;Exiting from addition_thread&quot;
end

sleep 1

puts &quot;In the main thread&quot;
</code></pre>
<pre><code class="language-ruby">$ RBENV_VERSION=2.4.0 ruby thread_example_4.rb

Calculating 4/0 in division_thread

#&lt;Thread:0x007fb10f018200@thread_example_4.rb:3 run&gt; terminated with exception:
thread_example_4.rb:5:in `/': divided by 0 (ZeroDivisionError)
  from thread_example_4.rb:5:in `block in &lt;main&gt;'

Calculating nil+4 in addition_thread

#&lt;Thread:0x007fb10f01aca8@thread_example_4.rb:9 run&gt; terminated with exception:
thread_example_4.rb:11:in `block in &lt;main&gt;': undefined method `+' for nil:NilClass (NoMethodError)

In the main thread
</code></pre>
<p>It now reports the exceptions in all threads. It prints that the
<code>Thread:0x007fb10f018200</code> was
<code>terminated with exception: divided by 0 (ZeroDivisionError)</code>. Similarly,
another thread <code>Thread:0x007fb10f01aca8</code> was
<code>terminated with exception: undefined method '+' for nil:NilClass (NoMethodError)</code>.</p>
<p>Instead of enabling it globally for all threads, we can enable it for a
particular thread using instance-level <code>Thread#report_on_exception</code>.</p>
<pre><code class="language-ruby">division_thread = Thread.new do
  puts &quot;Calculating 4/0 in division_thread&quot;
  puts &quot;Result is: #{4/0}&quot;
  puts &quot;Exiting from division_thread&quot;
end

addition_thread = Thread.new do
  Thread.current.report_on_exception = true

  puts &quot;Calculating nil+4 in addition_thread&quot;
  puts &quot;Result is: #{nil+4}&quot;
  puts &quot;Exiting from addition_thread&quot;
end

sleep 1

puts &quot;In the main thread&quot;
</code></pre>
<p>In the above case we have enabled <code>report_on_exception</code> flag just for
<code>addition_thread</code>.</p>
<p>Let's execute it.</p>
<pre><code class="language-ruby">$ RBENV_VERSION=2.4.0 ruby thread_example_5.rb

Calculating 4/0 in division_thread

Calculating nil+4 in addition_thread

#&lt;Thread:0x007f8e6b007f70@thread_example_5.rb:7 run&gt; terminated with exception:
thread_example_5.rb:11:in `block in &lt;main&gt;': undefined method `+' for nil:NilClass (NoMethodError)

In the main thread
</code></pre>
<p>Notice how it didn't report the exception which killed thread <code>division_thread</code>.
As expected, it reported the exception that killed thread <code>addition_thread</code>.</p>
<p>With the above changes ruby reports the exception as soon as it encounters.
However if these threads are joined then they will still raise exception.</p>
<pre><code class="language-ruby">division_thread = Thread.new do
  Thread.current.report_on_exception = true

  puts &quot;Calculating 4/0 in division_thread&quot;
  puts &quot;Result is: #{4/0}&quot;
  puts &quot;Exiting from division_thread&quot;
end

begin
  division_thread.join
rescue =&gt; exception
  puts &quot;Explicitly caught - #{exception.class}: #{exception.message}&quot;
end

puts &quot;In the main thread&quot;
</code></pre>
<pre><code class="language-ruby">$ RBENV_VERSION=2.4.0 ruby thread_example_6.rb

Calculating 4/0 in division_thread

#&lt;Thread:0x007f969d00d828@thread_example_6.rb:1 run&gt; terminated with exception:
thread_example_6.rb:5:in `/': divided by 0 (ZeroDivisionError)
  from thread_example_6.rb:5:in `block in &lt;main&gt;'

Explicitly caught - ZeroDivisionError: divided by 0

In the main thread
</code></pre>
<p>See how we were still be able to handle the exception raised in
<code>division_thread</code> above after joining it despite it reported it before due to
<code>Thread#report_on_exception</code> flag.</p>
<h2>report_on_exception defaults to true in Ruby 2.5</h2>
<p><a href="https://github.com/eregon">Benoit Daloze (eregon)</a> strongly advocated that both
the <code>Thread.report_on_exception</code> and <code>Thread#report_on_exception</code> should have
default value as <code>true</code>. <a href="https://bugs.ruby-lang.org/issues/14143">Here</a> is the
relevant feature request.</p>
<p>After <a href="https://bugs.ruby-lang.org/issues/14143#note-9">approval from Matz</a>,
Benoit Daloze pushed the
<a href="https://github.com/ruby/ruby/search?utf8=%E2%9C%93&amp;q=Feature+%5C%2314143&amp;type=Commits">implementation</a>
by fixing the failing tests and silencing the unnecessary verbose warnings.</p>
<p>It was released as part of Ruby 2.5.</p>
<p>Now in ruby 2.5 we can simply write like this.</p>
<pre><code class="language-ruby">division_thread = Thread.new do
  puts &quot;Calculating 4/0 in division_thread&quot;
  puts &quot;Result is: #{4/0}&quot;
  puts &quot;Exiting from division_thread&quot;
end

addition_thread = Thread.new do
  puts &quot;Calculating nil+4 in addition_thread&quot;
  puts &quot;Result is: #{nil+4}&quot;
  puts &quot;Exiting from addition_thread&quot;
end

sleep 1

puts &quot;In the main thread&quot;
</code></pre>
<p>Let's execute it with Ruby 2.5.</p>
<pre><code class="language-ruby">$ RBENV_VERSION=2.5.0 ruby thread_example_7.rb

Calculating 4/0 in division_thread

#&lt;Thread:0x00007f827689a238@thread_example_7.rb:1 run&gt; terminated with exception (report_on_exception is true):
Traceback (most recent call last):
  1: from thread_example_7.rb:3:in `block in &lt;main&gt;'
thread_example_7.rb:3:in `/': divided by 0 (ZeroDivisionError)

Calculating nil+4 in addition_thread

#&lt;Thread:0x00007f8276899b58@thread_example_7.rb:7 run&gt; terminated with exception (report_on_exception is true):
Traceback (most recent call last):
thread_example_7.rb:9:in `block in &lt;main&gt;': undefined method `+' for nil:NilClass (NoMethodError)

In the main thread
</code></pre>
<p>We can disable the thread exception reporting globally using
<code>Thread.report_on_exception = false</code> or for a particular thread using
<code>Thread.current.report_on_exception = false</code>.</p>
<h2>Future Possibilities</h2>
<p>In addition to this feature, Charles Nutter also
<a href="https://bugs.ruby-lang.org/issues/14143#note-4">suggested</a> that it will be good
if there exists a callback handler which can accept a block to be executed when
a thread dies due to an exception. The callback handler can be at global level
or it can be for a specific thread.</p>
<pre><code class="language-ruby">Thread.on_exception do
  # some stuff
end
</code></pre>
<p>In the absence of such handler libraries need to resort to custom code to handle
exceptions.
<a href="https://github.com/mperham/sidekiq/blob/a60a91d3dd857592a532965f0701d285f13f28f1/lib/sidekiq/util.rb#L15-L27">Here is how</a>
Sidekiq handles exceptions raised in threads.</p>
<p>Important thing to note is that <code>report_on_exception</code> does not change behavior
of the code. It does more reporting when a thread dies and when it comes to
thread dies more reporting is a good thing.</p>
]]></content>
    </entry>
</feed>