Stages will be between Import and the Import.Runner. Unlike Runners, which use 1 common Ecto.Multi, a Stage can produce 1 or more independent Ecto.Multi that will run in separate transactions. This allows addresses to be chunked into separate transactions, which releases the locks between transactions unlike the INSERT-level chunking that was all in 1 transaction.pull/1242/head
parent
48c2657dd2
commit
d36dac0dc2
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Address.CoinBalances do |
||||
defmodule Explorer.Chain.Import.Runner.Address.CoinBalances do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Address.CoinBalance.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Address.CurrentTokenBalances do |
||||
defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalances do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Address.CurrentTokenBalance.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Address.TokenBalances do |
||||
defmodule Explorer.Chain.Import.Runner.Address.TokenBalances do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Address.TokenBalance.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Addresses do |
||||
defmodule Explorer.Chain.Import.Runner.Addresses do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Address.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Block.SecondDegreeRelations do |
||||
defmodule Explorer.Chain.Import.Runner.Block.SecondDegreeRelations do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Block.SecondDegreeRelation.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Block.Rewards do |
||||
defmodule Explorer.Chain.Import.Runner.Block.Rewards do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Block.Reward.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Logs do |
||||
defmodule Explorer.Chain.Import.Runner.Logs do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Log.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.TokenTransfers do |
||||
defmodule Explorer.Chain.Import.Runner.TokenTransfers do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.TokenTransfer.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Tokens do |
||||
defmodule Explorer.Chain.Import.Runner.Tokens do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Token.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Transaction.Forks do |
||||
defmodule Explorer.Chain.Import.Runner.Transaction.Forks do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Transaction.Fork.t/0`. |
||||
""" |
@ -1,4 +1,4 @@ |
||||
defmodule Explorer.Chain.Import.Transactions do |
||||
defmodule Explorer.Chain.Import.Runner.Transactions do |
||||
@moduledoc """ |
||||
Bulk imports `t:Explorer.Chain.Transaction.t/0`. |
||||
""" |
@ -0,0 +1,50 @@ |
||||
defmodule Explorer.Chain.Import.Stage do |
||||
@moduledoc """ |
||||
Behaviour used to chunk `changes_list` into multiple `t:Ecto.Multi.t/0`` that can run in separate transactions to |
||||
limit the time that transactions take and how long blocking locks are held in Postgres. |
||||
""" |
||||
|
||||
alias Ecto.Multi |
||||
alias Explorer.Chain.Import.Runner |
||||
|
||||
@typedoc """ |
||||
Maps `t:Explorer.Chain.Import.Runner.t/0` callback module to the `t:Explorer.Chain.Import.Runner.changes_list/0` it |
||||
can import. |
||||
""" |
||||
@type runner_to_changes_list :: %{Runner.t() => Runner.changes_list()} |
||||
|
||||
@doc """ |
||||
The runners consumed by this stage in `c:multis/0`. The list should be in the order that the runners are executed. |
||||
""" |
||||
@callback runners() :: [Runner.t(), ...] |
||||
|
||||
@doc """ |
||||
Chunks `changes_list` into 1 or more `t:Ecto.Multi.t/0` that can be run in separate transactions. |
||||
|
||||
The runners used by the stage should be removed from the returned `runner_to_changes_list` map. |
||||
""" |
||||
@callback multis(runner_to_changes_list, %{optional(atom()) => term()}) :: {[Multi.t()], runner_to_changes_list} |
||||
|
||||
@doc """ |
||||
Uses a single `t:Explorer.Chain.Runner.t/0` and chunks the `changes_list` across multiple `t:Ecto.Multi.t/0` |
||||
""" |
||||
@spec chunk_every(runner_to_changes_list, Runner.t(), chunk_size :: pos_integer(), %{optional(atom()) => term()}) :: |
||||
{[Multi.t()], runner_to_changes_list} |
||||
def chunk_every(runner_to_changes_list, runner, chunk_size, options) |
||||
when is_map(runner_to_changes_list) and is_atom(runner) and is_integer(chunk_size) and is_map(options) do |
||||
{changes_list, unstaged_runner_to_changes_list} = Map.pop(runner_to_changes_list, runner) |
||||
multis = changes_list_chunk_every(changes_list, chunk_size, runner, options) |
||||
|
||||
{multis, unstaged_runner_to_changes_list} |
||||
end |
||||
|
||||
defp changes_list_chunk_every(nil, _, _, _), do: [] |
||||
|
||||
defp changes_list_chunk_every(changes_list, chunk_size, runner, options) do |
||||
changes_list |
||||
|> Stream.chunk_every(chunk_size) |
||||
|> Enum.map(fn changes_chunk -> |
||||
runner.run(Multi.new(), changes_chunk, options) |
||||
end) |
||||
end |
||||
end |
@ -0,0 +1,50 @@ |
||||
defmodule Explorer.Chain.Import.Stage.AddressReferencing do |
||||
@moduledoc """ |
||||
Imports any tables that reference `t:Explorer.Chain.Address.t/0` and that were imported by |
||||
`Explorer.Chain.Import.Stage.Addresses`. |
||||
""" |
||||
|
||||
alias Ecto.Multi |
||||
alias Explorer.Chain.Import.{Runner, Stage} |
||||
|
||||
@behaviour Stage |
||||
|
||||
@impl Stage |
||||
def runners, |
||||
do: [ |
||||
Runner.Address.CoinBalances, |
||||
Runner.Blocks, |
||||
Runner.Block.Rewards, |
||||
Runner.Block.SecondDegreeRelations, |
||||
Runner.Transactions, |
||||
Runner.Transaction.Forks, |
||||
Runner.InternalTransactions, |
||||
Runner.Logs, |
||||
Runner.Tokens, |
||||
Runner.TokenTransfers, |
||||
Runner.Address.CurrentTokenBalances, |
||||
Runner.Address.TokenBalances |
||||
] |
||||
|
||||
@impl Stage |
||||
def multis(runner_to_changes_list, options) do |
||||
{final_multi, final_remaining_runner_to_changes_list} = |
||||
runners() |
||||
|> Enum.reduce({Multi.new(), runner_to_changes_list}, fn runner, {multi, remaining_runner_to_changes_list} -> |
||||
{changes_list, new_remaining_runner_to_changes_list} = Map.pop(remaining_runner_to_changes_list, runner) |
||||
|
||||
new_multi = |
||||
case changes_list do |
||||
nil -> |
||||
multi |
||||
|
||||
_ -> |
||||
runner.run(multi, changes_list, options) |
||||
end |
||||
|
||||
{new_multi, new_remaining_runner_to_changes_list} |
||||
end) |
||||
|
||||
{[final_multi], final_remaining_runner_to_changes_list} |
||||
end |
||||
end |
@ -0,0 +1,22 @@ |
||||
defmodule Explorer.Chain.Import.Stage.Addresses do |
||||
@moduledoc """ |
||||
Imports addresses before anything else that references them because an unused address is still valid and recoverable |
||||
if the other stage(s) don't commit. |
||||
""" |
||||
|
||||
alias Explorer.Chain.Import.{Runner, Stage} |
||||
|
||||
@behaviour Stage |
||||
|
||||
@runner Runner.Addresses |
||||
|
||||
@impl Stage |
||||
def runners, do: [@runner] |
||||
|
||||
@chunk_size 50 |
||||
|
||||
@impl Stage |
||||
def multis(runner_to_changes_list, options) do |
||||
Stage.chunk_every(runner_to_changes_list, @runner, @chunk_size, options) |
||||
end |
||||
end |
@ -0,0 +1,19 @@ |
||||
defmodule Explorer.Logger do |
||||
@moduledoc """ |
||||
Helpers for `Logger`. |
||||
""" |
||||
|
||||
@doc """ |
||||
Sets `keyword` in `Logger.metadata/1` around `fun`. |
||||
""" |
||||
def metadata(fun, keyword) when is_function(fun, 0) and is_list(keyword) do |
||||
metadata_before = Logger.metadata() |
||||
|
||||
try do |
||||
Logger.metadata(keyword) |
||||
fun.() |
||||
after |
||||
Logger.reset_metadata(metadata_before) |
||||
end |
||||
end |
||||
end |
@ -1,8 +1,8 @@ |
||||
defmodule Explorer.Chain.Import.Address.CurrentTokenBalancesTest do |
||||
defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalancesTest do |
||||
use Explorer.DataCase |
||||
|
||||
alias Explorer.Chain.Address.CurrentTokenBalance |
||||
alias Explorer.Chain.Import.Address.CurrentTokenBalances |
||||
alias Explorer.Chain.Import.Runner.Address.CurrentTokenBalances |
||||
alias Explorer.Repo |
||||
|
||||
describe "insert/2" do |
Loading…
Reference in new issue