We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Setting updated_at yourself in Repo.update_all/3
almirsarajcic
Repo.update_all/3 writes the columns you give it and nothing else, so updated_at keeps its old value:
now = DateTime.utc_now(:second)
Invoice
|> where(id: 1)
|> Repo.update_all(set: [status: "void"])
Invoice
|> where(id: 2)
|> Repo.update_all(set: [status: "void", updated_at: now])
Invoice
|> order_by(:id)
|> select([i], {i.id, i.updated_at})
|> Repo.all()
#=> [{1, ~U[2026-01-01 00:00:00Z]}, {2, ~U[2026-09-22 17:40:25Z]}]
Both invoices are void now, but only the second one looks like it changed.
timestamps() values are filled in when Repo.insert/2 or Repo.update/2 works with a struct or changeset. update_all/3 sends a single UPDATE built from your query, so there’s nothing for Ecto to fill in. Anything that syncs, caches or sorts by updated_at can miss those rows.
Put updated_at in the set: list whenever the bulk change should count as a change. Repo.insert_all/3 and upserts with a query in on_conflict don’t autogenerate timestamps either.
copied to clipboard