Skip to content

Commit

Permalink
Chuck Norris! :hurtrealbad: (or: Added code for chapter 9)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamintanweihao committed Nov 14, 2015
1 parent daf3574 commit 34a85e3
Show file tree
Hide file tree
Showing 11 changed files with 499 additions and 0 deletions.
80 changes: 80 additions & 0 deletions chapter_9/chucky/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Chucky - A Distributed and Fault-Tolerant Chuck Norris Facts Disher

![Chuck](http://i.imgur.com/wwFsWiA.jpg)

## Step 1: Figure out your hostname

```
% hostname -s
imac
```

## Step 2: Configure `config/NODE_NAME.config`

Here's an example:

```elixir
[{kernel,
[{distributed,
[{chucky,
5000,
['a@imac', {'b@imac', 'c@imac'}]}]},
{sync_nodes_mandatory, ['b@imac', 'c@imac']},
{sync_nodes_timeout, 30000}
]}].
```

## Step 3: Compile


```
% mix compile
```

## Step 4: Run it!

Open 3 different terminals, and on each of them, run these commands:

```
% iex --sname a -pa _build/dev/lib/chucky/ebin --app chucky --erl "-config config/a.config"
% iex --sname b -pa _build/dev/lib/chucky/ebin --app chucky --erl "-config config/b.config"
% iex --sname c -pa _build/dev/lib/chucky/ebin --app chucky --erl "-config config/c.config"
```

In each terminal, run:

```elixir
iex > Chucky.fact
"In a fight between Batman and Darth Vader, the winner would be Chuck Norris."
```

You can also use `Application.started_applications/1` to see where the application is being run on.

## Step 5: Watching failover in action

Kill the first session (`a@HOSTNAME`), then watch `b@HOSTNAME` get started:

```
07:33:04.831 [info] b@manticore starting distributed
07:33:12.025 [info] Application chucky exited: :stopped
07:33:42.300 [info] b@manticore starting distributed
```

## Step 6: Watching takeover in action

Start `a@HOSTNAME` again:

```
% iex --sname a -pa _build/dev/lib/chucky/ebin --app chucky --erl "-config config/a.config"
```

Watch `a@HOSTNAME` take over `b@HOSTNAME`:

```
07:39:49.820 [info] a@manticore is taking over b@manticore
```

8 changes: 8 additions & 0 deletions chapter_9/chucky/config/a.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[{kernel,
[{distributed,
[{chucky,
5000,
[a@manticore, {b@manticore, c@manticore}]}]},
{sync_nodes_mandatory, [b@manticore, c@manticore]},
{sync_nodes_timeout, 30000}
]}].
8 changes: 8 additions & 0 deletions chapter_9/chucky/config/b.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[{kernel,
[{distributed,
[{chucky,
5000,
[a@manticore, {b@manticore, c@manticore}]}]},
{sync_nodes_mandatory, [a@manticore, c@manticore]},
{sync_nodes_timeout, 30000}
]}].
8 changes: 8 additions & 0 deletions chapter_9/chucky/config/c.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[{kernel,
[{distributed,
[{chucky,
5000,
[a@manticore, {b@manticore, c@manticore}]}]},
{sync_nodes_mandatory, [a@manticore, b@manticore]},
{sync_nodes_timeout, 30000}
]}].
30 changes: 30 additions & 0 deletions chapter_9/chucky/config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.

# You can configure for your application as:
#
# config :chucky, key: :value
#
# And access this configuration in your application as:
#
# Application.get_env(:chucky, :key)
#
# Or configure a 3rd-party app:
#
# config :logger, level: :info
#

# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env}.exs"
266 changes: 266 additions & 0 deletions chapter_9/chucky/facts.txt

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions chapter_9/chucky/lib/chucky.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule Chucky do
use Application
require Logger

def start(type, _args) do
import Supervisor.Spec
children = [
worker(Chucky.Server, [])
]

case type do
:normal ->
Logger.info("Application is started on #{node}")

{:takeover, old_node} ->
Logger.info("#{node} is taking over #{old_node}")

{:failover, old_node} ->
Logger.info("#{old_node} is failing over to #{node}")
end

opts = [strategy: :one_for_one, name: {:global, Chucky.Supervisor}]
Supervisor.start_link(children, opts)
end

def fact do
Chucky.Server.fact
end

end
37 changes: 37 additions & 0 deletions chapter_9/chucky/lib/server.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule Chucky.Server do
use GenServer

#######
# API #
#######

def start_link do
GenServer.start_link(__MODULE__, [], [name: {:global, __MODULE__}])
end

def fact do
GenServer.call({:global, __MODULE__}, :fact)
end

#############
# Callbacks #
#############

def init([]) do
:random.seed(:os.timestamp)
facts = "facts.txt"
|> File.read!
|> String.split("\n")

{:ok, facts}
end

def handle_call(:fact, _from, facts) do
random_fact = facts
|> Enum.shuffle
|> List.first

{:reply, random_fact, facts}
end

end
23 changes: 23 additions & 0 deletions chapter_9/chucky/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule Chucky.Mixfile do
use Mix.Project

def project do
[app: :chucky,
version: "0.0.1",
elixir: "~> 1.1",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps]
end

def application do
[applications: [:logger],
# registered: [Chucky, Chucky.Supervisor, Chucky.Server],
mod: {Chucky, []}]
end

defp deps do
[]
end

end
8 changes: 8 additions & 0 deletions chapter_9/chucky/test/chucky_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule ChuckyTest do
use ExUnit.Case
doctest Chucky

test "the truth" do
assert 1 + 1 == 2
end
end
1 change: 1 addition & 0 deletions chapter_9/chucky/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ExUnit.start()

0 comments on commit 34a85e3

Please sign in to comment.