mirror of
https://github.com/berkeleydb/libdb.git
synced 2024-11-17 01:26:25 +00:00
514 lines
18 KiB
HTML
514 lines
18 KiB
HTML
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|||
|
<head>
|
|||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|||
|
<title>Adding the Replication Manager to RepMgr</title>
|
|||
|
<link rel="stylesheet" href="gettingStarted.css" type="text/css" />
|
|||
|
<meta name="generator" content="DocBook XSL Stylesheets V1.73.2" />
|
|||
|
<link rel="start" href="index.html" title="Getting Started with Replicated Berkeley DB Applications" />
|
|||
|
<link rel="up" href="repapp.html" title="Chapter 3. The DB Replication Manager" />
|
|||
|
<link rel="prev" href="rep_init_code.html" title="Starting and Stopping Replication" />
|
|||
|
<link rel="next" href="fwrkpermmessage.html" title="Permanent Message Handling" />
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div xmlns="" class="navheader">
|
|||
|
<div class="libver">
|
|||
|
<p>Library Version 11.2.5.2</p>
|
|||
|
</div>
|
|||
|
<table width="100%" summary="Navigation header">
|
|||
|
<tr>
|
|||
|
<th colspan="3" align="center">Adding the Replication Manager to
|
|||
|
|
|||
|
<span xmlns="http://www.w3.org/1999/xhtml">RepMgr</span>
|
|||
|
|
|||
|
</th>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="20%" align="left"><a accesskey="p" href="rep_init_code.html">Prev</a> </td>
|
|||
|
<th width="60%" align="center">Chapter 3. The DB Replication Manager</th>
|
|||
|
<td width="20%" align="right"> <a accesskey="n" href="fwrkpermmessage.html">Next</a></td>
|
|||
|
</tr>
|
|||
|
</table>
|
|||
|
<hr />
|
|||
|
</div>
|
|||
|
<div class="sect1" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h2 class="title" style="clear: both"><a id="repmgr_init_example_c"></a>Adding the Replication Manager to
|
|||
|
|
|||
|
<span>RepMgr</span>
|
|||
|
|
|||
|
</h2>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
We now use the methods described above to add partial
|
|||
|
support to the
|
|||
|
|
|||
|
<span>RepMgr</span>
|
|||
|
|
|||
|
example that we presented in
|
|||
|
<a class="xref" href="txnapp.html" title="Chapter 2. Transactional Application">Transactional Application</a>.
|
|||
|
That is, in this section we will:
|
|||
|
</p>
|
|||
|
<div class="itemizedlist">
|
|||
|
<ul type="disc">
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Enhance our command line options to accept information
|
|||
|
of interest to a replicated application.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Configure our environment handle to use replication and
|
|||
|
the Replication Manager.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Minimally configure the Replication Manager.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Start replication.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Note that when we are done with this section, we will be
|
|||
|
only partially ready to run the application. Some critical
|
|||
|
pieces will be missing; specifically, we will not yet be
|
|||
|
handling the differences between a master and a
|
|||
|
replica. (We do that in the next chapter).
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Also, note that in the following code fragments, additions
|
|||
|
and changes to the code are marked in <strong class="userinput"><code>bold</code></strong>.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
To begin, we copy the
|
|||
|
|
|||
|
<span>SimpleTxn</span>
|
|||
|
code to a new file called
|
|||
|
<code class="literal">RepMgrGSG.cpp</code>.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<span>
|
|||
|
Having done that, we must make some significant
|
|||
|
changes to our
|
|||
|
<code class="classname">RepConfigInfo</code>
|
|||
|
class because now we will be using it to
|
|||
|
maintain a lot more information.
|
|||
|
</span>
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
First, we create a new structure,
|
|||
|
<code class="literal">RepHostInfoObj</code>, which we use to store
|
|||
|
host and port information for all "other" servers
|
|||
|
identified to the application via the
|
|||
|
<code class="literal">-o</code> command line option. This structure
|
|||
|
is chain-able, which makes cleaning up at program shutdown
|
|||
|
time easier.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">#include <db_cxx.h>
|
|||
|
#include <iostream>
|
|||
|
|
|||
|
<strong class="userinput"><code>// Chain-able struct used to store host information.
|
|||
|
typedef struct RepHostInfoObj{
|
|||
|
char* host;
|
|||
|
u_int16_t port;
|
|||
|
RepHostInfoObj* next; // used for chaining multiple "other" hosts.
|
|||
|
bool creator; // whether this site is the group creator.
|
|||
|
} REP_HOST_INFO; </code></strong></pre>
|
|||
|
<p>
|
|||
|
Next, we update our <code class="classname">RepConfigInfo</code> class
|
|||
|
definition to manage a lot more information and a new method.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">class RepConfigInfo {
|
|||
|
public:
|
|||
|
RepConfigInfo();
|
|||
|
virtual ~RepConfigInfo();
|
|||
|
|
|||
|
<strong class="userinput"><code>void addOtherHost(char* host, int port);</code></strong>
|
|||
|
public:
|
|||
|
<strong class="userinput"><code>u_int32_t start_policy;</code></strong>
|
|||
|
char* home;
|
|||
|
<strong class="userinput"><code>bool got_listen_address;
|
|||
|
REP_HOST_INFO this_host;
|
|||
|
int nrsites; // number of remote sites
|
|||
|
int priority;
|
|||
|
// used to store a set of optional other hosts.
|
|||
|
REP_HOST_INFO *other_hosts;</code></strong>
|
|||
|
}; </pre>
|
|||
|
<p>
|
|||
|
Then, we update our constructor to initialize our new variables.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">RepConfigInfo::RepConfigInfo()
|
|||
|
{
|
|||
|
<strong class="userinput"><code>start_policy = DB_REP_ELECTION;</code></strong>
|
|||
|
home = "TESTDIR";
|
|||
|
<strong class="userinput"><code>got_listen_address = false;
|
|||
|
nrsites = 0;
|
|||
|
priority = 100;
|
|||
|
other_hosts = NULL;</code></strong>
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Next, we implement our new method, <code class="methodname">RepConfigInfo::addOtherHost</code>,
|
|||
|
which is used to create <code class="literal">RepHostInfoObj</code> instances and add them to
|
|||
|
the chain of "other" hosts.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">RepConfigInfo::addOtherHost(char* host, int port)
|
|||
|
{
|
|||
|
REP_HOST_INFO *newinfo;
|
|||
|
newinfo = (REP_HOST_INFO*)malloc(sizeof(REP_HOST_INFO));
|
|||
|
newinfo->host = host;
|
|||
|
newinfo->port = port;
|
|||
|
if (other_hosts == NULL) {
|
|||
|
other_hosts = newinfo;
|
|||
|
newinfo->next = NULL;
|
|||
|
} else {
|
|||
|
newinfo->next = other_hosts;
|
|||
|
other_hosts = newinfo;
|
|||
|
}
|
|||
|
nrsites++;
|
|||
|
}</pre>
|
|||
|
<p>
|
|||
|
Having done that, we update our class destructor to release the <code class="literal">RepHostInfoObj</code>
|
|||
|
chain of objects at class destruction time.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">RepConfigInfo::~RepConfigInfo()
|
|||
|
{
|
|||
|
<strong class="userinput"><code>// release any other_hosts structs.
|
|||
|
if (other_hosts != NULL) {
|
|||
|
REP_HOST_INFO *CurItem = other_hosts;
|
|||
|
while (CurItem->next != NULL)
|
|||
|
{
|
|||
|
REP_HOST_INFO *TmpItem = CurItem;
|
|||
|
free(CurItem);
|
|||
|
CurItem = TmpItem;
|
|||
|
}
|
|||
|
free(CurItem);
|
|||
|
}
|
|||
|
other_hosts = NULL;</code></strong>
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Having completed our update to the
|
|||
|
<code class="classname">RepConfigInfo</code>
|
|||
|
|
|||
|
class, we can now start making
|
|||
|
changes to the main portion of our program. We begin by changing
|
|||
|
the program's name.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">using std::cout;
|
|||
|
using std::cin;
|
|||
|
using std::cerr;
|
|||
|
using std::endl;
|
|||
|
using std::flush;
|
|||
|
|
|||
|
#define CACHESIZE (10 * 1024 * 1024)
|
|||
|
#define DATABASE "quote.db"
|
|||
|
|
|||
|
<strong class="userinput"><code>const char *progname = "RepMgrGSG";</code></strong> </pre>
|
|||
|
<p>
|
|||
|
Next we update our usage function. The application will continue to
|
|||
|
accept the <code class="literal">-h</code> parameter so that we can identify
|
|||
|
the environment home directory used by this application. However,
|
|||
|
we also add the:
|
|||
|
</p>
|
|||
|
<div class="itemizedlist">
|
|||
|
<ul type="disc">
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
<code class="literal">-l</code> parameter which allows us to identify the
|
|||
|
host and port used by this application to listen for
|
|||
|
replication messages. This parameter is required unless the -L
|
|||
|
parameter is specified.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
<code class="literal">-L</code> parameter, which allows us to identify
|
|||
|
the local site as the group creator.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
<code class="literal">-r</code> parameter which allows us to specify
|
|||
|
other replicas.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
<code class="literal">-p</code> option, which is used to identify this
|
|||
|
replica's priority (recall that the priority is used as a tie
|
|||
|
breaker for elections)
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<pre class="programlisting">class RepMgrGSG
|
|||
|
{
|
|||
|
public:
|
|||
|
// Constructor.
|
|||
|
RepMgrGSG();
|
|||
|
// Initialization method. Creates and opens our environment handle.
|
|||
|
int init(RepConfigInfo* config);
|
|||
|
// The doloop is where all the work is performed.
|
|||
|
int doloop();
|
|||
|
// terminate() provides our shutdown code.
|
|||
|
int terminate();
|
|||
|
|
|||
|
private:
|
|||
|
// disable copy constructor.
|
|||
|
RepMgrGSG(const RepMgrGSG &);
|
|||
|
void operator = (const RepMgrGSG &);
|
|||
|
|
|||
|
// internal data members.
|
|||
|
RepConfigInfo *app_config;
|
|||
|
DbEnv dbenv;
|
|||
|
|
|||
|
// private methods.
|
|||
|
// print_stocks() is used to display the contents of our database.
|
|||
|
static int print_stocks(Db *dbp);
|
|||
|
};
|
|||
|
|
|||
|
static void usage()
|
|||
|
{
|
|||
|
cerr << "usage: " << progname << endl
|
|||
|
<< "-h home<strong class="userinput"><code>-l|L host:port [-r host:port]</code></strong>"
|
|||
|
<strong class="userinput"><code><< "[-p priority]" << endl;</code></strong>
|
|||
|
|
|||
|
cerr << "\t -h home directory" << endl
|
|||
|
<strong class="userinput"><code><< "\t -l host:port (required unless -L is specified;"
|
|||
|
<< "\t l stands for local)" << endl
|
|||
|
<< "\t -L host:port (optional, L means group "
|
|||
|
<< " creator)" << endl
|
|||
|
<< "\t -r host:port (optional; r stands for replica; any "
|
|||
|
<< "number of these may be specified)" << endl
|
|||
|
<< "\t -p priority (optional: defaults to 100)" << endl;</code></strong>
|
|||
|
|
|||
|
exit(EXIT_FAILURE);
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Now we can begin working on our <code class="literal">main()</code> function.
|
|||
|
We begin by adding a couple of variables that we will use to
|
|||
|
collect TCP/IP host and port information.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
</p>
|
|||
|
<pre class="programlisting">int main(int argc, char **argv)
|
|||
|
{
|
|||
|
RepConfigInfo config;
|
|||
|
char ch<strong class="userinput"><code>, *portstr, *tmphost;
|
|||
|
int tmpport;</code></strong>
|
|||
|
int ret; </pre>
|
|||
|
<p>
|
|||
|
Now we collect our command line arguments. As we do so, we will
|
|||
|
configure host and port information as required, and we will
|
|||
|
configure the application's election priority if necessary.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> // Extract the command line parameters
|
|||
|
while ((ch = getopt(argc, argv, "h:<strong class="userinput"><code>l:n:p:r:</code></strong>")) != EOF) {
|
|||
|
switch (ch) {
|
|||
|
case 'h':
|
|||
|
config.home = optarg;
|
|||
|
break;
|
|||
|
<strong class="userinput"><code>case 'L':
|
|||
|
config.this_host.creator = true; // FALLTHROUGH
|
|||
|
case 'l':
|
|||
|
config.this_host.host = strtok(optarg, ":");
|
|||
|
if ((portstr = strtok(NULL, ":")) == NULL) {
|
|||
|
cerr << "Bad host specification." << endl;
|
|||
|
usage();
|
|||
|
}
|
|||
|
config.this_host.port = (unsigned short)atoi(portstr);
|
|||
|
config.got_listen_address = true;
|
|||
|
break;
|
|||
|
case 'p':
|
|||
|
config.priority = atoi(optarg);
|
|||
|
break;
|
|||
|
case 'r':
|
|||
|
tmphost = strtok(optarg, ":");
|
|||
|
if ((portstr = strtok(NULL, ":")) == NULL) {
|
|||
|
cerr << "Bad host specification." << endl;
|
|||
|
usage();
|
|||
|
}
|
|||
|
tmpport = (unsigned short)atoi(portstr);
|
|||
|
config.addOtherHost(tmphost, tmpport);
|
|||
|
break;</code></strong>
|
|||
|
case '?':
|
|||
|
default:
|
|||
|
usage();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Error check command line.
|
|||
|
if ((!config.got_listen_address) || config.home == NULL)
|
|||
|
usage(); </pre>
|
|||
|
<p>
|
|||
|
Having done that, the remainder of our <code class="function">main()</code>
|
|||
|
function is left unchanged:
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> RepMgrGSG runner;
|
|||
|
try {
|
|||
|
if((ret = runner.init(&config)) != 0)
|
|||
|
goto err;
|
|||
|
if((ret = runner.doloop()) != 0)
|
|||
|
goto err;
|
|||
|
} catch (DbException dbe) {
|
|||
|
cerr << "Caught an exception during initialization or"
|
|||
|
<< " processing: " << dbe.what() << endl;
|
|||
|
}
|
|||
|
err:
|
|||
|
runner.terminate();
|
|||
|
return 0;
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Now we need to update our
|
|||
|
<code class="methodname">RepMgrGSG::init()</code>
|
|||
|
|
|||
|
method. Our updates are at first related to configuring
|
|||
|
replication. First, we need to update the method so that we can
|
|||
|
identify the local site to the environment handle (that is, the site identified by the
|
|||
|
<code class="literal">-l</code> command line option):
|
|||
|
</p>
|
|||
|
<pre class="programlisting">RepMgrGSG::RepMgrGSG() : app_config(0), dbenv(0)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
int RepMgrGSG::init(RepConfigInfo *config)
|
|||
|
{
|
|||
|
int ret = 0;
|
|||
|
|
|||
|
app_config = config;
|
|||
|
|
|||
|
dbenv.set_errfile(stderr);
|
|||
|
dbenv.set_errpfx(progname);
|
|||
|
|
|||
|
<strong class="userinput"><code>DbSite *dbsite;
|
|||
|
dbenv.repmgr_site(app_config->this_host.host,
|
|||
|
app_config->this_host.port, &dbsite, 0);
|
|||
|
dbsite->set_config(DB_LOCAL_SITE, 1);
|
|||
|
if (app_config->this_host.creator)
|
|||
|
dbsite->set_config(DB_GROUP_CREATOR, 1);
|
|||
|
|
|||
|
dbsite->close();</code></strong> </pre>
|
|||
|
<p>
|
|||
|
And we also add code to allow us to identify "other" sites to the
|
|||
|
environment handle (that is, the sites that we identify using the
|
|||
|
<code class="literal">-o</code> command line option). To do this, we iterate over
|
|||
|
each of the "other" sites provided to us using the
|
|||
|
<code class="literal">-o</code> command line option, and we add each one
|
|||
|
individually in turn:
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
We also add code here to set the environment's priority.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> <strong class="userinput"><code>int i = 1;
|
|||
|
for ( REP_HOST_INFO *cur = app_config->other_hosts;
|
|||
|
cur != NULL && i <= app_config->nrsites;
|
|||
|
cur = cur->next, i++) {
|
|||
|
dbenv.repmgr_site(cur->host, cur->port, &dbsite, 0);
|
|||
|
dbsite->set_config(DB_BOOTSTRAP_HELPER, 1);
|
|||
|
|
|||
|
dbsite->close();
|
|||
|
}
|
|||
|
dbenv.rep_set_priority(app_config->priority); </code></strong> </pre>
|
|||
|
<p>
|
|||
|
|
|||
|
|
|||
|
<span>We can now open our environment. Note that the flags</span>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
we use to open the environment are slightly different for a
|
|||
|
replicated application than they are for a non-replicated
|
|||
|
application. Namely, replication requires the
|
|||
|
<span>
|
|||
|
<code class="literal">DB_INIT_REP</code> flag.
|
|||
|
</span>
|
|||
|
|
|||
|
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Also, because we are using the Replication Manager, we must prepare
|
|||
|
our environment for threaded usage. For this reason, we also
|
|||
|
need the <code class="literal">DB_THREAD</code> flag.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> dbenv.set_cachesize(0, CACHESIZE, 0);
|
|||
|
dbenv.set_flags(DB_TXN_NOSYNC, 1);
|
|||
|
|
|||
|
try {
|
|||
|
dbenv.open(app_config->home,
|
|||
|
DB_CREATE |
|
|||
|
DB_INIT_LOCK |
|
|||
|
DB_INIT_LOG |
|
|||
|
DB_INIT_MPOOL |
|
|||
|
<strong class="userinput"><code>DB_INIT_REP |</code></strong>
|
|||
|
DB_INIT_TXN |
|
|||
|
DB_RECOVER <strong class="userinput"><code>|
|
|||
|
DB_THREAD;</code></strong>
|
|||
|
0);
|
|||
|
} catch(DbException dbe) {
|
|||
|
cerr << "Caught an exception during DB environment open." << endl
|
|||
|
<< "Ensure that the home directory is created prior to "
|
|||
|
<< "starting the application." << endl;
|
|||
|
ret = ENOENT;
|
|||
|
goto err;
|
|||
|
}</pre>
|
|||
|
<p>
|
|||
|
Finally, we start replication before we exit this method.
|
|||
|
Immediately after exiting this method, our application will go into
|
|||
|
the
|
|||
|
<code class="methodname">RepMgrGSG::doloop()</code>
|
|||
|
|
|||
|
method, which is where
|
|||
|
the bulk of our application's work is performed. We update that
|
|||
|
method in the next chapter.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> if ((ret = dbenv.repmgr_start(3, app_config->start_policy)) != 0)
|
|||
|
goto err;
|
|||
|
|
|||
|
err:
|
|||
|
return ret;
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
This completes our replication updates for the moment. We are not as
|
|||
|
yet ready to actually run this program; there remains a few
|
|||
|
critical pieces left to add to it. However, the work that we
|
|||
|
performed in this section represents a solid foundation for the
|
|||
|
remainder of our replication work.
|
|||
|
</p>
|
|||
|
</div>
|
|||
|
<div class="navfooter">
|
|||
|
<hr />
|
|||
|
<table width="100%" summary="Navigation footer">
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left"><a accesskey="p" href="rep_init_code.html">Prev</a> </td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="u" href="repapp.html">Up</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right"> <a accesskey="n" href="fwrkpermmessage.html">Next</a></td>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left" valign="top">
|
|||
|
Starting and Stopping Replication
|
|||
|
</td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="h" href="index.html">Home</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right" valign="top"> Permanent Message Handling</td>
|
|||
|
</tr>
|
|||
|
</table>
|
|||
|
</div>
|
|||
|
</body>
|
|||
|
</html>
|