Current section
Files
Jump to
Current section
Files
lib/backbone.ex
defmodule Agala.Backbone do
@moduledoc """
This behaviour specifies protocol, that should be implemented for each and every backbone,
that can be used with `Agala` framework.
"""
@typedoc """
Backbone is represented by it's name as Atom
"""
@type t :: atom()
@doc """
This function is given to configure the backbone.
"""
@callback bake_backbone_config() :: :ok | no_return()
@doc """
This function is used to retrieve backbone's config
"""
@callback get_config() :: any()
@doc """
This method is used to show bot's **receive <-> handle** load.
* **Active Receivers** can use this information in order to stop retrieving new updates from third-parties.
* **Passive Receivers** can use this information to stop serving for a moment until load will not decrease.
Example:
# For active receivers
def get_updates() do
# check if service is overloaded
case Agala.Backbone.Foo.get_load(MyApp.MyBot) do
{:ok, overload} when overload > 1000 ->
# This server is overloaded
# waiting a bit, to let handlers deal with overload
:timer.sleep(10_000)
download_updates()
{:ok, normal} ->
# We should not wait - load is normal
download_updates()
end
end
# For passive receivers
def call(conn, opts) do
# check if service is overloaded
case Agala.Backbone.Foo.get_load(MyApp.MyBot) do
{:ok, overload} when overload > 1000 ->
# This server is overloaded
# Stop serving
send_500_http_error(conn)
{:ok, normal} ->
# We should not wait - load is normal
proceed_update(conn)
end
end
"""
@callback get_load(bot_name :: atom()) :: {:ok, integer} | {:error, any()}
@doc """
This method is uused to initialize bot. It should be probably used upon bot initialization
"""
@callback init_bot(bot_name :: atom()) :: :ok | {:error, any()}
@doc """
This method is used to add new element to the end of queue, defined by `Agala.Bot` and queue's **CID**
"""
@callback push(bot_name :: Agala.Bot.name(), cid :: any(), value :: any()) ::
:ok | {:error, any()}
@doc """
This method is used to pull available element from the queue, defined by `Agala.Bot`
"""
@callback pull(bot_name :: Agala.Bot.name()) ::
{:ok, any()} | {:error, :empty} | {:error, any()}
# @doc """
# This method will subscribe caller process to get new events from the bot.
# Messages will be of type `Agala.Conn.t()`.
# """
# @callback subscribe(bot_name :: Agala.Bot.name()) :: {:ok, any()} | {:error, any()}
### -------------------------------------------------------------------------------------------------------------------------------------------------
### API
### -------------------------------------------------------------------------------------------------------------------------------------------------
@doc """
Returns supervision config for specififed backbone.
If the backbone is not specified - returns empty list
"""
@spec supervisor() :: [atom()] | []
def supervisor() do
case bake_backbone_config() do
{:ok, :empty} -> []
{:ok, backbone} -> [backbone]
end
end
@doc """
This function will check backbone configuration.
If everything is right - `ok tuple` with `Agala.Backbone` implementation module will be returned.
If backbone is not specified -
If it's not specified correct - function will rise.
"""
@spec bake_backbone_config() :: {:ok, t} | {:ok, :empty} | no_return()
def bake_backbone_config() do
# Checking backbone
case Application.get_env(:agala, :backbone, nil) do
nil ->
{:ok, :empty}
backbone ->
unless Code.ensure_loaded?(backbone) do
raise ArgumentError,
"backbone #{inspect(backbone)} was not compiled, " <>
"ensure it is correct and it is included as a module in the project"
end
unless Agala.Backbone in Agala.Util.behaviours_list(backbone) do
raise ArgumentError,
"backbone #{inspect(backbone)} does not implement Agala.Backbone behaviour, " <>
"ensure it is correct and it is included as a module in the project"
end
# All ok. Backing backbone specific config
:ok = backbone.bake_backbone_config()
{:ok, backbone}
end
end
end