User Tools

Site Tools


cs330_f2016:labz

This is an old revision of the document!


Note: Specs incomplete, proceed at own risk.

Objective:

To gain more significant experience in programming elixir, by implementing a server and constructing a supervisor tree.


Pre-requisites:

For this lab, you will need the scaffolding code.

The provided modules create a mock data center with inter module dependencies.


Deliverable:

For this lab, you will code two parts:

  1. First, you will build a nameserver, for local process registration.
  2. Second, you will construct a supervisor tree. The tree should be constructed such that it will automatically restart different “services” in our datacenter if they ever go down.

Part 1: Name Server:

In class, we learned that we could put processes on a global process registration system using Process.register(…). However, we want to implement this behavior on a local scale, so only processes that depend on each other know about each other. This is done by implementing a local name server.

You will construct a module named NameServer. The purpose of this module is to register names, in the form of atoms, to Pid's. Note that this must be done using your own data structure, such as a map, not the global process registration.

The server should accept two synchronous calls, and a single asynchronous call (also known as a cast).

#Synchronous 
{:register, name} #This will register the given name with the callers pid and return the caller :ok
{:resolve, name} #This will return to the caller the pid associated with name, or :error if no such pid can be found
 
#Asynchronous
{:register, name, pid} #register the given name to the given pid, no reply is expected

These simple calls allow modules to register themselves with the local name server so that they can easily talk to each other. The name server should return :ok to all other synchronous calls, and discard all other asynchronous calls. You can assume name and Pid are valid.

We have been have provided a skeleton of some GenServer code here.

Any time a pid goes down you will need to remove it from the name server, this is best done by placing a monitor on the pid before you put it into your data structure. The process will then notify the server through its handle_info function.

Hints:

We strongly encourage that your server implements Elixir's GenServer behavior. More info is found here. GenServer

The Map module documentation can be found here.

Elixir can't save your map structure to a global variable. You will need to pass it through the 'state' parameter that is the last value at the end of each GenServer function.

Example:

def handle_call({:register, name}, pid, mymap) do
  ...
  {:reply, :ok, mymap}
end

Monitors are in the Process module, documentation is found here.


Part 2: Supervisor tree

You've been asked to construct the supervisor tree for a series of connected modules in a virtual “data center”. Information for the modules and dependencies can be found below. Each module will be registered in the name server that you coded above. Each process will be started by a node in a supervisor tree.

A sample supervisor can be found here.

You must name the highest level supervisor TopSupervisor. The module should include start_link/1 which will take a name server as a parameter. You will pass this name server to each of the modules when your supervisors starts them. You will need more than one supervisor node.

There are six modules:

  • Info
  • Order
  • User
  • CustomerService
  • Shipper
  • Database

They have the following dependencies(A ⇒ B is read A depends on B).

  • Info ⇒ Database
  • Order ⇒ Database
  • Order ⇒ User
  • Shipper ⇒ Database
  • User ⇒ Database
  • User ⇒ Order

You must design the tree so when module X goes down, only modules that depend on X are shut down and restarted.

Creating your name server and supervisor tree might look like this:

{:ok, name_server} = NameServer.start()
response = TopSupervisor.start_link( name_server )

and if you have constructed your tree correctly, it will show output like the following:

iex(1)> {response1,name_server} = NameServer.start_link()
{:ok,<0.59.0>}
iex(2)> response2 = TopSupervisor.start_link( name_server )
CustomerService has started 
Database has started
Info has started
Shipper has started
User has started
Order has started
{ok,<0.61.0>}

From there, if you have implemented your code properly, you should be able to crash any module without the system going down. The module and its dependencies should just restart and continue working. You can crash a specific module using the included Crasher module. For example,

iex(3)> Crasher.crash(name_server, :Order)
Crashing the module...
true
Elixir.User has started
Elixir.Order has started

Again, you must design the tree so when module X goes down, only modules that depend on X are shut down and restarted.


Hints:

Don't create your own supervisor, use the built in Supervisor behavior. You will also need to create sub supervisors started by upper level ones.

It is helpful to draw your supervisor tree out on paper before starting to program the supervisor trees.

To create a name server, call NameServer.start(). It will return {ok, PID}. Pass this pid into the supervisor you are testing.

Helpful links:
https://hexdocs.pm/elixir/Supervisor.html

cs330_f2016/labz.1490229987.txt.gz · Last modified: 2021/06/30 23:40 (external edit)