Skip to content

Commit

Permalink
Delegate to JSON and fallback to Jason
Browse files Browse the repository at this point in the history
Adds a new `Oban.JSON` module that detects whether the new JSON module
is available at compile time. If it isn't available, then it falls back
to Jason, and if Jason isn't available (which is extremely rare) then it
warns about a missing module.

This approach was chosen over a config option for backward compatibility
because Oban will only support the JSON module once the minimum
supported Elixir version is v1.18.

Closes #1213
  • Loading branch information
sorentwo committed Jan 4, 2025
1 parent 79f5bdf commit c1f996e
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 21 deletions.
4 changes: 1 addition & 3 deletions guides/advanced/release_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@ defmodule MyApp.ConfigProvider do
end

defp parse_json(path) do
{:ok, _} = Application.ensure_all_started(:jason)

if File.exists?(path) do
path
|> File.read!()
|> Jason.decode!()
|> JSON.decode!()
|> Map.fetch!("queues")
|> Keyword.new(fn {key, value} -> {String.to_atom(key), value} end)
end
Expand Down
12 changes: 6 additions & 6 deletions lib/oban/engines/inline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule Oban.Engines.Inline do
import DateTime, only: [utc_now: 0]

alias Ecto.Changeset
alias Oban.{Config, Engine, Job}
alias Oban.{Config, Engine, Job, JSON}
alias Oban.Queue.Executor

@impl Engine
Expand Down Expand Up @@ -87,8 +87,8 @@ defmodule Oban.Engines.Inline do
|> Changeset.put_change(:attempted_by, [conf.node])
|> Changeset.put_change(:attempted_at, utc_now())
|> Changeset.put_change(:scheduled_at, utc_now())
|> Changeset.update_change(:args, &json_encode_decode/1)
|> Changeset.update_change(:meta, &json_encode_decode/1)
|> Changeset.update_change(:args, &json_recode/1)
|> Changeset.update_change(:meta, &json_recode/1)

case Changeset.apply_action(changeset, :insert) do
{:ok, job} ->
Expand All @@ -102,10 +102,10 @@ defmodule Oban.Engines.Inline do
end
end

defp json_encode_decode(map) do
defp json_recode(map) do
map
|> Jason.encode!()
|> Jason.decode!()
|> JSON.encode!()
|> JSON.decode!()
end

defp complete_job(%{job: job, state: :failure}) do
Expand Down
8 changes: 4 additions & 4 deletions lib/oban/engines/lite.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ defmodule Oban.Engines.Lite do

alias Ecto.Changeset
alias Oban.Engines.Basic
alias Oban.{Config, Engine, Job, Repo}
alias Oban.{Config, Engine, Job, JSON, Repo}

@forever 60 * 60 * 24 * 365 * 99

Expand Down Expand Up @@ -327,7 +327,7 @@ defmodule Oban.Engines.Lite do
dynamic([j], field(j, ^field) == ^value and ^acc)

keys == [] ->
encoded = Jason.encode!(value)
encoded = JSON.encode!(value)

dynamic(
[j],
Expand All @@ -336,7 +336,7 @@ defmodule Oban.Engines.Lite do
)

true ->
dynamic([j], json_contains(field(j, ^field), ^Jason.encode!(value)) and ^acc)
dynamic([j], json_contains(field(j, ^field), ^JSON.encode!(value)) and ^acc)
end

field, acc ->
Expand Down Expand Up @@ -385,6 +385,6 @@ defmodule Oban.Engines.Lite do
defp encode_unsaved(job) do
job
|> Job.format_attempt()
|> Jason.encode!()
|> JSON.encode!()
end
end
22 changes: 22 additions & 0 deletions lib/oban/json.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule Oban.JSON do
@moduledoc false

# Delegates to JSON in Elixir v1.18+ or Jason for earlier versions

cond do
Code.ensure_loaded?(JSON) ->
defdelegate decode!(data), to: JSON
defdelegate encode!(data), to: JSON
defdelegate encode_to_iodata!(data), to: JSON

Code.ensure_loaded?(Jason) ->
defdelegate decode!(data), to: Jason
defdelegate encode!(data), to: Jason
defdelegate encode_to_iodata!(data), to: Jason

true ->
message = "Missing a compatible JSON library, add `:jason` to your deps."

IO.warn(message, Macro.Env.stacktrace(__ENV__))
end
end
8 changes: 4 additions & 4 deletions lib/oban/notifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ defmodule Oban.Notifier do
end
"""

alias Oban.{Config, Registry, Sonar}
alias Oban.{Config, JSON, Registry, Sonar}

require Logger

Expand Down Expand Up @@ -304,7 +304,7 @@ defmodule Oban.Notifier do
defp encode(payload) do
payload
|> to_encodable()
|> Jason.encode!()
|> JSON.encode_to_iodata!()
|> :zlib.gzip()
|> Base.encode64()
end
Expand All @@ -314,11 +314,11 @@ defmodule Oban.Notifier do
{:ok, decoded} ->
decoded
|> :zlib.gunzip()
|> Jason.decode!()
|> JSON.decode!()

# Messages emitted by the insert trigger aren't compressed.
:error ->
Jason.decode!(payload)
JSON.decode!(payload)
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/oban/telemetry.ex
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ defmodule Oban.Telemetry do
output = Map.put(fun.(), :source, "oban")

if Keyword.fetch!(opts, :encode) do
Jason.encode_to_iodata!(output)
JSON.encode_to_iodata!(output)
else
output
end
Expand Down
4 changes: 2 additions & 2 deletions lib/oban/testing.ex
Original file line number Diff line number Diff line change
Expand Up @@ -572,8 +572,8 @@ defmodule Oban.Testing do

defp json_recode(map) do
map
|> Jason.encode!()
|> Jason.decode!()
|> JSON.encode!()
|> JSON.decode!()
end

defp assert_valid_result(result) do
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ defmodule Oban.MixProject do
[
{:ecto_sql, "~> 3.10"},
{:ecto_sqlite3, "~> 0.9", optional: true},
{:jason, "~> 1.1"},
{:jason, "~> 1.1", optional: true},
{:igniter, "~> 0.5", optional: true},
{:myxql, "~> 0.7", optional: true},
{:postgrex, "~> 0.16", optional: true},
Expand Down

0 comments on commit c1f996e

Please sign in to comment.