Packages

Schism provides a simple api for partitioning networked BEAM instances without having to leave elixir code.

Current section

Files

Jump to
schism lib schism.ex
Raw

lib/schism.ex

defmodule Schism do
@moduledoc """
Schism allows you to create network partitions in erlang nodes without
needing to leave elixir.
Let's say that we have 5 nodes and we want to test what happens when they
disconnect from each other. We can use Schism like so:
```elixir
test "netsplits" do
[n1, n2, n3, n4, n5] = nodes
# Partition our nodes
Schism.partition([n1, n3])
Schism.partition([n4])
Schism.partition([n2, n5])
# Test some stuff...
# Heal our partitions
Schism.heal([n1, n3])
Schism.heal([n2, n4, n5])
end
```
This api is useful for testing and development in conjunction with tools like
[local cluster](https://github.com/whitfin/local-cluster) and
[propcheck](https://github.com/alfert/propcheck).
"""
@doc """
Creates a partition amongst a set of nodes. Any nodes in the partition
will be able to see each other but no other nodes in the network. The
partitioned nodes will still be able to see the node that induced the
partition. Otherwise we would not be able to heal the partition.
"""
@spec partition([Node.t], String.t) :: [Node.t] | none()
def partition(nodes, id \\ random_string()) when is_binary(id) do
manager = Node.self()
for node <- nodes do
# Set the remote nodes cookie to a different value
true = :rpc.call(node, :erlang, :set_cookie, [node, String.to_atom(id)])
# Force the node to disconnect from all other nodes that aren't us or in
# the partition
node
|> :rpc.call(Node, :list, [])
|> Enum.reject(fn n -> n == manager || n in nodes end)
|> Enum.each(fn n -> :rpc.call(node, Node, :disconnect, [n]) end)
# Ensure we can still talk to the node
:pong = Node.ping(node)
end
nodes
end
@doc """
Re-connects the nodes to the cluster.
"""
@spec heal([Node.t]) :: [Node.t] | none()
def heal(nodes) do
for n <- nodes do
# Reset the node and ensure we can still talk with it
true = :rpc.call(n, :erlang, :set_cookie, [n, :erlang.get_cookie()])
:pong = Node.ping(n)
for other_node <- nodes do
:rpc.call(n, Node, :connect, [other_node])
end
end
end
defp random_string do
:crypto.strong_rand_bytes(10)
|> Base.url_encode64
|> binary_part(0, 10)
end
end