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:

  1. Create a module and use Maelstrom
  2. Implement one or more handle_message heads.
  3. 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

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

@type body() :: Map
Link to this type

error_code()

@type error_code() :: non_neg_integer()
Link to this type

handler_result()

@type handler_result() ::
  {:reply, body(), state()}
  | {:noreply, state()}
  | {:error, non_neg_integer(), String.t(), state()}
@type msg_id() :: non_neg_integer()
@type node_id() :: String.t()
Link to this type

node_state()

@type node_state() :: Map
Link to this type

rpc_callback()

@type rpc_callback() :: (body(), body(), state() -> handler_result())
@type state() :: Map

Link to this section Callbacks

Link to this callback

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

Link to this function

log(log, adapter \\ GenServer)

@spec log(String.t(), atom()) :: :ok

Logs an error to :stderr in the way that Maelstrom expects.

Link to this function

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.

Link to this function

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.

Link to this function

send_message(dest, body, adapter \\ GenServer)

@spec send_message(node_id(), body(), atom()) :: :ok

Send a message with the body body to dest.

Link to this function

send_reply(replyee, in_reply_to, body, adapter \\ GenServer)

@spec send_reply(node_id(), msg_id(), body(), atom()) :: :ok

Send a reply to replyee replying to the message with id in_reply_to. Include the body body.

Link to this function

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:

  1. The body of the original message.
  2. The body of the reply.
  3. The state of your server.

The callback should reply with a valid handler response:

  1. {:reply, reply_body, new_server_state}
  2. {:noreply, new_server_state}
  3. {:error, error_code, error_message, new_server_state}