Table of Contents
Every environment participating in a replicated application must know whether it is a master or replica. The reason for this is because, simply, the master can modify the database while replicas cannot. As a result, not only will you open databases differently depended on whether the environment is running as a master, but the environment will frequently behave quite a bit differently depending on whether it thinks it is operating as the read/write interface for your database.
Moreover, an environment must also be capable of gracefully switching between master and replica states. This means that the environment must be able to detect when it has switched states.
Not surprisingly, a large part of your application's code will be tied up in knowing which state a given environment is in and then in the logic of how to behave depending on its state.
As an alternative, write forwarding simplifies adding replication to an application. Once configured, there is no need to confine write operations to the master or even to know which site is the master. For more information, see Configuring for Write Forwarding.
This chapter shows you how to determine your environment's state, and it then shows you some sample code on how an application might behave depending on whether it is a master or a replica in a replicated application.
In order to determine whether your code is running as a
master or a replica, you implement an event handling
callback, which we initially describe in
Event Handling.
When the current environment becomes a client —
including at application startup — the
DB_EVENT_REP_CLIENT
event is raised.
When an election is held and a replica is elected to be a
master, the DB_EVENT_REP_MASTER
event is
raised on the newly elected master and the
DB_EVENT_REP_NEWMASTER
is raised on the
other replicas.
The implementation of the event handling callback is fairly simple. First you pass a structure to the environment handle that you can use to record the environment's state, and then you implement a switch statement within the callback that you use to record the current state, depending on the arriving event.
For example:
#include <db_cxx.h> /* Forward declaration */ void *event_callback(DbEnv *, u_int32_t, void *); ... /* The structure we use to track our environment's state */ typedef struct { int is_master; } APP_DATA; ... /* * Inside our main() function, we declare an APP_DATA variable. */ APP_DATA my_app_data; my_app_data.is_master = 0; /* Assume we start as a replica */ ... /* * Now we open our environment handle and set the APP_DATA structure * to it's app_private member. */ DbEnv *dbenv = new DbEnv(0); dbenv->set_app_private(&my_app_data); /* Having done that, register the callback with the * Berkeley DB library */ dbenv->set_event_notify(event_callback);
That done, we still need to implement the callback itself. This implementation can be fairly trivial.
/* * A callback used to determine whether the local environment is a * replica or a master. This is called by the Replication Manager * when the local environment changes state. */ void * event_callback(DbEnv *dbenv, u_int32_t which, void *info) { APP_DATA *app = dbenv->get_app_private(); info = NULL; /* Currently unused. */ switch (which) { case DB_EVENT_REP_MASTER: app->is_master = 1; break; case DB_EVENT_REP_CLIENT: app->is_master = 0; break; case DB_EVENT_REP_STARTUPDONE: /* fallthrough */ case DB_EVENT_REP_NEWMASTER: /* Ignore. */ break; default: dbenv->errx(dbenv, "ignoring event %d", which); } }
Notice how we access the APP_DATA
information using the environment handle's
app_private
data member. We also ignore the
DB_EVENT_REP_NEWMASTER
and
DB_EVENT_REP_STARTUPDONE
cases since these
are not relevant for simple replicated applications.
Of course, this only gives us the current state of the environment. We still need the code that determines what to do when the environment changes state and how to behave depending on the state (described in the next section).