mirror of
https://github.com/berkeleydb/je.git
synced 2024-11-15 01:46:24 +00:00
277 lines
13 KiB
HTML
277 lines
13 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>Managing Write Requests at a Replica</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 Berkeley DB, Java Edition High Availability Applications" />
|
|||
|
<link rel="up" href="progoverview.html" title="Chapter 2. Replication API First Steps" />
|
|||
|
<link rel="prev" href="repenvironmentopen.html" title="Opening a Replicated Environment" />
|
|||
|
<link rel="next" href="secondary.html" title="Secondary Nodes" />
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div xmlns="" class="navheader">
|
|||
|
<div class="libver">
|
|||
|
<p>Library Version 12.2.7.5</p>
|
|||
|
</div>
|
|||
|
<table width="100%" summary="Navigation header">
|
|||
|
<tr>
|
|||
|
<th colspan="3" align="center">Managing Write Requests at a Replica</th>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="20%" align="left"><a accesskey="p" href="repenvironmentopen.html">Prev</a> </td>
|
|||
|
<th width="60%" align="center">Chapter 2. Replication API First Steps</th>
|
|||
|
<td width="20%" align="right"> <a accesskey="n" href="secondary.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="replicawrites"></a>Managing Write Requests at a Replica</h2>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="toc">
|
|||
|
<dl>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="replicawrites.html#using-statechangelistener">Using the StateChangeListener</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="replicawrites.html#repwriteexception">Catching ReplicaWriteException</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
</dl>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
For a replicated JE application, read requests can be
|
|||
|
serviced by any electable or secondary node in the replication group, but
|
|||
|
write requests can only be serviced by the Master node. For
|
|||
|
this reason, your application must be prepared to deal with the
|
|||
|
difference in operating behavior between read-only Replicas and
|
|||
|
read-write Masters.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
It is possible to be quite sophisticated in terms of tracking
|
|||
|
which node is the Master and so which node can service write
|
|||
|
requests. You can even route write requests to the Master node
|
|||
|
by writing a special router process. For an example of an
|
|||
|
application that does this, see <a class="ulink" href="../examples/je/rep/quote/RouterDrivenStockQuotes.html" target="_top">RouterDrivenStockQuotes</a> and
|
|||
|
<a class="ulink" href="../examples/je/rep/quote/HARouter.html" target="_top">HARouter</a>, both of which are available in your JE
|
|||
|
distribution in the
|
|||
|
<code class="literal"><JE HOME>/examples/je/rep/quote</code>
|
|||
|
directory.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
However, for our purposes here, we simply want to
|
|||
|
make sure our Replica nodes can gracefully handle a situation
|
|||
|
where they receive a write request. The write request should be
|
|||
|
rejected by the node, with some notification being returned to
|
|||
|
the requester that the write activity is rejected. While not
|
|||
|
the most robust solution, this is the simplest thing your
|
|||
|
JE replicated application can do if it receives a write
|
|||
|
request at a Replica node.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
There are two ways to determine whether a write request can be
|
|||
|
handled at the local node:
|
|||
|
</p>
|
|||
|
<div class="itemizedlist">
|
|||
|
<ul type="disc">
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Use a monitor node to implement request routing.
|
|||
|
Monitor nodes are described in <a class="xref" href="monitors.html" title="Chapter 5. Writing Monitor Nodes">Writing Monitor Nodes</a>.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Use the <a class="ulink" href="../java/com/sleepycat/je/rep/StateChangeListener.html" target="_top">StateChangeListener</a> to detect when the local
|
|||
|
node becomes a Master. Otherwise, forward the write
|
|||
|
request to the Master node instead of attempting to
|
|||
|
service it locally.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Either way, any code that attempts database writes for an HA
|
|||
|
application should always be prepared to handle a
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/ReplicaWriteException.html" target="_top">ReplicaWriteException</a>.
|
|||
|
</p>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="using-statechangelistener"></a>Using the StateChangeListener</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
You use the <a class="ulink" href="../java/com/sleepycat/je/rep/StateChangeListener.html" target="_top">StateChangeListener</a> interface to implement a
|
|||
|
class that is capable of notifying your node when it has
|
|||
|
changed state. In this way, you can track whether a node
|
|||
|
is in the Master, Replica or Unknown state, and so know
|
|||
|
whether the node is capable of handling write requests.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
To do this, you must implement <a class="ulink" href="../java/com/sleepycat/je/rep/StateChangeListener.html#stateChange(com.sleepycat.je.rep.StateChangeEvent)" target="_top">StateChangeListener.stateChange()</a>,
|
|||
|
which receives a <a class="ulink" href="../java/com/sleepycat/je/rep/StateChangeEvent.html" target="_top">StateChangeEvent</a> object whenever it is
|
|||
|
called.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
If the node is not in the Master state, then the node
|
|||
|
can either reject write requests outright or, more
|
|||
|
usefully, forward write requests to the Master. For an
|
|||
|
example of an HA application that forwards write requests
|
|||
|
and uses the <a class="ulink" href="../java/com/sleepycat/je/rep/StateChangeListener.html" target="_top">StateChangeListener</a>, see the
|
|||
|
<a class="ulink" href="../examples/je/rep/quote/UpdateForwardingStockQuotes.html" target="_top">UpdateForwardingStockQuotes</a> example.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Alternatively, you can write a router based on an HA
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/monitor/Monitor.html" target="_top">Monitor</a>. See <a class="xref" href="monitors.html" title="Chapter 5. Writing Monitor Nodes">Writing Monitor Nodes</a>
|
|||
|
for more information.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Briefly, you can implement
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/StateChangeListener.html" target="_top">StateChangeListener</a> as follows. Notice that this partial
|
|||
|
implementation relies on <a class="ulink" href="../java/com/sleepycat/je/rep/StateChangeEvent.html#getState()" target="_top">StateChangeEvent.getState()</a> to
|
|||
|
determine the state that the node has just transitioned to.
|
|||
|
It then uses <a class="ulink" href="../java/com/sleepycat/je/rep/StateChangeEvent.html#getMasterNodeName()" target="_top">StateChangeEvent.getMasterNodeName()</a> to
|
|||
|
determine where write requests should be forwarded to in
|
|||
|
the event that the new state is not
|
|||
|
<code class="literal">MASTER</code>.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">private class Listener implements StateChangeListener {
|
|||
|
|
|||
|
private String currentMaster = null;
|
|||
|
|
|||
|
public void stateChange(StateChangeEvent se)
|
|||
|
throws RuntimeException {
|
|||
|
|
|||
|
switch (stateChangeEvent.getState()) {
|
|||
|
|
|||
|
case MASTER:
|
|||
|
// Do whatever your code needs you to do when the
|
|||
|
// current node is the MASTER. For example,
|
|||
|
// set a flag to indicate that the local node
|
|||
|
// is in the MASTER state. Here, we just fall
|
|||
|
// through and do the same thing as if we
|
|||
|
// transitioned to the REPLICA state.
|
|||
|
case REPLICA:
|
|||
|
// Again, do whatever your code needs done when
|
|||
|
// a node is in the REPLICA state. At a minimum,
|
|||
|
// you should probably capture which node is the
|
|||
|
// current Master.
|
|||
|
currentMaster = se.getMasterNodeName();
|
|||
|
break;
|
|||
|
|
|||
|
// We get here if we have transitioned to the UNKNOWN
|
|||
|
// state.
|
|||
|
default:
|
|||
|
currentmasterName = null;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public String getCurrentMasterName() {
|
|||
|
return currentMaster;
|
|||
|
}
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
In order to make use of the new listener, the application
|
|||
|
must call <a class="ulink" href="../java/com/sleepycat/je/rep/ReplicatedEnvironment.html#setStateChangeListener(com.sleepycat.je.rep.StateChangeListener)" target="_top">ReplicatedEnvironment.setStateChangeListener()</a>.
|
|||
|
Note that this method can be called at any time after the
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/ReplicatedEnvironment.html" target="_top">ReplicatedEnvironment</a> handle has been created. Also, the
|
|||
|
listener is set per environment, not per handle. So if you
|
|||
|
set different listeners for different
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/ReplicatedEnvironment.html" target="_top">ReplicatedEnvironment</a> handles, the last listener
|
|||
|
configured is used environment-wide.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> EnvironmentConfig envConfig = new EnvironmentConfig();
|
|||
|
envConfig.setAllowCreate(true);
|
|||
|
envConfig.setTransactional(true);
|
|||
|
|
|||
|
// Identify the node
|
|||
|
ReplicationConfig repConfig = new ReplicationConfig();
|
|||
|
repConfig.setGroupName("PlanetaryRepGroup");
|
|||
|
repConfig.setNodeName("Saturn");
|
|||
|
repConfig.setNodeHostPort("saturn.example.com:5001");
|
|||
|
|
|||
|
// Use the node at mars.example.com:5002 as a helper to find
|
|||
|
// the rest of the group.
|
|||
|
repConfig.setHelperHosts("mars.example.com:5002");
|
|||
|
|
|||
|
ReplicatedEnvironment repEnv =
|
|||
|
new ReplicatedEnvironment(home, repConfig, envConfig);
|
|||
|
StateChangeListener listener = new Listener();
|
|||
|
repEnv.setStateChangeListener(listener); </pre>
|
|||
|
</div>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="repwriteexception"></a>Catching ReplicaWriteException</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
If you perform a Database write operation on a node that is not in the
|
|||
|
Master state, a <a class="ulink" href="../java/com/sleepycat/je/rep/ReplicaWriteException.html" target="_top">ReplicaWriteException</a> is thrown when you attempt to commit the
|
|||
|
transaction. Therefore, whenever performing database write
|
|||
|
operations in an HA application, you should catch and
|
|||
|
handle <a class="ulink" href="../java/com/sleepycat/je/rep/ReplicaWriteException.html" target="_top">ReplicaWriteException</a>.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
For example:
|
|||
|
</p>
|
|||
|
<pre class="programlisting">Transaction txn = null;
|
|||
|
try {
|
|||
|
txn = env.beginTransaction(null, null);
|
|||
|
/*
|
|||
|
* Perform your write operations under the protection
|
|||
|
* of the transaction handle here.
|
|||
|
*/
|
|||
|
txn.commit();
|
|||
|
} catch (ReplicaWriteException replicaWrite) {
|
|||
|
/*
|
|||
|
* Perform whatever reporting (logging) activies you want
|
|||
|
* to do in order to acknowledge that the write operation(s)
|
|||
|
* failed. Then abort the transaction.
|
|||
|
*/
|
|||
|
|
|||
|
if (txn != null) {
|
|||
|
txn.abort();
|
|||
|
}
|
|||
|
} </pre>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="navfooter">
|
|||
|
<hr />
|
|||
|
<table width="100%" summary="Navigation footer">
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left"><a accesskey="p" href="repenvironmentopen.html">Prev</a> </td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="u" href="progoverview.html">Up</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right"> <a accesskey="n" href="secondary.html">Next</a></td>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left" valign="top">Opening a Replicated Environment </td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="h" href="index.html">Home</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right" valign="top"> Secondary Nodes</td>
|
|||
|
</tr>
|
|||
|
</table>
|
|||
|
</div>
|
|||
|
</body>
|
|||
|
</html>
|