Sindbad~EG File Manager
<?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>Program Listing</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="writeforwarding.html" title="Chapter 5. Configuring for Write Forwarding" />
<link rel="prev" href="writeforwarding.html" title="Chapter 5. Configuring for Write Forwarding" />
<link rel="next" href="addfeatures.html" title="Chapter 6. Additional Features" />
</head>
<body>
<div xmlns="" class="navheader">
<div class="libver">
<p>Library Version 18.1.40</p>
</div>
<table width="100%" summary="Navigation header">
<tr>
<th colspan="3" align="center">Program Listing</th>
</tr>
<tr>
<td width="20%" align="left"><a accesskey="p" href="writeforwarding.html">Prev</a> </td>
<th width="60%" align="center">Chapter 5. Configuring for Write Forwarding</th>
<td width="20%" align="right"> <a accesskey="n" href="addfeatures.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="wrfor_programlisting"></a>Program Listing</h2>
</div>
</div>
</div>
<div class="toc">
<dl>
<dt>
<span class="sect2">
<a href="wrfor_programlisting.html#wrfor_main_c">Function: main()</a>
</span>
</dt>
<dt>
<span class="sect2">
<a href="wrfor_programlisting.html#wrfor_create_env_c">Function: create_env()</a>
</span>
</dt>
<dt>
<span class="sect2">
<a href="wrfor_programlisting.html#wrfor_env_init_c">Function: env_init()</a>
</span>
</dt>
<dt>
<span class="sect2">
<a href="wrfor_programlisting.html#idm140270974093168">Function: doloop()</a>
</span>
</dt>
<dt>
<span class="sect2">
<a href="wrfor_programlisting.html#wrfor_printstocks_c">
<span>Function: print_stocks()</span>
</a>
</span>
</dt>
</dl>
</div>
<p>
Our example program builds from the simple transactional
application you created in the <a class="xref" href="txnapp.html" title="Chapter 2. Transactional Application">Transactional Application</a>
chapter and configures write forwarding. The application is
network-aware, so you can specify things like host names and
ports from the command line. This program has additional error
handling for replication errors.
</p>
<p>
When using replication with write forwarding, there are several benefits for your application code:
</p>
<div class="itemizedlist">
<ul type="disc">
<li>
<p>
You do not need to create an event handler to detect
changes of the master.
</p>
</li>
<li>
<p>
You do not need to use app_data to track whether the
current site is master.
</p>
</li>
<li>
<p>
You do not need to provide an error for put
operations on the client.
</p>
</li>
</ul>
</div>
<div class="sect2" lang="en" xml:lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="wrfor_main_c"></a>Function: main()</h3>
</div>
</div>
</div>
<p> Our program begins with some additional include statements: </p>
<pre class="programlisting">/*
* File: rep_mgr_wrfor_gsg.c
*/
include <stdlib.h>
include <string.h>
include <errno.h>
ifdef _WIN32
include <windows.h>
define sleep(s) Sleep(1000 * (s))
else /* !_WIN32 */
include <unistd.h>
endif
#include <db.h>
#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
#endif </pre>
<p>
We then define a few values. One is the size of our cache,
which we keep deliberately small for this example, and the other
is the name of our database. Also, you can define a sleep
time, which sets the time that a site waits before it retries
synchronizing with the master. We also provide a global variable that
is the name of our program; this is used for error reporting
later on.
</p>
<pre class="programlisting">#define CACHESIZE (10 * 1024 * 1024)
#define DATABASE "quote.db"
#define SLEEPTIME 3
const char *progname = "ex_rep_gsg_wrfor"; </pre>
<p>
Then we perform a couple of forward declarations. The first
of these, <code class="function">create_env()</code> and
<code class="function">env_init()</code> are used to open and
initialize our environment.
</p>
<p>
Next we declare <code class="function">doloop()</code>. This function
now takes an <code class="literal">int</code> as an argument, which we
describe later. </p>
<p> Finally, we have <code class="function">print_stocks</code> function.
</p>
<pre class="programlisting">int create_env(const char *, DB_ENV **);
int env_init(DB_ENV *, const char *);
int doloop (DB_ENV *, int);
int print_stocks(DB *); </pre>
<p>
Next we need our <code class="function">usage()</code> function, which
has additional options:
</p>
<pre class="programlisting">/* Usage function */
static void
usage()
{
fprintf(stderr, "usage: %s ", progname);
fprintf(stderr, "-h home -l|-L host:port [-r host:port]\n");
fprintf(stderr, "where:\n");
fprintf(stderr, "\t-h identifies the environment home directory ");
fprintf(stderr, "(required).\n");
fprintf(stderr, "\t-l identifies the host and port used by this ");
fprintf(stderr, "site (required unless L is specified).\n");
fprintf(stderr, "\t-L identifies the local site as group creator. \n");
fprintf(stderr, "\t-r identifies another site participating in ");
fprintf(stderr, "this replication group\n");
exit(EXIT_FAILURE);
} </pre>
<p>
where:
</p>
<div class="itemizedlist">
<ul type="disc">
<li>
<p>
<code class="literal">-h</code>
</p>
<p>
Identifies the environment home directory. You must
specify this option.
</p>
</li>
<li>
<p>
<code class="literal">-l</code>
</p>
<p>
Identifies the host and port used by this site. You
must specify this option unless <code class="literal">-L</code> is
specified.
</p>
</li>
<li>
<p>
<code class="literal">-L</code>
</p>
<p>
Identifies the local site as group creator. You must
specify this option unless <code class="literal">-l</code> is
specified.
</p>
</li>
<li>
<p>
<code class="literal">-r</code>
</p>
<p>
Optionally identifies another site participating in
this replication group.
</p>
</li>
</ul>
</div>
<p>
That completed, we can jump into our application's
<code class="function">main()</code> function. We begin by declaring
and initializing some variables used to collect
TCP/IP host and port information. We also declare a couple of
flags that we use to make sure some required information
is provided to this application:
</p>
<pre class="programlisting">int
main(int argc, char *argv[])
{
extern char *optarg;
DB_ENV *dbenv;
const char *home;
char ch, *host, *last_colon, *portstr;
int is_group_creator, local_is_set, ret;
u_int16_t port;
dbenv = NULL;
ret = is_group_creator = local_is_set = 0;
home = NULL; </pre>
<p>
Now we create and configure our environment handle. We do
this with our <code class="function">create_env()</code> function, which
we will show later in this example.
</p>
<pre class="programlisting"> if ((ret = create_env(progname, &dbenv)) != 0)
goto err; </pre>
<p>
Then we parse the command line arguments:
</p>
<pre class="programlisting"> while ((ch = getopt(argc, argv, "h:l:L:r:")) != EOF)
switch (ch) {
case 'h':
home = optarg;
break;
case 'L':
is_group_creator = 1; /* FALLTHROUGH */
case 'l':
host = optarg;
/*
* The final colon in host:port string is the
* boundary between the host and the port portions
* of the string.
*/
if ((last_colon = strrchr(host, ':')) == NULL ) {
fprintf(stderr, "Bad local host specification.\n");
goto err;
}
/*
* Separate the host and port portions of the
* string for further processing.
*/
portstr = last_colon + 1;
*last_colon = '\0';
port = (unsigned short)atoi(portstr);
if ((ret =
dbenv->repmgr_site(dbenv, host, port, &dbsite, 0)) != 0){
fprintf(stderr, "Could not set local address %s:%d.\n",
host, port);
goto err;
}
dbsite->set_config(dbsite, DB_LOCAL_SITE, 1);
if (is_group_creator)
dbsite->set_config(dbsite, DB_GROUP_CREATOR, 1);
if ((ret = dbsite->close(dbsite)) != 0) {
dbenv->err(dbenv, ret, "DB_SITE->close");
goto err;
}
local_is_set = 1;
break;
/* Identify another site in the replication group. */
case 'r':
host = optarg;
/*
* The final colon in host:port string is the
* boundary between the host and the port portions
* of the string.
*/
if ((last_colon = strrchr(host, ':')) == NULL ) {
fprintf(stderr, "Bad remote host specification.\n");
goto err;
}
/*
* Separate the host and port portions of the
* string for further processing.
*/
portstr = last_colon + 1;
*last_colon = '\0';
port = (unsigned short)atoi(portstr);
if ((ret =
dbenv->repmgr_site(dbenv, host, port, &dbsite, 0)) != 0) {
dbenv->err(dbenv, ret, "DB_ENV->repmgr_site");
goto err;
}
dbsite->set_config(dbsite, DB_BOOTSTRAP_HELPER, 1);
if ((ret = dbsite->close(dbsite)) != 0) {
dbenv->err(dbenv, ret, "DB_SITE->close");
goto err;
}
break;
case '?':
default:
usage();
} </pre>
<p>
Now we can open our environment. We do this with the
<code class="function">env_init()</code> function which we will
describe a little later in this chapter.
</p>
<pre class="programlisting"> if ((ret = env_init(dbenv, home)) != 0)
goto err; </pre>
<p>
You can now configure and start Replication Manager with
write forwarding. To configure write forwarding, use
<code class="function">rep_set_config</code> with the
<code class="literal">DB_REPMGR_CONF_FORWARD_WRITES</code> option.
</p>
<pre class="programlisting"> dbenv->rep_set_config(dbenv, DB_REPMGR_CONF_FORWARD_WRITES, 1); </pre>
<pre class="programlisting"> if ((ret = dbenv->repmgr_start(dbenv, 3, DB_REP_ELECTION)) != 0)
goto err; </pre>
<p>
Now that we have opened the environment and configured and
started Replication Manager with write forwarding, we can call
our <code class="function">doloop()</code> function.
</p>
<pre class="programlisting"> if ((ret = doloop(dbenv, is_group_creator)) != 0) {
dbenv->err(dbenv, ret, "Application failed");
goto err;
} </pre>
<p>
Finally, we provide our application shutdown code. Note,
again, that in a traditional transactional application all
databases would be closed here. In our replicated application,
the database will usually be closed in the <code class="function">doloop()</code>
function, but we also conditionally close the database here to handle
some error cases.
</p>
<pre class="programlisting">err: if (dbenv != NULL)
(void)dbenv->close(dbenv, 0);
return (ret);
} </pre>
</div>
<div class="sect2" lang="en" xml:lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="wrfor_create_env_c"></a>Function: create_env()</h3>
</div>
</div>
</div>
<p>
Having written our <code class="function">main()</code> function, we
now implement the usual <code class="function">create_env</code>
function.
</p>
<pre class="programlisting">int
create_env(const char *progname, DB_ENV **dbenvp)
{
DB_ENV *dbenv;
int ret;
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(stderr, "can't create env handle: %s\n",
db_strerror(ret));
return (ret);
}
dbenv->set_errfile(dbenv, stderr);
dbenv->set_errpfx(dbenv, progname);
*dbenvp = dbenv;
return (0);
} </pre>
</div>
<div class="sect2" lang="en" xml:lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="wrfor_env_init_c"></a>Function: env_init()</h3>
</div>
</div>
</div>
<p>
Now we implement the <code class="function">env_init</code> function,
which also uses the <code class="literal">DB_INIT_REP</code> and
<code class="literal">DB_THREAD</code> flags that are required for
Replication Manager.
</p>
<pre class="programlisting">int
env_init(DB_ENV *dbenv, const char *home)
{
u_int32_t flags;
int ret;
(void)dbenv->set_cachesize(dbenv, 0, CACHESIZE, 0);
(void)dbenv->set_flags(dbenv, DB_TXN_NOSYNC, 1);
/* DB_INIT_REP and DB_THREAD are required for Replication Manager. */
flags = DB_CREATE |
DB_INIT_LOCK |
DB_INIT_LOG |
DB_INIT_MPOOL |
DB_INIT_REP |
DB_INIT_TXN |
DB_RECOVER |
DB_THREAD;
if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0)
dbenv->err(dbenv, ret, "can't open environment");
return (ret);
} </pre>
</div>
<div class="sect2" lang="en" xml:lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="idm140270974093168"></a>Function: doloop()</h3>
</div>
</div>
</div>
<p>
Having written our <code class="function">main()</code> function and
utility functions, we now implement <code class="function">doloop</code>.
This function provides a command prompt at which the user can
enter a stock ticker value and a price for that value. This
information is then entered to the database.
</p>
<p>
To display the database, simply enter <code class="literal">return</code>
at the prompt.
</p>
<p>
To begin, we declare a database pointer, several
<code class="classname">DBT</code> variables, and the usual
assortment of variables used for buffers and return codes. We
also initialize all of this. Remember that doloop now takes
<code class="literal">is_group_creator</code> as an additional
argument.
</p>
<pre class="programlisting">#define BUFSIZE 1024
int
doloop(DB_ENV *dbenv, int is_group_creator)
{
DB *dbp;
DBT key, data;
char buf[BUFSIZE], *rbuf;
int ret;
u_int32_t db_flags;
dbp = NULL;
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
ret = 0; </pre>
<p>
Next, we begin the loop and we immediately open our database
if it has not already been opened.
</p>
<p>
If <code class="literal">-L</code> is set,
<code class="literal">is_group_creator</code> specifies the need to
create the database for the initial group creator startup. The
database will be replicated to the other sites when they first
start up. The database will already exist on each site for
subsequent startups.
</p>
<p>
Note that there is some logic for a site to retry in case it
needs time to synchronize with the master using
<code class="literal">SLEEPTIME</code>.
</p>
<pre class="programlisting"> for (;;) {
if (dbp == NULL) {
if ((ret = db_create(&dbp, dbenv, 0)) != 0)
return (ret);
db_flags = DB_AUTO_COMMIT;
/*
* Only need to create the database for the
* initial group creator startup. The database
* will be replicated to the other sites when they
* first start up. The database will already exist on
* each site for subsequent startups.
*/
if (is_group_creator)
db_flags |= DB_CREATE;
if ((ret = dbp->open(dbp, NULL, DATABASE,
NULL, DB_BTREE, db_flags, 0)) != 0) {
/* Retry in case site needs time to synchronize with master. */
if (ret == ENOENT) {
printf(
"No stock database yet available.\n");
if ((ret = dbp->close(dbp, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->close");
goto err;
}
dbp = NULL;
sleep(SLEEPTIME);
continue;
}
dbenv->err(dbenv, ret, "DB->open");
goto err;
} </pre>
<p>
Now we implement our command prompt. If the user
enters the keywords <code class="literal">exit</code> or
<code class="literal">quit</code>, the loop is exited and the
application ends. If the user enters nothing and instead simply
presses <code class="literal">return</code>, the entire contents of the
database is displayed. We use our
<code class="function">print_stocks()</code> function to display the
database. (That implementation is shown next in this chapter.)
</p>
<p>
We also now check for a dead replication handle, which can occur
in rare cases when a new master causes a previously committed
transaction to be rolled back. In such cases, all database handles
must be closed and opened again.
</p>
<p>
Remember that very little error checking is performed on the
data entered at this prompt. If the user fails to enter at least
one space in the value string, a simple help message is printed
and the prompt is returned to the user.
</p>
<pre class="programlisting"> printf("QUOTESERVER> ");
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) == NULL)
break;
if (strtok(&buf[0], " \t\n") == NULL) {
switch ((ret = print_stocks(dbp))) {
case 0:
continue;
case DB_REP_HANDLE_DEAD:
/* Must close and reopen the handle, then can retry. */
(void)dbp->close(dbp, 0);
dbp = NULL;
dbenv->errx(dbenv, "Could not traverse data,
retry operation");
continue;
default:
dbp->err(dbp, ret, "Error traversing data");
goto err;
}
}
rbuf = strtok(NULL, " \t\n");
if (rbuf == NULL || rbuf[0] == '\0') {
if (strncmp(buf, "exit", 4) == 0 ||
strncmp(buf, "quit", 4) == 0)
break;
dbenv->errx(dbenv, "Format: TICKER VALUE");
continue;
} </pre>
<p>
Now we assign data to the <code class="classname">DBT</code>s that we
will use to write the new information to the database.
</p>
<pre class="programlisting"> key.data = buf;
key.size = (u_int32_t)strlen(buf);
data.data = rbuf;
data.size = (u_int32_t)strlen(rbuf); </pre>
<p>
Having done that, we can write the new information to the
database. Here, the reason we do not need an explicit commit
on this put operation is that it uses the implicit NULL txnid,
so each one is automatically committed. Also, the application
retries if a deadlock, timeout or permission error occurs. A forwarded
put operation can return a timeout error if the operation takes
too long and a permission error if there is currently
no master.
</p>
<pre class="programlisting"> if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) != 0)
{
dbp->err(dbp, ret, "DB->put");
switch (ret) {
case DB_REP_HANDLE_DEAD:
/* Must close and reopen the handle, then can retry. */
(void)dbp->close(dbp, 0);
dbp = NULL;
/* FALLTHROUGH */
case DB_LOCK_DEADLOCK:
case DB_TIMEOUT:
case EACCES:
dbenv->errx(dbenv, "Could not update data, retry operation");
continue;
default:
dbp->err(dbp, ret, "Error updating data");
goto err;
}
} </pre>
<p>
Finally, we close our database before returning from the
function.
</p>
<pre class="programlisting">err: if (dbp != NULL)
(void)dbp->close(dbp, 0);
return (ret);
} </pre>
</div>
<div class="sect2" lang="en" xml:lang="en">
<div class="titlepage">
<div>
<div>
<h3 class="title"><a id="wrfor_printstocks_c"></a>
<span>Function: print_stocks()</span>
</h3>
</div>
</div>
</div>
<p>
This function is unmodified from when we
originally introduced it. For details on that function, see
<a class="xref" href="simpleprogramlisting.html#printstocks_c" title="Function: print_stocks()">
<span>Function: print_stocks()</span>
</a>.
</p>
</div>
</div>
<div class="navfooter">
<hr />
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left"><a accesskey="p" href="writeforwarding.html">Prev</a> </td>
<td width="20%" align="center">
<a accesskey="u" href="writeforwarding.html">Up</a>
</td>
<td width="40%" align="right"> <a accesskey="n" href="addfeatures.html">Next</a></td>
</tr>
<tr>
<td width="40%" align="left" valign="top">Chapter 5. Configuring for Write Forwarding </td>
<td width="20%" align="center">
<a accesskey="h" href="index.html">Home</a>
</td>
<td width="40%" align="right" valign="top"> Chapter 6. Additional Features</td>
</tr>
</table>
</div>
</body>
</html>
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists