Maelstrom behaviour (maelstrom v0.1.0)
Allows you to create servers which implement the Maelstrom protocol.
Maelstrom is a workbench for learning distributed systems by writing your own.
Usage
To implement a server:
- Create a module and
use Maelstrom
- Implement one or more
handle_message
heads. - Call
MyModule.run_forever()
.
I recommend you do this in a .exs script. Example:
defmodule Echo do
use Maelstrom
def handle_message(_src, _dest, %{"echo" => echo}, state, _) do
{:reply, %{"type" => "echo_ok", "echo" => echo}, state}
end
end
Echo.run_forever()
You could then run this with mix run
e.g. mix run echo.exs
.
Tip
Maelstrom expects a single binary with no arguments to call for testing. In order to accomplish this, wrap your mix run command in a shell script (see demos for examples).
Examples
For more examples, see demos.
Link to this section Summary
Callbacks
Docs on a callback?
Functions
Logs an error to :stderr in the way that Maelstrom expects.
Run the server forever and prevent the script from exiting. Handles IO to and
from your server node. You should invoke this directly on your server module
instead of calling it on Maelstrom
e.g. MyModule.run_forever()
. This function
is defined automatically when you use Maelstrom
.
Send an error message to replyee
in reply to the message indentified by in_reply_to
. The error
message will include the error code error_code
(see
the Maelstrom docs for
a list of codes) and a string message error_text
.
Send a message with the body body
to dest
.
Send a reply to replyee
replying to the message with id in_reply_to
. Include the body body
.
Send an RPC-style message with the body body
to dest
. callback
is a function
that will be invoked if and when dest
replies to the message. The arguments to callback are
Link to this section Types
body()
@type body() :: Map
error_code()
@type error_code() :: non_neg_integer()
handler_result()
msg_id()
@type msg_id() :: non_neg_integer()
node_id()
@type node_id() :: String.t()
node_state()
@type node_state() :: Map
rpc_callback()
@type rpc_callback() :: (body(), body(), state() -> handler_result())
state()
@type state() :: Map
Link to this section Callbacks
handle_message(node_id, node_id, body, state, node_state)
@callback handle_message(node_id(), node_id(), body(), state(), node_state()) :: handler_result()
Docs on a callback?
Link to this section Functions
log(log, adapter \\ GenServer)
Logs an error to :stderr in the way that Maelstrom expects.
run_forever(initial_state \\ %{})
@spec run_forever(state()) :: :ok
Run the server forever and prevent the script from exiting. Handles IO to and
from your server node. You should invoke this directly on your server module
instead of calling it on Maelstrom
e.g. MyModule.run_forever()
. This function
is defined automatically when you use Maelstrom
.
send_error(replyee, in_reply_to, error_code, error_text, adapter \\ GenServer)
@spec send_error(node_id(), msg_id(), error_code(), String.t(), atom()) :: :ok
Send an error message to replyee
in reply to the message indentified by in_reply_to
. The error
message will include the error code error_code
(see
the Maelstrom docs for
a list of codes) and a string message error_text
.
send_message(dest, body, adapter \\ GenServer)
Send a message with the body body
to dest
.
send_reply(replyee, in_reply_to, body, adapter \\ GenServer)
Send a reply to replyee
replying to the message with id in_reply_to
. Include the body body
.
send_rpc(dest, body, callback, adapter \\ GenServer)
@spec send_rpc(node_id(), body(), rpc_callback(), atom()) :: :ok
Send an RPC-style message with the body body
to dest
. callback
is a function
that will be invoked if and when dest
replies to the message. The arguments to callback are:
- The body of the original message.
- The body of the reply.
- The state of your server.
The callback should reply with a valid handler response:
{:reply, reply_body, new_server_state}
{:noreply, new_server_state}
{:error, error_code, error_message, new_server_state}