Embeds_many / put_embed performance related

I have this tree of nested embedded_schemas and it works just fine. My question here is about my concern regarding the performance of put_embed and apply_changes functions with and without id fields in the embedded schemas (e.g. with @primary_key set to false).

Namely, do the Ecto Changeset functions perform some kind of comparison between the existing members and the new members in order to figure out which to keep or they simply replace one list of members with the other? All my embeds_many and embeds_one are declared with on_replace: :delete, naturally as the desired behavior is for ecto to simply replace the list, but I can’t tell what is being done internally and I couldn’t find an in-depth reference in the documentation.

In other words, if there is a comparison between the elements (which in my case are relatively large trees), then this comparison is by default a deep comparison and I guess may therefore represent an unnecessary performance penalty. If that is the case, would it be faster if I first reset the embeds_many with an empty list, apply changes and then set it to a new list of members (by using put_embed in both operations, of course)?


There’s nothing done besides checking for matching ids between the existing data and the changes.

Thanks for the quick reply.

What if:
a) my embedded schemas do not have ids (i.e. primary keys are set to false); or
b) they have ids but they are not primary keys and are virtual so they are used only for a temporary reference by UI

Will Ecto compare anything if a) and will it still compare the ids if b)?

Thanks again.

Ecto doesn’t care about those points. I’ll explain this for a list of embeds, but the same is true for a single embed and more or less assocs as well:

A changeset holds the existing embeds (which is why you need to preload in case of assocs) and the changes for those separately.

  • Any existing embed which doesn’t match by its id to a change in the list of changes will be marked with :replace. It’ll be removed when persisted.
  • Any existing embed which does match a change by its id, which be marked with :update and the changes will be applied to it.
  • Any change, which doesn’t match an existing embed (having an id or not) will be marked with :insert effectively creating a new embed in the db when persisted.

How those ids came to be doesn’t concern this handling at all. They’re just used to match items to changes to decide if things are supposed to be created/updated/deleted. If ids are not present then there’s simply nothing to match and it behaves exactly as if there would’ve been ids present, but they didn’t match.

1 Like

Thanks a lot for the explanation.