I’m working on a project using GraphQL (Absinthe) which involves price data. We’re using ex_money.
When Absinthe serialises money data before sending responses to clients it uses Money.to_string()
. The performance is terrible!
If we have a query which looks like:
{
products {
id
}
}
it will return 50,000 items in a couple of seconds.
Yet if we have a query like:
{
products {
id
price
}
}
then Elixir chugs away for 30 seconds before timing out.
I did a little benchmarking using bmark (https://github.com/joekain/bmark) to compare the performance of Money.to_string() to a naive implementation:
defmodule MoneyBmark do
use Bmark
@money Money.from_integer({"EUR", 7001, 0, 0})
bmark :money_to_string do
Money.to_string(@money)
end
bmark :fast_money_to_string do
fast_money_to_string(@money)
end
defp fast_money_to_string(money) do
Atom.to_string(money.currency) <> " " <> Decimal.to_string(money.amount)
end
end
On my machine the naive implementation is 1400x faster than Money.to_string()
. Switching the implementation means our second query returns its data in a couple of seconds.
I know that ex_money does a lot of great stuff like localised formatting and rounding yet this is almost certainly the cause of its slow serialisation.
I feel like I’m missing something obvious. Surely it’s not that uncommon to send reasonably large amounts of price data over the wire with Elixir (e.g. for exporting data), yet it seems like it’s impossible if you use ex_money.
Whilst typing this out I ran some benchmarking on money
(https://hexdocs.pm/money/Money.html). It seems to be 200-250x faster than ex_money for serialisation. Perhaps we can fix our performance issue by switching package.
Can anyone weigh in on the pros and cons of using ex_money vs money?