From 29199eb54e047c3e3c6b2e6c4eba9f9b93528e3b Mon Sep 17 00:00:00 2001 From: svenski123 Date: Tue, 6 Nov 2018 17:50:34 +0000 Subject: [PATCH] Add nonce field to Address object and addresses table. This caches the highest nonce value seen for sent transactions for a given address. The top addresses query no longer needs to scan the transactions table for nonce values. The database migration script adds the nonce field to the addresses table and sets the nonce value according to the contents of the transactions table. --- apps/explorer/lib/explorer/chain.ex | 5 +--- apps/explorer/lib/explorer/chain/address.ex | 6 ++-- .../lib/explorer/chain/import/addresses.ex | 14 ++------- .../201811061523_add_nonce_to_addresses.exs | 30 +++++++++++++++++++ .../indexer/lib/indexer/address_extraction.ex | 18 +++++++---- 5 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/201811061523_add_nonce_to_addresses.exs diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index a2a55dcbd5..efcd2af83d 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -899,12 +899,9 @@ defmodule Explorer.Chain do def list_top_addresses do query = from(a in Address, - left_join: t in Transaction, - on: a.hash == t.from_address_hash, where: a.fetched_coin_balance > ^0, - group_by: [a.hash, a.fetched_coin_balance], order_by: [desc: a.fetched_coin_balance, asc: a.hash], - select: {a, fragment("coalesce(1 + max(?), 0)", t.nonce)}, + select: {a, fragment("coalesce(1 + ?, 0)", a.nonce)}, limit: 250 ) diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index c5e2df3c9a..5777606d84 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -8,7 +8,7 @@ defmodule Explorer.Chain.Address do alias Ecto.Changeset alias Explorer.Chain.{Address, Block, Data, Hash, InternalTransaction, SmartContract, Token, Wei} - @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number)a + @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce)a @required_attrs ~w(hash)a @allowed_attrs @optional_attrs ++ @required_attrs @@ -34,7 +34,8 @@ defmodule Explorer.Chain.Address do contract_code: Data.t() | nil, names: %Ecto.Association.NotLoaded{} | [Address.Name.t()], inserted_at: DateTime.t(), - updated_at: DateTime.t() + updated_at: DateTime.t(), + nonce: non_neg_integer() | nil } @primary_key {:hash, Hash.Address, autogenerate: false} @@ -42,6 +43,7 @@ defmodule Explorer.Chain.Address do field(:fetched_coin_balance, Wei) field(:fetched_coin_balance_block_number, :integer) field(:contract_code, Data) + field(:nonce, :integer) has_one(:smart_contract, SmartContract) has_one(:token, Token, foreign_key: :contract_address_hash) diff --git a/apps/explorer/lib/explorer/chain/import/addresses.ex b/apps/explorer/lib/explorer/chain/import/addresses.ex index ae6c92e887..fd0546ef3a 100644 --- a/apps/explorer/lib/explorer/chain/import/addresses.ex +++ b/apps/explorer/lib/explorer/chain/import/addresses.ex @@ -95,18 +95,10 @@ defmodule Explorer.Chain.Import.Addresses do # MAX on two columns fetched_coin_balance_block_number: fragment( - """ - CASE WHEN EXCLUDED.fetched_coin_balance_block_number IS NOT NULL AND - (? IS NULL OR - EXCLUDED.fetched_coin_balance_block_number >= ?) THEN - EXCLUDED.fetched_coin_balance_block_number - ELSE ? - END - """, - address.fetched_coin_balance_block_number, - address.fetched_coin_balance_block_number, + "GREATEST(EXCLUDED.fetched_coin_balance_block_number, ?)", address.fetched_coin_balance_block_number - ) + ), + nonce: fragment("GREATEST(EXCLUDED.nonce, ?)", address.nonce) ] ] ) diff --git a/apps/explorer/priv/repo/migrations/201811061523_add_nonce_to_addresses.exs b/apps/explorer/priv/repo/migrations/201811061523_add_nonce_to_addresses.exs new file mode 100644 index 0000000000..cbdfe4b1b9 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/201811061523_add_nonce_to_addresses.exs @@ -0,0 +1,30 @@ +defmodule Explorer.Repo.Migrations.AddNonceToAddresses do + use Ecto.Migration + + def up do + # Add nonce + alter table(:addresses) do + add(:nonce, :integer) + end + + # Populate nonce field from transactions table + execute(""" + WITH t AS ( + SELECT from_address_hash AS hash, MAX(nonce) AS nonce + FROM transactions + GROUP BY hash + ) + UPDATE addresses AS a + SET nonce = t.nonce + FROM t + WHERE a.hash = t.hash + """) + end + + def down do + # Remove nonce + alter table(:addresses) do + remove(:nonce) + end + end +end diff --git a/apps/indexer/lib/indexer/address_extraction.ex b/apps/indexer/lib/indexer/address_extraction.ex index 995465589a..37e93e3987 100644 --- a/apps/indexer/lib/indexer/address_extraction.ex +++ b/apps/indexer/lib/indexer/address_extraction.ex @@ -84,7 +84,8 @@ defmodule Indexer.AddressExtraction do ], [ %{from: :block_number, to: :fetched_coin_balance_block_number}, - %{from: :from_address_hash, to: :hash} + %{from: :from_address_hash, to: :hash}, + %{from: :nonce, to: :nonce} ], [ %{from: :block_number, to: :fetched_coin_balance_block_number}, @@ -136,6 +137,7 @@ defmodule Indexer.AddressExtraction do required(:hash) => String.t(), required(:fetched_coin_balance_block_number) => non_neg_integer(), optional(:fetched_coin_balance) => non_neg_integer(), + optional(:nonce) => non_neg_integer(), optional(:contract_code) => String.t() } @@ -209,11 +211,13 @@ defmodule Indexer.AddressExtraction do ...> %{ ...> block_number: 1, ...> from_address_hash: "0x0000000000000000000000000000000000000001", - ...> to_address_hash: "0x0000000000000000000000000000000000000002" + ...> to_address_hash: "0x0000000000000000000000000000000000000002", + ...> nonce: 3 ...> }, ...> %{ ...> block_number: 2, - ...> from_address_hash: "0x0000000000000000000000000000000000000003" + ...> from_address_hash: "0x0000000000000000000000000000000000000003", + ...> nonce: 4 ...> } ...> ] ...> } @@ -221,7 +225,8 @@ defmodule Indexer.AddressExtraction do [ %{ fetched_coin_balance_block_number: 1, - hash: "0x0000000000000000000000000000000000000001" + hash: "0x0000000000000000000000000000000000000001", + nonce: 3 }, %{ fetched_coin_balance_block_number: 1, @@ -229,7 +234,8 @@ defmodule Indexer.AddressExtraction do }, %{ fetched_coin_balance_block_number: 2, - hash: "0x0000000000000000000000000000000000000003" + hash: "0x0000000000000000000000000000000000000003", + nonce: 4 } ] @@ -366,6 +372,7 @@ defmodule Indexer.AddressExtraction do %{ required(:block_number) => non_neg_integer(), required(:from_address_hash) => String.t(), + required(:nonce) => non_neg_integer(), optional(:to_address_hash) => String.t(), optional(:created_contract_address_hash) => String.t() } @@ -477,6 +484,7 @@ defmodule Indexer.AddressExtraction do Map.merge(first, second) end end + |> Map.put(:nonce, max_nil_last(first[:nonce], second[:nonce])) end # `nil > 5 == true`, but we want numbers instead