doc/file.README.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
File: README
— Documentation by YARD 0.9.26
</title>
<link rel="stylesheet" href="css/style.css" type="text/css" />
<link rel="stylesheet" href="css/common.css" type="text/css" />
<script type="text/javascript">
pathId = "README";
relpath = '';
</script>
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
</head>
<body>
<div class="nav_wrap">
<iframe id="nav" src="file_list.html?1"></iframe>
<div id="resizer"></div>
</div>
<div id="main" tabindex="-1">
<div id="header">
<div id="menu">
<a href="_index.html">Index</a> »
<span class="title">File: README</span>
</div>
<div id="search">
<a class="full_list_link" id="class_list_link"
href="class_list.html">
<svg width="24" height="24">
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
</svg>
</a>
</div>
<div class="clear"></div>
</div>
<div id="content"><div id='filecontents'><h1 id="sidekiquniquejobs">SidekiqUniqueJobs</h1>
<p><a href="https://gitter.im/mhenrixon/sidekiq-unique-jobs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img src="https://badges.gitter.im/mhenrixon/sidekiq-unique-jobs.svg" alt="Join the chat at https://gitter.im/mhenrixon/sidekiq-unique-jobs"></a> <img src="https://github.com/mhenrixon/sidekiq-unique-jobs/actions/workflows/rspec.yml/badge.svg?branch=master" alt="Build Status"> <a href="https://codeclimate.com/github/mhenrixon/sidekiq-unique-jobs"><img src="https://codeclimate.com/github/mhenrixon/sidekiq-unique-jobs.svg" alt="Code Climate"></a> <a href="https://codeclimate.com/github/mhenrixon/sidekiq-unique-jobs/coverage"><img src="https://codeclimate.com/github/mhenrixon/sidekiq-unique-jobs/badges/coverage.svg" alt="Test Coverage"></a></p>
<h2 id="support-me">Support Me</h2>
<p>Want to show me some ❤️ for the hard work I do on this gem? You can use the following PayPal link: <a href="https://paypal.me/mhenrixon1">https://paypal.me/mhenrixon1</a>. Any amount is welcome and let me tell you it feels good to be appreciated. Even a dollar makes me super excited about all of this.</p>
<!-- MarkdownTOC -->
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#usage">Usage</a>
<ul>
<li><a href="#installation">Installation</a></li>
<li><a href="#add-the-middleware">Add the middleware</a></li>
<li><a href="#your-first-worker">Your first worker</a></li>
</ul></li>
<li><a href="#requirements">Requirements</a></li>
<li><a href="#locks">Locks</a>
<ul>
<li><a href="#until-executing">Until Executing</a></li>
<li><a href="#example-worker">Example worker</a></li>
<li><a href="#until-executed">Until Executed</a></li>
<li><a href="#example-worker-1">Example worker</a></li>
<li><a href="#until-expired">Until Expired</a></li>
<li><a href="#example-worker-2">Example worker</a></li>
<li><a href="#until-and-while-executing">Until And While Executing</a></li>
<li><a href="#example-worker-3">Example worker</a></li>
<li><a href="#while-executing">While Executing</a></li>
<li><a href="#example-worker-4">Example worker</a></li>
<li><a href="#custom-locks">Custom Locks</a></li>
</ul></li>
<li><a href="#conflict-strategy">Conflict Strategy</a>
<ul>
<li><a href="#log">log</a></li>
<li><a href="#raise">raise</a></li>
<li><a href="#reject">reject</a></li>
<li><a href="#replace">replace</a></li>
<li><a href="#reschedule">Reschedule</a></li>
<li><a href="#custom-strategies">Custom Strategies</a></li>
<li><a href="#3-cleanup-dead-locks">3 Cleanup Dead Locks</a></li>
</ul></li>
<li><a href="#debugging">Debugging</a>
<ul>
<li><a href="#sidekiq-web">Sidekiq Web</a></li>
<li><a href="#reflections-metrics-logging-etc">Reflections (metrics, logging, etc.)</a></li>
<li><a href="#after_unlock_callback_failed">after_unlock_callback_failed</a></li>
<li><a href="#error">error</a></li>
<li><a href="#execution_failed">execution_failed</a></li>
<li><a href="#lock_failed">lock_failed</a></li>
<li><a href="#locked">locked</a></li>
<li><a href="#reschedule_failed">reschedule_failed</a></li>
<li><a href="#rescheduled">rescheduled</a></li>
<li><a href="#timeout">timeout</a></li>
<li><a href="#unlock_failed">unlock_failed</a></li>
<li><a href="#unlocked">unlocked</a></li>
<li><a href="#unknown_sidekiq_worker">unknown_sidekiq_worker</a></li>
<li><a href="#show-locks">Show Locks</a></li>
<li><a href="#show-lock">Show Lock</a></li>
</ul></li>
<li><a href="#testing">Testing</a>
<ul>
<li><a href="#validating-worker-configuration">Validating Worker Configuration</a></li>
<li><a href="#uniqueness">Uniqueness</a></li>
</ul></li>
<li><a href="#configuration">Configuration</a>
<ul>
<li><a href="#other-sidekiq-gems">Other Sidekiq gems</a></li>
<li><a href="#apartment-sidekiq">apartment-sidekiq</a></li>
<li><a href="#sidekiq-global_id">sidekiq-global_id</a></li>
<li><a href="#sidekiq-status">sidekiq-status</a></li>
<li><a href="#global-configuration">Global Configuration</a></li>
<li><a href="#debug_lua">debug_lua</a></li>
<li><a href="#lock_timeout">lock_timeout</a></li>
<li><a href="#lock_ttl">lock_ttl</a></li>
<li><a href="#enabled">enabled</a></li>
<li><a href="#logger">logger</a></li>
<li><a href="#max_history">max_history</a></li>
<li><a href="#reaper">reaper</a></li>
<li><a href="#reaper_count">reaper_count</a></li>
<li><a href="#reaper_interval">reaper_interval</a></li>
<li><a href="#reaper_timeout">reaper_timeout</a></li>
<li><a href="#lock_prefix">lock_prefix</a></li>
<li><a href="#lock_info">lock_info</a></li>
<li><a href="#worker-configuration">Worker Configuration</a></li>
<li><a href="#lock_info-1">lock_info</a></li>
<li><a href="#lock_prefix-1">lock_prefix</a></li>
<li><a href="#lock_ttl-1">lock_ttl</a></li>
<li><a href="#lock_timeout-1">lock_timeout</a></li>
<li><a href="#unique_across_queues">unique_across_queues</a></li>
<li><a href="#unique_across_workers">unique_across_workers</a></li>
<li><a href="#finer-control-over-uniqueness">Finer Control over Uniqueness</a></li>
<li><a href="#after-unlock-callback">After Unlock Callback</a></li>
</ul></li>
<li><a href="#communication">Communication</a></li>
<li><a href="#contributing">Contributing</a></li>
<li><a href="#contributors">Contributors</a></li>
</ul>
<!-- /MarkdownTOC -->
<h2 id="introduction">Introduction</h2>
<p>This gem adds unique constraints to sidekiq jobs. The uniqueness is achieved by creating a set of keys in redis based off of <code>queue</code>, <code>class</code>, <code>args</code> (in the sidekiq job hash).</p>
<p>By default, only one lock for a given hash can be acquired. What happens when a lock can't be acquired is governed by a chosen <a href="#conflict-strategy">Conflict Strategy</a> strategy. Unless a conflict strategy is chosen</p>
<p>This is the documentation for the <code>main</code> branch. You can find the documentation for each release by navigating to its tag.</p>
<p>Here are links to some of the old versions</p>
<ul>
<li><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/tree/v7.0.12">v7.0.12</a></li>
<li><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/tree/v6.0.25">v6.0.25</a></li>
<li><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/tree/v5.0.10">v5.0.10</a></li>
<li><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/tree/v4.0.18">v4.0.18</a></li>
</ul>
<h2 id="usage">Usage</h2>
<h3 id="installation">Installation</h3>
<p>Add this line to your application's Gemfile:</p>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_gem'>gem</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>sidekiq-unique-jobs</span><span class='tstring_end'>'</span></span>
</code></pre>
<p>And then execute:</p>
<pre class="code bash"><code class="bash">bundle
</code></pre>
<h3 id="add-the-middleware">Add the middleware</h3>
<p>Before v7, the middleware was configured automatically. Since some people reported issues with other gems (see <a href="#other-sidekiq-gems">Other Sidekiq Gems</a>) it was decided to give full control over to the user.</p>
<p><em>NOTE</em> if you want to use the reaper you also need to configure the server middleware.</p>
<p><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/myapp/config/initializers/sidekiq.rb#L12">A full example</a></p>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure_server'>configure_server</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_redis'>redis</span> <span class='op'>=</span> <span class='lbrace'>{</span> <span class='label'>url:</span> <span class='const'>ENV</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>REDIS_URL</span><span class='tstring_end'>"</span></span><span class='rbracket'>]</span><span class='comma'>,</span> <span class='label'>driver:</span> <span class='symbol'>:hiredis</span> <span class='rbrace'>}</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_client_middleware'>client_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Client.html" title="SidekiqUniqueJobs::Middleware::Client (class)">Client</a></span></span>
<span class='kw'>end</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_server_middleware'>server_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Server.html" title="SidekiqUniqueJobs::Middleware::Server (class)">Server</a></span></span>
<span class='kw'>end</span>
<span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Server.html" title="SidekiqUniqueJobs::Server (class)">Server</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure'><span class='object_link'><a href="SidekiqUniqueJobs/Server.html#configure-class_method" title="SidekiqUniqueJobs::Server.configure (method)">configure</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_config'>config</span><span class='rparen'>)</span>
<span class='kw'>end</span>
<span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure_client'>configure_client</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_redis'>redis</span> <span class='op'>=</span> <span class='lbrace'>{</span> <span class='label'>url:</span> <span class='const'>ENV</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>REDIS_URL</span><span class='tstring_end'>"</span></span><span class='rbracket'>]</span><span class='comma'>,</span> <span class='label'>driver:</span> <span class='symbol'>:hiredis</span> <span class='rbrace'>}</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_client_middleware'>client_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Client.html" title="SidekiqUniqueJobs::Middleware::Client (class)">Client</a></span></span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<h3 id="your-first-worker">Your first worker</h3>
<p>The most likely to be used worker is <code>:until_executed</code>. This type of lock creates a lock from when <code>UntilExecutedWorker.perform_async</code> is called until right after <code>UntilExecutedWorker.new.perform</code> has been called.</p>
<pre class="code ruby"><code class="ruby"><span class='comment'># frozen_string_literal: true
</span>
<span class='kw'>class</span> <span class='const'>UntilExecutedWorker</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Sidekiq/Worker.html" title="Sidekiq::Worker (module)">Worker</a></span></span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>queue:</span> <span class='symbol'>:until_executed</span><span class='comma'>,</span>
<span class='label'>lock:</span> <span class='symbol'>:until_executed</span>
<span class='kw'>def</span> <span class='id identifier rubyid_perform'>perform</span>
<span class='id identifier rubyid_logger'>logger</span><span class='period'>.</span><span class='id identifier rubyid_info'>info</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>cowboy</span><span class='tstring_end'>"</span></span><span class='rparen'>)</span>
<span class='id identifier rubyid_sleep'>sleep</span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span> <span class='comment'># hardcore processing
</span> <span class='id identifier rubyid_logger'>logger</span><span class='period'>.</span><span class='id identifier rubyid_info'>info</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>beebop</span><span class='tstring_end'>"</span></span><span class='rparen'>)</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<p>You can read more about the worker configuration in <a href="#worker-configuration">Worker Configuration</a> below.</p>
<h2 id="requirements">Requirements</h2>
<ul>
<li>Sidekiq <code>>= 5.0</code> (<code>>= 5.2</code> recommended)</li>
<li>Ruby:
<ul>
<li>MRI <code>>= 2.5</code> (<code>>= 2.6</code> recommended)</li>
<li>JRuby <code>>= 9.0</code> (<code>>= 9.2</code> recommended)</li>
<li>Truffleruby</li>
</ul></li>
<li>Redis Server <code>>= 3.2</code> (<code>>= 5.0</code> recommended)</li>
<li>[ActiveJob officially not supported][48]</li>
<li>[redis-namespace officially not supported][49]</li>
</ul>
<p>See [Sidekiq requirements][24] for detailed requirements of Sidekiq itself (be sure to check the right sidekiq version).</p>
<h2 id="locks">Locks</h2>
<h3 id="until-executing">Until Executing</h3>
<p>A lock is created when <code>UntilExecuting.perform_async</code> is called. Then it is either unlocked when <code>lock_ttl</code> is hit or before Sidekiq calls the <code>perform</code> method on your worker.</p>
<h4 id="example-worker">Example worker</h4>
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>UntilExecuting</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Workers</span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock:</span> <span class='symbol'>:until_executing</span>
<span class='kw'>def</span> <span class='id identifier rubyid_perform'>perform</span><span class='lparen'>(</span><span class='id identifier rubyid_id'>id</span><span class='rparen'>)</span>
<span class='comment'># Do work
</span> <span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<p><strong>NOTE</strong> this is probably not so good for jobs that shouldn't be running simultaneously (aka slow jobs).</p>
<p>The reason this type of lock exists is to fix the following problem: <a href="https://github.com/mperham/sidekiq/issues/3471#issuecomment-300866335">sidekiq/issues/3471</a></p>
<h3 id="until-executed">Until Executed</h3>
<p>A lock is created when <code>UntilExecuted.perform_async</code> is called. Then it is either unlocked when <code>lock_ttl</code> is hit or when Sidekiq has called the <code>perform</code> method on your worker.</p>
<h4 id="example-worker">Example worker</h4>
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>UntilExecuted</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Workers</span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock:</span> <span class='symbol'>:until_executed</span>
<span class='kw'>def</span> <span class='id identifier rubyid_perform'>perform</span><span class='lparen'>(</span><span class='id identifier rubyid_id'>id</span><span class='rparen'>)</span>
<span class='comment'># Do work
</span> <span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<h3 id="until-expired">Until Expired</h3>
<p>This lock behaves identically to the <a href="#until-executed">Until Executed</a> except for one thing. This job won't be unlocked until the expiration is hit. For jobs that need to run only once per day, this would be the perfect lock. This way, we can't create more jobs until one day after this job was first pushed.</p>
<h4 id="example-worker">Example worker</h4>
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>UntilExpired</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Workers</span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock:</span> <span class='symbol'>:until_expired</span><span class='comma'>,</span> <span class='label'>lock_ttl:</span> <span class='int'>1</span><span class='period'>.</span><span class='id identifier rubyid_day'>day</span>
<span class='kw'>def</span> <span class='id identifier rubyid_perform'>perform</span>
<span class='comment'># Do work
</span> <span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<h3 id="until-and-while-executing">Until And While Executing</h3>
<p>This lock is a combination of two locks (<code>:until_executing</code> and <code>:while_executing</code>). Please see the configuration for <a href="#until-executing">Until Executing</a> and <a href="#while-executing">While Executing</a></p>
<h4 id="example-worker">Example worker</h4>
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>UntilAndWhileExecutingWorker</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Workers</span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock:</span> <span class='symbol'>:until_and_while_executing</span><span class='comma'>,</span>
<span class='label'>lock_timeout:</span> <span class='int'>2</span><span class='comma'>,</span>
<span class='label'>on_conflict:</span> <span class='lbrace'>{</span>
<span class='label'>client:</span> <span class='symbol'>:log</span><span class='comma'>,</span>
<span class='label'>server:</span> <span class='symbol'>:raise</span>
<span class='rbrace'>}</span>
<span class='kw'>def</span> <span class='id identifier rubyid_perform'>perform</span><span class='lparen'>(</span><span class='id identifier rubyid_id'>id</span><span class='rparen'>)</span>
<span class='comment'># Do work
</span> <span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<h3 id="while-executing">While Executing</h3>
<p>These locks are put on a queue without any type of locking mechanism, the locking doesn't happen until Sidekiq pops the job from the queue and starts processing it.</p>
<h4 id="example-worker">Example worker</h4>
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>WhileExecutingWorker</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Workers</span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock:</span> <span class='symbol'>:while_executing</span><span class='comma'>,</span>
<span class='label'>lock_timeout:</span> <span class='int'>2</span><span class='comma'>,</span>
<span class='label'>on_conflict:</span> <span class='lbrace'>{</span>
<span class='label'>server:</span> <span class='symbol'>:raise</span>
<span class='rbrace'>}</span>
<span class='kw'>def</span> <span class='id identifier rubyid_perform'>perform</span><span class='lparen'>(</span><span class='id identifier rubyid_id'>id</span><span class='rparen'>)</span>
<span class='comment'># Do work
</span> <span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<p><strong>NOTE</strong> Unless a conflict strategy of <code>:raise</code> is specified, if lock fails, the job will be dropped without notice. When told to raise, the job will be put back and retried. It would also be possible to use <code>:reschedule</code> with this lock.</p>
<p><strong>NOTE</strong> Unless this job is configured with a <code>lock_timeout: nil</code> or <code>lock_timeout: > 0</code> then all jobs that are attempted to be executed will just be dropped without waiting.</p>
<p>There is an example of this to try it out in the <code>myapp</code> application. Run <code>foreman start</code> in the root of the directory and open the url: <code>localhost:5000/work/duplicate_while_executing</code>.</p>
<p>In the console you should see something like:</p>
<pre class="code bash"><code class="bash">0:32:24 worker.1 | 2017-04-23T08:32:24.955Z 84404 TID-ougq4thko WhileExecutingWorker JID-400ec51c9523f41cd4a35058 INFO: start
10:32:24 worker.1 | 2017-04-23T08:32:24.956Z 84404 TID-ougq8csew WhileExecutingWorker JID-8d6d9168368eedaed7f75763 INFO: start
10:32:24 worker.1 | 2017-04-23T08:32:24.957Z 84404 TID-ougq8crt8 WhileExecutingWorker JID-affcd079094c9b26e8b9ba60 INFO: start
10:32:24 worker.1 | 2017-04-23T08:32:24.959Z 84404 TID-ougq8cs8s WhileExecutingWorker JID-9e197460c067b22eb1b5d07f INFO: start
10:32:24 worker.1 | 2017-04-23T08:32:24.959Z 84404 TID-ougq4thko WhileExecutingWorker JID-400ec51c9523f41cd4a35058 WhileExecutingWorker INFO: perform(1, 2)
10:32:34 worker.1 | 2017-04-23T08:32:34.964Z 84404 TID-ougq4thko WhileExecutingWorker JID-400ec51c9523f41cd4a35058 INFO: done: 10.009 sec
10:32:34 worker.1 | 2017-04-23T08:32:34.965Z 84404 TID-ougq8csew WhileExecutingWorker JID-8d6d9168368eedaed7f75763 WhileExecutingWorker INFO: perform(1, 2)
10:32:44 worker.1 | 2017-04-23T08:32:44.965Z 84404 TID-ougq8crt8 WhileExecutingWorker JID-affcd079094c9b26e8b9ba60 WhileExecutingWorker INFO: perform(1, 2)
10:32:44 worker.1 | 2017-04-23T08:32:44.965Z 84404 TID-ougq8csew WhileExecutingWorker JID-8d6d9168368eedaed7f75763 INFO: done: 20.009 sec
10:32:54 worker.1 | 2017-04-23T08:32:54.970Z 84404 TID-ougq8cs8s WhileExecutingWorker JID-9e197460c067b22eb1b5d07f WhileExecutingWorker INFO: perform(1, 2)
10:32:54 worker.1 | 2017-04-23T08:32:54.969Z 84404 TID-ougq8crt8 WhileExecutingWorker JID-affcd079094c9b26e8b9ba60 INFO: done: 30.012 sec
10:33:04 worker.1 | 2017-04-23T08:33:04.973Z 84404 TID-ougq8cs8s WhileExecutingWorker JID-9e197460c067b22eb1b5d07f INFO: done: 40.014 sec
</code></pre>
<h3 id="custom-locks">Custom Locks</h3>
<p>You may need to define some custom lock. You can define it in one project folder:</p>
<pre class="code ruby"><code class="ruby"><span class='comment'># lib/locks/my_custom_lock.rb
</span><span class='kw'>module</span> <span class='const'>Locks</span>
<span class='kw'>class</span> <span class='const'>MyCustomLock</span> <span class='op'><</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Lock.html" title="SidekiqUniqueJobs::Lock (class)">Lock</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Lock/BaseLock.html" title="SidekiqUniqueJobs::Lock::BaseLock (class)">BaseLock</a></span></span>
<span class='kw'>def</span> <span class='id identifier rubyid_execute'>execute</span>
<span class='comment'># Do something ...
</span> <span class='kw'>end</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<p>You can refer on all the locks defined in <code>lib/sidekiq_unique_jobs/lock/*.rb</code>.</p>
<p>In order to make it available, you should call in your project startup:</p>
<p>(For rails application config/initializers/sidekiq_unique_jobs.rb or other projects, wherever you prefer)</p>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure'><span class='object_link'><a href="SidekiqUniqueJobs.html#configure-class_method" title="SidekiqUniqueJobs.configure (method)">configure</a></span></span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_add_lock'>add_lock</span> <span class='symbol'>:my_custom_lock</span><span class='comma'>,</span> <span class='const'>Locks</span><span class='op'>::</span><span class='const'>MyCustomLock</span>
<span class='kw'>end</span>
</code></pre>
<p>And then you can use it in the jobs definition:</p>
<p><code>sidekiq_options lock: :my_custom_lock, on_conflict: :log</code></p>
<p>Please not that if you try to override a default lock, an <code>ArgumentError</code> will be raised.</p>
<h2 id="conflict-strategy">Conflict Strategy</h2>
<p>Decides how we handle conflict. We can either reject the job to the dead queue or reschedule it. Both are useful for jobs that absolutely need to run and have been configured to use the lock <code>WhileExecuting</code> that is used only by the sidekiq server process.</p>
<p>The last one is log which can be be used with the lock <code>UntilExecuted</code> and <code>UntilExpired</code>. Now we write a log entry saying the job could not be pushed because it is a duplicate of another job with the same arguments.</p>
<p>It is possible for locks to have different conflict strategy for the client and server. This is useful for <code>:until_and_while_executing</code>.</p>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock:</span> <span class='symbol'>:until_and_while_executing</span><span class='comma'>,</span>
<span class='label'>on_conflict:</span> <span class='lbrace'>{</span> <span class='label'>client:</span> <span class='symbol'>:log</span><span class='comma'>,</span> <span class='label'>server:</span> <span class='symbol'>:reject</span> <span class='rbrace'>}</span>
</code></pre>
<h3 id="log">log</h3>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>on_conflict:</span> <span class='symbol'>:log</span>
</code></pre>
<p>This strategy is intended to be used with <code>UntilExecuted</code> and <code>UntilExpired</code>. It will log a line about that this is job is a duplicate of another.</p>
<h3 id="raise">raise</h3>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>on_conflict:</span> <span class='symbol'>:raise</span>
</code></pre>
<p>This strategy is intended to be used with <code>WhileExecuting</code>. Basically it will allow us to let the server process crash with a specific error message and be retried without messing up the Sidekiq stats.</p>
<h3 id="reject">reject</h3>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>on_conflict:</span> <span class='symbol'>:reject</span>
</code></pre>
<p>This strategy is intended to be used with <code>WhileExecuting</code> and will push the job to the dead queue on conflict.</p>
<h3 id="replace">replace</h3>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>on_conflict:</span> <span class='symbol'>:replace</span>
</code></pre>
<p>This strategy is intended to be used with client locks like <code>UntilExecuted</code>.
It will delete any existing job for these arguments from retry, schedule and
queue and retry the lock again.</p>
<p>This is slightly dangerous and should probably only be used for jobs that are
always scheduled in the future. Currently only attempting to retry one time.</p>
<h3 id="reschedule">Reschedule</h3>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>on_conflict:</span> <span class='symbol'>:reschedule</span>
</code></pre>
<p>This strategy is intended to be used with <code>WhileExecuting</code> and will delay the job to be tried again in 5 seconds. This will mess up the sidekiq stats but will prevent exceptions from being logged and confuse your sysadmins.</p>
<h3 id="custom-strategies">Custom Strategies</h3>
<p>You may need to define some custom strategy. You can define it in one project folder:</p>
<pre class="code ruby"><code class="ruby"><span class='comment'># lib/strategies/my_custom_strategy.rb
</span><span class='kw'>module</span> <span class='const'>Strategies</span>
<span class='kw'>class</span> <span class='const'>MyCustomStrategy</span> <span class='op'><</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/OnConflict.html" title="SidekiqUniqueJobs::OnConflict (module)">OnConflict</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/OnConflict/Strategy.html" title="SidekiqUniqueJobs::OnConflict::Strategy (class)">Strategy</a></span></span>
<span class='kw'>def</span> <span class='id identifier rubyid_call'>call</span>
<span class='comment'># Do something ...
</span> <span class='kw'>end</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<p>You can refer to all the strategies defined in <code>lib/sidekiq_unique_jobs/on_conflict</code>.</p>
<p>In order to make it available, you should call in your project startup:</p>
<p>(For rails application config/initializers/sidekiq_unique_jobs.rb for other projects, wherever you prefer)</p>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure'><span class='object_link'><a href="SidekiqUniqueJobs.html#configure-class_method" title="SidekiqUniqueJobs.configure (method)">configure</a></span></span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_add_strategy'>add_strategy</span> <span class='symbol'>:my_custom_strategy</span><span class='comma'>,</span> <span class='const'>Strategies</span><span class='op'>::</span><span class='const'>MyCustomStrategy</span>
<span class='kw'>end</span>
</code></pre>
<p>And then you can use it in the jobs definition:</p>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock:</span> <span class='symbol'>:while_executing</span><span class='comma'>,</span> <span class='label'>on_conflict:</span> <span class='symbol'>:my_custom_strategy</span>
</code></pre>
<p>Please not that if you try to override a default lock, an <code>ArgumentError</code> will be raised.</p>
<h3 id="3-cleanup-dead-locks">3 Cleanup Dead Locks</h3>
<p>For sidekiq versions < 5.1 a <code>sidekiq_retries_exhausted</code> block is required per worker class. This is deprecated in Sidekiq 6.0</p>
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>MyWorker</span>
<span class='id identifier rubyid_sidekiq_retries_exhausted'>sidekiq_retries_exhausted</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_msg'>msg</span><span class='comma'>,</span> <span class='id identifier rubyid__ex'>_ex</span><span class='op'>|</span>
<span class='id identifier rubyid_digest'>digest</span> <span class='op'>=</span> <span class='id identifier rubyid_msg'>msg</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>lock_digest</span><span class='tstring_end'>'</span></span><span class='rbracket'>]</span>
<span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Digests.html" title="SidekiqUniqueJobs::Digests (class)">Digests</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="SidekiqUniqueJobs/Digests.html#initialize-instance_method" title="SidekiqUniqueJobs::Digests#initialize (method)">new</a></span></span><span class='period'>.</span><span class='id identifier rubyid_delete_by_digest'><span class='object_link'><a href="SidekiqUniqueJobs/Digests.html#delete_by_digest-instance_method" title="SidekiqUniqueJobs::Digests#delete_by_digest (method)">delete_by_digest</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_digest'>digest</span><span class='rparen'>)</span> <span class='kw'>if</span> <span class='id identifier rubyid_digest'>digest</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<p>Starting in v5.1, Sidekiq can also fire a global callback when a job dies: In version 7, this is handled automatically for you. You don't need to add a death handler, if you configure v7 like in <a href="#add-the-middleware">Add the middleware</a> you don't have to worry about the below.</p>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure_server'>configure_server</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_death_handlers'>death_handlers</span> <span class='op'><<</span> <span class='tlambda'>-></span><span class='lparen'>(</span><span class='id identifier rubyid_job'>job</span><span class='comma'>,</span> <span class='id identifier rubyid__ex'>_ex</span><span class='rparen'>)</span> <span class='kw'>do</span>
<span class='id identifier rubyid_digest'>digest</span> <span class='op'>=</span> <span class='id identifier rubyid_job'>job</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>lock_digest</span><span class='tstring_end'>'</span></span><span class='rbracket'>]</span>
<span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Digests.html" title="SidekiqUniqueJobs::Digests (class)">Digests</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="SidekiqUniqueJobs/Digests.html#initialize-instance_method" title="SidekiqUniqueJobs::Digests#initialize (method)">new</a></span></span><span class='period'>.</span><span class='id identifier rubyid_delete_by_digest'><span class='object_link'><a href="SidekiqUniqueJobs/Digests.html#delete_by_digest-instance_method" title="SidekiqUniqueJobs::Digests#delete_by_digest (method)">delete_by_digest</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_digest'>digest</span><span class='rparen'>)</span> <span class='kw'>if</span> <span class='id identifier rubyid_digest'>digest</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<h2 id="debugging">Debugging</h2>
<p>There are several ways of removing keys that are stuck. The prefered way is by using the unique extension to <code>Sidekiq::Web</code>. The old console and command line versions still work but might be deprecated in the future. It is better to search for the digest itself and delete the keys matching that digest.</p>
<h3 id="sidekiq-web">Sidekiq Web</h3>
<p>To use the web extension you need to require it in your routes.</p>
<pre class="code ruby"><code class="ruby"><span class='comment'>#app/config/routes.rb
</span><span class='id identifier rubyid_require'>require</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>sidekiq_unique_jobs/web</span><span class='tstring_end'>'</span></span>
<span class='id identifier rubyid_mount'>mount</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Web</span><span class='comma'>,</span> <span class='label'>at:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>/sidekiq</span><span class='tstring_end'>'</span></span>
</code></pre>
<p>There is no need to <code>require 'sidekiq/web'</code> since <code>sidekiq_unique_jobs/web</code>
already does this.</p>
<p>To filter/search for keys we can use the wildcard <code>*</code>. If we have a unique digest <code>'uniquejobs:9e9b5ce5d423d3ea470977004b50ff84</code> we can search for it by enter <code>*ff84</code> and it should return all digests that end with <code>ff84</code>.</p>
<h3 id="reflections-metrics-logging-etc">Reflections (metrics, logging, etc.)</h3>
<p>To be able to gather some insights on what is going on inside this gem. I provide a reflection API that can be used.</p>
<p>To setup reflections for logging or metrics, use the following API:</p>
<pre class="code ruby"><code class="ruby">
<span class='kw'>def</span> <span class='id identifier rubyid_extract_log_from_job'>extract_log_from_job</span><span class='lparen'>(</span><span class='id identifier rubyid_message'>message</span><span class='comma'>,</span> <span class='id identifier rubyid_job_hash'>job_hash</span><span class='rparen'>)</span>
<span class='id identifier rubyid_worker'>worker</span> <span class='op'>=</span> <span class='id identifier rubyid_job_hash'>job_hash</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>class</span><span class='tstring_end'>'</span></span><span class='rbracket'>]</span>
<span class='id identifier rubyid_args'>args</span> <span class='op'>=</span> <span class='id identifier rubyid_job_hash'>job_hash</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>args</span><span class='tstring_end'>'</span></span><span class='rbracket'>]</span>
<span class='id identifier rubyid_lock_args'>lock_args</span> <span class='op'>=</span> <span class='id identifier rubyid_job_hash'>job_hash</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>lock_args</span><span class='tstring_end'>'</span></span><span class='rbracket'>]</span>
<span class='id identifier rubyid_queue'>queue</span> <span class='op'>=</span> <span class='id identifier rubyid_job_hash'>job_hash</span><span class='lbracket'>[</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>queue</span><span class='tstring_end'>'</span></span><span class='rbracket'>]</span>
<span class='lbrace'>{</span>
<span class='label'>message:</span> <span class='id identifier rubyid_message'>message</span><span class='comma'>,</span>
<span class='label'>worker:</span> <span class='id identifier rubyid_worker'>worker</span><span class='comma'>,</span>
<span class='label'>args:</span> <span class='id identifier rubyid_args'>args</span><span class='comma'>,</span>
<span class='label'>lock_args:</span> <span class='id identifier rubyid_lock_args'>lock_args</span><span class='comma'>,</span>
<span class='label'>queue:</span> <span class='id identifier rubyid_queue'>queue</span>
<span class='rbrace'>}</span>
<span class='kw'>end</span>
<span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_reflect'><span class='object_link'><a href="SidekiqUniqueJobs.html#reflect-class_method" title="SidekiqUniqueJobs.reflect (method)">reflect</a></span></span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_on'>on</span><span class='op'>|</span>
<span class='id identifier rubyid_on'>on</span><span class='period'>.</span><span class='id identifier rubyid_lock_failed'>lock_failed</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_job_hash'>job_hash</span><span class='op'>|</span>
<span class='id identifier rubyid_message'>message</span> <span class='op'>=</span> <span class='id identifier rubyid_extract_log_from_job'>extract_log_from_job</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Lock Failed</span><span class='tstring_end'>'</span></span><span class='comma'>,</span> <span class='id identifier rubyid_job_hash'>job_hash</span><span class='rparen'>)</span>
<span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_logger'>logger</span><span class='period'>.</span><span class='id identifier rubyid_warn'>warn</span><span class='lparen'>(</span><span class='id identifier rubyid_message'>message</span><span class='rparen'>)</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<h4 id="after_unlock_callback_failed">after_unlock_callback_failed</h4>
<p>This is called when you have configured a custom callback for when a lock has been released.</p>
<h4 id="error">error</h4>
<p>Not in use yet but will be used deep into the stack to provide a means to catch and report errors inside the gem.</p>
<h4 id="execution_failed">execution_failed</h4>
<p>When the sidekiq processor picks the job of the queue for certain jobs but your job raised an error to the middleware. This will be the reflection. It is probably nothing to worry about. When your worker raises an error, we need to handle some edge cases for until and while executing.</p>
<h4 id="lock_failed">lock_failed</h4>
<p>If we can't achieve a lock, this will be the reflection. It most likely is nothing to worry about. We just couldn't retrieve a lock in a timely fashion.</p>
<p>The biggest reason for this reflection would be to gather metrics on which workers fail the most at the locking step for example.</p>
<h4 id="locked">locked</h4>
<p>For when a lock has been successful. Again, mostly useful for metrics I suppose.</p>
<h4 id="reschedule_failed">reschedule_failed</h4>
<p>For when the reschedule strategy failed to reschedule the job.</p>
<h4 id="rescheduled">rescheduled</h4>
<p>For when a job was successfully rescheduled</p>
<h4 id="timeout">timeout</h4>
<p>This is also mostly useful for reporting/metrics purposes. What this reflection does is signal that the job was configured to wait (<code>lock_timeout</code> was configured), but we couldn't retrieve a lock even though we waited for some time.</p>
<h3 id="unlock_failed">unlock_failed</h3>
<p>This is not got, this is worth</p>
<h3 id="unlocked">unlocked</h3>
<p>Also mostly useful for reporting purposes. The job was successfully unlocked.</p>
<h3 id="unknown_sidekiq_worker">unknown_sidekiq_worker</h3>
<p>The reason this happens is that the server couldn't find a valid sidekiq worker class. Most likely, that worker isn't intended to be processed by this sidekiq server instance.</p>
<h4 id="show-locks">Show Locks</h4>
<p><img src="assets/unique_digests_1.png" alt="Locks"></p>
<h4 id="show-lock">Show Lock</h4>
<p><img src="assets/unique_digests_2.png" alt="Lock"></p>
<h2 id="testing">Testing</h2>
<h3 id="validating-worker-configuration">Validating Worker Configuration</h3>
<p>Since v7 it is possible to perform some simple validation against your workers sidekiq_options. What it does is scan for some issues that are known to cause problems in production.</p>
<p>Let's take a <em>bad</em> worker:</p>
<pre class="code ruby"><code class="ruby"><span class='comment'>#app/workers/bad_worker.rb
</span><span class='kw'>class</span> <span class='const'>BadWorker</span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock:</span> <span class='symbol'>:while_executing</span><span class='comma'>,</span> <span class='label'>on_conflict:</span> <span class='symbol'>:replace</span>
<span class='kw'>end</span>
<span class='comment'>#spec/workers/bad_worker_spec.rb
</span>
<span class='id identifier rubyid_require'>require</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>sidekiq_unique_jobs/testing</span><span class='tstring_end'>"</span></span>
<span class='comment'>#OR
</span><span class='id identifier rubyid_require'>require</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>sidekiq_unique_jobs/rspec/matchers</span><span class='tstring_end'>"</span></span>
<span class='const'>RSpec</span><span class='period'>.</span><span class='id identifier rubyid_describe'>describe</span> <span class='const'>BadWorker</span> <span class='kw'>do</span>
<span class='id identifier rubyid_specify'>specify</span> <span class='lbrace'>{</span> <span class='id identifier rubyid_expect'>expect</span><span class='lparen'>(</span><span class='id identifier rubyid_described_class'>described_class</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_to'>to</span> <span class='id identifier rubyid_have_valid_sidekiq_options'>have_valid_sidekiq_options</span> <span class='rbrace'>}</span>
<span class='kw'>end</span>
</code></pre>
<p>This gives us a helpful error message for a wrongly configured worker:</p>
<pre class="code bash"><code class="bash">Expected BadWorker to have valid sidekiq options but found the following problems:
on_server_conflict: :replace is incompatible with the server process
</code></pre>
<p>If you are not using RSpec (a lot of people prefer minitest or test unit) you can do something like:</p>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_assert'>assert</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_validate_worker!'><span class='object_link'><a href="SidekiqUniqueJobs.html#validate_worker!-class_method" title="SidekiqUniqueJobs.validate_worker! (method)">validate_worker!</a></span></span><span class='lparen'>(</span><span class='const'>BadWorker</span><span class='period'>.</span><span class='id identifier rubyid_get_sidekiq_options'>get_sidekiq_options</span><span class='rparen'>)</span>
</code></pre>
<h3 id="uniqueness">Uniqueness</h3>
<p>This has been probably the most confusing part of this gem. People get really confused with how unreliable the unique jobs have been. I there for decided to do what Mike is doing for sidekiq enterprise. Read the section about unique jobs: <a href="https://www.dailydrip.com/topics/sidekiq/drips/sidekiq-enterprise-unique-jobs">Enterprise unique jobs</a></p>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure'><span class='object_link'><a href="SidekiqUniqueJobs.html#configure-class_method" title="SidekiqUniqueJobs.configure (method)">configure</a></span></span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_enabled'>enabled</span> <span class='op'>=</span> <span class='op'>!</span><span class='const'>Rails</span><span class='period'>.</span><span class='id identifier rubyid_env'>env</span><span class='period'>.</span><span class='id identifier rubyid_test?'>test?</span>
<span class='kw'>end</span>
</code></pre>
<p>If you truly wanted to test the sidekiq client push you could do something like below. Note that it will only work for the jobs that lock when the client pushes the job to redis (UntilExecuted, UntilAndWhileExecuting and UntilExpired).</p>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_require'>require</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>sidekiq_unique_jobs/testing</span><span class='tstring_end'>"</span></span>
<span class='const'>RSpec</span><span class='period'>.</span><span class='id identifier rubyid_describe'>describe</span> <span class='const'>Workers</span><span class='op'>::</span><span class='const'>CoolOne</span> <span class='kw'>do</span>
<span class='id identifier rubyid_before'>before</span> <span class='kw'>do</span>
<span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_enabled'>enabled</span> <span class='op'>=</span> <span class='kw'>false</span>
<span class='kw'>end</span>
<span class='comment'># ... your tests that don't test uniqueness
</span>
<span class='id identifier rubyid_context'>context</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>when Sidekiq::Testing.disabled?</span><span class='tstring_end'>'</span></span> <span class='kw'>do</span>
<span class='id identifier rubyid_before'>before</span> <span class='kw'>do</span>
<span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Testing</span><span class='period'>.</span><span class='id identifier rubyid_disable!'>disable!</span>
<span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_redis'>redis</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:flushdb</span><span class='rparen'>)</span>
<span class='kw'>end</span>
<span class='id identifier rubyid_after'>after</span> <span class='kw'>do</span>
<span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_redis'>redis</span><span class='lparen'>(</span><span class='op'>&</span><span class='symbol'>:flushdb</span><span class='rparen'>)</span>
<span class='kw'>end</span>
<span class='id identifier rubyid_it'>it</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>prevents duplicate jobs from being scheduled</span><span class='tstring_end'>'</span></span> <span class='kw'>do</span>
<span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_use_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#use_config-class_method" title="SidekiqUniqueJobs.use_config (method)">use_config</a></span></span><span class='lparen'>(</span><span class='label'>enabled:</span> <span class='kw'>true</span><span class='rparen'>)</span> <span class='kw'>do</span>
<span class='id identifier rubyid_expect'>expect</span><span class='lparen'>(</span><span class='id identifier rubyid_described_class'>described_class</span><span class='period'>.</span><span class='id identifier rubyid_perform_in'>perform_in</span><span class='lparen'>(</span><span class='int'>3600</span><span class='comma'>,</span> <span class='int'>1</span><span class='rparen'>)</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_not_to'>not_to</span> <span class='id identifier rubyid_eq'>eq</span><span class='lparen'>(</span><span class='kw'>nil</span><span class='rparen'>)</span>
<span class='id identifier rubyid_expect'>expect</span><span class='lparen'>(</span><span class='id identifier rubyid_described_class'>described_class</span><span class='period'>.</span><span class='id identifier rubyid_perform_async'>perform_async</span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_to'>to</span> <span class='id identifier rubyid_eq'>eq</span><span class='lparen'>(</span><span class='kw'>nil</span><span class='rparen'>)</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<p>It is recommended to leave the uniqueness testing to the gem maintainers. If you care about how the gem is integration tested have a look at the following specs:</p>
<ul>
<li><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/spec/sidekiq_unique_jobs/lock/until_and_while_executing_spec.rb">spec/sidekiq_unique_jobs/lock/until_and_while_executing_spec.rb</a></li>
<li><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/spec/sidekiq_unique_jobs/lock/until_executed_spec.rb">spec/sidekiq_unique_jobs/lock/until_executed_spec.rb</a></li>
<li><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/spec/sidekiq_unique_jobs/lock/until_expired_spec.rb">spec/sidekiq_unique_jobs/lock/until_expired_spec.rb</a></li>
<li><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/spec/sidekiq_unique_jobs/lock/while_executing_reject_spec.rb">spec/sidekiq_unique_jobs/lock/while_executing_reject_spec.rb</a></li>
<li><a href="https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/spec/sidekiq_unique_jobs/lock/while_executing_spec.rb">spec/sidekiq_unique_jobs/lock/while_executing_spec.rb</a></li>
</ul>
<h2 id="configuration">Configuration</h2>
<h3 id="other-sidekiq-gems">Other Sidekiq gems</h3>
<h4 id="apartment-sidekiq">apartment-sidekiq</h4>
<p>It was reported in <a href="https://github.com/mhenrixon/sidekiq-unique-jobs/issues/536">#536</a> that the order of the Sidekiq middleware needs to be as follows.</p>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_client_middleware'>client_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'>Apartment</span><span class='op'>::</span><span class='const'>Sidekiq</span><span class='op'>::</span><span class='const'>Middleware</span><span class='op'>::</span><span class='const'>Client</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Client.html" title="SidekiqUniqueJobs::Middleware::Client (class)">Client</a></span></span>
<span class='kw'>end</span>
<span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_server_middleware'>server_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'>Apartment</span><span class='op'>::</span><span class='const'>Sidekiq</span><span class='op'>::</span><span class='const'>Middleware</span><span class='op'>::</span><span class='const'>Server</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Server.html" title="SidekiqUniqueJobs::Middleware::Server (class)">Server</a></span></span>
<span class='kw'>end</span>
</code></pre>
<p>The reason being that this gem needs to be configured AFTER the apartment gem or the apartment will not be able to be considered for uniqueness</p>
<h4 id="sidekiq-global_id">sidekiq-global_id</h4>
<p>It was reported in <a href="https://github.com/mhenrixon/sidekiq-unique-jobs/issues/235">#235</a> that the order of the Sidekiq middleware needs to be as follows.</p>
<p>For a working setup check the following <a href="https://github.com/mhenrixon/sidekiq-unique-jobs/blob/master/myapp/config/sidekiq.rb#L12">file</a>.</p>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_client_middleware'>client_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>GlobalId</span><span class='op'>::</span><span class='const'>ClientMiddleware</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Client.html" title="SidekiqUniqueJobs::Middleware::Client (class)">Client</a></span></span>
<span class='kw'>end</span>
<span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_server_middleware'>server_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>GlobalId</span><span class='op'>::</span><span class='const'>ServerMiddleware</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Server.html" title="SidekiqUniqueJobs::Middleware::Server (class)">Server</a></span></span>
<span class='kw'>end</span>
</code></pre>
<p>The reason for this is that the global id needs to be set before the unique jobs middleware runs. Otherwise that won't be available for uniqueness.</p>
<h4 id="sidekiq-status">sidekiq-status</h4>
<p>It was reported in <a href="https://github.com/mhenrixon/sidekiq-unique-jobs/issues/564">#564</a> that the order of the middleware needs to be as follows.</p>
<pre class="code ruby"><code class="ruby"><span class='comment'># Thanks to @ArturT for the correction
</span>
<span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure_server'>configure_server</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_client_middleware'>client_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Client.html" title="SidekiqUniqueJobs::Middleware::Client (class)">Client</a></span></span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Status</span><span class='op'>::</span><span class='const'>ClientMiddleware</span><span class='comma'>,</span> <span class='label'>expiration:</span> <span class='int'>30</span><span class='period'>.</span><span class='id identifier rubyid_minutes'>minutes</span>
<span class='kw'>end</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_server_middleware'>server_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Status</span><span class='op'>::</span><span class='const'>ServerMiddleware</span><span class='comma'>,</span> <span class='label'>expiration:</span> <span class='int'>30</span><span class='period'>.</span><span class='id identifier rubyid_minutes'>minutes</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Server.html" title="SidekiqUniqueJobs::Middleware::Server (class)">Server</a></span></span>
<span class='kw'>end</span>
<span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Server.html" title="SidekiqUniqueJobs::Server (class)">Server</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure'><span class='object_link'><a href="SidekiqUniqueJobs/Server.html#configure-class_method" title="SidekiqUniqueJobs::Server.configure (method)">configure</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_config'>config</span><span class='rparen'>)</span>
<span class='kw'>end</span>
<span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure_client'>configure_client</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_client_middleware'>client_middleware</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_chain'>chain</span><span class='op'>|</span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware.html" title="SidekiqUniqueJobs::Middleware (module)">Middleware</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs/Middleware/Client.html" title="SidekiqUniqueJobs::Middleware::Client (class)">Client</a></span></span>
<span class='id identifier rubyid_chain'>chain</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>Status</span><span class='op'>::</span><span class='const'>ClientMiddleware</span><span class='comma'>,</span> <span class='label'>expiration:</span> <span class='int'>30</span><span class='period'>.</span><span class='id identifier rubyid_minutes'>minutes</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<p>The reason for this is that if a job is duplicated it shouldn't end up with the status middleware at all. Status is just a monitor so to prevent clashes, leftovers and ensure cleanup. The status middleware should run after uniqueness on client and before on server. This will lead to less surprises.</p>
<h3 id="global-configuration">Global Configuration</h3>
<p>The gem supports a few different configuration options that might be of interest if you run into some weird issues.</p>
<p>Configure SidekiqUniqueJobs in an initializer or the sidekiq initializer on application startup.</p>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_configure'><span class='object_link'><a href="SidekiqUniqueJobs.html#configure-class_method" title="SidekiqUniqueJobs.configure (method)">configure</a></span></span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
<span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_logger'>logger</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='period'>.</span><span class='id identifier rubyid_logger'>logger</span> <span class='comment'># default, change at your own discretion
</span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_debug_lua'>debug_lua</span> <span class='op'>=</span> <span class='kw'>false</span> <span class='comment'># Turn on when debugging
</span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_lock_info'>lock_info</span> <span class='op'>=</span> <span class='kw'>false</span> <span class='comment'># Turn on when debugging
</span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_lock_ttl'>lock_ttl</span> <span class='op'>=</span> <span class='int'>600</span> <span class='comment'># Expire locks after 10 minutes
</span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_lock_timeout'>lock_timeout</span> <span class='op'>=</span> <span class='kw'>nil</span> <span class='comment'># turn off lock timeout
</span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_max_history'>max_history</span> <span class='op'>=</span> <span class='int'>0</span> <span class='comment'># Turn on when debugging
</span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_reaper'>reaper</span> <span class='op'>=</span> <span class='symbol'>:ruby</span> <span class='comment'># :ruby, :lua or :none/nil
</span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_reaper_count'>reaper_count</span> <span class='op'>=</span> <span class='int'>1000</span> <span class='comment'># Stop reaping after this many keys
</span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_reaper_interval'>reaper_interval</span> <span class='op'>=</span> <span class='int'>600</span> <span class='comment'># Reap orphans every 10 minutes
</span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_reaper_timeout'>reaper_timeout</span> <span class='op'>=</span> <span class='int'>150</span> <span class='comment'># Timeout reaper after 2.5 minutes
</span><span class='kw'>end</span>
</code></pre>
<h4 id="debug_lua">debug_lua</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_debug_lua'>debug_lua</span> <span class='comment'>#=> false
</span></code></pre>
<p>Turning on debug_lua will allow the lua scripts to output debug information about what the lua scripts do. It will log all redis commands that are executed and also some helpful messages about what is going on inside the lua script.</p>
<h4 id="lock_timeout">lock_timeout</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_lock_timeout'>lock_timeout</span> <span class='comment'>#=> 0
</span></code></pre>
<p>Set a global lock_timeout to use for all jobs that don't otherwise specify a lock_timeout.</p>
<p>Lock timeout decides how long to wait for acquiring the lock. A value of nil means to wait indefinitely for a lock resource to become available.</p>
<h4 id="lock_ttl">lock_ttl</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_lock_ttl'>lock_ttl</span> <span class='comment'>#=> nil
</span></code></pre>
<p>Set a global lock_ttl to use for all jobs that don't otherwise specify a lock_ttl.</p>
<p>Lock TTL decides how long to wait at most before considering a lock to be expired and making it possible to reuse that lock.</p>
<h4 id="enabled">enabled</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_enabled'>enabled</span> <span class='comment'>#=> true
</span></code></pre>
<p>Globally turn the locking mechanism on or off.</p>
<h4 id="logger">logger</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_logger'><span class='object_link'><a href="SidekiqUniqueJobs.html#logger-class_method" title="SidekiqUniqueJobs.logger (method)">logger</a></span></span> <span class='comment'>#=> #<Sidekiq::Logger:0x00007fdc1f96d180>
</span></code></pre>
<p>By default this gem piggybacks on the Sidekiq logger. It is not recommended to change this as the gem uses some features in the Sidekiq logger and you might run into problems. If you need a different logger and you do run into problems then get in touch and we'll see what we can do about it.</p>
<h4 id="max_history">max_history</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_max_history'>max_history</span> <span class='comment'>#=> 1_000
</span></code></pre>
<p>The max_history setting can be used to tweak the number of changelogs generated. It can also be completely turned off if performance suffers or if you are just not interested in using the changelog.</p>
<p>This is a log that can be accessed by a lock to see what happened for that lock. Any items after the configured <code>max_history</code> will be automatically deleted as new items are added.</p>
<h4 id="reaper">reaper</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_reaper'>reaper</span> <span class='comment'>#=> :ruby
</span></code></pre>
<p>If using the orphans cleanup process it is critical to be aware of the following. The <code>:ruby</code> job is much slower but the <code>:lua</code> job locks redis while executing. While doing intense processing it is best to avoid locking redis with a lua script. There for the batch size (controlled by the <code>reaper_count</code> setting) needs to be reduced.</p>
<p>In my benchmarks deleting 1000 orphaned locks with lua performs around 65% faster than deleting 1000 keys in ruby.</p>
<p>On the other hand if I increase it to 10 000 orphaned locks per cleanup (<code>reaper_count: 10_0000</code>) then redis starts throwing:</p>
<blockquote>
<p>BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. (RedisClient::CommandError)</p>
</blockquote>
<p>If you want to disable the reaper set it to <code>:none</code>, <code>nil</code> or <code>false</code>. Actually, any value that isn't <code>:ruby</code> or <code>:lua</code> will disable the reaping.</p>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_reaper'>reaper</span> <span class='op'>=</span> <span class='symbol'>:none</span>
<span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_reaper'>reaper</span> <span class='op'>=</span> <span class='kw'>nil</span>
<span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_reaper'>reaper</span> <span class='op'>=</span> <span class='kw'>false</span>
</code></pre>
<h4 id="reaper_count">reaper_count</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_reaper_count'>reaper_count</span> <span class='comment'>#=> 1_000
</span></code></pre>
<p>The reaper_count setting configures how many orphans at a time will be cleaned up by the orphan cleanup job. This might have to be tweaked depending on which orphan job is running.</p>
<h4 id="reaper_interval">reaper_interval</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_reaper_interval'>reaper_interval</span> <span class='comment'>#=> 600
</span></code></pre>
<p>The number of seconds between reaping.</p>
<h4 id="reaper_timeout">reaper_timeout</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_reaper_timeout'>reaper_timeout</span> <span class='comment'>#=> 10
</span></code></pre>
<p>The number of seconds to wait for the reaper to finish before raising a TimeoutError. This is done to ensure that the next time we reap isn't getting stuck due to the previous process already running.</p>
<h4 id="lock_prefix">lock_prefix</h4>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_lock_prefix'>lock_prefix</span> <span class='comment'>#=> "uniquejobs"
</span></code></pre>
<p>Use if you want a different key prefix for the keys in redis.</p>
<h3 id="lock_info">lock_info</h3>
<pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="SidekiqUniqueJobs.html" title="SidekiqUniqueJobs (module)">SidekiqUniqueJobs</a></span></span><span class='period'>.</span><span class='id identifier rubyid_config'><span class='object_link'><a href="SidekiqUniqueJobs.html#config-class_method" title="SidekiqUniqueJobs.config (method)">config</a></span></span><span class='period'>.</span><span class='id identifier rubyid_lock_info'>lock_info</span> <span class='comment'>#=> false
</span></code></pre>
<p>Using lock info will create an additional key for the lock with a json object containing information about the lock. This will be presented in the web interface and might help track down why some jobs are getting stuck.</p>
<h3 id="worker-configuration">Worker Configuration</h3>
<h4 id="lock_info">lock_info</h4>
<p>Lock info gathers information about a specific lock. It collects things like which <code>lock_args</code> where used to compute the <code>lock_digest</code> that is used for maintaining uniqueness.</p>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock_info:</span> <span class='kw'>false</span> <span class='comment'># this is the default, set to true to turn on
</span></code></pre>
<h4 id="lock_prefix">lock_prefix</h4>
<p>Use if you want a different key prefix for the keys in redis.</p>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock_prefix:</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>uniquejobs</span><span class='tstring_end'>"</span></span> <span class='comment'># this is the default value
</span></code></pre>
<h4 id="lock_ttl">lock_ttl</h4>
<p>Lock TTL decides how long to wait at most before considering a lock to be expired and making it possible to reuse that lock.</p>
<p>Starting from <code>v7</code> the expiration will take place when the job is pushed to the queue.</p>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock_ttl:</span> <span class='kw'>nil</span> <span class='comment'># default - don't expire keys
</span><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock_ttl:</span> <span class='int'>20</span><span class='period'>.</span><span class='id identifier rubyid_days'>days</span><span class='period'>.</span><span class='id identifier rubyid_to_i'>to_i</span> <span class='comment'># expire this lock in 20 days
</span></code></pre>
<h4 id="lock_timeout">lock_timeout</h4>
<p>This is the timeout (how long to wait) when creating the lock. By default we don't use a timeout so we won't wait for the lock to be created. If you want it is possible to set this like below.</p>
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock_timeout:</span> <span class='int'>0</span> <span class='comment'># default - don't wait at all
</span><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock_timeout:</span> <span class='int'>5</span> <span class='comment'># wait 5 seconds
</span><span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock_timeout:</span> <span class='kw'>nil</span> <span class='comment'># lock indefinitely, this process won't continue until it gets a lock. VERY DANGEROUS!!
</span></code></pre>
<h4 id="unique_across_queues">unique_across_queues</h4>
<p>This configuration option is slightly misleading. It doesn't disregard the queue on other jobs. Just on itself, this means that a worker that might schedule jobs into multiple queues will be able to have uniqueness enforced on all queues it is pushed to.</p>
<p>This is mainly intended for <code>Worker.set(queue: :another).perform_async</code>.</p>
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>Worker</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Sidekiq/Worker.html" title="Sidekiq::Worker (module)">Worker</a></span></span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>unique_across_queues:</span> <span class='kw'>true</span><span class='comma'>,</span> <span class='label'>queue:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>default</span><span class='tstring_end'>'</span></span>
<span class='kw'>def</span> <span class='id identifier rubyid_perform'>perform</span><span class='lparen'>(</span><span class='id identifier rubyid_args'>args</span><span class='rparen'>)</span><span class='semicolon'>;</span> <span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<p>Now if you push override the queue with <code>Worker.set(queue: 'another').perform_async(1)</code> it will still be considered unique when compared to <code>Worker.perform_async(1)</code> (that was actually pushed to the queue <code>default</code>).</p>
<h4 id="unique_across_workers">unique_across_workers</h4>
<p>This configuration option is slightly misleading. It doesn't disregard the worker class on other jobs. Just on itself, this means that the worker class won't be used for generating the unique digest. The only way this option really makes sense is when you want to have uniqueness between two different worker classes.</p>
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>WorkerOne</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Sidekiq/Worker.html" title="Sidekiq::Worker (module)">Worker</a></span></span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>unique_across_workers:</span> <span class='kw'>true</span><span class='comma'>,</span> <span class='label'>queue:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>default</span><span class='tstring_end'>'</span></span>
<span class='kw'>def</span> <span class='id identifier rubyid_perform'>perform</span><span class='lparen'>(</span><span class='id identifier rubyid_args'>args</span><span class='rparen'>)</span><span class='semicolon'>;</span> <span class='kw'>end</span>
<span class='kw'>end</span>
<span class='kw'>class</span> <span class='const'>WorkerTwo</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Sidekiq/Worker.html" title="Sidekiq::Worker (module)">Worker</a></span></span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>unique_across_workers:</span> <span class='kw'>true</span><span class='comma'>,</span> <span class='label'>queue:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>default</span><span class='tstring_end'>'</span></span>
<span class='kw'>def</span> <span class='id identifier rubyid_perform'>perform</span><span class='lparen'>(</span><span class='id identifier rubyid_args'>args</span><span class='rparen'>)</span><span class='semicolon'>;</span> <span class='kw'>end</span>
<span class='kw'>end</span>
<span class='const'>WorkerOne</span><span class='period'>.</span><span class='id identifier rubyid_perform_async'>perform_async</span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span>
<span class='comment'># => 'the jobs unique id'
</span>
<span class='const'>WorkerTwo</span><span class='period'>.</span><span class='id identifier rubyid_perform_async'>perform_async</span><span class='lparen'>(</span><span class='int'>1</span><span class='rparen'>)</span>
<span class='comment'># => nil because WorkerOne just stole the lock
</span></code></pre>
<h3 id="finer-control-over-uniqueness">Finer Control over Uniqueness</h3>
<p>Sometimes it is desired to have a finer control over which arguments are used in determining uniqueness of the job, and others may be <em>transient</em>. For this use-case, you need to define either a <code>lock_args</code> method, or a ruby proc.</p>
<p><em>NOTE:</em> The lock_args method need to return an array of values to use for uniqueness check.</p>
<p><em>NOTE:</em> The arguments passed to the proc or the method is always an array. If your method takes a single array as argument the value of args will be <code>[[...]]</code>.</p>
<p>The method or the proc can return a modified version of args without the transient arguments included, as shown below:</p>
<pre class="code ruby"><code class="ruby">class UniqueJobWithFilterMethod
include Sidekiq::Worker
sidekiq_options lock: :until_and_while_executing,
lock_args_method: :lock_args # this is default and will be used if such a method is defined
def self.lock_args(args)
[ args[0], args[2][:type] ]
end
...
end
class UniqueJobWithFilterProc
include Sidekiq::Worker
sidekiq_options lock: :until_executed,
lock_args_method: ->(args) { [ args.first ] }
...
end
</code></pre>
<p>It is possible to ensure different types of unique args based on context. I can't vouch for the below example but see <a href="https://github.com/mhenrixon/sidekiq-unique-jobs/issues/203">#203</a> for the discussion.</p>
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>UniqueJobWithFilterMethod</span>
<span class='id identifier rubyid_include'>include</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Sidekiq/Worker.html" title="Sidekiq::Worker (module)">Worker</a></span></span>
<span class='id identifier rubyid_sidekiq_options'>sidekiq_options</span> <span class='label'>lock:</span> <span class='symbol'>:until_and_while_executing</span><span class='comma'>,</span> <span class='label'>lock_args_method:</span> <span class='symbol'>:lock_args</span>
<span class='kw'>def</span> <span class='kw'>self</span><span class='period'>.</span><span class='id identifier rubyid_lock_args'>lock_args</span><span class='lparen'>(</span><span class='id identifier rubyid_args'>args</span><span class='rparen'>)</span>
<span class='kw'>if</span> <span class='const'><span class='object_link'><a href="Sidekiq.html" title="Sidekiq (module)">Sidekiq</a></span></span><span class='op'>::</span><span class='const'>ProcessSet</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='period'>.</span><span class='id identifier rubyid_size'>size</span> <span class='op'>></span> <span class='int'>1</span>
<span class='comment'># sidekiq runtime; uniqueness for the object (first arg)
</span> <span class='id identifier rubyid_args'>args</span><span class='period'>.</span><span class='id identifier rubyid_first'>first</span>
<span class='kw'>else</span>
<span class='comment'># queuing from the app; uniqueness for all params
</span> <span class='id identifier rubyid_args'>args</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
<span class='kw'>end</span>
</code></pre>
<h3 id="after-unlock-callback">After Unlock Callback</h3>
<p>If you need to perform any additional work after the lock has been released you can provide an <code>#after_unlock</code> instance method. The method will be called when the lock has been unlocked. Most times this means after yield but there are two exceptions to that.</p>
<p><strong>Exception 1:</strong> UntilExecuting unlocks and uses callback before yielding.
<strong>Exception 2:</strong> UntilExpired expires eventually, no after_unlock hook is called.</p>
<p><strong>NOTE:</strong> <em>It is also possible to write this code as a class method.</em></p>
<pre class="code ruby"><code class="ruby">class UniqueJobWithFilterMethod
include Sidekiq::Worker
sidekiq_options lock: :while_executing,
def self.after_unlock
# block has yielded and lock is released
end
def after_unlock
# block has yielded and lock is released
end
...
end.
</code></pre>
<h2 id="communication">Communication</h2>
<p>There is a <a href="https://gitter.im/mhenrixon/sidekiq-unique-jobs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img src="https://badges.gitter.im/mhenrixon/sidekiq-unique-jobs.svg" alt="Join the chat at https://gitter.im/mhenrixon/sidekiq-unique-jobs"></a> for praise or scorn. This would be a good place to have lengthy discuss or brilliant suggestions or simply just nudge me if I forget about anything.</p>
<h2 id="contributing">Contributing</h2>
<ol>
<li>Fork it</li>
<li>Create your feature branch (<code>git checkout -b my-new-feature</code>)</li>
<li>Commit your changes (<code>git commit -am 'Add some feature'</code>)</li>
<li>Push to the branch (<code>git push origin my-new-feature</code>)</li>
<li>Create new Pull Request</li>
</ol>
<h2 id="contributors">Contributors</h2>
<p>You can find a list of contributors over on <a href="https://github.com/mhenrixon/sidekiq-unique-jobs/graphs/contributors">Contributors</a></p>
</div></div>
<div id="footer">
Generated on Mon Sep 27 15:29:05 2021 by
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.26 (ruby-3.0.2).
</div>
</div>
</body>
</html>