bodhilogic

bodhilogic

Ecto Multi in a Loop

I load a CSV into two separate tables in my database and it works great.

Now that I have moved the database to the cloud, it’s time to use batch inserts/updates, but all of the examples I find for Ecto.Multi are trivial examples and I’m not sure how to go about building a process that can ultimately do one Repo.transaction for each of the two tables, using the data that I have collected inside of a loop that steps through each row of the CSV.

I reckon I have to, somehow, incorporate the use of Multi.append but don’t really know where to start.

Does each loop create its own Multi and before the next iteration, do a Multi.append into a ‘master’ multi? Or can I just pass the ‘master’ multi, which is created outside of the loop, to the sub-processes that run ‘inner multis’ inside the loop?

I found this example, which is close to what I’m after, but it runs the transaction each time which isn’t saving me any trips to the database server.

Here is some pseudocode to show you what I’m doing:

  def parse_csv(conn, file) do
    stage_date = get_date_from_filename(file)

    File.stream!("uploads/#{file}", [:trim_bom])
    |> CSV.decode(headers: true)
    |> Stream.each(fn row -> _process_csv_row(conn, row, stage_date) end)
    |> Stream.run()

    conn
  end

  defp _process_csv_row(conn, row, stage_date) do
    tenant = tenant(conn)

    # Set ticket_params from row data

    # Build ticket changeset and do a Repo.insert_or_update
    _process_ticket(ticket_params, tenant)

    # Get the id of the ticket just inserted or updated

    # Set activity_params from row data and ticket id

    # Build activity changeset and do a Repo.insert
    _process_activity(activity_params, tenant)
  end

Marked As Solved

blatyo

blatyo

Conduit Core Team

Hey @bodhilogic,

Ecto.Multi is for executing multiple statements in a single transaction. It doesn’t batch inserts and updates. For that, you’d want to use insert_all or update_all defined on your repo. If all of your rows are inserted/updated in a single insert_all/update_all, you won’t need a transaction, because it’ll be part of a single atomic statement. However, if your CSV is large, you will likely want to batch insert in chunks. You can split your stream into chunks by using Stream.chunk_every/2. Then you can do a batch db operation with that chunk. It is likely best to not wrap the update of all chunks in a transaction either, because inserting/updating a large amount of rows in a table, will acquire write locks for all of those rows, preventing reads for as long as the transaction is open. That can usually affect other parts of your system negatively.

Also Liked

1player

1player

To answer your question “how do I use Ecto.Multi in a loop”, you would use Enum.reduce/3 to build up the Multi as you process elements in an enumerable.

def parse(_) do
  multi = Ecto.Multi.new()

  multi = 
    File.stream!("uploads/#{file}", [:trim_bom])
    |> CSV.decode(headers: true)
    |> Enum.reduce(multi, &process_row/2)
  
  Repo.transaction(multi)
end

defp process_row(row, multi) do
  parsed_row = ...

  Ecto.Multi.insert(multi, ...)
end
blatyo

blatyo

Conduit Core Team

It can use SQL functions to set different values. Though, if you’re looking to pass data to update, insert_all + the upsert options on_conflict and conflict_target are probably what you want.

Where Next?

Popular in Questions Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> someth...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> someth...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

We're in Beta

About us Mission Statement