Avoid importing internal_transactions of pending transactions

Problem: While fetching the internal transactions for a block sometimes we receive some that are part of a pending transaction.
By inserting them we also try to update the pending transaction's field, but this violates the `status` check constraint (rightfully).

Solution: Filter out internal_transactions that are part of a pending transaction.
pull/2523/head
pasqu4le 5 years ago
parent da52997fdc
commit 52a0b19d5e
No known key found for this signature in database
GPG Key ID: 8F3EE01F1DC90687
  1. 1
      CHANGELOG.md
  2. 28
      apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
  3. 24
      apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs

@ -6,6 +6,7 @@
- [#2456](https://github.com/poanetwork/blockscout/pull/2456) - fetch pending transactions for geth
### Fixes
- [#2523](https://github.com/poanetwork/blockscout/pull/2523) - Avoid importing internal_transactions of pending transactions
- [#2503](https://github.com/poanetwork/blockscout/pull/2503) - Mitigate autocompletion library influence to page loading performance
- [#2502](https://github.com/poanetwork/blockscout/pull/2502) - increase reward task timeout
- [#2463](https://github.com/poanetwork/blockscout/pull/2463) - dark theme fixes

@ -73,10 +73,12 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
# order so that row ShareLocks are grabbed in a consistent order
ordered_changes_list = Enum.sort_by(changes_list, &{&1.transaction_hash, &1.index})
final_changes_list = reject_pending_transactions(ordered_changes_list, repo)
{:ok, internal_transactions} =
Import.insert_changes_list(
repo,
ordered_changes_list,
final_changes_list,
conflict_target: [:transaction_hash, :index],
for: InternalTransaction,
on_conflict: on_conflict,
@ -156,6 +158,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
from(
t in Transaction,
where: t.hash in ^ordered_transaction_hashes,
where: not is_nil(t.block_hash),
update: [
set: [
internal_transactions_indexed_at: ^timestamps.updated_at,
@ -180,10 +183,8 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
]
)
transaction_count = Enum.count(ordered_transaction_hashes)
try do
{^transaction_count, result} = repo.update_all(query, [], timeout: timeout)
{_transaction_count, result} = repo.update_all(query, [], timeout: timeout)
{:ok, result}
rescue
@ -191,4 +192,23 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
{:error, %{exception: postgrex_error, transaction_hashes: ordered_transaction_hashes}}
end
end
defp reject_pending_transactions(ordered_changes_list, repo) do
transaction_hashes =
ordered_changes_list
|> Enum.map(& &1.transaction_hash)
|> Enum.dedup()
query =
from(t in Transaction,
where: t.hash in ^transaction_hashes,
where: is_nil(t.block_hash),
select: t.hash
)
pending_transactions = repo.all(query)
ordered_changes_list
|> Enum.reject(fn %{transaction_hash: hash} -> Enum.member?(pending_transactions, hash) end)
end
end

@ -2,7 +2,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do
use Explorer.DataCase
alias Ecto.Multi
alias Explorer.Chain.{Data, Wei, Transaction}
alias Explorer.Chain.{Data, Wei, Transaction, InternalTransaction}
alias Explorer.Chain.Import.Runner.InternalTransactions
describe "run/1" do
@ -20,6 +20,28 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do
assert :error == Repo.get(Transaction, transaction.hash).status
end
test "pending transactions don't get updated not its internal_transactions inserted" do
transaction = insert(:transaction) |> with_block(status: :ok)
pending = insert(:transaction)
assert :ok == transaction.status
assert is_nil(pending.block_hash)
index = 0
transaction_changes = make_internal_transaction_changes(transaction.hash, index, nil)
pending_changes = make_internal_transaction_changes(pending.hash, index, nil)
assert {:ok, _} = run_internal_transactions([transaction_changes, pending_changes])
assert %InternalTransaction{} =
Repo.one(from(i in InternalTransaction, where: i.transaction_hash == ^transaction.hash))
assert from(i in InternalTransaction, where: i.transaction_hash == ^pending.hash) |> Repo.one() |> is_nil()
assert is_nil(Repo.get(Transaction, pending.hash).block_hash)
end
end
defp run_internal_transactions(changes_list) when is_list(changes_list) do

Loading…
Cancel
Save