User Tools

Site Tools


cs330_f2016:labz

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. You do not need to modify this code


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 provided a skeleton of some GenServer code here.

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, _from}, mymap) do
  ...
  {:reply, :ok, mymap}
end

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 supervisor 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


Submitting Your Lab

For submitting this lab, please put all of your code into a single file called “Elixir2.ex”. This should include all of the code from the provided modules, all of the code for you name server, and all of your code for your supervisor modules.

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