mirror of
https://github.com/berkeleydb/libdb.git
synced 2024-11-16 17:16:25 +00:00
549 lines
21 KiB
HTML
549 lines
21 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 SimpleTxn</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="repapp.html" title="Chapter 3. The DB Replication Manager" />
|
||
<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.3</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">SimpleTxn</span>
|
||
</th>
|
||
</tr>
|
||
<tr>
|
||
<td width="20%" align="left"><a accesskey="p" href="repapp.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>SimpleTxn</span>
|
||
</h2>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
We now use the methods described above to add partial
|
||
support to the
|
||
|
||
|
||
<span>SimpleTxn</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 make some significant changes to our
|
||
<code class="classname">RepConfig</code> class because we will be
|
||
using it to maintain a lot more information that we needed
|
||
for our simple transactional example.
|
||
</p>
|
||
<p>
|
||
We begin by importing a few new
|
||
classes. <code class="classname">java.util.Vector</code> is used to
|
||
organize a list of "other host" definitions (that is, the host and
|
||
port information for the other replication participants known to
|
||
this application). We also need a couple of classes used to manage
|
||
individual host and port information, as well as replication
|
||
sites and startup policy information.
|
||
</p>
|
||
<pre class="programlisting"><strong class="userinput"><code>package db.repquote_gsg;
|
||
|
||
import java.util.Vector;
|
||
|
||
import com.sleepycat.db.ReplicationHostAddress;
|
||
import com.sleepycat.db.ReplicationManagerSiteConfig;
|
||
import com.sleepycat.db.ReplicationManagerStartPolicy;</code></strong>
|
||
|
||
public class RepConfig
|
||
{ </pre>
|
||
<p>
|
||
Next we add considerably to the constants and data members used by
|
||
this class. All of this is used to manage information necessary for
|
||
replication purposes. We also at this point change the program's
|
||
name, since we will be doing that to the main class in our
|
||
application a little later in this description.
|
||
</p>
|
||
<pre class="programlisting"> // Constant values used in the RepQuote application.
|
||
public static final String progname = <strong class="userinput"><code>"RepQuoteExampleGSG";</code></strong>
|
||
public static final int CACHESIZE = 10 * 1024 * 1024;
|
||
<strong class="userinput"><code>public static final int SLEEPTIME = 5000;</code></strong>
|
||
|
||
// member variables containing configuration information
|
||
// String specifying the home directory for rep files.
|
||
public String home;
|
||
// Stores an optional set of "other" hosts.
|
||
<strong class="userinput"><code>public Vector<ReplicationHostAddress> otherHosts;
|
||
// Priority within the replication group.
|
||
public int priority;
|
||
public ReplicationManagerStartPolicy startPolicy;
|
||
// The host address to listen to.
|
||
public ReplicationHostAddress thisHost;
|
||
|
||
// member variables used internally.
|
||
private int currOtherHost;
|
||
private boolean gotListenAddress;</code></strong></pre>
|
||
<p>
|
||
Now we update our class constructor to initialize all of these new
|
||
variables:
|
||
</p>
|
||
<pre class="programlisting"> public RepConfig()
|
||
{
|
||
<strong class="userinput"><code>startPolicy = ReplicationManagerStartPolicy.REP_ELECTION;</code></strong>
|
||
home = "TESTDIR";
|
||
<strong class="userinput"><code>gotListenAddress = false;
|
||
priority = 100;
|
||
currOtherHost = 0;
|
||
thisHost = new ReplicationManagerSiteConfig();
|
||
otherHosts = new Vector();</code></strong>
|
||
} </pre>
|
||
<p>
|
||
Finally, we finish updating this class by providing a series of new
|
||
getter and setter methods. These are used primarily for setting a
|
||
retrieving host information of interest to our replicated
|
||
application:
|
||
</p>
|
||
<pre class="programlisting"> public java.io.File getHome()
|
||
{
|
||
return new java.io.File(home);
|
||
}
|
||
|
||
<strong class="userinput"><code>public void setThisHost(String host, int port, boolean creator)
|
||
{
|
||
gotListenAddress = true;
|
||
thisHost.setHost(host);
|
||
thisPort.setPort(port);
|
||
thisHost.setGroupCreator(creator);
|
||
}
|
||
|
||
public ReplicationManagerSiteConfig getThisHost()
|
||
{
|
||
if (!gotListenAddress) {
|
||
System.err.println("Warning: no host specified.");
|
||
System.err.println("Returning default.");
|
||
}
|
||
return thisHost;
|
||
}
|
||
|
||
public ReplicationHostAddress getThisHostAddress()
|
||
{
|
||
if (!gotListenAddress) {
|
||
System.err.println("Warning: no host specified.");
|
||
System.err.println("Returning default.");
|
||
}
|
||
return thisHost.getAddress();
|
||
}
|
||
|
||
public boolean gotListenAddress() {
|
||
return gotListenAddress;
|
||
}
|
||
|
||
public void addOtherHost(String host, int port)
|
||
{
|
||
ReplicationHostAddress newInfo =
|
||
new ReplicationHostAddress(host, port);
|
||
otherHosts.add(newInfo);
|
||
}
|
||
|
||
public ReplicationHostAddress getFirstOtherHost()
|
||
{
|
||
currOtherHost = 0;
|
||
if (otherHosts.size() == 0)
|
||
return null;
|
||
return (ReplicationHostAddress)otherHosts.get(currOtherHost);
|
||
}
|
||
|
||
public ReplicationHostAddress getNextOtherHost()
|
||
{
|
||
currOtherHost++;
|
||
if (currOtherHost >= otherHosts.size())
|
||
return null;
|
||
return (ReplicationHostAddress)otherHosts.get(currOtherHost);
|
||
}
|
||
|
||
public ReplicationHostAddress getOtherHost(int i)
|
||
{
|
||
if (i >= otherHosts.size())
|
||
return null;
|
||
return (ReplicationHostAddress)otherHosts.get(i);
|
||
}</code></strong>
|
||
} </pre>
|
||
<p>
|
||
Having completed our update to the
|
||
|
||
<code class="classname">RepConfig</code>
|
||
class, we can now start making
|
||
changes to the main portion of our program. We begin by changing
|
||
the program's name. <span>(This, of course, means that we copy our
|
||
<code class="classname">SimpleTxn</code> code to a file named <code class="literal">RepQuoteExampleGSG.java</code>.)</span>
|
||
</p>
|
||
<pre class="programlisting">package db.repquote_gsg;
|
||
|
||
import java.io.FileNotFoundException;
|
||
import java.io.BufferedReader;
|
||
import java.io.InputStreamReader;
|
||
import java.io.IOException;
|
||
import java.io.UnsupportedEncodingException;
|
||
<strong class="userinput"><code>import java.lang.Thread;
|
||
import java.lang.InterruptedException;</code></strong>
|
||
|
||
import com.sleepycat.db.Cursor;
|
||
import com.sleepycat.db.Database;
|
||
import com.sleepycat.db.DatabaseConfig;
|
||
import com.sleepycat.db.DatabaseEntry;
|
||
import com.sleepycat.db.DatabaseException;
|
||
import com.sleepycat.db.DatabaseType;
|
||
import com.sleepycat.db.EnvironmentConfig;
|
||
import com.sleepycat.db.LockMode;
|
||
import com.sleepycat.db.OperationStatus;
|
||
<strong class="userinput"><code>import com.sleepycat.db.ReplicationHostAddress;
|
||
import com.sleepycat.db.ReplicationManagerSiteConfig;</code></strong>
|
||
|
||
import db.repquote.RepConfig;
|
||
|
||
public class <strong class="userinput"><code>RepQuoteExampleGSG</code></strong>
|
||
{
|
||
private RepConfig repConfig;
|
||
private Environment dbenv; </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">
|
||
public <strong class="userinput"><code>RepQuoteExampleGSG()</code></strong>
|
||
throws DatabaseException
|
||
{
|
||
repConfig = null;
|
||
dbenv = null;
|
||
}
|
||
|
||
public static void usage()
|
||
{
|
||
System.err.println("usage: " + repConfig.progname);
|
||
System.err.println("-h home<strong class="userinput"><code>[-r host:port][-l|-L host:port]" +
|
||
"[-r host:port][-p priority]</code></strong>");
|
||
|
||
System.err.println("\t -h home directory (required)\n" +
|
||
<strong class="userinput"><code>"\t -l host:port (required, unless -L is specified; " +
|
||
"l stands for local)\n" +
|
||
"\t -L host:port (optional;L mean group creator)\n" +
|
||
"\t -r host:port (optional; r stands for remote; any " +
|
||
"number of these may be specified)\n" +
|
||
"\t -p priority (optional: defaults to 100)\n");</code></strong>
|
||
|
||
System.exit(1);
|
||
} </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"> public static void main(String[] argv)
|
||
throws Exception
|
||
{
|
||
RepConfig config = new RepConfig();
|
||
<strong class="userinput"><code>String tmpHost;
|
||
int tmpPort = 0;</code></strong> </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
|
||
for (int i = 0; i < argv.length; i++)
|
||
{
|
||
if (argv[i].compareTo("-h") == 0) {
|
||
// home is a string arg.
|
||
i++;
|
||
config.home = argv[i];
|
||
<strong class="userinput"><code>} else if (argv[i].compareTo("-l") == 0) ||
|
||
argv[i].compareTo("-L") == 0) {
|
||
if (i == argv.length - 1)
|
||
usage();
|
||
if (argv[i].compareTo("-L") == 0)
|
||
isCreator = true;
|
||
// "local" should be host:port.
|
||
i++;
|
||
String[] words = argv[i].split(":");
|
||
if (words.length != 2) {
|
||
System.err.println(
|
||
"Invalid host specification host:port needed.");
|
||
usage();
|
||
}
|
||
try {
|
||
tmpPort = Integer.parseInt(words[1]);
|
||
} catch (NumberFormatException nfe) {
|
||
System.err.println("Invalid host specification, " +
|
||
"could not parse port number.");
|
||
usage();
|
||
}
|
||
config.setThisHost(words[0], tmpPort, isCreator);
|
||
} else if (argv[i].compareTo("-p") == 0) {
|
||
i++;
|
||
config.priority = Integer.parseInt(argv[i]);
|
||
} else if (argv[i].compareTo("-r") == 0) {
|
||
i++;
|
||
String[] words = argv[i].split(":");
|
||
if (words.length != 2) {
|
||
System.err.println(
|
||
"Invalid host specification host:port needed.");
|
||
usage();
|
||
}
|
||
try {
|
||
tmpPort = Integer.parseInt(words[1]);
|
||
} catch (NumberFormatException nfe) {
|
||
System.err.println("Invalid host specification, " +
|
||
"could not parse port number.");
|
||
usage();
|
||
}
|
||
config.addOtherHost(words[0], tmpPort);
|
||
}</code></strong> else {
|
||
System.err.println("Unrecognized option: " + argv[i]);
|
||
usage();
|
||
}
|
||
}
|
||
|
||
// Error check command line.
|
||
if (<strong class="userinput"><code>(!config.gotListenAddress())</code></strong> || config.home.length() == 0)
|
||
usage(); </pre>
|
||
<p>
|
||
Having done that, the remainder of our <code class="function">main()</code>
|
||
function is left unchanged, with the exception of a few name changes required by the
|
||
new class name:
|
||
</p>
|
||
<pre class="programlisting"> <strong class="userinput"><code>RepQuoteExampleGSG</code></strong> runner = null;
|
||
try {
|
||
runner = new <strong class="userinput"><code>RepQuoteExampleGSG();</code></strong>
|
||
runner.init(config);
|
||
|
||
runner.doloop();
|
||
runner.terminate();
|
||
} catch (DatabaseException dbe) {
|
||
System.err.println("Caught an exception during " +
|
||
"initialization or processing: " + dbe.toString());
|
||
if (runner != null)
|
||
runner.terminate();
|
||
}
|
||
System.exit(0);
|
||
} // end main </pre>
|
||
<p>
|
||
Now we need to update our
|
||
|
||
<code class="methodname">RepQuoteExampleGSG.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"> public int init(RepConfig config)
|
||
throws DatabaseException
|
||
{
|
||
int ret = 0;
|
||
repConfig = config;
|
||
EnvironmentConfig envConfig = new EnvironmentConfig();
|
||
envConfig.setErrorStream(System.err);
|
||
envConfig.setErrorPrefix(RepConfig.progname);
|
||
|
||
<strong class="userinput"><code>envConfig.addReplicationManagerSite(repConfig.getThisHost());</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>
|
||
for (ReplicationHostAddress host =
|
||
repConfig.getFirstOtherHost(); host != null;
|
||
host = repConfig.getNextOtherHost()) {
|
||
|
||
ReplicationManagerSiteConfig repmgrRemoteSiteConfig =
|
||
new ReplicationManagerSiteConfig(host.host, host.port);
|
||
repmgrRemoteSiteConfig.setBootstrapHelper(true);
|
||
envConfig.addReplicationManagerSite(
|
||
repmgrRemoteSiteConfig);
|
||
}
|
||
|
||
envConfig.addReplicationPriority(repConfig.priority);
|
||
</code></strong> </pre>
|
||
<p>
|
||
|
||
|
||
|
||
|
||
<span>We can now open our environment. Note that the options</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="methodname">EnvironmentConfig.setInitializeReplication()</code> option.
|
||
</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"> envConfig.setCacheSize(RepConfig.CACHESIZE);
|
||
envConfig.setTxnNoSync(true);
|
||
|
||
envConfig.setAllowCreate(true);
|
||
envConfig.setRunRecovery(true);
|
||
<strong class="userinput"><code>envConfig.setInitializeReplication(true);</code></strong>
|
||
envConfig.setInitializeLocking(true);
|
||
envConfig.setInitializeLogging(true);
|
||
envConfig.setInitializeCache(true);
|
||
envConfig.setTransactional(true);
|
||
try {
|
||
dbenv = new Environment(appConfig.getHome(), envConfig);
|
||
} catch(FileNotFoundException e) {
|
||
System.err.println("FileNotFound exception: " + e.toString());
|
||
System.err.println(
|
||
"Ensure that the environment directory is pre-created.");
|
||
ret = 1;
|
||
} </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">RepQuoteExampleGSG.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"> // start Replication Manager
|
||
dbenv.replicationManagerStart(3, appConfig.startPolicy);
|
||
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="repapp.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">Chapter 3. The DB Replication Manager </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>
|