An Ecto SQLite3 adapter.
0

Configure Feed

Select the types of activity you want to include in your feed.

add insert benchmarks comparing myxql/postgrex/exqlite (#111)

authored by

Kevin Lang and committed by
GitHub
(Mar 16, 2021, 9:52 PM -0500) e67d3954 de25b5e3

+410 -1
+36
bench/README.md
··· 1 + # Ecto Benchmarks 2 + 3 + Ecto has a benchmark suite to track performance of sensitive operations. Benchmarks 4 + are run using the [Benchee](https://github.com/PragTob/benchee) library and 5 + need PostgreSQL and MySQL up and running. 6 + 7 + To run the benchmarks tests just type in the console: 8 + 9 + ``` 10 + # POSIX-compatible shells 11 + $ MIX_ENV=bench mix run bench/bench_helper.exs 12 + ``` 13 + 14 + ``` 15 + # other shells 16 + $ env MIX_ENV=bench mix run bench/bench_helper.exs 17 + ``` 18 + 19 + Benchmarks are inside the `scripts/` directory and are divided into two 20 + categories: 21 + 22 + * `micro benchmarks`: Operations that don't actually interface with the database, 23 + but might need it up and running to start the Ecto agents and processes. 24 + 25 + * `macro benchmarks`: Operations that are actually run in the database. This are 26 + more likely to integration tests. 27 + 28 + You can also run a benchmark individually by giving the path to the benchmark 29 + script instead of `bench/bench_helper.exs`. 30 + 31 + # Docker 32 + I had Postgres already installed and running locally, but needed to get MySQL up and running. The easiest way to do this is with this command: 33 + 34 + ``` 35 + docker run -p 3306:3306 --name mysql_server -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql:5.7 36 + ```
+7
bench/bench_helper.exs
··· 1 + # Micro benchmarks 2 + Code.require_file("scripts/micro/load_bench.exs", __DIR__) 3 + Code.require_file("scripts/micro/to_sql_bench.exs", __DIR__) 4 + 5 + ## Macro benchmarks needs postgresql and mysql up and running 6 + Code.require_file("scripts/macro/insert_bench.exs", __DIR__) 7 + Code.require_file("scripts/macro/all_bench.exs", __DIR__)
+53
bench/scripts/macro/all_bench.exs
··· 1 + # -----------------------------------Goal-------------------------------------- 2 + # Compare the performance of querying all objects of the different supported 3 + # databases 4 + 5 + # -------------------------------Description----------------------------------- 6 + # This benchmark tracks performance of querying a set of objects registered in 7 + # the database with Repo.all/2 function. The query pass through 8 + # the steps of translating the SQL statements, sending them to the database and 9 + # load the results into Ecto structures. Both, Ecto Adapters and Database itself 10 + # play a role and can affect the results of this benchmark. 11 + 12 + # ----------------------------Factors(don't change)--------------------------- 13 + # Different adapters supported by Ecto with the proper database up and running 14 + 15 + # ----------------------------Parameters(change)------------------------------- 16 + # There is only a unique parameter in this benchmark, the User objects to be 17 + # fetched. 18 + 19 + Code.require_file("../../support/setup.exs", __DIR__) 20 + 21 + alias Ecto.Bench.User 22 + 23 + limit = 5_000 24 + 25 + users = 26 + 1..limit 27 + |> Enum.map(fn _ -> User.sample_data() end) 28 + 29 + # We need to insert data to fetch 30 + Ecto.Bench.PgRepo.insert_all(User, users) 31 + Ecto.Bench.MyXQLRepo.insert_all(User, users) 32 + 33 + jobs = %{ 34 + "Pg Repo.all/2" => fn -> Ecto.Bench.PgRepo.all(User, limit: limit) end, 35 + "MyXQL Repo.all/2" => fn -> Ecto.Bench.MyXQLRepo.all(User, limit: limit) end 36 + } 37 + 38 + path = System.get_env("BENCHMARKS_OUTPUT_PATH") || "bench/results" 39 + file = Path.join(path, "all.json") 40 + 41 + Benchee.run( 42 + jobs, 43 + formatters: [Benchee.Formatters.JSON, Benchee.Formatters.Console], 44 + formatter_options: [json: [file: file]], 45 + time: 10, 46 + after_each: fn results -> 47 + ^limit = length(results) 48 + end 49 + ) 50 + 51 + # Clean inserted data 52 + Ecto.Bench.PgRepo.delete_all(User) 53 + Ecto.Bench.MyXQLRepo.delete_all(User)
+46
bench/scripts/macro/insert_bench.exs
··· 1 + # -----------------------------------Goal-------------------------------------- 2 + # Compare the performance of inserting changesets and structs in the different 3 + # supported databases 4 + 5 + # -------------------------------Description----------------------------------- 6 + # This benchmark tracks performance of inserting changesets and structs in the 7 + # database with Repo.insert!/1 function. The query pass through 8 + # the steps of translating the SQL statements, sending them to the database and 9 + # returning the result of the transaction. Both, Ecto Adapters and Database itself 10 + # play a role and can affect the results of this benchmark. 11 + 12 + # ----------------------------Factors(don't change)--------------------------- 13 + # Different adapters supported by Ecto with the proper database up and running 14 + 15 + # ----------------------------Parameters(change)------------------------------- 16 + # Different inputs to be inserted, aka Changesets and Structs 17 + 18 + Code.require_file("../../support/setup.exs", __DIR__) 19 + 20 + alias Ecto.Bench.User 21 + 22 + inputs = %{ 23 + "Struct" => struct(User, User.sample_data()), 24 + "Changeset" => User.changeset(User.sample_data()) 25 + } 26 + 27 + jobs = %{ 28 + "Exqlite Insert" => fn entry -> Ecto.Bench.ExqliteRepo.insert!(entry) end, 29 + "Pg Insert" => fn entry -> Ecto.Bench.PgRepo.insert!(entry) end, 30 + "MyXQL Insert" => fn entry -> Ecto.Bench.MyXQLRepo.insert!(entry) end 31 + } 32 + 33 + path = System.get_env("BENCHMARKS_OUTPUT_PATH") || "bench/results" 34 + file = Path.join(path, "insert.json") 35 + 36 + Benchee.run( 37 + jobs, 38 + inputs: inputs, 39 + formatters: [Benchee.Formatters.JSON, Benchee.Formatters.Console], 40 + formatter_options: [json: [file: file]] 41 + ) 42 + 43 + # Clean inserted data 44 + Ecto.Bench.ExqliteRepo.delete_all(User) 45 + Ecto.Bench.PgRepo.delete_all(User) 46 + Ecto.Bench.MyXQLRepo.delete_all(User)
+55
bench/scripts/micro/load_bench.exs
··· 1 + # -----------------------------------Goal-------------------------------------- 2 + # Compare the implementation of loading raw database data into Ecto structures by 3 + # the different database adapters 4 + 5 + # -------------------------------Description----------------------------------- 6 + # Repo.load/2 is an important step of a database query. 7 + # This benchmark tracks performance of loading "raw" data into ecto structures 8 + # Raw data can be in different types (e.g. keyword lists, maps), in this tests 9 + # we benchmark against map inputs 10 + 11 + # ----------------------------Factors(don't change)--------------------------- 12 + # Different adapters supported by Ecto, each one has its own implementation that 13 + # is tested against different inputs 14 + 15 + # ----------------------------Parameters(change)------------------------------- 16 + # Different sizes of raw data(small, medium, big) and different attribute types 17 + # such as UUID, Date and Time fetched from the database and needs to be 18 + # loaded into Ecto structures. 19 + 20 + Code.require_file("../../support/setup.exs", __DIR__) 21 + 22 + alias Ecto.Bench.User 23 + 24 + inputs = %{ 25 + "Small 1 Thousand" => 26 + 1..1_000 |> Enum.map(fn _ -> %{name: "Alice", email: "email@email.com"} end), 27 + "Medium 100 Thousand" => 28 + 1..100_000 |> Enum.map(fn _ -> %{name: "Alice", email: "email@email.com"} end), 29 + "Big 1 Million" => 30 + 1..1_000_000 |> Enum.map(fn _ -> %{name: "Alice", email: "email@email.com"} end), 31 + "Time attr" => 32 + 1..100_000 |> Enum.map(fn _ -> %{name: "Alice", time_attr: ~T[21:25:04.361140]} end), 33 + "Date attr" => 1..100_000 |> Enum.map(fn _ -> %{name: "Alice", date_attr: ~D[2018-06-20]} end), 34 + "NaiveDateTime attr" => 35 + 1..100_000 36 + |> Enum.map(fn _ -> %{name: "Alice", naive_datetime_attr: ~N[2019-06-20 21:32:07.424178]} end), 37 + "UUID attr" => 38 + 1..100_000 39 + |> Enum.map(fn _ -> %{name: "Alice", uuid: Ecto.UUID.bingenerate()} end) 40 + } 41 + 42 + jobs = %{ 43 + "Pg Loader" => fn data -> Enum.map(data, &Ecto.Bench.PgRepo.load(User, &1)) end, 44 + "MyXQL Loader" => fn data -> Enum.map(data, &Ecto.Bench.MyXQLRepo.load(User, &1)) end 45 + } 46 + 47 + path = System.get_env("BENCHMARKS_OUTPUT_PATH") || "bench/results" 48 + file = Path.join(path, "load.json") 49 + 50 + Benchee.run( 51 + jobs, 52 + inputs: inputs, 53 + formatters: [Benchee.Formatters.JSON, Benchee.Formatters.Console], 54 + formatter_options: [json: [file: file]] 55 + )
+64
bench/scripts/micro/to_sql_bench.exs
··· 1 + # -----------------------------------Goal-------------------------------------- 2 + # Compare the implementation of parsing Ecto.Query objects into SQL queries by 3 + # the different database adapters 4 + 5 + # -------------------------------Description----------------------------------- 6 + # Repo.to_sql/2 is an important step of a database query. 7 + # This benchmark tracks performance of parsing Ecto.Query structures into 8 + # "raw" SQL query strings. 9 + # Different Ecto.Query objects has multiple combinations and some different attributes 10 + # depending on the query type. In this tests we benchmark against different 11 + # query types and complexity. 12 + 13 + # ----------------------------Factors(don't change)--------------------------- 14 + # Different adapters supported by Ecto, each one has its own implementation that 15 + # is tested against different query inputs 16 + 17 + # ----------------------------Parameters(change)------------------------------- 18 + # Different query objects (select, delete, update) to be translated into pure SQL 19 + # strings. 20 + 21 + Code.require_file("../../support/setup.exs", __DIR__) 22 + 23 + import Ecto.Query 24 + 25 + alias Ecto.Bench.{User, Game} 26 + 27 + inputs = %{ 28 + "Ordinary Select All" => {:all, from(User)}, 29 + "Ordinary Delete All" => {:delete_all, from(User)}, 30 + "Ordinary Update All" => {:update_all, from(User, update: [set: [name: "Thor"]])}, 31 + "Ordinary Where" => {:all, from(User, where: [name: "Thanos", email: "blah@blah"])}, 32 + "Fetch First Registry" => {:all, first(User)}, 33 + "Fetch Last Registry" => {:all, last(User)}, 34 + "Ordinary Order By" => {:all, order_by(User, desc: :name)}, 35 + "Complex Query 2 Joins" => 36 + {:all, 37 + from(User, where: [name: "Thanos"]) 38 + |> join(:left, [u], ux in User, on: u.id == ux.id) 39 + |> join(:right, [j], uj in User, on: j.id == 1 and j.email == "email@email") 40 + |> select([u, ux], {u.name, ux.email})}, 41 + "Complex Query 4 Joins" => 42 + {:all, 43 + from(User) 44 + |> join(:left, [u], g in Game, on: g.name == u.name) 45 + |> join(:right, [g], u in User, on: g.id == 1 and u.email == "email@email") 46 + |> join(:inner, [u], g in fragment("SELECT * from games where game.id = ?", u.id)) 47 + |> join(:left, [g], u in fragment("SELECT * from users = ?", g.id)) 48 + |> select([u, g], {u.name, g.price})} 49 + } 50 + 51 + jobs = %{ 52 + "Pg Query Builder" => fn {type, query} -> Ecto.Bench.PgRepo.to_sql(type, query) end, 53 + "MyXQL Query Builder" => fn {type, query} -> Ecto.Bench.MyXQLRepo.to_sql(type, query) end 54 + } 55 + 56 + path = System.get_env("BENCHMARKS_OUTPUT_PATH") || "bench/results" 57 + file = Path.join(path, "to_sql.json") 58 + 59 + Benchee.run( 60 + jobs, 61 + inputs: inputs, 62 + formatters: [Benchee.Formatters.JSON, Benchee.Formatters.Console], 63 + formatter_options: [json: [file: file]] 64 + )
+15
bench/support/migrations.exs
··· 1 + defmodule Ecto.Bench.CreateUser do 2 + use Ecto.Migration 3 + 4 + def change do 5 + create table(:users) do 6 + add(:name, :string) 7 + add(:email, :string) 8 + add(:password, :string) 9 + add(:time_attr, :time) 10 + add(:date_attr, :date) 11 + add(:naive_datetime_attr, :naive_datetime) 12 + add(:uuid, :binary_id) 13 + end 14 + end 15 + end
+43
bench/support/repo.exs
··· 1 + pg_bench_url = System.get_env("PG_URL") || "postgres:postgres@localhost" 2 + myxql_bench_url = System.get_env("MYXQL_URL") || "root@localhost" 3 + 4 + Application.put_env( 5 + :ecto_sql, 6 + Ecto.Bench.PgRepo, 7 + url: "ecto://" <> pg_bench_url <> "/ecto_test", 8 + adapter: Ecto.Adapters.Postgres, 9 + show_sensitive_data_on_connection_error: true 10 + ) 11 + 12 + Application.put_env( 13 + :ecto_sql, 14 + Ecto.Bench.MyXQLRepo, 15 + url: "ecto://" <> myxql_bench_url <> "/ecto_test_myxql", 16 + adapter: Ecto.Adapters.MyXQL, 17 + protocol: :tcp, 18 + show_sensitive_data_on_connection_error: true 19 + ) 20 + 21 + Application.put_env( 22 + :ecto_sql, 23 + Ecto.Bench.ExqliteRepo, 24 + adapter: Ecto.Adapters.Exqlite, 25 + database: "/tmp/exqlite_bench.db", 26 + journal_mode: :wal, 27 + cache_size: -64000, 28 + temp_store: :memory, 29 + pool_size: 5, 30 + show_sensitive_data_on_connection_error: true 31 + ) 32 + 33 + defmodule Ecto.Bench.PgRepo do 34 + use Ecto.Repo, otp_app: :ecto_sql, adapter: Ecto.Adapters.Postgres, log: false 35 + end 36 + 37 + defmodule Ecto.Bench.MyXQLRepo do 38 + use Ecto.Repo, otp_app: :ecto_sql, adapter: Ecto.Adapters.MyXQL, log: false 39 + end 40 + 41 + defmodule Ecto.Bench.ExqliteRepo do 42 + use Ecto.Repo, otp_app: :ecto_sql, adapter: Ecto.Adapters.Exqlite, log: false 43 + end
+52
bench/support/schemas.exs
··· 1 + defmodule Ecto.Bench.User do 2 + use Ecto.Schema 3 + 4 + schema "users" do 5 + field(:name, :string) 6 + field(:email, :string) 7 + field(:password, :string) 8 + field(:time_attr, :time) 9 + field(:date_attr, :date) 10 + field(:naive_datetime_attr, :naive_datetime) 11 + field(:uuid, :binary_id) 12 + end 13 + 14 + @required_attrs [ 15 + :name, 16 + :email, 17 + :password, 18 + :time_attr, 19 + :date_attr, 20 + :naive_datetime_attr, 21 + :uuid 22 + ] 23 + 24 + def changeset() do 25 + changeset(sample_data()) 26 + end 27 + 28 + def changeset(data) do 29 + Ecto.Changeset.cast(%__MODULE__{}, data, @required_attrs) 30 + end 31 + 32 + def sample_data do 33 + %{ 34 + name: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 35 + email: "foobar@email.com", 36 + password: "mypass", 37 + time_attr: Time.utc_now() |> Time.truncate(:second), 38 + date_attr: Date.utc_today(), 39 + naive_datetime_attr: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second), 40 + uuid: Ecto.UUID.generate() 41 + } 42 + end 43 + end 44 + 45 + defmodule Ecto.Bench.Game do 46 + use Ecto.Schema 47 + 48 + schema "games" do 49 + field(:name, :string) 50 + field(:price, :float) 51 + end 52 + end
+26
bench/support/setup.exs
··· 1 + Code.require_file("repo.exs", __DIR__) 2 + Code.require_file("migrations.exs", __DIR__) 3 + Code.require_file("schemas.exs", __DIR__) 4 + 5 + alias Ecto.Bench.{PgRepo, MyXQLRepo, ExqliteRepo, CreateUser} 6 + 7 + {:ok, _} = Ecto.Adapters.Postgres.ensure_all_started(PgRepo.config(), :temporary) 8 + {:ok, _} = Ecto.Adapters.MyXQL.ensure_all_started(MyXQLRepo.config(), :temporary) 9 + {:ok, _} = Ecto.Adapters.Exqlite.ensure_all_started(ExqliteRepo.config(), :temporary) 10 + 11 + _ = Ecto.Adapters.Postgres.storage_down(PgRepo.config()) 12 + :ok = Ecto.Adapters.Postgres.storage_up(PgRepo.config()) 13 + 14 + _ = Ecto.Adapters.MyXQL.storage_down(MyXQLRepo.config()) 15 + :ok = Ecto.Adapters.MyXQL.storage_up(MyXQLRepo.config()) 16 + 17 + _ = Ecto.Adapters.Exqlite.storage_down(ExqliteRepo.config()) 18 + :ok = Ecto.Adapters.Exqlite.storage_up(ExqliteRepo.config()) 19 + 20 + {:ok, _pid} = PgRepo.start_link(log: false) 21 + {:ok, _pid} = MyXQLRepo.start_link(log: false) 22 + {:ok, _pid} = ExqliteRepo.start_link(log: false) 23 + 24 + :ok = Ecto.Migrator.up(PgRepo, 0, CreateUser, log: false) 25 + :ok = Ecto.Migrator.up(MyXQLRepo, 0, CreateUser, log: false) 26 + :ok = Ecto.Migrator.up(ExqliteRepo, 0, CreateUser, log: false)
+7 -1
mix.exs
··· 37 37 {:elixir_make, "~> 0.6", runtime: false}, 38 38 {:ex_doc, "~> 0.23.0", only: [:dev], runtime: false}, 39 39 {:jason, ">= 0.0.0", only: [:test, :docs]}, 40 - {:temp, "~> 0.4", only: [:test]} 40 + {:temp, "~> 0.4", only: [:test]}, 41 + 42 + # Benchmarks 43 + {:benchee, "~> 0.11.0", only: :bench}, 44 + {:benchee_json, "~> 0.4.0", only: :bench}, 45 + {:postgrex, "~> 0.15.0", only: :bench}, 46 + {:myxql, "~> 0.4.0", only: :bench} 41 47 ] 42 48 end 43 49
+6
mix.lock
··· 1 1 %{ 2 + "benchee": {:hex, :benchee, "0.11.0", "cf96e328ff5d69838dd89c21a9db22716bfcc6ef772e9d9dddf7ba622102722d", [:mix], [{:deep_merge, "~> 0.1", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm", "c345e090e0a61bf33e0385aa3ad394fcb7d863e313bc3fca522e390c7f39166e"}, 3 + "benchee_json": {:hex, :benchee_json, "0.4.0", "59d3277829bd1dca8373cdb20b916cb435c2647be235d09963fc0959db908c36", [:mix], [{:benchee, "~> 0.10", [hex: :benchee, repo: "hexpm", optional: false]}, {:poison, ">= 1.4.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "71a3edb6a30708de2a01368aa8f288e1c0ed7897b125adc396ce7c2c7245b1e7"}, 2 4 "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, 3 5 "db_connection": {:hex, :db_connection, "2.3.1", "4c9f3ed1ef37471cbdd2762d6655be11e38193904d9c5c1c9389f1b891a3088e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "abaab61780dde30301d840417890bd9f74131041afd02174cf4e10635b3a63f5"}, 4 6 "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, 7 + "deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm", "e3bf435a54ed27b0ba3a01eb117ae017988804e136edcbe8a6a14c310daa966e"}, 5 8 "earmark_parser": {:hex, :earmark_parser, "1.4.12", "b245e875ec0a311a342320da0551da407d9d2b65d98f7a9597ae078615af3449", [:mix], [], "hexpm", "711e2cc4d64abb7d566d43f54b78f7dc129308a63bc103fbd88550d2174b3160"}, 6 9 "ecto": {:hex, :ecto, "3.5.8", "8ebf12be6016cb99313348ba7bb4612f4114b9a506d6da79a2adc7ef449340bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ea0be182ea8922eb7742e3ae8e71b67ee00ae177de1bf76210299a5f16ba4c77"}, 7 10 "ecto_sql": {:hex, :ecto_sql, "3.5.4", "a9e292c40bd79fff88885f95f1ecd7b2516e09aa99c7dd0201aa84c54d2358e4", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.5.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1fff1a28a898d7bbef263f1f3ea425b04ba9f33816d843238c84eff883347343"}, ··· 11 14 "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, 12 15 "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, 13 16 "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, 17 + "myxql": {:hex, :myxql, "0.4.5", "49784e6a3e4fc33088cc9004948ef255ee698b0d7b533fb1fa453cc99a3f9972", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:geo, "~> 3.3", [hex: :geo, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "40a6166ab0a54f44a6e2c437aed6360ce51ce7f779557ae30d1cc4c4b4e7ad13"}, 14 18 "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, 19 + "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, 20 + "postgrex": {:hex, :postgrex, "0.15.8", "f5e782bbe5e8fa178d5e3cd1999c857dc48eda95f0a4d7f7bd92a50e84a0d491", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "698fbfacea34c4cf22c8281abeb5cf68d99628d541874f085520ab3b53d356fe"}, 15 21 "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"}, 16 22 "temp": {:hex, :temp, "0.4.7", "2c78482cc2294020a4bc0c95950b907ff386523367d4e63308a252feffbea9f2", [:mix], [], "hexpm", "6af19e7d6a85a427478be1021574d1ae2a1e1b90882586f06bde76c63cd03e0d"}, 17 23 }