From ac2bf31856bfccf884849653aca8907b7814f334 Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Wed, 24 Oct 2018 11:30:45 -0500 Subject: [PATCH] Don't replace_all fields of internal_transactions on_conflict Fixes #978 Replace all field except the primary key and timestamps. For the primary key fields (transaction_hash, index), leave them as is as they are the conflict target and have no changed by definition. For the timestamps that the least inserted_at and the greatest updated_at to preserve the bounds of the timeline. --- .../chain/import/internal_transactions.ex | 32 ++++++++++++++++++- .../explorer/chain/internal_transaction.ex | 8 +++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/import/internal_transactions.ex b/apps/explorer/lib/explorer/chain/import/internal_transactions.ex index 2b21a5b38b..059a1284bb 100644 --- a/apps/explorer/lib/explorer/chain/import/internal_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/internal_transactions.ex @@ -58,7 +58,7 @@ defmodule Explorer.Chain.Import.InternalTransactions do | {:error, [Changeset.t()]} defp insert(changes_list, %{timeout: timeout, timestamps: timestamps} = options) when is_list(changes_list) do - on_conflict = Map.get(options, :on_conflict, :replace_all) + on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0) # order so that row ShareLocks are grabbed in a consistent order ordered_changes_list = Enum.sort_by(changes_list, &{&1.transaction_hash, &1.index}) @@ -81,6 +81,36 @@ defmodule Explorer.Chain.Import.InternalTransactions do )} end + defp default_on_conflict do + from( + internal_transaction in InternalTransaction, + update: [ + set: [ + block_number: fragment("EXCLUDED.block_number"), + call_type: fragment("EXCLUDED.call_type"), + created_contract_address_hash: fragment("EXCLUDED.created_contract_address_hash"), + created_contract_code: fragment("EXCLUDED.created_contract_code"), + error: fragment("EXCLUDED.error"), + from_address_hash: fragment("EXCLUDED.from_address_hash"), + gas: fragment("EXCLUDED.gas"), + gas_used: fragment("EXCLUDED.gas_used"), + # Don't update `index` as it is part of the composite primary key and used for the conflict target + init: fragment("EXCLUDED.init"), + input: fragment("EXCLUDED.input"), + output: fragment("EXCLUDED.output"), + to_address_hash: fragment("EXCLUDED.to_address_hash"), + trace_address: fragment("EXCLUDED.trace_address"), + # Don't update `transaction_hash` as it is part of the composite primary key and used for the conflict target + transaction_index: fragment("EXCLUDED.transaction_index"), + type: fragment("EXCLUDED.type"), + value: fragment("EXCLUDED.value"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", internal_transaction.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", internal_transaction.updated_at) + ] + ] + ) + end + defp update_transactions(internal_transactions, %{ timeout: timeout, timestamps: timestamps diff --git a/apps/explorer/lib/explorer/chain/internal_transaction.ex b/apps/explorer/lib/explorer/chain/internal_transaction.ex index dfce9c8f78..ca392e567f 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction.ex @@ -7,6 +7,7 @@ defmodule Explorer.Chain.InternalTransaction do alias Explorer.Chain.InternalTransaction.{CallType, Type} @typedoc """ + * `block_number` - the `t:Explorer.Chain.Block.t/0` `number` that the `transaction` is collated into. * `call_type` - the type of call. `nil` when `type` is not `:call`. * `created_contract_code` - the code of the contract that was created when `type` is `:create`. * `error` - error message when `:call` or `:create` `type` errors @@ -23,13 +24,15 @@ defmodule Explorer.Chain.InternalTransaction do * `trace_address` - list of traces * `transaction` - transaction in which this transaction occurred * `transaction_hash` - foreign key for `transaction` + * `transaction_index` - the `t:Explorer.Chain.Transaction.t/0` `index` of `transaction` in `block_number`. * `type` - type of internal transaction * `value` - value of transferred from `from_address` to `to_address` """ @type t :: %__MODULE__{ + block_number: Explorer.Chain.Block.block_number() | nil, call_type: CallType.t() | nil, created_contract_address: %Ecto.Association.NotLoaded{} | Address.t() | nil, - created_contract_address_hash: Explorer.Chain.Hash.t() | nil, + created_contract_address_hash: Hash.t() | nil, created_contract_code: Data.t() | nil, error: String.t(), from_address: %Ecto.Association.NotLoaded{} | Address.t(), @@ -44,7 +47,8 @@ defmodule Explorer.Chain.InternalTransaction do to_address_hash: Hash.Address.t() | nil, trace_address: [non_neg_integer()], transaction: %Ecto.Association.NotLoaded{} | Transaction.t(), - transaction_hash: Explorer.Chain.Hash.t(), + transaction_hash: Hash.t(), + transaction_index: Transaction.transaction_index() | nil, type: Type.t(), value: Wei.t() }