Skip to content

Commit d2f3102

Browse files
committed
Fix crash on error detail page when using MySQL/MariaDB
Instead of using `{:array, :string}` for the `:breadcrumbs` field in `ErrorTracker.Occurrence` we use a new custom field type `ErrorTracker.Types.StringArray`. It uses `Jason` to decode arrays when retrieving data from MySQL/MariaDB. Therefore it works very similarly to the `:array` type but without crashing on retrieval. This fix does not make a schema migration necessary. An additional test module `ErrorTracker.StoreFetchTest` was implemented to avoid regreessions in the future. Fixes elixir-error-tracker#150.
1 parent 99c3fb2 commit d2f3102

File tree

7 files changed

+85
-5
lines changed

7 files changed

+85
-5
lines changed

.github/workflows/elixir.yml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ jobs:
4747
--health-interval 10s
4848
--health-timeout 5s
4949
--health-retries 5
50+
mysql:
51+
image: mysql:9
52+
ports: ["3307:3306"]
53+
env:
54+
MYSQL_ROOT_PASSWORD: root
55+
options: >-
56+
--health-cmd "mysqladmin ping --password=root"
57+
--health-interval 10s
58+
--health-timeout 5s
59+
--health-retries 5
5060
name: Elixir v${{ matrix.elixir }}, Erlang v${{ matrix.erlang }}
5161
steps:
5262
- uses: actions/checkout@v4
@@ -96,7 +106,12 @@ jobs:
96106
env:
97107
DB: postgres
98108

99-
- name: Run Tests - MySQL/MariaDB
109+
- name: Run Tests - MariaDB
110+
run: mix test
111+
env:
112+
DB: mariadb
113+
114+
- name: Run Tests - MySQL
100115
run: mix test
101116
env:
102-
DB: mysql
117+
DB: mysql

config/test.example.exs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,15 @@ config :error_tracker, ErrorTracker.Test.Repo,
55
pool: Ecto.Adapters.SQL.Sandbox,
66
log: false
77

8+
config :error_tracker, ErrorTracker.Test.MariaDBRepo,
9+
url: "ecto://root:[email protected]:3306/error_tracker_test",
10+
pool: Ecto.Adapters.SQL.Sandbox,
11+
log: false,
12+
# Use the same migrations as the PostgreSQL repo
13+
priv: "priv/repo"
14+
815
config :error_tracker, ErrorTracker.Test.MySQLRepo,
9-
url: "ecto://root:[email protected]/error_tracker_test",
16+
url: "ecto://root:[email protected]:3307/error_tracker_test",
1017
pool: Ecto.Adapters.SQL.Sandbox,
1118
log: false,
1219
# Use the same migrations as the PostgreSQL repo

lib/error_tracker/schemas/occurrence.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ defmodule ErrorTracker.Occurrence do
1818
field :reason, :string
1919

2020
field :context, :map
21-
field :breadcrumbs, {:array, :string}
21+
field :breadcrumbs, ErrorTracker.Types.StringArray
2222

2323
embeds_one :stacktrace, ErrorTracker.Stacktrace
2424
belongs_to :error, ErrorTracker.Error
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
defmodule ErrorTracker.Types.StringArray do
2+
@moduledoc """
3+
Custom Ecto type for lists.
4+
5+
The built-in `:array` type is not properly implemented for the Ecto adapter `Ecto.Adapters.MyXQL`.
6+
Therefore we can not use it and have to impelement our own.
7+
"""
8+
use Ecto.Type
9+
10+
def type, do: {:array, :string}
11+
12+
def cast(list) when is_list(list) do
13+
{:ok, list}
14+
end
15+
16+
def cast(_), do: :error
17+
18+
def load(list) when is_binary(list) do
19+
Jason.decode(list)
20+
end
21+
22+
def load(list) when is_nil(list) do
23+
[]
24+
end
25+
26+
def load(list), do: {:ok, list}
27+
28+
def dump(list) when is_list(list) do
29+
ErrorTracker.Repo.with_adapter(fn
30+
:mysql -> Jason.encode(list)
31+
_ -> {:ok, list}
32+
end)
33+
end
34+
35+
def dump(_), do: :error
36+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
defmodule ErrorTracker.StoreFetchTest do
2+
@moduledoc """
3+
Test if simple store-retrieve operations are successful.
4+
5+
This is necessary, because some Ecto adapters like `Ecto.Adapters.MyXQL` may successfully store a field, but crash on retrieval.
6+
"""
7+
use ErrorTracker.Test.Case
8+
9+
test "after reporting an error its occurrence should be retrievable from DB" do
10+
assert %ErrorTracker.Occurrence{id: occurrence_id} =
11+
report_error(fn -> raise "BOOM" end)
12+
13+
assert %ErrorTracker.Occurrence{} = repo().get!(ErrorTracker.Occurrence, occurrence_id)
14+
end
15+
end

test/support/mariadb_repo.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
defmodule ErrorTracker.Test.MariaDBRepo do
2+
@moduledoc false
3+
use Ecto.Repo, otp_app: :error_tracker, adapter: Ecto.Adapters.MyXQL
4+
end

test/test_helper.exs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ repo =
77
"mysql" ->
88
ErrorTracker.Test.MySQLRepo
99

10+
"mariadb" ->
11+
ErrorTracker.Test.MariaDBRepo
12+
1013
"postgres" ->
1114
ErrorTracker.Test.Repo
1215

1316
_other ->
14-
raise "Please run either `DB=sqlite mix test`, `DB=postgres mix test` or `DB=mysql mix test`"
17+
raise "Please run either `DB=sqlite mix test`, `DB=postgres mix test`, `DB=mariadb mix test` or `DB=mysql mix test`"
1518
end
1619

1720
Application.put_env(:error_tracker, :repo, repo)

0 commit comments

Comments
 (0)