Exercise 2: a proper phonebook Mochi component
This exercise (as well as Exercise 3) does not need the
margo-tutorial-exercises repository used in Exercise 1.
If you come from Exercise 1, make sure to deactivate the Spack
environment before starting this exercise (we will build a new one,
which we will also use in Exercise 3). This can be done with
spack env deactivate in any terminal in which you had
activated the environment. You can check whether an environment
is active using
spack env status.
In this exercise, we will use the Margo microservice template to develop a proper phonebook microservice.
Click on the green “Use this template” button and select “Create a new repository”. Give the repository a name (e.g. “phonebook”). Put the repository on “private” if you wish, then click on “Create repository from template”.
Click on Settings > Actions > General, and set the Workflow permissions to “Read and write permissions”, then click “Save” in the Workflow Permissions section (there are save buttons for each section that will save only the modifications made to that section).
Go back to the root of the code (on Github in your browser), and edit
initial-setup.json. Change “alpha” to a service name of your
choosing (this name will also be used to prefix your API functions,
so choose something short, e.g. YP, for yellow pages. In the following,
we will assume this is the name you used). Change “resource” to the name
of the resource we are going to manage, here “phonebook”.
Click on the green “Commit changes” button.
Wait a little and refresh the page. You might see a brown dot indicating
a workflow action is in progress. Or you might get a 404, which means
the workflow completed: as part of the GitHub workflow that sets up your
code, it will delete
Other github workflows will run to test your code and upload a coverage report to codecov.io whenever you push commits to GitHub. These workflows will not work properly if you have made the repository private, so you may receive emails from GitHub about some failed workflows. Simply ignore them.
Clone your code in the
mochi-tutorial directory on your machine,
then create a Spack environment and build the code like you did in Exercise 1
spack.yaml file at the root of your new project
spack env create ex2 spack.yaml,
spack env activate ex2,
build directory, cd into it, and build the code (
cmake .. -DENABLE_TESTS=ON).
You may want to use the flag
-DENABLE_TESTS=ON when calling cmake to
make sure that the tests are also built. You can run tests using
cmake will download Catch2
as part of the build process,
make will build it.
It is now time to edit the code to make it do what we want. The template provides the implementation of two RPCs, “hello” and “sum”. You will recognize the latter as the same as provided in Exercise 1. This can be helpful to reproduce what you have coded in Exercise 1 in the context of this new exercise.
include/YP/YP-client.h contains the functions required to
create and free a
YP_client object, which will be used to
register the RPCs and contact servers. There is nothing to modify in this file.
include/YP/YP-phonebook.h declares a
type, which represents a phonebook managed by a remote server. This file
is also where the client interface will be defined. Go ahead and add
declarations for a
YP_insert and a
following what you did in Ex 1.
src/client.h contains the definition of the
structure. Go ahead and add two
hg_id_t RPC ids to represent
the insert and lookup.
Before looking further into the client implementation, open
and add the type definitions for our RPCs (
Take the example of the
sum_* structures for that.
src/client.c contains the implementation of the client-side
YP_client_init, add the registration of our two new RPCs.
src/client.c and using code from Ex 1 (or by copying
and adapting the content of the
implement the client-side function that sends the insert and lookup RPCs to the server.
At this point, feel free to compile your code to make sure it builds fine. You won’t be able to test it yet since there is no server-side implementation of our RPCs, so let’s focus on the server library next.
include/YP/YP-backend.h contains the definition of a backend,
i.e. the interface that a phonebook implementation must satisfy.
Add the proper insert and lookup function pointers to this structure.
src/dummy/dummy-backend.c contains a “dummy” implementation of
such a backend. Copy the
phonebook.h file from the Ex 1 to the
dummy folder, and include it in
dummy-backend.c. Add a
phonebook_t phonebook field to the
then edit (1)
to add set this field (using
Still in the same file, add the implementation of the
dummy_lookup functions and add the function pointers in the
static YP_backend_impl dummy_backend structure definition.
src/provider.h contains the state of a phonebook provider.
YP_provider structure to add the
for our two new RPCs, just like you did for the client.
src/provider.c contains the implementation of the provider
functions. Find the
YP_provider_register function and add
the registration of your new RPCs by taking the existing RPCs
as examples. Such registration involves (1) calling
with the appropriate arguments, (2) calling
to associate the provider with the RPC, and (3) setting the RPC ID
in the provider structure.
src/provider.c, find the
function and add the calls necessary to deregister the two new RPCs.
You will first need to add declarations of your new RPCs, at the
beginning of the file, where
DECLARE_MARGO_RPC_HANDLER is used.
We can now implement the functions that will handle the RPCs.
In the same file, find the
YP_sum_ult function, copy it
DEFINE_MARGO_RPC_HANDLER line that follows it)
and edit it to transform it into a
then do the same with a
At this point, you can make sure your code builds fine.
Your microservice is ready! If you have time, feel free to look
into the tests folder, in particular the
and edit it (replacing calls to the “sum” RPC) to try out your
In practice, the next steps at this point would be to (1) add more tests, (2) remove everything related to the “hello” and “sum” RPCs (because obviously a phonebook is not a calculator), and (3) implement more complex backends by copying the code of the “dummy” backend and changing it to use external libraries or more complicated implementations.