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.
pull/985/head
Luke Imhoff 6 years ago
parent 95e3774e9a
commit ac2bf31856
  1. 32
      apps/explorer/lib/explorer/chain/import/internal_transactions.ex
  2. 8
      apps/explorer/lib/explorer/chain/internal_transaction.ex

@ -58,7 +58,7 @@ defmodule Explorer.Chain.Import.InternalTransactions do
| {:error, [Changeset.t()]} | {:error, [Changeset.t()]}
defp insert(changes_list, %{timeout: timeout, timestamps: timestamps} = options) defp insert(changes_list, %{timeout: timeout, timestamps: timestamps} = options)
when is_list(changes_list) do 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 # order so that row ShareLocks are grabbed in a consistent order
ordered_changes_list = Enum.sort_by(changes_list, &{&1.transaction_hash, &1.index}) ordered_changes_list = Enum.sort_by(changes_list, &{&1.transaction_hash, &1.index})
@ -81,6 +81,36 @@ defmodule Explorer.Chain.Import.InternalTransactions do
)} )}
end 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, %{ defp update_transactions(internal_transactions, %{
timeout: timeout, timeout: timeout,
timestamps: timestamps timestamps: timestamps

@ -7,6 +7,7 @@ defmodule Explorer.Chain.InternalTransaction do
alias Explorer.Chain.InternalTransaction.{CallType, Type} alias Explorer.Chain.InternalTransaction.{CallType, Type}
@typedoc """ @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`. * `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`. * `created_contract_code` - the code of the contract that was created when `type` is `:create`.
* `error` - error message when `:call` or `:create` `type` errors * `error` - error message when `:call` or `:create` `type` errors
@ -23,13 +24,15 @@ defmodule Explorer.Chain.InternalTransaction do
* `trace_address` - list of traces * `trace_address` - list of traces
* `transaction` - transaction in which this transaction occurred * `transaction` - transaction in which this transaction occurred
* `transaction_hash` - foreign key for `transaction` * `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 * `type` - type of internal transaction
* `value` - value of transferred from `from_address` to `to_address` * `value` - value of transferred from `from_address` to `to_address`
""" """
@type t :: %__MODULE__{ @type t :: %__MODULE__{
block_number: Explorer.Chain.Block.block_number() | nil,
call_type: CallType.t() | nil, call_type: CallType.t() | nil,
created_contract_address: %Ecto.Association.NotLoaded{} | Address.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, created_contract_code: Data.t() | nil,
error: String.t(), error: String.t(),
from_address: %Ecto.Association.NotLoaded{} | Address.t(), from_address: %Ecto.Association.NotLoaded{} | Address.t(),
@ -44,7 +47,8 @@ defmodule Explorer.Chain.InternalTransaction do
to_address_hash: Hash.Address.t() | nil, to_address_hash: Hash.Address.t() | nil,
trace_address: [non_neg_integer()], trace_address: [non_neg_integer()],
transaction: %Ecto.Association.NotLoaded{} | Transaction.t(), 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(), type: Type.t(),
value: Wei.t() value: Wei.t()
} }

Loading…
Cancel
Save