mirror of
https://github.com/berkeleydb/je.git
synced 2024-11-15 01:46:24 +00:00
573 lines
28 KiB
HTML
573 lines
28 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>Example Run Transaction Class</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="txn-management.html" title="Chapter 3. Transaction Management" />
|
|||
|
<link rel="prev" href="txnrollback.html" title="Managing Transaction Rollbacks" />
|
|||
|
<link rel="next" href="utilities.html" title="Chapter 4. Utilities" />
|
|||
|
</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">Example Run Transaction Class</th>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="20%" align="left"><a accesskey="p" href="txnrollback.html">Prev</a> </td>
|
|||
|
<th width="60%" align="center">Chapter 3. Transaction Management</th>
|
|||
|
<td width="20%" align="right"> <a accesskey="n" href="utilities.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="runtransaction"></a>Example Run Transaction Class</h2>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="toc">
|
|||
|
<dl>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="runtransaction.html#exruntransaction">RunTransaction Class</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="runtransaction.html#usingruntransaction">Using RunTransaction</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
</dl>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Usage of JE HA requires you to handle many different
|
|||
|
HA-specific exceptions. While some of these are Master-specific
|
|||
|
and others are Replica-specific, your code may still need to
|
|||
|
handle both. The reason why is that it is not uncommon for HA
|
|||
|
applications to have standard classes that perform database
|
|||
|
access, regardless of whether the class is used for a node in
|
|||
|
the Master state or a node in the Replica state.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
The following class is an example class that can be used to
|
|||
|
perform transactional reads and writes in an HA application.
|
|||
|
This class is used by the on-disk HA examples that you can find
|
|||
|
in your JE distribution (see <a class="xref" href="repexample.html" title="Chapter 6. Replication Examples">Replication Examples</a> for more information). However, we
|
|||
|
think this particular example class is important enough that we
|
|||
|
also describe it here.
|
|||
|
</p>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="exruntransaction"></a>RunTransaction Class</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
The <code class="classname">RunTransaction</code> abstract class is
|
|||
|
used to implement a utility class that performs database access
|
|||
|
for HA applications. It provides all the
|
|||
|
transaction error handling and retry framework that
|
|||
|
is required for database access in an HA environment.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Because <code class="classname">RunTransaction</code> is a class that
|
|||
|
is meant to be used by different example HA applications, it
|
|||
|
does not actually implement the database operations. Instead,
|
|||
|
it provides an abstract method that must be implemented by the
|
|||
|
HA application that uses <code class="classname">RunTransaction</code>.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
We begin by importing the classes that
|
|||
|
<code class="classname">RunTransaction</code> uses.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">package je.rep.quote;
|
|||
|
|
|||
|
import java.io.PrintStream;
|
|||
|
|
|||
|
import com.sleepycat.je.EnvironmentFailureException;
|
|||
|
import com.sleepycat.je.LockConflictException;
|
|||
|
import com.sleepycat.je.OperationFailureException;
|
|||
|
import com.sleepycat.je.Transaction;
|
|||
|
import com.sleepycat.je.rep.InsufficientAcksException;
|
|||
|
import com.sleepycat.je.rep.InsufficientReplicasException;
|
|||
|
import com.sleepycat.je.rep.ReplicaConsistencyException;
|
|||
|
import com.sleepycat.je.rep.ReplicaWriteException;
|
|||
|
import com.sleepycat.je.rep.ReplicatedEnvironment;</pre>
|
|||
|
<p>
|
|||
|
Then we define a series of private data members that identify how
|
|||
|
our HA transactions are going to behave in the event of an error
|
|||
|
condition.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">abstract class RunTransaction {
|
|||
|
|
|||
|
/* The maximum number of times to retry the transaction. */
|
|||
|
private static final int TRANSACTION_RETRY_MAX = 10;
|
|||
|
|
|||
|
/*
|
|||
|
* The number of seconds to wait between retries when a sufficient
|
|||
|
* number of replicas are not available for a transaction.
|
|||
|
*/
|
|||
|
private static final int INSUFFICIENT_REPLICA_RETRY_SEC = 1;
|
|||
|
|
|||
|
/* Amount of time to wait to let a replica catch up before
|
|||
|
* retrying.
|
|||
|
*/
|
|||
|
private static final int CONSISTENCY_RETRY_SEC = 1;
|
|||
|
|
|||
|
/* Amount of time to wait after a lock conflict. */
|
|||
|
private static final int LOCK_CONFLICT_RETRY_SEC = 1;
|
|||
|
|
|||
|
private final ReplicatedEnvironment env;
|
|||
|
private final PrintStream out; </pre>
|
|||
|
<p>
|
|||
|
Then we implement our class constructor, which is very simple
|
|||
|
because all the heavy lifting is done by whatever application calls
|
|||
|
this utility class.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> RunTransaction(ReplicatedEnvironment repEnv,
|
|||
|
PrintStream printStream) {
|
|||
|
env = repEnv;
|
|||
|
out = printStream;
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Now we implement our <code class="methodname">run()</code>
|
|||
|
method. This is what actually performs all the error checking and
|
|||
|
retry work for the class.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
The <code class="methodname">run()</code> method catches the exceptions
|
|||
|
most likely to occur as we are reading and writing the database,
|
|||
|
and then handles them, but it will also throw
|
|||
|
<a class="ulink" href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/InterruptedException.html" target="_top">InterruptedException</a> and <a class="ulink" href="../java/com/sleepycat/je/EnvironmentFailureException.html" target="_top">EnvironmentFailureException</a>.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
<a class="ulink" href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/InterruptedException.html" target="_top">InterruptedException</a> can be thrown if the thread calling this
|
|||
|
method is sleeping and some other thread interrupts it. The
|
|||
|
exception is possible because this method calls <a class="ulink" href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html#sleep(long)" target="_top">Thread.sleep</a> in
|
|||
|
the retry cycle.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/EnvironmentFailureException.html" target="_top">EnvironmentFailureException</a> can occur both when beginning a
|
|||
|
transaction and also when committing a transaction. It means that
|
|||
|
there is something significantly wrong with the node's environment.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
The <code class="literal">readOnly</code> parameter for this method is
|
|||
|
used to indicate that the transaction will only perform
|
|||
|
database reads. When that happens, the durability guarantee for
|
|||
|
the transaction is changed to <a class="ulink" href="../java/com/sleepycat/je/Durability.html#READ_ONLY_TXN" target="_top">Durability.READ_ONLY_TXN</a>
|
|||
|
because that policy does not call for any acknowledgements.
|
|||
|
This eliminates the possibility of an
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/InsufficientReplicasException.html" target="_top">InsufficientReplicasException</a> being thrown from the
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/Environment.html#beginTransaction(com.sleepycat.je.Transaction, com.sleepycat.je.TransactionConfig)" target="_top">Environment.beginTransaction()</a> operation.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">
|
|||
|
public void run(boolean readOnly)
|
|||
|
throws InterruptedException, EnvironmentFailureException { </pre>
|
|||
|
<p>
|
|||
|
Now we begin our retry loop and define our sleep cycle between
|
|||
|
retries. Initially, we do not actually sleep before
|
|||
|
retrying the transaction. However, some of the
|
|||
|
error conditions caught by this method will cause the thread to
|
|||
|
sleep before the operation is retried. After every sleep operation,
|
|||
|
the sleep time is returned to 0 because usually putting the thread
|
|||
|
to sleep is of no benefit.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> OperationFailureException exception = null;
|
|||
|
boolean success = false;
|
|||
|
long sleepMillis = 0;
|
|||
|
final TransactionConfig txnConfig = readOnly ?
|
|||
|
new TransactionConfig().setDurability(Durability.READ_ONLY_TXN) :
|
|||
|
null;
|
|||
|
|
|||
|
for (int i = 0; i < TRANSACTION_RETRY_MAX; i++) {
|
|||
|
/* Sleep before retrying. */
|
|||
|
if (sleepMillis != 0) {
|
|||
|
Thread.sleep(sleepMillis);
|
|||
|
sleepMillis = 0;
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Now we create our transaction, perform the database operations, and
|
|||
|
then do the work. The <code class="methodname">doTransactionWork()</code>
|
|||
|
method is an abstract method that must be implemented by the
|
|||
|
application using this class. Otherwise, this is standard
|
|||
|
transaction begin/commit code that should hold no surprises for
|
|||
|
you.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> Transaction txn = null;
|
|||
|
try {
|
|||
|
txn = env.beginTransaction(null, null);
|
|||
|
doTransactionWork(txn); /* CALL APP-SPECIFIC CODE */
|
|||
|
txn.commit();
|
|||
|
success = true;
|
|||
|
return; </pre>
|
|||
|
<p>
|
|||
|
The first error case that we check for is
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/InsufficientReplicasException.html" target="_top">InsufficientReplicasException</a>. This exception means that the
|
|||
|
Master is not in contact with enough Electable Replicas to successfully
|
|||
|
commit the transaction. It is possible that Replicas are still
|
|||
|
starting up after an application restart, so we put the thread to
|
|||
|
sleep before attempting the transaction again.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/InsufficientReplicasException.html" target="_top">InsufficientReplicasException</a> is thrown by <a class="ulink" href="../java/com/sleepycat/je/Transaction.html#commit()" target="_top">Transaction.commit()</a>,
|
|||
|
so we do have to perform the transaction all over again.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> } catch (InsufficientReplicasException insufficientReplicas) {
|
|||
|
|
|||
|
/*
|
|||
|
* Retry the transaction. Give replicas a chance to
|
|||
|
* contact this master, in case they have not had a
|
|||
|
* chance to do so following an election.
|
|||
|
*/
|
|||
|
exception = insufficientReplicas;
|
|||
|
out.printf(insufficientReplicas.toString());
|
|||
|
sleepMillis = INSUFFICIENT_REPLICA_RETRY_SEC * 1000;
|
|||
|
continue; </pre>
|
|||
|
<p>
|
|||
|
Next we check for <a class="ulink" href="../java/com/sleepycat/je/rep/InsufficientAcksException.html" target="_top">InsufficientAcksException</a>. This exception
|
|||
|
means that the transaction has successfully committed on the
|
|||
|
Master, but not enough Electable Replicas have acknowledged the commit
|
|||
|
within the allowed period of time. Whether you consider this to
|
|||
|
be a successful commit depends on your durability policy.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
As provided here, the code considers this situation to be
|
|||
|
an unsuccessful commit. But if you have a lot of Electable Replicas and you
|
|||
|
have a strong durability guarantee on the Master, then you might
|
|||
|
be able to still consider this to be a successful commit. If so,
|
|||
|
you should set <code class="literal">success = true;</code> before
|
|||
|
returning from the method.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
For more information on this error case, see
|
|||
|
<a class="xref" href="txn-management.html#replicaacktimeout" title="Managing Acknowledgement Timeouts">Managing Acknowledgement Timeouts</a>.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> } catch (InsufficientAcksException insufficientReplicas) {
|
|||
|
|
|||
|
/*
|
|||
|
* Transaction has been committed at this node. The
|
|||
|
* other acknowledgments may be late in arriving,
|
|||
|
* or may never arrive because the replica just
|
|||
|
* went down.
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
* INSERT APP-SPECIFIC CODE HERE: For example, repeat
|
|||
|
* idempotent changes to ensure they went through.
|
|||
|
*
|
|||
|
* Note that 'success' is false at this point, although
|
|||
|
* some applications may consider the transaction to be
|
|||
|
* complete.
|
|||
|
*/
|
|||
|
out.println(insufficientReplicas.toString());
|
|||
|
txn = null;
|
|||
|
return; </pre>
|
|||
|
<p>
|
|||
|
Next we check for <a class="ulink" href="../java/com/sleepycat/je/rep/ReplicaWriteException.html" target="_top">ReplicaWriteException</a>. This happens when a
|
|||
|
write operation is attempted on a Replica. In response to this, any
|
|||
|
number of things can be done, including reporting the problem to
|
|||
|
the application attempting the write operation and then aborting,
|
|||
|
to forwarding the write request to the Master. This particular
|
|||
|
method responds to this condition based on how the
|
|||
|
<code class="methodname">onReplicaWrite()</code> method is implemented.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
For more information on how to handle this exception, see
|
|||
|
<a class="xref" href="replicawrites.html" title="Managing Write Requests at a Replica">Managing Write Requests at a Replica</a>.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> } catch (ReplicaWriteException replicaWrite) {
|
|||
|
|
|||
|
/*
|
|||
|
* Attempted a modification while in the Replica
|
|||
|
* state.
|
|||
|
*
|
|||
|
* CALL APP-SPECIFIC CODE HERE: Cannot accomplish
|
|||
|
* the changes on this node, redirect the write to
|
|||
|
* the new master and retry the transaction there.
|
|||
|
* This could be done by forwarding the request to
|
|||
|
* the master here, or by returning an error to the
|
|||
|
* requester and retrying the request at a higher
|
|||
|
* level.
|
|||
|
*/
|
|||
|
onReplicaWrite(replicaWrite);
|
|||
|
return; </pre>
|
|||
|
<p>
|
|||
|
Now we check for <a class="ulink" href="../java/com/sleepycat/je/LockConflictException.html" target="_top">LockConflictException</a>, which is thrown whenever
|
|||
|
a transaction experiences a lock conflict with another thread. Note
|
|||
|
that by catching this exception, we are also catching the
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/LockPreemptedException.html" target="_top">LockPreemptedException</a>, which happens whenever the underlying HA
|
|||
|
code "steals" a lock from an application transaction. The most
|
|||
|
common cause of this is when the HA replication stream is updating
|
|||
|
a Replica, and the Replica is holding a read lock that the
|
|||
|
replication stream requires.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Here, it is useful to sleep for a period of time before retrying
|
|||
|
the transaction.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> } catch (LockConflictException lockConflict) {
|
|||
|
|
|||
|
/*
|
|||
|
* Retry the transaction. Note that LockConflictException
|
|||
|
* covers the HA LockPreemptedException.
|
|||
|
*/
|
|||
|
exception = lockConflict;
|
|||
|
out.println(lockConflict.toString());
|
|||
|
sleepMillis = LOCK_CONFLICT_RETRY_SEC * 1000;
|
|||
|
continue; </pre>
|
|||
|
<p>
|
|||
|
The last error we check for is <a class="ulink" href="../java/com/sleepycat/je/rep/ReplicaConsistencyException.html" target="_top">ReplicaConsistencyException</a>. This
|
|||
|
exception can be thrown when the transaction begins. It means that
|
|||
|
the <code class="methodname">beginTransaction()</code> method has waited
|
|||
|
too long for the Replica to catch up relative to the Master. This
|
|||
|
situation does not really represent a failed transaction because
|
|||
|
the transaction never had a chance to proceed in the first place.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
In any case, the proper thing to do is to put the thread to sleep
|
|||
|
for a period of time so that the Replica has the chance to meet its
|
|||
|
consistency requirements. Then we retry the transaction.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Note that at this point in time, the transaction handle is in
|
|||
|
whatever state it was in when <code class="methodname">beginTransaction()</code>
|
|||
|
was called. If the handle was in the null state before
|
|||
|
attempting the operation, then it will still be in the null
|
|||
|
state. The important thing to realize here is that the
|
|||
|
transaction does not have to be aborted, because the
|
|||
|
transaction never began in the first place.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
For more information on consistency policies, see
|
|||
|
<a class="xref" href="consistency.html" title="Managing Consistency">Managing Consistency</a>.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> } catch (ReplicaConsistencyException replicaConsistency) {
|
|||
|
|
|||
|
/*
|
|||
|
* Retry the transaction. The timeout associated with
|
|||
|
* the ReplicaConsistencyPolicy may need to be
|
|||
|
* relaxed if it's too stringent.
|
|||
|
*/
|
|||
|
exception = replicaConsistency;
|
|||
|
out.println(replicaConsistency.toString());
|
|||
|
sleepMillis = CONSISTENCY_RETRY_SEC * 1000;
|
|||
|
continue; </pre>
|
|||
|
<p>
|
|||
|
Finally, we abort our transaction and loop again as needed.
|
|||
|
<code class="methodname">onRetryFailure()</code> is called if the
|
|||
|
transaction has been retried too many times (as defined by
|
|||
|
<code class="literal">TRANSACTION_RETRY_MAX</code>. It provides the option to
|
|||
|
log the situation.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> } finally {
|
|||
|
|
|||
|
if (!success) {
|
|||
|
if (txn != null) {
|
|||
|
txn.abort();
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* INSERT APP-SPECIFIC CODE HERE: Perform any
|
|||
|
* app-specific cleanup.
|
|||
|
*/
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* CALL APP-SPECIFIC CODE HERE:
|
|||
|
* Transaction failed, despite retries.
|
|||
|
*/
|
|||
|
onRetryFailure(exception);
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Having done that, the class is almost completed. Left to do is to
|
|||
|
define a couple of methods, one of which is an abstract method
|
|||
|
that must be implemented by the application that uses this
|
|||
|
class.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
<code class="methodname">doTransactionWork()</code> is an abstract method
|
|||
|
where the actual database operations are performed.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
<code class="methodname">onReplicaWrite()</code> is a method that should be
|
|||
|
implemented by the HA application that uses this class. It is used to
|
|||
|
define whatever action the Replica should
|
|||
|
take if a write is attempted on it. For examples of how this is
|
|||
|
used, see the next section.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
For this implementation of the class, we simply throw
|
|||
|
the <a class="ulink" href="../java/com/sleepycat/je/rep/ReplicaWriteException.html" target="_top">ReplicaWriteException</a> that got us here in the first place.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> abstract void doTransactionWork(Transaction txn);
|
|||
|
|
|||
|
void onReplicaWrite(ReplicaWriteException replicaWrite) {
|
|||
|
throw replicaWrite;
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Finally, we implement <code class="methodname">onRetryFailure()</code>,
|
|||
|
which is what this class does if the transaction retry loop
|
|||
|
goes through too many iterations. Here, we simply print the error
|
|||
|
to the console. A more robust application should probably write the
|
|||
|
error to the application logs.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> void onRetryFailure(OperationFailureException lastException) {
|
|||
|
out.println("Failed despite retries." +
|
|||
|
((lastException == null) ?
|
|||
|
"" :
|
|||
|
" Encountered exception:" +
|
|||
|
lastException));
|
|||
|
}
|
|||
|
} </pre>
|
|||
|
</div>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="usingruntransaction"></a>Using RunTransaction</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Having implemented the <code class="classname">RunTransaction</code> class, it is fairly easy to use.
|
|||
|
Essentially, you only have to implement the
|
|||
|
<code class="methodname">RunTransaction.doTransactionWork()</code>
|
|||
|
method so that it performs whatever database access you
|
|||
|
want.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
For example, the following method performs a read on an
|
|||
|
<a class="ulink" href="../java/com/sleepycat/persist/EntityStore.html" target="_top">EntityStore</a> used by the <a class="ulink" href="../examples/je/rep/quote/StockQuotes.html" target="_top">StockQuotes</a> example HA
|
|||
|
application. Notice that the class is instantiated,
|
|||
|
<code class="methodname">doTransactionWork()</code> is
|
|||
|
implemented, and the <code class="methodname">RunTransaction.run()</code>
|
|||
|
method are all called in one place. This makes for fairly
|
|||
|
easy maintenance of the code.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> private void printStocks(final PrintStream out)
|
|||
|
throws InterruptedException {
|
|||
|
|
|||
|
new RunTransaction(repEnv, out) {
|
|||
|
|
|||
|
@Override
|
|||
|
void doTransactionWork(Transaction txn) {
|
|||
|
|
|||
|
// dao is a DataAccessor class used to access
|
|||
|
// an entity store.
|
|||
|
final EntityCursor<Quote> quotes =
|
|||
|
dao.quoteById.entities(txn, null);
|
|||
|
try {
|
|||
|
out.println("\tSymbol\tPrice");
|
|||
|
out.println("\t======\t=====");
|
|||
|
|
|||
|
int count = 0;
|
|||
|
for (Quote quote : quotes) {
|
|||
|
out.println("\t" + quote.stockSymbol +
|
|||
|
"\t" + quote.lastTrade);
|
|||
|
count++;
|
|||
|
}
|
|||
|
out.println("\n\t" + count + " stock"
|
|||
|
+ ((count == 1) ? "" : "s") +
|
|||
|
" listed.\n");
|
|||
|
} finally {
|
|||
|
quotes.close();
|
|||
|
}
|
|||
|
}
|
|||
|
}.run(true /*readOnly*/);
|
|||
|
|
|||
|
/* Output local indication of processing. */
|
|||
|
System.out.println("Processed print request");
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
In the previous example, we do not bother to override the
|
|||
|
<code class="methodname">RunTransaction.onReplicaWrite()</code>
|
|||
|
method because this transaction is performing read-only
|
|||
|
access to the database. Regardless of whether the
|
|||
|
transaction is run on a Master or a Replica,
|
|||
|
<a class="ulink" href="../java/com/sleepycat/je/rep/ReplicaWriteException.html" target="_top">ReplicaWriteException</a> can not be raised here, so we can
|
|||
|
safely use the default implementation.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
However, if we were running a transaction that performs a
|
|||
|
database write, then we should probably do something with
|
|||
|
<code class="methodname">onReplicaWrite()</code> other than merely
|
|||
|
re-throwing the exception.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
The following is an example usage of
|
|||
|
<code class="classname">RunTransaction</code> that is also used in
|
|||
|
the <a class="ulink" href="../examples/je/rep/quote/StockQuotes.html" target="_top">StockQuotes</a> example.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> void updateStock(final String line, final PrintStream printStream)
|
|||
|
throws InterruptedException {
|
|||
|
|
|||
|
// Quote is a utility class used to parse a line of input
|
|||
|
// obtained from the console.
|
|||
|
final Quote quote = QuoteUtil.parseQuote(line);
|
|||
|
if (quote == null) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
new RunTransaction(repEnv, printStream) {
|
|||
|
|
|||
|
void doTransactionWork(Transaction txn) {
|
|||
|
// dao is a Data Accessor class used to perform access
|
|||
|
// to the entity store.
|
|||
|
dao.quoteById.put(txn, quote);
|
|||
|
/* Output local indication of processing. */
|
|||
|
System.out.println("Processed update request: " + line);
|
|||
|
}
|
|||
|
|
|||
|
// For this example, we simply log the error condition.
|
|||
|
// For a more robust example, so other action might be
|
|||
|
// taken; for example, log the situation and then route
|
|||
|
// the write request to the Master.
|
|||
|
void onReplicaWrite(ReplicaWriteException replicaWrite) {
|
|||
|
/* Attempted a modification while in the replica state. */
|
|||
|
printStream.println
|
|||
|
(repEnv.getNodeName() +
|
|||
|
" is not currently the master. Perform the update" +
|
|||
|
" at the node that's currently the master.");
|
|||
|
}
|
|||
|
}.run(false /*not readOnly */);
|
|||
|
} </pre>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="navfooter">
|
|||
|
<hr />
|
|||
|
<table width="100%" summary="Navigation footer">
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left"><a accesskey="p" href="txnrollback.html">Prev</a> </td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="u" href="txn-management.html">Up</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right"> <a accesskey="n" href="utilities.html">Next</a></td>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left" valign="top">Managing Transaction Rollbacks </td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="h" href="index.html">Home</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right" valign="top"> Chapter 4. Utilities</td>
|
|||
|
</tr>
|
|||
|
</table>
|
|||
|
</div>
|
|||
|
</body>
|
|||
|
</html>
|