[erlang-questions] Newbie Question: Module with DB Connection as a "global state"

Garrett Smith g@REDACTED
Wed Oct 5 17:34:41 CEST 2011


Hi Michael,

On Wed, Oct 5, 2011 at 8:36 AM, Michael Weibel - Amiado Group
<michael.weibel@REDACTED> wrote:
> Hi all,
>
> I'm new to erlang, new to this list and I hope here's the right place to ask this question.

Welcome!

>
> I'm currently implementing a module for ejabberd where I need to log certain packets to a MySQL DB and I'm using the MySQL Native Driver for it.
>
> My current implementation is on github:
> https://github.com/amiadogroup/mod_log_chat_mysql5/blob/master/src/mod_log_chat_mysql5.erl
>
> In this file you see that I'm opening the MySQL Connection in the function "open_mysql_connection" and assign it to a ETS Table because I didn't find another way to store the DB Reference (I tried something with records but didn't succeed).
>
> This worked pretty well but I ran into the problem that after some days/weeks the ets table didn't have the DB Reference anymore.
> To prevent this, I want to create functions which setup the DB Connection again, if it doesn't have it anymore.

You typically don't want to do this. There's a much, much better way...

> As I'm trying to do this I run into the problem that the informations about how to connect to the db is only in the init-function available and I don't really want to store this information also in the ets table.

The "go to" pattern in Erlang for what you're trying to do is a
supervised process. Using standard OTP facilities, you get this:

- Registration of process IDs using atoms (analogous to you storing a
connection PID in ets)
- Well defined "init" logic that's automatically applied when a
process needs to be restarted
- Encapsulation of the messy details of handling service provisioning

Fortunately, the MySQL driver (at the more recent ones - there are
several lineages out there) provides an OTP compliant connection
process, so you can do something as simple as this:

-module(my_db).

-export([start_link/0, fetch/1]).

start_link() ->
    Host = "localhost",
    Port = 3306,
    Db = "`my-db`",
    User = "root",
    Password = "password",
    mysql:start_link(?MODULE, Host, Port, User, Password, Db, fun mysql_log/4).

fetch(Sql) ->
    mysql:fetch(?MODULE, Sql).

mysql_log(_Module, _Line, _Level, _FormatFun) ->
    ok.

This is a registered process (i.e. there's one of them at runtime)
that provides access to your database.

The connection config in start_link/0 would typically come from an
application config, which you can read using application:get_env/2.
You can also create a start_link that takes the params.

To use this module as a service, you need to plug it into your
supervisory hierarchy. Here's what your top-level supervisor might
look like:

-module(my_app_sup).

-behaviour(supervisor).

-export([start_link/0]).

-export([init/1]).

-define(SERVER, ?MODULE).

start_link() ->
    supervisor:start_link({local, ?SERVER}, ?MODULE, []).

init([]) ->
    {ok, {{one_for_one, 5, 5},
          [{my_db, {my_db, start_link, []},
            permanent, 5000, worker, [my_db]}]}}.

If your my_db service crashes, the supervisor will restart it. The
"config" that you'd use for that restart would come from either the
OTP app config. (You *could* provide it in the supervisor child spec,
but I wouldn't go that way.)

Erlang gives you very nice facilities to create a mini "service
oriented architecture" -- you want to look for ways to create very
opaque services that have drop-dead simple start/restart semantics
(i.e. simple as pushing a button) and let supervisors monitor and
recover failed services as needed.

If for some reason you needed to dynamically configure your MySQL
connection params, you could store the config in a database, external
file, etc. and force a restart of your my_db process to re-read that
config on init.

Garrett



More information about the erlang-questions mailing list