<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marco Imperatore</title>
    <description>The latest articles on DEV Community by Marco Imperatore (@marcoimperatore).</description>
    <link>https://dev.to/marcoimperatore</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F315020%2F38f4b7af-b2a8-4d33-b813-f7dfe7040603.jpg</url>
      <title>DEV Community: Marco Imperatore</title>
      <link>https://dev.to/marcoimperatore</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcoimperatore"/>
    <language>en</language>
    <item>
      <title>Adding support for queues to RubyJob</title>
      <dc:creator>Marco Imperatore</dc:creator>
      <pubDate>Mon, 13 Jan 2020 00:15:49 +0000</pubDate>
      <link>https://dev.to/marcoimperatore/adding-support-for-queues-to-rubyjob-45kd</link>
      <guid>https://dev.to/marcoimperatore/adding-support-for-queues-to-rubyjob-45kd</guid>
      <description>&lt;p&gt;Over the holidays, with a bit of spare time between family gatherings, I decided to scratch an itch and create a new open source job processing framework for Ruby, called &lt;a href="https://github.com/mimperatore/ruby_job"&gt;RubyJob&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'd used various other such frameworks in the past, such as Sidekiq, Resque, Delayed::Job and SuckerPunch, but none of them behaved and performed to my liking.&lt;/p&gt;

&lt;p&gt;I'm not going to go over the pros &amp;amp; cons of all of these frameworks here, but feel free to check out the nice article the &lt;strong&gt;Scout APM Team&lt;/strong&gt; put together &lt;a href="https://scoutapm.com/blog/which-ruby-background-job-framework-is-right-for-you"&gt;here&lt;/a&gt; if you're interested in that.&lt;/p&gt;

&lt;p&gt;To my surprise, in the past two days, after I released my gem on &lt;strong&gt;RubyGems.org&lt;/strong&gt;, it has been downloaded over &lt;em&gt;200 times&lt;/em&gt;!  I have no idea who might be trying it out, but I'm glad they are.  The interest shown further increases my desire to write the next feature I had been planning: &lt;em&gt;Queue Support&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Remaining in the spirit of openness, I've decided to capture the thought process and work I'm going to go through in order to take the current version of &lt;em&gt;RubyJob&lt;/em&gt;, which currently has no support for multiple queues, and enhance it to allow the developer to specify both a queue to use, as well as its priority relative to other queues.&lt;/p&gt;

&lt;h2&gt;
  
  
  TDD: Test Driven [Design|Development]
&lt;/h2&gt;

&lt;p&gt;I generally like to work using my own flavour of &lt;em&gt;TDD&lt;/em&gt;, one that isn't exactly like the often discussed &lt;a href="https://www.codecademy.com/articles/tdd-red-green-refactor"&gt;red/green/refactor&lt;/a&gt; method, but rather a less constraining form that sees me sometimes writing tests first, and sometimes writing tests second (but in all cases before any change is deemed complete.)&lt;/p&gt;

&lt;p&gt;I like to use &lt;a href="https://github.com/guard/guard"&gt;guard&lt;/a&gt;, and its &lt;a href="https://github.com/guard/guard-rspec"&gt;RSpec-plugin&lt;/a&gt;, to watch the filesystem for any changes to my source code and automatically re-run tests.  This not only makes it easy for me to remember to think about the tests, but also helps steer me in a direction that allows me to make incremental changes that &lt;em&gt;always&lt;/em&gt; keep the tests green while refactoring, and &lt;em&gt;almost always&lt;/em&gt; when in the process of adding a breaking change.&lt;/p&gt;

&lt;p&gt;You may have noticed the title of this section having &lt;code&gt;Design|Development&lt;/code&gt; as the last &lt;code&gt;D&lt;/code&gt; in &lt;em&gt;TDD&lt;/em&gt;.  That's because I firmly believe that tests are a great tool to use to actually flesh out emergent software design.  Often, and as is the case for the feature I'm going to be adding, we don't know exactly what we want the API to look like in advance, and writing tests actually helps us better visualize what the end result may end up looking like.  That's because test code tends to look similar, or have similar properties, to production code that uses the software we're developing.&lt;/p&gt;

&lt;p&gt;Let's dig into thinking about the design of the queue/priority feature...&lt;/p&gt;

&lt;h2&gt;
  
  
  Typical Queue Support In Other Gems
&lt;/h2&gt;

&lt;p&gt;Most gems similar to &lt;strong&gt;RubyJob&lt;/strong&gt; have some way of partitioning jobs across one of more queues, and to assign to each queue a relative priority, such that jobs get processed in an order that can be controlled by the developer.  For example, you may put urgent jobs on a queue called &lt;code&gt;critical&lt;/code&gt;, and regular jobs on the &lt;code&gt;default&lt;/code&gt; queue.&lt;/p&gt;

&lt;p&gt;In Sidekiq, this option can be specified with the &lt;code&gt;:queue&lt;/code&gt; &lt;em&gt;option&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyCriticalWorker&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Worker&lt;/span&gt;

  &lt;span class="n"&gt;sidekiq_options&lt;/span&gt; &lt;span class="ss"&gt;queue: &lt;/span&gt;&lt;span class="s1"&gt;'critical'&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;
    &lt;span class="c1"&gt;#job code goes here&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Adding to that a bit of configuration in &lt;em&gt;sidekiq.yml&lt;/em&gt;, you also establish the queue priorities.  In the case below, queue &lt;code&gt;critical&lt;/code&gt; is 10x more important than &lt;code&gt;default&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="ss"&gt;:queues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Specifying Queues In RubyJob
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;RubyJob&lt;/code&gt; is a bit different from the other job processing frameworks in that it has the notion of a &lt;em&gt;Job Store&lt;/em&gt;, which is an &lt;em&gt;abstraction&lt;/em&gt; that enables us to choose how we want to keep track of our jobs.&lt;/p&gt;

&lt;p&gt;For example, if our job worker looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyWorker&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Worker&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;
    &lt;span class="c1"&gt;#job code goes here&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;then we tell the framework to track &lt;code&gt;MyWorker&lt;/code&gt; jobs like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;MyWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InMemoryJobStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this case, the jobs are being tracked (stored) in an &lt;code&gt;InMemoryJobStore&lt;/code&gt;, a data structure backed by a &lt;a href="https://en.wikipedia.org/wiki/Fibonacci_heap"&gt;&lt;em&gt;Fibonacci Heap&lt;/em&gt;&lt;/a&gt; which is very efficient for this use case.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;server&lt;/code&gt; to then process the jobs is started like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ThreadedServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;num_threads: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;jobstore: &lt;/span&gt;&lt;span class="no"&gt;MyWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;server_thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;Job Store&lt;/em&gt; is explicitly passed to the constructor of &lt;code&gt;ThreadedServer&lt;/code&gt;.  The &lt;em&gt;server&lt;/em&gt; spawns the specified number of threads, each of which repeatedly fetches the next ready job from the job store and processes it.  (For additional information on the full Job Store API, check out the docs &lt;a href="https://mimperatore.github.io/ruby_job/"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So, how could we envision job &lt;em&gt;queues&lt;/em&gt; meshing into this design?&lt;/p&gt;

&lt;h2&gt;
  
  
  Job Stores As Queues
&lt;/h2&gt;

&lt;p&gt;In some ways, the job stores themselves are already queues; they already keep track of jobs to process, and the order in which to process them.&lt;/p&gt;

&lt;p&gt;The issue with the example above is that all threads read the same, single, job store.  But what if we could specify a different number of threads for each different job stores, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ThreadedServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;config: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;num_threads: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;jobstore: &lt;/span&gt;&lt;span class="no"&gt;MyCriticalWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;num_threads: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;jobstore: &lt;/span&gt;&lt;span class="no"&gt;MyRegularWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On the surface, this seems like a solution that could work, and it would be pretty trivial to implement.  The code in &lt;code&gt;ThreadedServer#start&lt;/code&gt; which is currently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;
      &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="vi"&gt;@num_threads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="no"&gt;JobProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@jobstore&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="vi"&gt;@options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:join&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;would need to be only slightly tweaked so we iterate over the specified &lt;code&gt;config&lt;/code&gt; elements, and create the right sets of threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different Server Per Queue
&lt;/h2&gt;

&lt;p&gt;But what about a different approach -- one that doesn't require us to change anything at all?&lt;/p&gt;

&lt;p&gt;We know we can create a multi-threaded server object with a specified number of threads to process a single job store.  And the server itself is merely a top-level parent thread of the worker threads. What if we simply created multiple servers?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;critical_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ThreadedServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;num_threads: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;jobstore: &lt;/span&gt;&lt;span class="no"&gt;MyCriticalWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;regular_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ThreadedServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;num_threads: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;jobstore: &lt;/span&gt;&lt;span class="no"&gt;MyRegularWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;critical_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;span class="n"&gt;regular_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This option to me feels more robust and extensible than the first, because it doesn't couple the behaviour of the entire set of queues into a single &lt;code&gt;ThreadedServer&lt;/code&gt; running instance.  Granted, you could pass different &lt;code&gt;config&lt;/code&gt;'s using the first approach as well, but that seems counterintuitive and forced, whereas this approach fits in naturally with the current API design.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, RubyJob Already Supports Queues &amp;amp; Priorities?
&lt;/h2&gt;

&lt;p&gt;If we want to go with the approach above, yes!  And we do want to go with the approach above, for the reasons already stated.&lt;/p&gt;

&lt;p&gt;Pretty cool!  We started off thinking we didn't have the queueing and priority features, but thinking through it, we actually discovered that how the system had been designed already afforded us that capability!  Nice, pleasant surprise!  And this is not to say that things may not change in the future, but for now there is no real reason to make things more complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  We're Not Done Though
&lt;/h2&gt;

&lt;p&gt;Even though we do have the intrinsic ability to partition jobs into various queues and ensure that they run with a priority that we control, doing so requires a bit more boilerplate code than desirable.&lt;/p&gt;

&lt;p&gt;Ideally, we wouldn't need to write all this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;MyCriticalWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InMemoryJobStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="no"&gt;MyRegularWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InMemoryJobStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;

&lt;span class="n"&gt;critical_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ThreadedServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;num_threads: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;jobstore: &lt;/span&gt;&lt;span class="no"&gt;MyCriticalWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;regular_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ThreadedServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;num_threads: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;jobstore: &lt;/span&gt;&lt;span class="no"&gt;MyRegularWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jobstore&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;critical_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;span class="n"&gt;regular_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Keeping Queue and Worker Decoupled
&lt;/h3&gt;

&lt;p&gt;One of the problems with Sidekiq's and Resque's way of specifying the queue is that it is coupled to the code that defines the worker.  The decision of which queue and priority a particular job should have should rest with whoever is orchestrating and managing the overall system, not the worker class itself.&lt;/p&gt;

&lt;p&gt;Likewise, and for similar reasons, the number of threads should not be specified in the worker either, like Sucker Punch does.&lt;/p&gt;

&lt;p&gt;For RubyJob, it would be therefore desirable to keep the configuration completely external (e.g. in a &lt;em&gt;YAML&lt;/em&gt; file), such that it can be read by a coordinator which configures and starts the necessary &lt;code&gt;ThreadedServer&lt;/code&gt;s.  I'm envisioning a config file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;MyCriticalWorker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="ss"&gt;num_threads: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="ss"&gt;jobstore: &lt;/span&gt;&lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InMemoryJobStore&lt;/span&gt;
&lt;span class="no"&gt;MyRegularWorker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="ss"&gt;num_threads: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="ss"&gt;jobstore: &lt;/span&gt;&lt;span class="no"&gt;RubyJob&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;InMemoryJobStore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and a way of starting the servers like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ruby_job &lt;span class="nt"&gt;-w&lt;/span&gt; MyCriticalWorker start
&lt;span class="nv"&gt;$ &lt;/span&gt;ruby_job &lt;span class="nt"&gt;-w&lt;/span&gt; MyRegularWorker start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This, however, is beyond the scope of what I had intended to cover in today's post, and will be subject of a future day's work and probable post.  It's a different enough topic that deserves its own set of design considerations.  I'll be busting out &lt;a href="https://github.com/erikhuda/thor"&gt;thor&lt;/a&gt; and some interesting &lt;em&gt;RSpec&lt;/em&gt; techniques for testing command-line interfaces!&lt;/p&gt;

&lt;p&gt;I had hoped that I'd actually be writing some more code that I could share with you today, but hopefully sharing my thoughts about what was next on my list of things to do on &lt;code&gt;RubyJob&lt;/code&gt; was at least a bit interesting or informative.  Feel free to share any comments or questions below... thanks for reading!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>tdd</category>
      <category>design</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
