Spawn Elixir SDK is the support library for the Spawn Actors System. Spawn is a Stateful Serverless Platform for providing the multi-language Actor Model. For a broader understanding of Spawn, please consult its official repository.
The advantage of the Elixir SDK over other SDKs is in Elixir’s native ability to connect directly to an Erlang network. For this reason, the Elixir SDK allows any valid Elixir application to be part of a Spawn network without needing a sidecar attached.
Available in Hex, the package can be installed
by adding spawn_sdk
and spawn_statestores_*
to your list of dependencies in mix.exs
:
def deps do
[
{:spawn_sdk, "~> 2.0.0-RC9"},
# You can uncomment one of those dependencies if you are going to use Persistent Actors
#{:spawn_statestores_mariadb, "~> 2.0.0-RC9"},
#{:spawn_statestores_postgres, "~> 2.0.0-RC9"},
]
end
After creating an Elixir application project, create the protobuf files for your business domain. It is common practice to do this under the priv/ folder. Let’s demonstrate an example:
syntax = "proto3";
package io.eigr.spawn.example;
message MyState {
int32 value = 1;
}
message MyBusinessMessage {
int32 value = 1;
}
It is important to try to separate the type of message that must be stored as the actors’ state from the type of messages that will be exchanged between their actors’ operations calls. In other words, the Actor’s internal state is also represented as a protobuf type, and it is a good practice to separate these types of messages from the others in its business domain.
In the above case MyState
is the type protobuf that represents the state of the Actor that we will create later
while MyBusiness
is the type of message that we will send and receive from this Actor.
Now that we have defined our input and output types as Protobuf types we will need to compile these files to generate their respective Elixir modules. An example of how to do this can be found here
NOTE: You need to have installed the elixir plugin for protoc. More information on how to obtain and install the necessary tools can be found here here
Now that the protobuf types have been created we can proceed with the code. Example definition of an Actor.
In this example we are creating an actor in a Named way, that is, it is a known actor at compile time.
defmodule SpawnSdkExample.Actors.MyActor do
use SpawnSdk.Actor,
name: "jose", # Default is Full Qualified Module name a.k.a __MODULE__
kind: :named, # Default is already :named. Valid are :named | :unnamed
stateful: true, # Default is already true
state_type: Io.Eigr.Spawn.Example.MyState, # or :json if you don't care about protobuf types
deactivate_timeout: 30_000,
snapshot_timeout: 2_000
require Logger
alias Io.Eigr.Spawn.Example.{MyState, MyBusinessMessage}
# The callback could also be referenced to an existing function:
# action "SomeAction", &some_defp_handler/0
# action "SomeAction", &SomeModule.handler/1
# action "SomeAction", &SomeModule.handler/2
init fn %Context{state: state} = ctx ->
Logger.info("[joe] Received InitRequest. Context: #{inspect(ctx)}")
Value.of()
|> Value.state(state)
end
action "Sum", fn %Context{state: state} = ctx, %MyBusinessMessage{value: value} = data ->
Logger.info("Received Request: #{inspect(data)}. Context: #{inspect(ctx)}")
new_value = if is_nil(state), do: value, else: (state.value || 0) + value
Value.of(%MyBusinessMessage{value: new_value}, %MyState{value: new_value})
end
end
Once we define our actors we can now declare our supervision tree:
defmodule SpawnSdkExample.Application do
@moduledoc false
use Application
@impl true
def start(_type, _args) do
children = [
{
SpawnSdk.System.Supervisor,
system: "spawn-system",
actors: [
SpawnSdkExample.Actors.MyActor,
SpawnSdkExample.Actors.UnnamedActor,
SpawnSdkExample.Actors.ClockActor,
SpawnSdkExample.Actors.PooledActor
]
}
]
opts = [strategy: :one_for_one, name: SpawnSdkExample.Supervisor]
Supervisor.start_link(children, opts)
end
end
To deploy your actors in a cluster you need to use our Controller for Kubernetes. Detailed information on how to proceed can be found here and here. But you can also run your Elixir application in the traditional way as follows:
MIX_ENV=prod USER_FUNCTION_PORT=8092 PROXY_DATABASE_TYPE=mysql SPAWN_STATESTORE_KEY=3Jnb0hZiHIzHTOih7t2cTEPEpY98Tu1wvQkPfq/XwqE= iex --name [email protected] -S mix
NOTE: For reasons of compatibility with our controller, it is necessary to configure your Spawn application using environment variables instead of the traditional Elixir configuration mechanism.
WARNING: This example uses the MySQL database as persistent storage for its actors. And it is also expected that you have previously created a database called eigr-functions-db in the MySQL instance.
The full example of this application can be found here.
And links to other examples can be found in our github readme page.