Terminus v0.1.1 Terminus.Planaria behaviour View Source
A behaviour module for implementing Planaria-like state machines in Elixir.
A module using Terminus.Planaria
is a GenStage consumer process that will
automatically mangage its own producer processes to crawl and listen to Bitcoin
transaction events. Developers only need to implement callback functions to
handle transaction events.
Example
The following code demonstrates how a Twetch scraper can be built in a few lines of code.
defmodule TwetchScraper do
@query %{
"find" => %{
"out.s2": "19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut",
"out.s25": "twetch"
}
}
use Terminus.Planaria, token: {:my_app, :planaria_token},
from: 600000,
query: @query
def handle_data(:block, txns) do
# Handle confirmed transactions
end
def handle_data(:mempool, txns) do
# Handle unconfirmed transactions
end
end
The handle_data/2
callback can be implemented for each tx_event
,
and is typically be used to persist required data from each transaction. The
handle_tape/2
callback can also be implemented for loading and persisting
the tape head so a re-crawl isn't necessary if the process is interrupted.
Options
When invoking use Terminus.Planaria
, the following config
options are accepted:
:token
- Planaria Token. Required.:host
- The Bitbus/Bitsocket endpoint to use. Defaults to:txo
.:from
- The block height from which to crawl for transactions. Required.:query
- Full or shorthand Bitquery map.:poll
- Interval (in seconds) to poll Bitbus for new blocks. Defaults to300
(5 minutes).:recycle
- Interval (in seconds) to recycle quiet Bitsocket requests. Defaults to900
(15 minutes).
Supervision
Each Terminus.Planaria
will most commonly be started under your application's
supervision tree. When you invoke use Terminus.Planaria
, it automatically
defines a child_spec/1
function so your Planaria modules can be started
directly under a supervisor.
And this is where we can have some fun and take full advantage of Elixir's concurrency model. Why not run many Planarias concurrently in your app?
children = [
TwetchScraper,
PreevScraper,
WeathersvScraper
]
Supervisor.start_link(children, strategy: :one_for_all)
Link to this section Summary
Types
Planaria config.
Planaria state.
Planaria tape.
Planaria tape event.
Planaria transaction event.
Functions
Starts a Terminus.Planaria
process without links (outside of a supervision
tree).
Starts a Terminus.Planaria
process linked to the current process.
Callbacks
Invoked for each new transaction seen by the Planaria.
Invoked when a Planaria starts and also after each crawl of new blocks.
Link to this section Types
Specs
Planaria config.
Specs
t() :: %Terminus.Planaria{ config: config(), crawl_sub: {pid(), GenStage.subscription_tag()}, listen_sub: {pid(), GenStage.subscription_tag()}, mod: atom(), tape: tape() }
Planaria state.
Specs
Planaria tape.
Specs
tape_event() :: :start | :block
Planaria tape event.
Specs
tx_event() :: :block | :mempool
Planaria transaction event.
Link to this section Functions
Specs
start(atom(), config(), keyword()) :: GenServer.on_start()
Starts a Terminus.Planaria
process without links (outside of a supervision
tree).
See start_link/3
for more information.
Specs
start_link(atom(), config(), keyword()) :: GenServer.on_start()
Starts a Terminus.Planaria
process linked to the current process.
This is often used to start the Planaria as part of a supervision tree.
Link to this section Callbacks
Specs
Invoked for each new transaction seen by the Planaria.
This is the main callback you will need to implement for your Planaria module. Typically it will be used to pull the necessary data from each transaction event and store it to a local database.
When an unconfirmed transaction is seen the callback is invoked with the
tx_event
of :mempool
. For each confirmed transaction,
the callback is invoked with the tx_event
of :block
.
The callback can return any value.
Examples
def handle_data(:block, txns) do
txns
|> Enum.map(&MyApp.Transaction.build/1)
|> Repo.insert(on_conflict: :replace_all, conflict_target: :txid)
end
def handle_data(:mempool, txns) do
txns
|> Enum.map(&MyApp.Transaction.build/1)
|> Repo.insert
end
Specs
handle_tape(tape_event(), tape()) :: {:ok, tape()} | any()
Invoked when a Planaria starts and also after each crawl of new blocks.
This callback can be used to load and persist the tape head so a re-crawl isn't necessary if the process is interrupted.
When a Planaria starts the callback is invoked with the tape_event
of :start
. This provides an oppurtunity to load the current :head
of the
tape from a database and update the given tape
.
The callback must return {:ok, tape}
.
After each crawl of block data the callback in invoked with the tape_event
if :block
. This allows us to store the tape
:head
. In this case any return value is acceptable.
Examples
Load the :head
from a database when the Planaria starts.
def handle_tape(:start, tape) do
tape = case MyApp.Config.get("tape_head") do
{:ok, head} -> put_in(tape.head, head)
_ -> tape
end
{:ok, tape}
end
Persist the :head
after each crawl of new blocks.
def handle_tape(:update, tape) do
MyApp.Config.put("tape_head", tape.head)
end