Ecto has_many empty relation is causing an error when retrieving the record

I have a schema (“customers”) with a has_many association (“subscriptions”), and my issue is that when the “customers” record is created, the has_many “subscriptions” association will always be an empty enum. This is fine when inserting, but results in a ** (UndefinedFunctionError) function Mongo.Ecto.in_transaction?/1 is undefined or private error when retrieving the record from the DB.

Any ideas how to resolve this?

Customers Schema

  schema "customers" do
    field :name, :string
    field :phone, :string
    field :email, :string

    field :stripe_id, :string
    field :description, :string

    field :balance, :float
    field :currency, :string
    field :discount, :float
    field :payment_method, :string

    field :deleted_from_stripe, :boolean

    has_one :address, Address
    has_many :subscriptions, Subscription

    timestamps()
  end

Customers Changeset

  def changeset(customer, params) do
    customer
    |> cast(params, @attrs)
    |> cast_assoc(:address, with: &Address.changeset/2)
    |> cast_assoc(:subscriptions, with: &Subscription.changeset/2)
  end

Customers Query

  def find_customers() do
    query =
      from t in Customer,
      preload: [:address, :subscriptions],
      select: t

    Repo.all(query)
  end

Couple things that would be helpful for understanding this:

  • specific version numbers (of Ecto & the Mongo adapter especially)
  • the full stack trace

The following is wildly guessing based on the symptoms.


I’m suspicious of this line - prior to this commit there was an in_transaction? call used while preloading more than one association.

Version Numbers

mongo_ecto: 0.2.0
phoenix_ecto: 3.0.1
ecto: 2.0.6

Stack Trace

13:29:08.495 [error] GenServer #PID<0.629.0> terminating
** (UndefinedFunctionError) function Mongo.Ecto.in_transaction?/1 is undefined or private
    (mongo_ecto 0.2.0) Mongo.Ecto.in_transaction?(MyApp.Repo)
    (ecto 2.0.6) lib/ecto/repo/queryable.ex:133: Ecto.Repo.Queryable.execute/5
    (ecto 2.0.6) lib/ecto/repo/queryable.ex:40: Ecto.Repo.Queryable.all/4
    (myapp 0.0.2) lib/myapp_web/channels/customers_channel.ex:33: MyAppWeb.CustomersChannel.handle_in/3
    (phoenix 1.5.8) lib/phoenix/channel/server.ex:315: Phoenix.Channel.Server.handle_info/2
    (stdlib 3.12.1.2) gen_server.erl:637: :gen_server.try_dispatch/4
    (stdlib 3.12.1.2) gen_server.erl:711: :gen_server.handle_msg/6
    (stdlib 3.12.1.2) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: %Phoenix.Socket.Message{event: "load_all", join_ref: "12", payload: %{"token" => "SFMyNTY.g2gDYQJuBgA0Y8eFhAFiAAFRgA.W65RbNogwpPasDOHQ7Wjh1yQdQskoSVuZE_e70gMGeQ"}, ref: "13", topic: "all:customers"}

Yup, this line calls the preloader:

Based on the chatter in the elixir_mongo issues, it sounds like support for Ecto 2 wasn’t easy and required some pushback to the core Ecto library, for instance:

That commit didn’t land until Ecto 2.1.0 though.

1 Like

Thanks, I’ve updated my dependencies to use mongodb_ecto which supports Ecto 2.1.0 rather than mongo_ecto.

I’m now receiving the following error when I try to compile my app:

** (CompileError) lib/my_app/accounts/subscriptions/price.ex:3: module Ecto.Model is not loaded and could not be found
    (elixir 1.10.3) expanding macro: Kernel.use/1
    lib/my_app/accounts/subscriptions/price.ex:3: MyApp.Accounts.Subscriptions.Price (module)
    expanding macro: MyApp.Model.__using__/1
    lib/my_app/accounts/subscriptions/price.ex:3: MyApp.Accounts.Subscriptions.Price (module)
    (elixir 1.10.3) expanding macro: Kernel.use/1
    lib/my_app/accounts/subscriptions/price.ex:3: MyApp.Accounts.Subscriptions.Price (module)
    (elixir 1.10.3) lib/kernel/parallel_compiler.ex:304: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7

Can you shed any light on this?


Set Up

Am I missing something here? I’ve been over this with a fine tooth comb and all seems to be correct.

Dependencies

      {:phoenix_ecto, "~> 3.1"},
      {:mongodb_ecto, "~> 0.2"},

Application

  def application do
    [
      mod: {MyApp.Application, []},
      extra_applications: [:logger, :mongodb_ecto, :ecto, :runtime_tools, :edeliver]
    ]
  end

Repo

defmodule MyApp.Repo do
  use Ecto.Repo,
    otp_app: :my_app,
    adapter: Mongo.Ecto
end

Model

defmodule MyApp.Model do
  defmacro __using__(_) do
    quote do
      use Ecto.Model
      @primary_key {:id, :binary_id, autogenerate: true}
      @foreign_key_type :binary_id # For associations
    end
  end
end

And in my models: use MyApp.Model

Ecto.Model was deprecated in 2.0 and removed outright in 2.1

1 Like

Ok thanks for that, I’ve replaced Ecto.Model with Ecto.Schema and compilation is now fine, but the app is now failing to load the front end JS - looks like I’ve started down a rabbit hole :joy:

Indeed :stuck_out_tongue: My only recommendation is to take it slow and not try to upgrade straight from 2.1 to 3.9, even though that’s going to mean making changes that are then undone/further changed later.

Oh I’m not trying to upgrade to 3.9, 2.1 is fine for me, don’t need anything special from Ecto, only basic CRUD operations.

Strange how this has impacted on loading my assets though, the only dependencies I’ve changed are phoenix_ecto (upgraded from 3.0 to 3.1), mongo_ecto (changed to mongodb_ecto) :man_shrugging: