• Stars
    star
    193
  • Rank 201,081 (Top 4 %)
  • Language
    Elixir
  • License
    MIT License
  • Created about 6 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Easy local cluster creation for Elixir to aid in unit testing

LocalCluster

Build Status Hex.pm Version Documentation

This library is designed to assist in testing distributed states in Elixir which require a number of local nodes.

The aim is to provide a small set of functions which hide the complexity of spawning local nodes, as well as providing the ease of cleaning up the started nodes. The entire library is simple shimming around the Erlang APIs for dealing with distributed nodes, As some of it is non-obvious, and as I need this code for several projects, I span it out as a smaller project.

Installation

To install it for your project, you can pull it directly from Hex. Rather than use the version shown below, you can use the the latest version from Hex (shown at the top of this README).

def deps do
  [{:local_cluster, "~> 1.2", only: [:test]}]
end

Documentation and examples can be found on Hexdocs as they're updated automatically alongside each release. Note that you should only use the :test flag in your dependency if you're not using it for other environments.

Setup

To configure your test suites for cluster testing, you need to run through a one-time setup to change some stuff in your test_helper.exs. This is required to avoid some potential issues with your node name changing after your application tree has already stated. This also reduces some bloat due to having LocalCluster.start/0 in most test cases. The snippet below can be used as a sample helper file. Make sure to change the application name to match your application name.

# start the current node as a manager
:ok = LocalCluster.start()

# start your application tree manually
Application.ensure_all_started(:my_app)

# run all tests!
ExUnit.start()

You will also need to pass the --no-start flag to mix test. Fortunately this is easy enough, as you can add an alias in your mix.exs to do this automatically:

def project do
  [
    # ...
    aliases: [
      test: "test --no-start"
    ]
    # ...
  ]
end

This library itself uses this setup, so you can copy/paste as needed or use as an example when integrating into your own codebase. Note that you must have ensured that epmd has been started before using this lib; typically with epmd -daemon.

Usage

As mentioned above, the API is deliberately tiny to make it easier to use this library when testing. Below is an example of using this library to spawn a set of child nodes for testing:

defmodule MyTest do
  use ExUnit.Case

  test "something with a required cluster" do
    nodes = LocalCluster.start_nodes("my-cluster", 3)

    [node1, node2, node3] = nodes

    assert Node.ping(node1) == :pong
    assert Node.ping(node2) == :pong
    assert Node.ping(node3) == :pong

    :ok = LocalCluster.stop_nodes([node1])

    assert Node.ping(node1) == :pang
    assert Node.ping(node2) == :pong
    assert Node.ping(node3) == :pong

    :ok = LocalCluster.stop()

    assert Node.ping(node1) == :pang
    assert Node.ping(node2) == :pang
    assert Node.ping(node3) == :pang
  end
end

After calling start_nodes/2, you will receive a list of node names you can then use to communicate with via RPC or however you'd like. Although they're automatically cleaned up when the calling process dies, you can manually stop nodes as well to test disconnection.

In the case you need to control application startup manually, you can make use of the :applications option. This option determines startup order of your applications, and allows you to exclude applications from the startup sequence. If this is not provided, the default behaviour will be to start the same applications as are running on the local node. These applications are loaded with all dependencies via Application.ensure_all_started/2.

LocalCluster.start_nodes(:spawn, 3, [
  applications: [
    :start_this_application,
    :and_then_this_one
  ]
])

If you need to load any additional files onto the remote nodes, you can make use of the :files option at startup time by providing an absolute file path to compile on the cluster. This is necessary if you wish to spawn tasks onto the cluster from inside your test code, as your test code is not loaded into the cluster automatically:

defmodule MyTest do
  use ExUnit.Case

  test "spawning tasks on a cluster" do
    nodes = LocalCluster.start_nodes(:spawn, 3, [
      files: [
        __ENV__.file
      ]
    ])

    [node1, node2, node3] = nodes

    assert Node.ping(node1) == :pong
    assert Node.ping(node2) == :pong
    assert Node.ping(node3) == :pong

    caller = self()

    Node.spawn(node1, fn ->
      send(caller, :from_node_1)
    end)

    Node.spawn(node2, fn ->
      send(caller, :from_node_2)
    end)

    Node.spawn(node3, fn ->
      send(caller, :from_node_3)
    end)

    assert_receive :from_node_1
    assert_receive :from_node_2
    assert_receive :from_node_3
  end
end

If you need to override the application environment inherrited by the remote nodes, you can use the :environment option at startup. This koption set is merged over the environment inside the started nodes:

LocalCluster.start_nodes(:spawn, 1, [
  environment: [
    my_app: [
      port: 9999
    ]
  ]
])

More Repositories

1

cachex

A powerful caching library for Elixir with support for transactions, fallbacks and expirations
Elixir
1,519
star
2

runiq

An efficient way to filter duplicate lines from input, Γ  la uniq.
Rust
203
star
3

eternal

Keep your ETS tables running forever using bouncing GenServers
Elixir
84
star
4

bytelines

Read input lines as byte slices for high efficiency
Rust
61
star
5

jen

A fast utility to generate fake/test documents based on a template
Rust
59
star
6

sleeplocks

BEAM friendly spinlocks for Elixir/Erlang
Erlang
53
star
7

s3-utils

Utilities and tools based around Amazon S3 to provide convenience APIs in a CLI
Rust
53
star
8

stash

A small and user-friendly ETS wrapper for caching in Elixir
Elixir
52
star
9

limber

A simple (but quick) tool for backing up Elasticsearch documents
Rust
50
star
10

retainer

Minimal async cache in Rust with support for key expirations
Rust
49
star
11

s3-meta

Gather metadata about your S3 buckets
Rust
49
star
12

tiny

A small, fast and fully compliant JSON parser in Elixir
Elixir
47
star
13

s3-concat

Concatenate Amazon S3 files remotely using flexible patterns
Rust
38
star
14

usher

Parameterized routing for generic resources in Rust
Rust
36
star
15

efflux

Easy Hadoop Streaming and MapReduce interfaces in Rust
Rust
34
star
16

siphash-java

SipHash in Java; zero-allocation and streaming implementations
Java
29
star
17

expool

Extremely simple Process pooling and task submission in Elixir
Elixir
25
star
18

vessel

Elixir MapReduce interfaces with Hadoop Streaming integration
Elixir
23
star
19

neek

A simple way to filter duplicate lines from a list, Γ  la uniq.
JavaScript
22
star
20

it.each

A Mocha extension allowing for test looping with (a)sync calls
JavaScript
21
star
21

hypex

Fast HyperLogLog implementation for Elixir/Erlang
Elixir
20
star
22

siphash-elixir

An Elixir implementation of the SipHash cryptographic hash family
Elixir
18
star
23

detox

Quickly clean up your development directories before backups
Rust
18
star
24

capture-console

Simple and easy stdio capture for Node.js
JavaScript
16
star
25

RSSDemo

A simple demonstration of an RSS reader for Android.
Java
16
star
26

sentix

A cross-platform file watcher for Elixir based on fswatch.
Elixir
15
star
27

siphash-cpp

A small C++ implementation of SipHash using a streaming algorithm with CLI
C++
12
star
28

dot-notes-js

Simple dot/bracket notation parsing/conversion for JSON
JavaScript
12
star
29

jumper

Jump consistent hash implementation in Elixir (without NIFs)
Elixir
12
star
30

rabbitmq-delimiter-exchange

Performant RabbitMQ exchange with support for multiple routing keys per message
Makefile
12
star
31

unsafe

Generate unsafe (!) bindings for Elixir functions
Elixir
12
star
32

deppie

Elixir's coolest deprecation logger
Elixir
11
star
33

pre_plug

Guarantee your Elixir Plugs execute on every request
Elixir
9
star
34

docker-geoipupdate

Minimal container for updating GeoIP databases on your host system
Shell
8
star
35

global-flags

Write once global flags for Erlang and Elixir.
Erlang
8
star
36

kscrash-converter

Converts KSCrash JSON output to Apple format
JavaScript
7
star
37

dot-notes-elixir

Simple dot/bracket notation parsing/conversion for Maps/Lists
Elixir
7
star
38

gen_delegate

Macro based delegates for GenServer functions
Elixir
7
star
39

andrest

Basic REST protocol implementation for Android.
Java
6
star
40

phoenix_mongo

An example of setting up Phoenix to use the unofficial Mongo Ecto library
CSS
6
star
41

rabbitmq-manager

A RabbitMQ monitoring system written in NodeJS.
JavaScript
6
star
42

elasticsearch-bulk-operator

Elasticsearch Bulk API bindings using the Java REST client
Java
5
star
43

lowdb-titanium-adapter

Titanium SDK adapter for the LowDB embedded database
JavaScript
4
star
44

loki-titanium-adapter

Titanium SDK adapter for the LokiJS embedded database
JavaScript
4
star
45

dot-notes-java

Jackson JSON flattening/iteration/inflation using simple key parsing
Java
4
star
46

siphash-c

A C (89) implementation of the SipHash cryptographic hash family, using a single pass algorithm
C
3
star
47

child-spec-compat

Compatibility macros for Elixir v1.5+ child specifications
Elixir
3
star
48

global-lazy

Lazy global initialization for Elixir, without state
Elixir
3
star
49

argle

Convenient argument shifting because you know you need it
JavaScript
2
star
50

dropwizard-environment-substitutor

Global environment overrides for Dropwizard configuration
Java
2
star
51

native-hashset

A native HashSet implementation for Node.js and io.js.
C++
2
star
52

luger

Handy logging plug for Elixir with IP and status support
Elixir
2
star
53

dottie

A small library for dealing with JSON dot notation.
Java
2
star
54

archive_bundle

A small task to bundle dependencies into an archive build in Elixir
Elixir
1
star
55

dep-validate

Dependency verification for npm packages with Gulp support
JavaScript
1
star
56

granular-logger

A Winston wrapper to allow time-based log files
JavaScript
1
star
57

expansion-js

Simple library for NodeJS to expand (and make it easier to expand) the functionality of objects
JavaScript
1
star
58

neps

Node.js REPL with package installation and automatic loading support
JavaScript
1
star
59

appddl

Extremely small CLI tool used to update agent archives from AppDynamics
Rust
1
star
60

noddle

Simple loading of the current project workspace into a REPL
JavaScript
1
star
61

json-output-format

JSON output formats for Hadoop MapReduce jobs
Java
1
star
62

svn-to-git

Scripts to convert a Subversion repository to Git.
Shell
1
star
63

projects

A collection of notes and whatnot in regards to either planned or existing projects
JavaScript
1
star
64

waterline-express-example

A base MVC layout using Waterline and Express
JavaScript
1
star