Skip to content
This repository has been archived by the owner on May 2, 2023. It is now read-only.

Driver Implementation (Simple)

Gonçalo Tomás edited this page Oct 2, 2018 · 1 revision

This document describes the general procedure for writing a new database driver for FMKe. The requirements to perform this task are:

  • Minimal Erlang experience in order to implement the required functions
  • Availability of an Erlang client library for the database of choice. Fortunately, Erlang has a large amount of database client library implementations widely available, however, not all of them are compatible with the latest Erlang versions. To qualify for eligibility, the library needs to be compatible with Erlang 20 or newer.

Begin by cloning this repository and checking out a new branch. If you were writing the driver for mongodb, you could create the branch feature/mongodb by typing out the following command in a terminal (change directory to the project root first!):

git checkout -b feature/mongodb

There is a MongoDB client library published on Hex called mongodb_erlang, so we need to modify FMKe's rebar.config file in order to include this new dependency. According to Hex, the latest version of this package is "3.2.0", so we will append that version to the list of dependencies:

{deps, [
    %% Hex dependencies
    {eredis_cluster, "~>0.5"},
    {jsx, "~>2.9"},
    {lager, "~>3.6"},
    {poolboy, "~>1.5"},
    {cowboy, "~>2.4"},
    {riak_client, "~>2.5"},
    {antidotec_pb, "~>0.2"},
    {erlcass, "~>3.1"},
    %% use the latest version whenever possible
    %% MongoDB Erlang client library
    {mongodb_erlang, "~>3.2"}
]}.

Now that we have a new dependency, we can verify it compiles by running make and make test to see if everything still works. If for some reason either of these operations return errors, it is likely that this particular client library is not compatible with the Erlang version that you are using. You will need to find a new one or fork it and make appropriate changes in order to make it work.

Assuming that the project compiles after adding the dependency, the next task is to create the MongoDB driver. This approach to driver implementation is more simple, and we will only need to implement basic read and write functions.
First, let us create the module itself:

touch src/fmke_driver_mongodb.erl

Inside the empty module, we can use the behavior keyword in order to tell the compiler we will implement the gen_fmke_kv_driver interface:

-module(fmke_driver_mongodb).
-behaviour(gen_fmke_kv_driver).
-behaviour(gen_server).

With just these 3 lines, we can already save the file and run rebar3 compile, which will return the following error:

===> Compiling src/fmke_driver_mongodb.erl failed
src/fmke_driver_mongodb.erl:2: undefined callback function commit_transaction/2 (behaviour 'gen_fmke_kv_driver')
src/fmke_driver_mongodb.erl:2: undefined callback function get/2 (behaviour 'gen_fmke_kv_driver')
src/fmke_driver_mongodb.erl:2: undefined callback function put/2 (behaviour 'gen_fmke_kv_driver')
src/fmke_driver_mongodb.erl:2: undefined callback function start/1 (behaviour 'gen_fmke_kv_driver')
src/fmke_driver_mongodb.erl:2: undefined callback function start_transaction/1 (behaviour 'gen_fmke_kv_driver')
src/fmke_driver_mongodb.erl:2: undefined callback function stop/0 (behaviour 'gen_fmke_kv_driver')
src/fmke_driver_mongodb.erl:3: undefined callback function handle_call/3 (behaviour 'gen_server')
src/fmke_driver_mongodb.erl:3: undefined callback function handle_cast/2 (behaviour 'gen_server')
src/fmke_driver_mongodb.erl:3: undefined callback function init/1 (behaviour 'gen_server')

This means that we need to implement 3 functions relative to the gen_server interface, and 6 other functions for the gen_fmke_kv_driver behavior. If you already know how to create gen_servers (a basic OTP construct, but you can look for inspiration in other existing drivers), the only thing left is to actually implement the functions that constitute gen_fmke_kv_driver and you will have implemented a new FMKe driver.

Open the src/gen_fmke_kv_driver.erl file and implement the missing functions while respecting the type definitions in the behavior. (this is the most challenging part and should use about 150 lines of code)

Once all of the functions have been implemented and the compiler produces no errors or warnings for your new driver module, the next step is to tell FMKe to accept mongodb as a valid option and load the appropriate module. This is done in the src/fmke_setup_sup.erl:

-spec driver(Database::database()) -> module().
driver(antidote) ->     fmke_driver_antidote;
driver(cassandra) ->    fmke_driver_cassandra;
driver(ets) ->          fmke_driver_ets;
driver(mongodb) ->      fmke_driver_mongodb;
driver(riak) ->         fmke_driver_riak_kv;
driver(redis) ->        fmke_driver_redis.

By adding an additional clause specifying which module to load in case the user selects mongodb as the target_database option, you should now be able to run FMKe while connected to MongoDB. In order to experiment, you can run rebar3 shell after changing the previously mentioned option in the config/fmke.config file. This will start an Erlang shell connected to MongoDB, and you can try to run operations like:

fmke:create_patient(1, "Peter Smith", "Mountain View, CA").

NOTE: Although appreciated, you don't need to run unit tests for your database driver. FMKe already has a test suite where a database instance is launched (i.e. via Docker), then a comprehensive test battery is performed in order to ensure that a particular driver. See how to create tests for a recently written driver in order to make sure that your driver is running without problems.

If you can perform all FMKe operations, congratulations, you've successfully implemented a database driver for FMKe! 👏 👍

With or without test suite, at this point you can and should submit a Pull Request to this repository with the changes you have made in order to add the new driver.