Page 62 of 170

Multiple Sidekiq processes for multiple Rails/Sinatra applications – namespacing

Sidekiq is a great background processing tool that can be used with many applications deployed on the same server. In order to make it work without any issues or collisions, you need to define namespace for each Sidekiq process group within single application. To do so, you need to create an initializer that will contain both: server connection details and a namespace name:

Sidekiq.configure_server do |config|
  config.redis = {
    url: 'redis://localhost:6379',
    namespace: 'my_app_name_production'
  }
end

Sidekiq.configure_client do |config|
  config.redis = {
    url: 'redis://localhost:6379',
    namespace: 'my_app_name_production'
  }
end

Keep in mind, that you need to provide both url and namespace for client and server. I like to hook it up with SettingsLogic gem:

Sidekiq.configure_server do |config|
  config.redis = System::Settings.sidekiq.redis
end

Sidekiq.configure_client do |config|
  config.redis = System::Settings.sidekiq.redis
end

After you deploy all the apps, you can also easily check if everything is working, by simply executing redis console:

$: redis-cli

and running following command:

redis 127.0.0.1:6379> keys *NAMESPACE*

For example:

redis 127.0.0.1:6379> keys *api*
 1) "youtube_api_v2_production:stat:failed:2014-07-04"
 2) "youtube_api_v2_production:MIA-VPS-VM0974:770"
 3) "facebook_api_production:processes"
 4) "facebook_api_production:queues"
 5) "twitter_api_production:MIA-VPS-VM0974:32655"
 6) "youtube_api_v2_production:queues"
 7) "facebook_api_production:stat:processed"
 8) "youtube_api_v2_production:stat:processed:2014-07-04"
 9) "youtube_api_v2_production:stat:processed"
10) "youtube_api_v2_production:processes"
11) "facebook_api_production:MIA-VPS-VM0974:459"
12) "facebook_api_production:stat:processed:2014-07-04"
13) "twitter_api_production:stat:processed:2014-07-04"
14) "twitter_api_production:stat:processed"
15) "twitter_api_production:processes"
16) "twitter_api_production:queues"
17) "youtube_api_v2_production:stat:failed"

Above you can see multiple queues for multiple namespaced apps.

Testing Sinatra application methods in isolation with RSpec

Sometimes you may want to test some Sinatra app methods in isolation (outside of a standard request flow). You probably even tried to do something like this:

require 'spec_helper'

describe App do
  subject { App.new }

  it 'expect run some specs here'
    expect(subject.current_user).to eq user
  end
end

Unfortunately you will end up with error like this:

NoMethodError: undefined method `current_user' for #<App app_file="/home/something/app.rb">
from (irb):2
from /home/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'

This will occur, because Sinatra's App new method creates a Sinatra::Wrapper, not an App instance. Of course everything will work as it should, if you follow the get/post/put/delete way of testing Sinatra apps:

require 'spec_helper'

describe App do
  before { get '/api/v1/credit_cards.json' }
    
  it { expect(last_response).to be_ok }
end

but it will fail when trying to invoke any App methods directly.

Luckily there's a really simple solution to this issue. You just need to initialize it by manually allocating space and "bypassing" the new method:

require 'spec_helper'

describe App do
  subject do
    app = described_class.allocate
    app.send :initialize
    app
  end

  describe '#current_user' do
    before do
      expect(subject)
        .to receive(:params)
        .and_return(params)
        .at_least(:once)
    end

    context 'when there is no user_id' do
      let(:params) { {} }

      it { expect(subject.send :current_user).to be_nil }
    end
  end
end

That way you can get the "real" app instance (not a wrapper) that you can use as a subject in your specs. That way you can test your Sinatra app methods in isolation - without having to call a full request.

Copyright © 2025 Closer to Code

Theme by Anders NorenUp ↑