Deadlines
Lab 4: CCHAT Workers
Having a working chat server is cool, but sending text messages back and forth quickly gets boring. Let's use our network of chat clients to do something interesting!
For this lab, we want to use the clients connected to a server as workers, each computing some small tasks which form part of a larger job. This kind of distributed computing is often used for computationally intensive tasks, such as mining BitCoins or computing protein folding.
Specification
We will not use the client GUI for this task.
Instead, you will implement the function cchat:send_job/3
with the following arguments:
- Server name as a string
- Function (of arity 1) to apply to each input
- List of inputs
Calling this function should take care of splitting your job into sub-tasks, distributing these amongst the clients connected to the chat server, and collecting and returning the final results. Assuming you have a server running with some clients connected, a job can be sent as follows:
> cchat:send_job("shire", fun(X) -> X * 3 end, [10,5,8]). [30,15,24]
Note that the function is not restricted to numbers.
You can pass any functions you want!
Here for example we use a function from the string
library:
> cchat:send_job("shire", fun string:to_upper/1, ["erlang","is","fun"]). ["ERLANG","IS","FUN"]
The idea is that the function could be computationally intensive, therefore the server should not block while waiting for the answer (but it's ok for each of the workers to block while computing their sub-tasks).
Assigning tasks
You will need to share the tasks amongst the available clients already connected to the server. To simpify things, unlike the workers pattern shown in the lectures, the assignment of tasks does not need to be dynamic. If you want you can use or adapt the following function:
assign_tasks([], _) -> [] ; assign_tasks(Users, Tasks) -> [ {lists:nth(((N-1) rem length(Users)) + 1, Users), Task} || {N,Task} <- lists:zip(lists:seq(1,length(Tasks)), Tasks) ].
Which works as follows:
> assign_tasks([u1,u2],[t1,t2,t3]). [{u1,t1},{u2,t2},{u1,t3}]
Order of results
It is important that the order of the results is correct! You should explicity make sure that the following does not happen:
> cchat:send_job("shire", fun(X) -> X * 3 end, [1,5,10]). [15,3,30]
The test case described below specifically tests for this.
Timeouts
The system shouldn't timeout if the task takes long to compute. Think asynchronously!
When using genserver:request
, you can extend the default timeout of 3000ms like so:
genserver:request(Pid, Data, 5000)
or alternatively remove the timeout altogether with:
genserver:request(Pid, Data, infinity)
Error handling
There are a few things which can go wrong:
- The server is not running or unreachable
- There are no clients connected, and thus no workers available
- One or more clients fail to return a response
We recommend that you come up with suitable behaviour for these cases, but for this lab it is not required for you to handle them.
Code skeleton
You should use the same one given for CCHAT.
You can get the code skeleton here.
Note that it has been slightly updated, specifically the files Makefile
and test_client.erl
.
Requirements
- All the requirements from CCHAT still apply for this lab.
- Implement the function
cchat:send_job/3
as described above. - Your code must pass the new test cases described below.
Test cases
run_workers_tests
makefile
target was missing. Please update your code.Now, your software should pass the same test cases as before together with some extra ones designed to check the new functionality. The test cases are given in the same testing framework (eunit) as we did previously.
To run the new tests, make sure to get the latest version of the code skeleton and execute the following:
$ make -s run_workers_tests
You should see the following kind of output:
# Test: workers server startup: Ok spawning 3 clients sending input: [4,1,7,3,-2,5,2] (7 tasks) results: [8,2,14,6,-4,10,4] received in 2413ms results received correctly: Ok Test passed.
The total amount of time taken to receive the results is not that important. What is important is that the results arrive in the correct order, and that your server did not block while the job was being calculated.
Alternatives to make
If your system doesn't have the make
command, you can run the tests like so:
$ erl -noshell -eval "eunit:test({timeout, 10, {test,test_client,workers}}), halt()"
Submission
You should submit your code based on the skeleton code and whatever other files are needed for your solution to work. In addition, you should comment your code so that graders can easily review your submission.
Please do not submit compressed archives. Just upload the individual source files which you have worked in.