• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/db-4.8.30/docs/gsg_db_rep/JAVA/
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3<html xmlns="http://www.w3.org/1999/xhtml">
4  <head>
5    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6    <title>Example Processing Loop</title>
7    <link rel="stylesheet" href="gettingStarted.css" type="text/css" />
8    <meta name="generator" content="DocBook XSL Stylesheets V1.73.2" />
9    <link rel="start" href="index.html" title="Getting Started with Replicated Berkeley DB Applications" />
10    <link rel="up" href="fwrkmasterreplica.html" title="Chapter��4.��Replica versus Master Processes" />
11    <link rel="prev" href="processingloop.html" title="Processing Loop" />
12    <link rel="next" href="addfeatures.html" title="Chapter��5.��Additional Features" />
13  </head>
14  <body>
15    <div class="navheader">
16      <table width="100%" summary="Navigation header">
17        <tr>
18          <th colspan="3" align="center">Example Processing Loop</th>
19        </tr>
20        <tr>
21          <td width="20%" align="left"><a accesskey="p" href="processingloop.html">Prev</a>��</td>
22          <th width="60%" align="center">Chapter��4.��Replica versus Master Processes</th>
23          <td width="20%" align="right">��<a accesskey="n" href="addfeatures.html">Next</a></td>
24        </tr>
25      </table>
26      <hr />
27    </div>
28    <div class="sect1" lang="en" xml:lang="en">
29      <div class="titlepage">
30        <div>
31          <div>
32            <h2 class="title" style="clear: both"><a id="exampledoloop"></a>Example Processing Loop</h2>
33          </div>
34        </div>
35      </div>
36      <div class="toc">
37        <dl>
38          <dt>
39            <span class="sect2">
40              <a href="exampledoloop.html#runningit">Running It</a>
41            </span>
42          </dt>
43        </dl>
44      </div>
45      <p>
46                          In this section we take the example
47                          processing loop that we presented in the
48                          previous section and we flesh it out to
49                          provide a more complete example. We do this
50                          by updating the
51                          <code class="function">doloop()</code>
52                          function that our original transaction
53                          application used 
54                          
55                          
56                          <span>(see <a class="xref" href="simpleprogramlisting.html#doloop_java" title="Method: SimpleTxn.doloop()">Method: SimpleTxn.doloop()</a>)</span>
57                          to fully support our replicated application. 
58                  </p>
59      <p>
60                          In the following example code, code that we
61                          add to the original example is presented in 
62                          <strong class="userinput"><code>bold</code></strong>.
63                  </p>
64      <p>
65                        To begin, we must implement a way to track whether
66                        our application is running as a master or a client.
67                        There are many ways to do this, but in this case
68                        what we will do is extend
69                        <code class="classname">com.sleepycat.db.Environment</code>
70                        to carry the information. We do this by creating
71                        the <code class="classname">RepQuoteEnvironment</code>
72                        class.
73                   </p>
74      <pre class="programlisting">
75        <strong class="userinput">
76          <code>package db.repquote;
77
78import com.sleepycat.db.DatabaseException;
79import com.sleepycat.db.Environment;
80import com.sleepycat.db.EnvironmentConfig;
81
82public class RepQuoteEnvironment extends Environment
83{
84    private boolean isMaster;
85
86    public RepQuoteEnvironment(final java.io.File host,
87        EnvironmentConfig config)
88        throws DatabaseException, java.io.FileNotFoundException
89    {
90        super(host, config);
91        isMaster = false;
92    }
93
94    boolean getIsMaster()
95    {
96        return isMaster;
97    }
98
99    public void setIsMaster(boolean isMaster)
100    {
101        this.isMaster = isMaster;
102    }
103} </code>
104        </strong>
105      </pre>
106      <p>
107                          Next, we go to <code class="filename">RepQuoteExampleGSG.java</code> and
108                          we include the
109                          <code class="classname">RepQuoteEnvironment</code> class
110                          as well as the
111                          <code class="classname">EventHandler</code> class. We then
112                          cause our <code class="classname">RepQuoteExampleGSG</code>
113                          class to implement
114                          <code class="classname">EventHandler</code>. We also
115                          change our environment handle to be an instance
116                          of <code class="classname">RepQuoteEnvironment</code>
117                          instead of <code class="classname">Environment</code>.
118                  </p>
119      <p>
120                          Note that we also import the
121                          <code class="classname">com.sleepycat.db.ReplicationHandleDeadException</code>
122                          class. We will discuss what that exception is
123                          used for a little later in this example.
124                  </p>
125      <pre class="programlisting">package db.repquote;
126
127import java.io.FileNotFoundException;
128import java.io.BufferedReader;
129import java.io.InputStreamReader;
130import java.io.IOException;
131import java.io.UnsupportedEncodingException;
132import java.lang.Thread;
133import java.lang.InterruptedException;
134
135import com.sleepycat.db.Cursor;
136import com.sleepycat.db.Database;
137import com.sleepycat.db.DatabaseConfig;
138import com.sleepycat.db.DatabaseEntry;
139import com.sleepycat.db.DatabaseException;
140import com.sleepycat.db.DatabaseType;
141import com.sleepycat.db.EnvironmentConfig;
142<strong class="userinput"><code>import com.sleepycat.db.EventHandler;</code></strong>
143import com.sleepycat.db.LockMode;
144import com.sleepycat.db.OperationStatus;
145<strong class="userinput"><code>import com.sleepycat.db.ReplicationHandleDeadException;</code></strong>
146
147import db.repquote.RepConfig;
148<strong class="userinput"><code>import db.repquote.RepQuoteEnvironment</code></strong>
149
150public class RepQuoteExampleGSG <strong class="userinput"><code> implements EventHandler</code></strong>
151{
152    private RepConfig repConfig;
153    private <strong class="userinput"><code>RepQuoteEnvironment</code></strong> dbenv; </pre>
154      <p>
155        That done, we can skip the 
156        
157        
158        <span><code class="methodname">main()</code> method and
159                our class constructor, because they do not change.</span>
160        
161        Instead, we skip down to our
162        
163
164        <span><code class="methodname">init()</code> method
165                where we take care of opening our environment and setting
166                the event handler.  </span>
167</p>
168      <p>
169        To update our <code class="methodname">init()</code> method, we only need
170        to do a couple of things. First, we identify the current class as
171        the event handler. Then, when we open our environment, we
172        instantiate a <code class="classname">RepQuoteEnvironment</code>
173        class instead of an <code class="classname">Environment</code>
174        class.
175</p>
176      <pre class="programlisting">    public int init(RepConfig config)
177        throws DatabaseException
178    {
179        int ret = 0;
180        repConfig = config;
181        EnvironmentConfig envConfig = new EnvironmentConfig();
182        envConfig.setErrorStream(System.err);
183        envConfig.setErrorPrefix(RepConfig.progname);
184
185        envConfig.setReplicationManagerLocalSite(repConfig.getThisHost());
186        for (ReplicationHostAddress host = repConfig.getFirstOtherHost();
187            host != null; host = repConfig.getNextOtherHost())
188        {
189            envConfig.replicationManagerAddRemoteSite(host);
190        }
191
192        if (appConfig.totalSites &gt; 0)
193            envConfig.setReplicationNumSites(repConfig.totalSites);
194        envConfig.setReplicationPriority(repConfig.priority);
195
196        envConfig.setReplicationManagerAckPolicy(
197            ReplicationManagerAckPolicy.ALL);
198        envConfig.setCacheSize(RepConfig.CACHESIZE);
199        envConfig.setTxnNoSync(true);
200
201        <strong class="userinput"><code>envConfig.setEventHandler(this);</code></strong>
202
203        envConfig.setAllowCreate(true);
204        envConfig.setRunRecovery(true);
205        envConfig.setThreaded(true);
206        envConfig.setInitializeReplication(true);
207        envConfig.setInitializeLocking(true);
208        envConfig.setInitializeLogging(true);
209        envConfig.setInitializeCache(true);
210        envConfig.setTransactional(true);
211        envConfig.setVerboseReplication(appConfig.verbose);
212        try {
213            dbenv = new <strong class="userinput"><code>RepQuoteEnvironment</code></strong>(repConfig.getHome(), envConfig);
214        } catch(FileNotFoundException e) {
215            System.err.println("FileNotFound exception: " + e.toString());
216            System.err.println(
217                "Ensure that the environment directory is pre-created.");
218            ret = 1;
219        }
220
221        // start Replication Manager
222        dbenv.replicationManagerStart(3, repConfig.startPolicy);
223        return ret;
224    } </pre>
225      <p>
226    That done, we need to implement the methods
227    required for responding to replication events.
228        These methods are required because we are now implementing 
229        <code class="classname">com.sleepycat.db.EventHandler</code>.  While
230        we are required to provide an implementation for all of these
231        methods, for our simple application we are really only interested
232        in these because they allow us to track whether a replication
233        instance is a master.
234</p>
235      <pre class="programlisting">    public void handleRepClientEvent()
236    {
237        dbenv.setIsMaster(false);
238    }
239
240    public void handleRepMasterEvent()
241    {
242        dbenv.setIsMaster(true);
243    }
244
245    public void handleRepNewMasterEvent(int envId)
246    {
247        // Ignored for now
248    }
249
250    public void handleWriteFailedEvent(int errorCode)
251    {
252        // Ignored for now
253    }
254
255    public void handleRepStartupDoneEvent()
256    {
257        // Ignored for now
258    }
259
260    public void handleRepPermFailedEvent()
261    {
262        // Ignored for now
263    }
264
265    public void handleRepElectedEvent()
266    {
267        // Safely ignored for Replication Manager applications.
268    }
269
270    public void handlePanicEvent()
271    {
272        System.err.println("Panic encountered!");
273        System.err.println("Shutting down.");
274        System.err.println("You should restart, running recovery.");
275        try {
276            terminate();
277        } catch (DatabaseException dbe) {
278            System.err.println("Caught an exception during " +
279                "termination in handlePanicEvent: " + dbe.toString());
280        }
281        System.exit(-1);
282    } </pre>
283      <p>
284        That done, we need to update our
285        <code class="function">doloop()</code> 
286        
287
288        <span>method.</span>
289</p>
290      <p>
291            We begin by updating our <code class="classname">DatabaseConfig</code>
292            instance to determine which options to use, depending on whether the
293            application is running as a master.
294    </p>
295      <pre class="programlisting">    public int doloop()
296        throws DatabaseException
297    {
298        Database db = null;
299
300        for (;;)
301        {
302            if (db == null) {
303                DatabaseConfig dbconf = new DatabaseConfig();
304                dbconf.setType(DatabaseType.BTREE);
305                <strong class="userinput"><code>if (dbenv.getIsMaster()) {
306                    dbconf.setAllowCreate(true);
307                }</code></strong>
308                dbconf.setTransactional(true); </pre>
309      <p>
310    When we open the database, we modify our error handling to
311    account for the case where the database does not yet exist. This can
312    happen if our code is running as a replica and the Replication Manager has not
313    yet had a chance to create the databases for us. Recall that replicas never
314    write to their own databases directly, and so they cannot
315    create databases on their own.
316</p>
317      <p>
318        If we detect that the database does not yet exist, we simply
319        close the database handle, sleep for a short period of time
320        and then continue processing. This gives the Replication Manager a chance to
321        create the database so that our replica can continue
322        operations.
323</p>
324      <pre class="programlisting">                try {
325                    db = dbenv.openDatabase
326                        (null, RepConfig.progname, null, dbconf);
327                } catch (java.io.FileNotFoundException e) {
328                    <strong class="userinput"><code>System.err.println("no stock database available yet.");
329                    if (db != null) {
330                        db.close(true);
331                        db = null;
332                    }
333                    try {
334                        Thread.sleep(RepConfig.SLEEPTIME);
335                    } catch (InterruptedException ie) {}
336                    continue;</code></strong>
337                }
338            } </pre>
339      <p>
340        Next we modify our prompt, so that if the local process is running
341        as a replica, we can tell from the shell that the prompt is for a
342        read-only process.
343    </p>
344      <pre class="programlisting">            BufferedReader stdin =
345                new BufferedReader(new InputStreamReader(System.in));
346
347            // listen for input, and add it to the database.
348            System.out.print("QUOTESERVER");
349            <strong class="userinput"><code>if (!dbenv.getIsMaster())
350                System.out.print("(read-only)");
351            System.out.print("&gt; ");</code></strong>
352            System.out.flush();
353            String nextline = null;
354            try {
355                nextline = stdin.readLine();
356            } catch (IOException ioe) {
357                System.err.println("Unable to get data from stdin");
358                break;
359            }
360            String[] words = nextline.split("\\s"); </pre>
361      <p>
362            When we collect data from the prompt, there is a case that says
363            if no data is entered then show the entire stocks database.
364            This display is performed by our
365            <code class="function">print_stocks()</code> 
366             
367            <span>method</span> 
368            (which has not
369            required a modification since we first introduced it in 
370            <a class="xref" href="simpleprogramlisting.html#printstocks_c" title="Method: SimpleTxn.printStocks()">
371                            
372                            
373                            <span>Method: SimpleTxn.printStocks()</span>
374                    </a>).
375    </p>
376      <p>
377            When we call 
378               
379               <span><code class="function">printStocks()</code>, </span>
380            we check for a dead replication handle. Dead
381            replication handles happen whenever a replication election
382            results in a previously committed transaction becoming
383            invalid. This is an error scenario caused by a new master having a
384            slightly older version of the data than the original
385            master and so all replicas must modify their database(s) to
386            reflect that of the new master. In this situation, some
387            number of previously committed transactions may have to be
388            unrolled. From the replica's perspective, the database
389            handles should all be closed and then opened again.
390    </p>
391      <pre class="programlisting">            // A blank line causes the DB to be dumped to stdout.
392            if (words.length == 0 ||
393                (words.length == 1 &amp;&amp; words[0].length() == 0)) {
394                try {
395                    printStocks(db);
396                <strong class="userinput"><code>} catch (DeadlockException de) {
397                    continue;
398                // Dead replication handles are caused by an election
399                // resulting in a previously committing read becoming
400                // invalid. Close the db handle and reopen.
401                } catch (ReplicationHandleDeadException rhde) {
402                    db.close(true); // close no sync.
403                    db = null;
404                    continue;</code></strong>
405                } catch (DatabaseException e) {
406                    System.err.println("Got db exception reading " +
407                        "replication DB: " + e.toString());
408                    break;
409                }
410                continue;
411            }
412
413            if (words.length == 1 &amp;&amp;
414                (words[0].compareToIgnoreCase("quit") == 0 ||
415                words[0].compareToIgnoreCase("exit") == 0)) {
416                break;
417            } else if (words.length != 2) {
418                System.err.println("Format: TICKER VALUE");
419                continue;
420            } </pre>
421      <p>
422            That done, we need to add a little error checking to our
423            command prompt to make sure the user is not attempting to
424            modify the database at a replica. Remember, replicas must never
425            modify their local databases on their own. This guards against
426            that happening due to user input at the prompt.
427    </p>
428      <pre class="programlisting">            <strong class="userinput"><code>if (!dbenv.getIsMaster()) {
429                System.err.println("Can't update client.");
430                continue;
431            }</code></strong>
432
433            DatabaseEntry key = new DatabaseEntry(words[0].getBytes());
434            DatabaseEntry data = new DatabaseEntry(words[1].getBytes());
435
436            db.put(null, key, data);
437        }
438        if (db != null)
439            db.close(true);
440        return 0;
441    } </pre>
442      <p>
443        With that completed, we are all done updating our application
444        for replication. 
445
446        The only remaining 
447         
448         
449        <span>method, <code class="function">printStocks()</code>,</span> 
450
451        is unmodified from when we
452        originally introduced it. For details on that function, see
453        <a class="xref" href="simpleprogramlisting.html#printstocks_c" title="Method: SimpleTxn.printStocks()">
454                            
455                            
456                            <span>Method: SimpleTxn.printStocks()</span>
457                    </a>.
458</p>
459      <div class="sect2" lang="en" xml:lang="en">
460        <div class="titlepage">
461          <div>
462            <div>
463              <h3 class="title"><a id="runningit"></a>Running It</h3>
464            </div>
465          </div>
466        </div>
467        <p>
468                To run our replicated application, we need to make
469                sure each participating environment has its own unique
470                home directory. We can do this by running
471                each site on a separate networked machine, but that
472                is not strictly necessary; multiple instances of this
473                code can run on the same machine provided the
474                environment home restriction is observed.
475        </p>
476        <p>
477                To run a process, make sure the environment home
478                exists and then start the process using the
479                <code class="literal">-h</code> option to specify that
480                directory. You must also use the <code class="literal">-l</code>
481                option to identify the local host and port that this
482                process will use to listen for replication messages, the
483                <code class="literal">-n</code> option to specify the number of sites
484                in the replication group, and
485                the <code class="literal">-r</code> option to identify the other
486                processes in the replication group. Finally, use the
487                <code class="literal">-p</code> option to specify a priority.
488                The process that you designate to have the highest priority will become
489                the master.
490        </p>
491        <pre class="programlisting">&gt; mkdir env1
492&gt; java db.repquote.RepQuoteExampleGSG -h env1 -n 2 -l localhost:8080 \
493-r localhost:8081 -p 10
494No stock database yet available.
495No stock database yet available.  </pre>
496        <p>
497        Now, start another process. This time, change the environment
498        home to something else, use the <code class="literal">-l</code> flag to at
499        least change the port number the process is listening on, and
500        use the <code class="literal">-r</code> option to identify the host and
501        port of the other replication process:
502</p>
503        <pre class="programlisting">&gt; mkdir env2
504&gt; java db.repquote.RepQuoteExampleGSG -h env2 -n 2 -l localhost:8081 \
505-r localhost:8080 -p 20</pre>
506        <p>
507        After a short pause, the second process should display the master
508        prompt:
509</p>
510        <pre class="programlisting">
511QUOTESERVER &gt; </pre>
512        <p>
513        And the first process should 
514        display the read-only prompt:
515</p>
516        <pre class="programlisting">
517QUOTESERVER (read-only)&gt; </pre>
518        <p>
519        Now go to the master process and give it a couple of stocks and stock
520        prices:
521</p>
522        <pre class="programlisting">QUOTESERVER&gt; FAKECO 9.87
523QUOTESERVER&gt; NOINC .23
524QUOTESERVER&gt; </pre>
525        <p>
526    Then, go to the replica and hit <strong class="userinput"><code>return</code></strong> at the prompt to
527    see the new values:
528</p>
529        <pre class="programlisting">QUOTESERVER (read-only)&gt; 
530        Symbol  Price
531        ======  =====
532        FAKECO  9.87
533        NOINC    .23 
534QUOTESERVER (read-only)&gt; </pre>
535        <p>
536        Doing the same at the master results in the same thing:
537</p>
538        <pre class="programlisting">QUOTESERVER&gt; 
539        Symbol  Price
540        ======  =====
541        FAKECO  9.87
542        NOINC    .23 
543QUOTESERVER&gt; </pre>
544        <p>
545        You can change a stock by simply entering the stock value and
546        new price at the master's prompt:
547</p>
548        <pre class="programlisting">QUOTESERVER&gt; FAKECO 10.01 
549QUOTESERVER&gt; </pre>
550        <p>
551        Then, go to either the master or the replica to see the updated
552        database. On the master:
553</p>
554        <pre class="programlisting">QUOTESERVER&gt; 
555        Symbol  Price
556        ======  =====
557        FAKECO  10.01
558        NOINC    .23 
559QUOTESERVER&gt; </pre>
560        <p>
561        And on the replica:
562</p>
563        <pre class="programlisting">QUOTESERVER (read-only)&gt; 
564        Symbol  Price
565        ======  =====
566        FAKECO  10.01
567        NOINC    .23 
568QUOTESERVER (read-only)&gt; </pre>
569        <p>
570        Finally, to quit the applications, simply type
571        <code class="literal">quit</code> at both prompts. On the replica:
572</p>
573        <pre class="programlisting">QUOTESERVER (read-only)&gt; quit
574&gt; </pre>
575        <p>
576        And on the master as well:
577</p>
578        <pre class="programlisting">QUOTESERVER&gt; quit
579&gt; </pre>
580      </div>
581    </div>
582    <div class="navfooter">
583      <hr />
584      <table width="100%" summary="Navigation footer">
585        <tr>
586          <td width="40%" align="left"><a accesskey="p" href="processingloop.html">Prev</a>��</td>
587          <td width="20%" align="center">
588            <a accesskey="u" href="fwrkmasterreplica.html">Up</a>
589          </td>
590          <td width="40%" align="right">��<a accesskey="n" href="addfeatures.html">Next</a></td>
591        </tr>
592        <tr>
593          <td width="40%" align="left" valign="top">Processing Loop��</td>
594          <td width="20%" align="center">
595            <a accesskey="h" href="index.html">Home</a>
596          </td>
597          <td width="40%" align="right" valign="top">��Chapter��5.��Additional Features</td>
598        </tr>
599      </table>
600    </div>
601  </body>
602</html>
603