<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="180624" data-post-id="180624">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="dimitarvp" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/120/38664_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  dimitarvp
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Pretty interesting read that CHANGELOG, liked it! <img src="https://forum.elixirforum.com/uploads/default/original/2X/a/a1c41c89c299189a3f85edfeb4973563d8bac781.gif?v=15" title=":023:" class="emoji emoji-custom" alt=":023:" loading="lazy" width="20" height="20"></p>
<p>I’d like to give you an idea for a blog article: how do you monetise a project like this? And how do you enforce the paid licenses don’t get pirated? I am behind on this topic (last I read about it was like 10 years ago) and I’d be very curious to read about it.</p>
<p>Do you plan on writing an article like that?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="180624" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/223">Post #222</a>
	                </div>
	            </div>
              <div id="likers-container-180624" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="180624"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #222"></div>
  </section>
</div>
    <div class="postbit" id="180646" data-post-id="180646">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="sorentwo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorentwo/120/37360_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  sorentwo
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Oban Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group quote-modified" data-username="dimitarvp" data-post="223" data-topic="22449">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>How do you monetise a project like this?</p>
</blockquote>
</aside>
<p>I suppose that’s the trick, isn’t it? I’m trying to figure it out as we go while drawing inspiration from others that have gone before. It’s hard to write a blog post when you don’t feel like you have the answer <img src="https://forum.elixirforum.com/images/emoji/apple/slightly_smiling_face.png?v=15" title=":slightly_smiling_face:" class="emoji" alt=":slightly_smiling_face:" loading="lazy" width="20" height="20"></p>
<aside class="quote no-group" data-username="dimitarvp" data-post="223" data-topic="22449">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>Do you plan on writing an article like that?</p>
</blockquote>
</aside>
<p>I hadn’t, but I’ll certainly keep it in mind <img src="https://forum.elixirforum.com/images/emoji/apple/+1.png?v=15" title=":+1:" class="emoji" alt=":+1:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="180646" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/224">Post #223</a>
	                </div>
	            </div>
              <div id="likers-container-180646" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="180646"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #223"></div>
  </section>
</div>
    <div class="postbit" id="180744" data-post-id="180744">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tushar" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tushar
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hi ,</p>
<p>I am evaluating Oban for job processing which would publish messages to a Kafka broker , I have a doubt and would be great if anyone of you could answer it.</p>
<p>So in prod my application would be deployed in 3 nodes , where the Oban workers will run , there is a single postgres DB though from where the Workers would read/write from through the Oban’s Repo.</p>
<p>how does it handle the multi-node scenario as in is there any chance of a conflict where all the workers are reading the same job since the DB and the Repo is common ?</p>
<p>If yes is there any way to handle/tackle/configure it so that theres no clash among the workers ?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="180744" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/225">Post #224</a>
	                </div>
	            </div>
              <div id="likers-container-180744" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="180744"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #224"></div>
  </section>
</div>
    <div class="postbit" id="180757" data-post-id="180757">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="benwilson512" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/benwilson512/120/1457_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  benwilson512
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Author of Craft GraphQL APIs in Elixir with Absinthe</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Oban guarantees that a single job will only ever be run by a single worker at a time.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="180757" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/226">Post #225</a>
	                </div>
	            </div>
              <div id="likers-container-180757" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="180757"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #225"></div>
  </section>
</div>
    <div class="postbit" id="182512" data-post-id="182512">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="sorentwo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorentwo/120/37360_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  sorentwo
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Oban Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Oban 2.0 final is out, along with ObanWeb 2.0 and ObanPro 0.3. I haven’t been especially vigilant about announcing releases in this thread because so much has changed since Oban 1.2.</p>
<p>Check out the full <a href="https://github.com/sorentwo/oban/blob/master/CHANGELOG.md" rel="noopener nofollow ugc">CHANGELOG</a>, take a look at the <a href="https://hexdocs.pm/oban/Oban.html" rel="noopener nofollow ugc">official docs on hex</a>, or browse the broadly <a href="https://hexdocs.pm/oban/installation.html#content" rel="noopener nofollow ugc">expanded guides</a>.</p>
<p>Thanks for all of the support and feedback along this journey. Please ask any questions you may have about the changes, or share your experiences upgrading <img src="https://forum.elixirforum.com/images/emoji/apple/rocket.png?v=15" title=":rocket:" class="emoji" alt=":rocket:" loading="lazy" width="20" height="20"> <img src="https://forum.elixirforum.com/images/emoji/apple/tada.png?v=15" title=":tada:" class="emoji" alt=":tada:" loading="lazy" width="20" height="20"> <img src="https://forum.elixirforum.com/images/emoji/apple/yellow_heart.png?v=15" title=":yellow_heart:" class="emoji" alt=":yellow_heart:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="182512" data-batch-url="/posts/batch_likers">
                        8
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/227">Post #226</a>
	                </div>
	            </div>
              <div id="likers-container-182512" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="182512"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #226"></div>
  </section>
</div>
    <div class="postbit" id="182522" data-post-id="182522">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="benwilson512" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/benwilson512/120/1457_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  benwilson512
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Author of Craft GraphQL APIs in Elixir with Absinthe</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hey <a class="mention" href="/u/sorentwo" rel="nofollow">@sorentwo</a> congrats on the release. Is there any chance you could compile a list of 1.0 vs 2.0 changes in the changelog? The reason I ask is that some of the items there seem to track diffs between 2.0 pre release versions and it isn’t clear whether all of them are still applicable. One that comes to mind specifically are the pruner changes. One set of changes were introduced in rc.0, and then some rolled back in rc.3, and it’s no longer clear what differences exist between 1.0 and 2.0 final.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="182522" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/228">Post #227</a>
	                </div>
	            </div>
              <div id="likers-container-182522" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="182522"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #227"></div>
  </section>
</div>
    <div class="postbit" id="182530" data-post-id="182530">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="sorentwo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorentwo/120/37360_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  sorentwo
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Oban Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Absolutely! Here’s a condensed and comprehensive list of the breaking changes, changes, additions, fixes and removals between 1.2 and 2.0:</p>
<h3><a name="p-182530-breaking-changes-1" class="anchor" href="#p-182530-breaking-changes-1" aria-label="Heading link" rel="nofollow"></a>Breaking Changes</h3>
<ul>
<li>
<p>[Oban.Worker] The <code>perform/2</code> callback is replaced with <code>perform/1</code>, where the only argument is an <code>Oban.Job</code> struct. This unifies the interface for all <code>Oban.Worker</code> callbacks and helps to eliminate confusion around pattern matching on arguments.</p>
<p>To migrate change all worker definitions from accepting an <code>args</code> map and a <code>job</code> struct:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def perform(%{"id" =&gt; id}, _job), do: IO.inspect(id)
</code></pre>
<p>To accept a single <code>job</code> struct and match on the <code>args</code> key directly:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def perform(%Job{args: %{"id" =&gt; id}}), do: IO.inspect(id)
</code></pre>
</li>
<li>
<p>[Oban.Worker] The <code>backoff/1</code> callback now expects a job struct instead of an integer. That allows applications to finely control backoff based on more than just the current attempt number. Use of <code>backoff/1</code> with an integer is no longer supported.</p>
<p>To migrate change any worker definitions that used a raw <code>attempt</code> like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def backoff(attempt), do: attempt * 60
</code></pre>
<p>To match on a job struct instead, like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def backoff(%Job{attempt: attempt}), do: attempt * 60
</code></pre>
</li>
<li>
<p>[Oban.Config] The <code>:verbose</code> setting is renamed to <code>:log</code>. The setting started off as a simple boolean, but it has morphed to align with the log values accepted by calls to <code>Ecto.Repo</code>.</p>
<p>To migrate, replace any <code>:verbose</code> declarations:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">config :my_app, Oban,
  verbose: false,
  ...
</code></pre>
<p>With use of <code>:log</code> instead:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">config :my_app, Oban,
  log: false,
  ...
</code></pre>
</li>
<li>
<p>[Oban] The interface for <code>start_queue/3</code> is replaced with <code>start_queue/2</code> and <code>stop_queue/2</code> no longer accepts a queue name as the second argument. Instead, both functions now accept a keyword list of options. This enables the new <code>local_only</code> flag, which allows you to dynamically start and stop queues only for the local node.</p>
<p>Where you previously called <code>start_queue/2,3</code> or <code>stop_queue/2</code> like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">:ok = Oban.start_queue(:myqueue, 10)
:ok = Oban.stop_queue(:myqueue)
</code></pre>
<p>You’ll now them with options, like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">:ok = Oban.start_queue(queue: :myqueue, limit: 10)
:ok = Oban.stop_queue(queue: :myqueue)
</code></pre>
<p>Or, to only control the queue locally:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">:ok = Oban.start_queue(queue: :myqueue, limit: 10, local_only: true)
:ok = Oban.stop_queue(queue: :myqueue, local_only: true)
</code></pre>
</li>
<li>
<p>[Oban] Replace <code>drain_queue/3</code> with <code>drain_queue/2</code>, which now has an interface consistent with the other <code>*_queue/2</code> operations.</p>
<p>Where you previously called <code>drain_queue/2,3</code> like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Oban.drain_queue(:myqueue, with_safety: false)
</code></pre>
<p>You’ll now it with options, like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Oban.drain_queue(queue: :myqueue, with_safety: false)
</code></pre>
</li>
<li>
<p>[Oban] The interface for <code>pause_queue/2</code>, <code>resume_queue/2</code> and <code>scale_queue/3</code> now matches the recently changed <code>start_queue/2</code> and <code>stop_queue/2</code>. All queue manipulation functions now have a consistent interface, including the ability to work in <code>:local_only</code> mode.</p>
</li>
<li>
<p>[Oban.Telemetry] The format for telemetry events has changed to match the new telemetry <code>span</code> convention. This listing maps the old event to the new one:</p>
<ul>
<li><code>[:oban, :started]</code> → <code>[:oban, :job, :start]</code></li>
<li><code>[:oban, :success]</code> → <code>[:oban, :job, :stop]</code></li>
<li><code>[:oban, :failure]</code> → <code>[:oban, :job, :exception]</code></li>
<li><code>[:oban, :trip_circuit]</code> → <code>[:oban, :circuit, :trip]</code></li>
<li><code>[:oban, :open_circuit]</code> → <code>[:oban, :circuit, :open]</code></li>
</ul>
<p>In addition, for exceptions the stacktrace meta key has changed from <code>:stack</code> to the standardized <code>:stacktrace</code>.</p>
</li>
<li>
<p>[Oban.Beat] Pulse tracking and periodic job rescue are no longer available.  Pulse tracking and rescuing will be handled by an external plugin. This is primarily an implementation detail, but it means that jobs may be left in the <code>executing</code> state after a crash or forced shutdown.</p>
<p>Remove any <code>:beats_maxage</code>, <code>:rescue_after</code> or <code>:rescue_interval</code> settings from your config.</p>
</li>
<li>
<p>[Oban.Plugins.Pruner] Built in pruning is handled by the new plugin system. A fixed period pruning module is enabled as a default plugin. The plugin allows light configuration through a <code>max_age</code> value. For customizable per-queue, per-worker or per-state pruning see the <code>DynamicPruner</code> available in Oban Pro.</p>
<p>Remove any <code>:prune</code>, <code>:prune_interval</code> or <code>prune_limit</code> settings from your config. To disable the pruning plugin in test mode set <code>plugins: false</code> instead.</p>
<p>Replace any use of <code>:prune</code>, <code>:prune_interval</code> or <code>:prune_limit</code> in your config and pass a <code>max_age</code> value to the plugin:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">config :my_app, Oban,
  plugins: [{Oban.Plugins.Pruner, max_age: 60}]
  ...
</code></pre>
</li>
</ul>
<h3><a name="p-182530-fixed-2" class="anchor" href="#p-182530-fixed-2" aria-label="Heading link" rel="nofollow"></a>Fixed</h3>
<ul>
<li>
<p>[Oban.Scheduler] Ensure isolation between transaction locks in different prefixes. A node with multiple prefix-isolated instances (i.e. “public” and “private”) would always attempt to schedule cron jobs at the same moment. The first scheduler would acquire a lock and block out the second, preventing the second scheduler from ever scheduling jobs.</p>
</li>
<li>
<p>[Oban.Query] Correctly prefix unprepared unique queries. Unique queries always targeted the “public” prefix, which either caused incorrect results when there were both “public” and an alternate prefix. In situations where there wasn’t a public <code>oban_jobs</code> table at all it would cause cryptic transaction errors.</p>
</li>
<li>
<p>[Oban.Query] Wrap all job fetching in an explicit transaction to enforce <code>FOR UPDATE SKIP LOCKED</code> semantics. Prior to this it was possible to run the same job at the same time on multiple nodes.</p>
</li>
<li>
<p>[Oban.Crontab] Fix weekday matching for Sunday, which is represented as <code>0</code> in crontabs.</p>
</li>
<li>
<p>[Oban.Crontab.Cron] Do not raise an <code>ArgumentError</code> exception when the crontab configuration includes a step of 1, which is a valid step value.</p>
</li>
<li>
<p>[Oban.Breaker] Prevent connection bomb when the <code>Notifier</code> experiences repeated disconnections.</p>
</li>
<li>
<p>[Oban.Telemetry] Correctly record timings using native time units, but log them using microseconds. Previously they used a mixture of native and microseconds, which yielded inconsistent values.</p>
</li>
<li>
<p>[Oban.Telemetry] Stop logging the <code>:error</code> value for circuit trip events. The error is a struct that isn’t JSON encodable. We include the normalized Postgrex / DBConnection message already, so the error is redundant.</p>
</li>
</ul>
<h3><a name="p-182530-changed-3" class="anchor" href="#p-182530-changed-3" aria-label="Heading link" rel="nofollow"></a>Changed</h3>
<ul>
<li>
<p>[Oban.Notifier] Make the module public and clean up the primary function interfaces. Listening for and delivering notifications is simplified and no longer requires macros for pattern matching.</p>
<p>Notifier dispatching performance is slightly improved as well. It is now a no-op if no processes are listening to a notification’s channel.</p>
</li>
<li>
<p>[Oban.Query] The <code>completed_at</code> timestamp is no longer set for failed jobs, whether they are put in the <code>discarded</code> or <code>retryable</code> state. However, the information is still available and is recorded in the <code>errors</code> array as the <code>at</code> value with the error for that attempt.</p>
<p>This corrects a long standing inconsistency between discarding a job manually or automatically when it exhausts retries.</p>
</li>
<li>
<p>[Oban.Producer] Stop dispatching jobs immediately on queue startup. Instead, only dispatch on the first poll. This makes it possible to send the producer a message or allow sandboxed connection access before the initial dispatch.</p>
</li>
<li>
<p>[Oban.Worker] Limit default backoff calculations to 20 attempts, or roughly 24 days. The change addresses an issue with snoozing, which can increase a job’s attempts into the hundreds or thousands. In this situation the algorithm calculates the backoff using a ratio of attempts to max attempts, but is still limited to roughly 24 days.</p>
</li>
</ul>
<h3><a name="p-182530-added-4" class="anchor" href="#p-182530-added-4" aria-label="Heading link" rel="nofollow"></a>Added</h3>
<ul>
<li>
<p>[Oban.Worker] Support returning <code>{:snooze, seconds}</code> from <code>perform/1</code> to re-schedule a job some number of seconds in the future. This is useful for recycling jobs that aren’t ready to run yet, e.g. because of rate limiting.</p>
</li>
<li>
<p>[Oban.Worker] Support returning <code>:discard</code> from <code>perform/1</code> to immediately discard a job. This is useful when a job encounters an error that won’t resolve with time, e.g. invalid arguments or a missing record.</p>
</li>
<li>
<p>[Oban.Job] Introduce a virtual <code>unsaved_error</code> field, which is populated with an error map after failed execution. The <code>unsaved_error</code> field is set before any calls to the worker’s <code>backoff/1</code> callback, allowing workers to calculate a custom backoff depending on the error that failed the job.</p>
</li>
<li>
<p>[Oban.Worker] Add <code>:infinity</code> option for unique period.</p>
</li>
<li>
<p>[Oban] Bubble up errors and exits when draining queues by passing <code>with_safety: false</code> as an option to <code>Oban.drain_queue/3</code>.</p>
</li>
<li>
<p>[Oban] Add <code>Oban.cancel_job/2</code> for safely discarding scheduled jobs or killing executing jobs. This deprecates <code>kill_job/2</code>, which isn’t as flexible.</p>
</li>
<li>
<p>[Oban.Telemetry] Add <code>span/3</code> for reporting normalized <code>:start</code>, <code>:stop</code> and <code>:exception</code> events with timing information.</p>
</li>
<li>
<p>[Oban.Telemetry] Include the configured <code>prefix</code> in all event metadata. This makes it possible to identify which schema prefix a job ran with, which is useful for differentiating errors in a multi-tenant system.</p>
</li>
<li>
<p>[Oban.Telemetry] Include <code>queue_time</code> as a measurement with <code>stop</code> and <code>exception</code> events. This is a measurement in milliseconds of the amount of time between when a job was scheduled to run and when it was last attempted.</p>
</li>
<li>
<p>[Oban.Testing] Add <code>perform_job/2,3</code> helper to automate validating, normalizing and performing jobs while unit testing. This is now the preferred way to unit test workers.</p>
<p>To update your tests replace any calls to <code>perform/1,2</code> with the new <code>Oban.Testing.perform_job/2,3</code> helper:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyApp.WorkerTest do
  use MyApp.DataCase, async: true

  use Oban.Testing, repo: MyApp.Repo

  alias MyApp.Worker

  test "doing business in my worker" do
    assert :ok = perform_job(Worker, %{id: 1})
  end
end
</code></pre>
<p>The <code>perform_job/2,3</code> helper will verify the worker, the arguments and any provided options. It will then verify that your worker returns a valid result and return the value for you to assert on.</p>
</li>
<li>
<p>[Oban.Crontab] Add support for non-standard expressions such as <code>@daily</code>, <code>@hourly</code>, <code>@midnight</code>, and <code>@reboot</code></p>
</li>
<li>
<p>[Oban.Crontab] Add support for using step values in conjunction with ranges, enabling expressions like <code>10-30/2</code>, <code>15-45/3</code>, etc.</p>
</li>
<li>
<p>[Oban.Telemetry] Include job <code>queue_time</code> in the default logger output.</p>
</li>
<li>
<p>[Oban.Telemetry] Add new <code>:producer</code> events for descheduling and dispatching jobs from queue producers.</p>
</li>
</ul>
<h3><a name="p-182530-removed-5" class="anchor" href="#p-182530-removed-5" aria-label="Heading link" rel="nofollow"></a>Removed</h3>
<ul>
<li>[Oban] Removed <code>Oban.kill_job/2</code>, which wasn’t as flexible as <code>Oban.cancel_job/2</code>. Use <code>Oban.cancel_job/2</code> instead to safely discard scheduled jobs or killing executing jobs.</li>
</ul> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="182530" data-batch-url="/posts/batch_likers">
                        11
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/229">Post #228</a>
	                </div>
	            </div>
              <div id="likers-container-182530" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="182530"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #228"></div>
  </section>
</div>
    <div class="postbit" id="183267" data-post-id="183267">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="sorentwo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorentwo/120/37360_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  sorentwo
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Oban Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>In light of how expansive the 2.0 CHANGELOG is I’ve published an <a href="https://hexdocs.pm/oban/v2-0.html#content" rel="noopener nofollow ugc">upgrade guide</a> to walk you through the important / breaking changes. If you find any issues or anything that’s missing please let me know.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="183267" data-batch-url="/posts/batch_likers">
                        9
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/230">Post #229</a>
	                </div>
	            </div>
              <div id="likers-container-183267" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="183267"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #229"></div>
  </section>
</div>
    <div class="postbit" id="186984" data-post-id="186984">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="sorentwo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorentwo/120/37360_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  sorentwo
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Oban Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Oban v2.1.0 is out with a few small bug fixes and some great ease-of-use improvements <img src="https://forum.elixirforum.com/images/emoji/apple/tada.png?v=15" title=":tada:" class="emoji" alt=":tada:" loading="lazy" width="20" height="20"></p>
<p>As always the full <a href="https://github.com/sorentwo/oban/blob/master/CHANGELOG.md#210--2020-08-21" rel="noopener nofollow ugc">CHANGELOG</a> is avilable, and here are some highlights:</p>
<h3><a name="p-186984-changed-1" class="anchor" href="#p-186984-changed-1" aria-label="Heading link" rel="nofollow"></a>Changed</h3>
<ul>
<li>
<p>Wrap <code>{:error, reason}</code>, <code>{:discard, reason}</code>, <code>:timeout</code> and crash errors in proper exception structs (<code>Oban.PerformError</code>, <code>Oban.TimeoutError</code> and <code>Oban.CrashError</code> respectively). This provides better error grouping and, most importantly, <strong>fixes error reporting for Sentry &gt;= 8.0</strong>. Thanks to <a class="mention" href="/u/anthonator" rel="nofollow">@anthonator</a> for <a href="https://github.com/sorentwo/oban/issues/305" rel="noopener nofollow ugc">the issue</a>.</p>
</li>
<li>
<p>No more misleading stacktraces when <code>perform/1</code> returns an error, discard, or timeout. The stacktrace always came from the executor module and didn’t contain any information from the worker module itself. To get a useful stacktrace you must raise or crash the job process.</p>
</li>
</ul>
<h3><a name="p-186984-added-2" class="anchor" href="#p-186984-added-2" aria-label="Heading link" rel="nofollow"></a>Added</h3>
<ul>
<li>
<p>Discard jobs with a reason using a <code>{:discard, reason}</code> tuple. This behaves similarly to an error tuple, except that the job is discarded and a <code>[:oban, :job, :stop]</code> telemetry event is emitted rather than an <code>:exception</code> event. Thanks to <a class="mention" href="/u/jc00ke" rel="nofollow">@jc00ke</a> for <a href="https://github.com/sorentwo/oban/issues/296" rel="noopener nofollow ugc">the issue</a>.</p>
</li>
<li>
<p>It is now possible to override settings like <code>:poll_interval</code> at the queue level. This allows finer grained control for busy queues and paves the way for some upcoming custom producers through Oban Pro.</p>
</li>
<li>
<p>Add support for sub-arg uniqueness with a new <code>:keys</code> option. This allows you to only consider <em>some</em> of the keys in args when comparing them to historic jobs for uniqueness. It is valuable in situations where you add other metadata to job args but still need the job to be considered unique. For example</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># Args from an existing job
%{from: "somebody", url: "https://a.co"}

# Args from a new job that should be considered duplicate because they share a `url`.
%{from: "somebody-else", url: "https://a.co"}
</code></pre>
<p>For more details see the <a href="https://github.com/sorentwo/oban/issues/283" rel="noopener nofollow ugc">original issue</a>.</p>
</li>
</ul> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="186984" data-batch-url="/posts/batch_likers">
                        8
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/231">Post #230</a>
	                </div>
	            </div>
              <div id="likers-container-186984" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="186984"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #230"></div>
  </section>
</div>
    <div class="postbit" id="191793" data-post-id="191793">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="sorentwo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/sorentwo/120/37360_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  sorentwo
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Oban Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Oban v2.2.0 is now available! It includes some minor bug fixes and several <em>wonderful</em> features from contributors <img src="https://forum.elixirforum.com/images/emoji/apple/yellow_heart.png?v=15" title=":yellow_heart:" class="emoji" alt=":yellow_heart:" loading="lazy" width="20" height="20">. There are some large invisible architectural changes in this release that will <strong>require subscribers to upgrade to Oban Web v2.2.2 and Oban Pro v0.4.1</strong>.</p>
<p>Here are some highlights and attributions from the <a href="https://hexdocs.pm/oban/changelog.html#2-2-0-2020-10-12" rel="noopener nofollow ugc">CHANGELOG</a>:</p>
<h3><a name="p-191793-added-1" class="anchor" href="#p-191793-added-1" aria-label="Heading link" rel="nofollow"></a>Added</h3>
<ul>
<li>
<p>Replace local dynamically composed names with a registry. This dramatically simplifies locating nested children, avoids unnecessary atom creation at runtime and improves the performance of config lookups. Thanks to <a class="mention" href="/u/sasajuric" rel="nofollow">@sasajuric</a> for all the hard work that went into making this change.</p>
</li>
<li>
<p>The new <code>Oban.Repo</code> module wraps interactions with underlying Ecto repos. This ensures consistent prefix and log level handling, while also adding full dynamic repo support. Also contributed by <a class="mention" href="/u/sasajuric" rel="nofollow">@sasajuric</a>.</p>
</li>
<li>
<p>Augment the unique keys option with <code>replace_args</code>, which allows enqueuing a unique job and replacing the args subsequently. For example, given a job with these args:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">%{some_value: 1, id: 123}
</code></pre>
<p>Attempting to insert a new job:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">%{some_value: 2, id: 123}
|&gt; MyJob.new(schedule_in: 10, replace_args: true unique: [keys: [:id]])
|&gt; Oban.insert()
</code></pre>
<p>Will result in a single job with the args:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">%{some_value: 2, id: 123}
</code></pre>
<p>Thanks to <a class="mention" href="/u/gazler" rel="nofollow">@Gazler</a> for the feature suggestion <em>and</em> the PR itself.</p>
</li>
</ul>
<h3><a name="p-191793-changed-2" class="anchor" href="#p-191793-changed-2" aria-label="Heading link" rel="nofollow"></a>Changed</h3>
<ul>
<li>The default backoff algorithm now includes a small amount of jitter. The jitter helps prevent jobs that fail simultaneously from repeatedly retrying together. Thanks to <a class="mention" href="/u/coladarci" rel="nofollow">@coladarci</a> for the feature request.</li>
</ul> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="191793" data-batch-url="/posts/batch_likers">
                        10
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/oban-reliable-and-observable-job-processing/22449/232">Post #231</a>
	                </div>
	            </div>
              <div id="likers-container-191793" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="191793"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #231"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/22449/load_more?page=24">Load more posts (81 remaining)</a>
</div></template></turbo-stream>