Packages
spawn_sdk
0.5.1
2.0.0-RC9
2.0.0-RC8
2.0.0-RC7
2.0.0-RC6
2.0.0-RC5
2.0.0-RC4
2.0.0-RC3
2.0.0-RC14
2.0.0-RC13
2.0.0-RC12
2.0.0-RC11
2.0.0-RC10
1.4.3
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-rc3
1.0.0-rc16
1.0.0-rc1
1.0.0-rc.38
1.0.0-rc.37
1.0.0-rc.36
1.0.0-rc.35
1.0.0-rc.34
1.0.0-rc.33
1.0.0-rc.32
1.0.0-rc.31
1.0.0-rc.30
1.0.0-rc.29
1.0.0-rc.28
1.0.0-rc.27
1.0.0-rc.26
1.0.0-rc.25
1.0.0-rc.24
1.0.0-rc.23
1.0.0-rc.22
1.0.0-rc.21
1.0.0-rc.20
1.0.0-rc.19
1.0.0-rc.18
1.0.0-rc.17
1.0.0-rc.2
0.6.3
0.6.2
0.6.1
0.6.0
0.5.5
0.5.4
0.5.3
0.5.1
0.5.0
0.5.0-rc.13
0.5.0-rc.12
0.5.0-rc.11
0.5.0-rc.10
0.5.0-rc.9
0.5.0-rc.8
0.5.0-rc.7
0.5.0-rc.6
0.5.0-rc.5
0.5.0-rc.3
0.5.0-alpha.13
0.5.0-alpha.12
0.5.0-alpha.11
0.5.0-alpha.10
0.5.0-alpha.9
0.5.0-alpha.8
0.5.0-alpha.7
0.5.0-alpha.6
0.5.0-alpha.5
0.5.0-alpha.4
0.5.0-alpha.3
0.5.0-alpha.2
0.5.0-alpha.1
0.1.0
Spawn Elixir SDK is the support library for the Spawn Actors System
Current section
Files
Jump to
Current section
Files
lib/actor.ex
defmodule SpawnSdk.Actor do
@moduledoc """
Documentation for `Actor`.
Actor look like this:
defmodule MyActor do
use SpawnSdk.Actor,
name: "joe",
persistent: false,
state_type: Io.Eigr.Spawn.Example.MyState,
deactivate_timeout: 5_000,
snapshot_timeout: 2_000
require Logger
alias Io.Eigr.Spawn.Example.{MyState, MyBusinessMessage}
defact sum(%MyBusinessMessage{value: value} = data}, %Context{state: state} = ctx) do
Logger.info("Received Request...")
new_value = (state.value || 0) + value
%Value{}
|> Value.of(%MyBusinessMessage{value: new_value}, %MyState{value: new_value})
|> Value.reply!()
end
"""
alias SpawnSdk.{Context, Value}
@type command :: String.t()
@type context :: Context.t()
@type data :: module()
@type error :: any()
@type value :: Value.t()
@callback handle_command({command(), data()}, context()) ::
{:reply, value()} | {:error, error()} | {:error, error(), value()}
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
alias SpawnSdk.{
Context,
Flow.Broadcast,
Flow.Pipe,
Flow.Forward,
Flow.SideEffect,
Value
}
import SpawnSdk.Actor
use SpawnSdk.Defact
import SpawnSdk.System.SpawnSystem,
only: [
invoke: 2,
register: 2,
spawn_actor: 2
]
Module.register_attribute(__MODULE__, :actor_opts, persist: true)
Module.put_attribute(__MODULE__, :actor_opts, opts)
@behaviour SpawnSdk.Actor
@before_compile SpawnSdk.Actor
end
end
defmacro __before_compile__(_a) do
opts = Module.get_attribute(__CALLER__.module, :actor_opts)
actions = Module.get_attribute(__CALLER__.module, :defact_exports)
actor_name = Keyword.get(opts, :name, Atom.to_string(__CALLER__.module))
actor_kind = Keyword.get(opts, :kind, :SINGLETON)
caller_module = __CALLER__.module
channel_group = Keyword.get(opts, :channel, nil)
min_pool_size = Keyword.get(opts, :min_pool_size, 1)
max_pool_size = Keyword.get(opts, :max_pool_size, 0)
state_type = Keyword.get(opts, :state_type, nil)
stateful = Keyword.get(opts, :stateful, true)
tags = Keyword.get(opts, :tags, nil)
if stateful and !Code.ensure_loaded?(Statestores.Supervisor) do
raise """
ArgumentError. You need to add :spawn_statestores to your dependency if you are going to use persistent actors.
Otherwise, set `stateful: false` in your Actor attributes
"""
end
if state_type == nil and stateful do
raise "ArgumentError. State type is mandatory if stateful is true"
end
if stateful and actor_kind == :POOLED do
raise ArgumentError, """
Pooled Actors cannot be stateful.
Please set stateful attribute to false to be able to register Actor #{actor_name}
"""
end
deactivate_timeout = Keyword.get(opts, :deactivate_timeout, 10_000)
snapshot_timeout = Keyword.get(opts, :snapshot_timeout, 2_000)
quote do
def __meta__(:actions) do
unquote(actions)
|> Enum.filter(fn {_action, %{timer: timer}} -> is_nil(timer) end)
|> Enum.map(fn {action, %{timer: timer}} -> action end)
end
def __meta__(:timers) do
unquote(actions)
|> Enum.reject(fn {_action, %{timer: timer}} -> is_nil(timer) end)
|> Enum.map(fn {action, %{timer: timer}} -> {action, timer} end)
end
def __meta__(:channel), do: unquote(channel_group)
def __meta__(:name) do
actor_name = unquote(actor_name)
kind = unquote(actor_kind)
if kind == :ABSTRACT do
unless :persistent_term.get("actor:#{actor_name}", false) do
:persistent_term.put("actor:#{actor_name}", unquote(caller_module))
end
actor_name
else
actor_name
end
end
def __meta__(:kind), do: unquote(actor_kind)
def __meta__(:stateful), do: unquote(stateful)
def __meta__(:state_type), do: unquote(state_type)
def __meta__(:min_pool_size), do: unquote(min_pool_size)
def __meta__(:max_pool_size), do: unquote(max_pool_size)
def __meta__(:snapshot_timeout), do: unquote(snapshot_timeout)
def __meta__(:deactivate_timeout), do: unquote(deactivate_timeout)
def __meta__(:tags), do: Map.new(unquote(tags) || %{})
end
end
end