DaemonSnake

DaemonSnake

Oban Pro 1.5 migration: unique jobs with period:infinity

According to the 1.5 upgrade guide:

If uniqueness is essential to your system, upgrade to Pro v1.4.13+ and let it run as long as your longest unique period before upgrading to v1.5. For example, if your longest unique period is 1 day, let it run for at least a day. That will ensure the correct unique values are present for index-backed uniqueness when you upgrade.

We have an issue with this:
we have jobs with an infinite unique period that MUST be kept in table.

Is there a way to compute the uniq_bmp in postgresql so we can change the old records ?

Most Liked

sorentwo

sorentwo

Oban Core Team

The old unique keys were calculated with phash2, and only intended for unique checks on insert. With 180 days worth of retained jobs you’re bound to have conflicts. That’s why the new unique keys are calculated as cryptographic hashes. We posted about the issue a few months ago.

This is all a big change, but essential for accuracy, performance, and compatibility with distributed databases.

Since you need to retain those old jobs, you’ll need a way to update the unique keys for them, which must be done in Elixir. Here’s an example script that shows how to query for the old unique format and update them as a stream:

import Ecto.Query

# These are the default unique options, tweak as necessary
unique = %{
  fields: ~w(args queue worker)a,
  keys: [],
  period: 60,
  states: ~w(scheduled available executing retryable completed)a
}

stream =
  Oban.Job
  |> where([j], j.state in ~w(completed cancelled discarded))
  |> where([j], fragment(~s|jsonb_path_query(?, '$.uniq_key.type()') = '"number"'::jsonb|, j.meta))
  |> order_by(:id)
  |> MyApp.Repo.stream()

MyApp.Repo.transaction(fn ->
  Enum.each(stream, fn job ->
    job
    |> Ecto.Changeset.change(%{unique: unique})
    |> Oban.Pro.Unique.with_uniq_meta()
    |> MyApp.Repo.update!()
  end)
end)

Depending on the number of jobs you have, it may be better to batch than to stream. The important bit is to make a changeset and call Oban.Pro.Unique.with_uniq_meta/1 on it.

sorentwo

sorentwo

Oban Core Team

How long are you retaining those jobs in the table? Meaning, what’s your maximum prune time?

You can inject the correct uniq_bmp with a query, but you’ll have to map the states to the correct integers beforehand because the unique states option isn’t in the database. Here’s an example that sets a uniq_bmp using the standard set of unique states (everything but cancelled and discarded):

update oban_jobs
set meta || '{"uniq_bmp":[0,1,2,3,4]}'
where worker = 'MyApp.SomeWorker' and meta ? 'uniq_key'

If you’ve set uniqueness across all states, then you would use all values, 0..6.

Where Next?

Popular in Questions Top

albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
Phillipp
Hey, I have a NanoPi-M3 and try to install Elixir on their Ubuntu image. I followed the Raspberry Pi installation instructions from the ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New

Other popular topics Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New

We're in Beta

About us Mission Statement