From: <mar...@us...> - 2013-09-10 13:56:03
|
Revision: 7392 http://bigdata.svn.sourceforge.net/bigdata/?rev=7392&view=rev Author: martyncutcher Date: 2013-09-10 13:55:55 +0000 (Tue, 10 Sep 2013) Log Message: ----------- Change DumpLogDigests to provide entries for logs that are not found to simplify comparisons between services with different available log files. And register protection with the HALogNexus to avoid file removal while digests are being computed. Modified Paths: -------------- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/DumpLogDigests.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3DumpLogs.java Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/DumpLogDigests.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/DumpLogDigests.java 2013-09-10 13:50:17 UTC (rev 7391) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/DumpLogDigests.java 2013-09-10 13:55:55 UTC (rev 7392) @@ -52,6 +52,7 @@ import com.bigdata.journal.ITransactionService; import com.bigdata.journal.jini.ha.HAClient.HAConnection; import com.bigdata.journal.jini.ha.HALogIndex.IHALogRecord; +import com.bigdata.journal.jini.ha.SnapshotIndex.ISnapshotRecord; import com.bigdata.quorum.Quorum; import com.bigdata.quorum.QuorumClient; @@ -61,8 +62,6 @@ * Accesses Zookeeper state and then connects to each service member to * retrieve log digest information. * - * The states are pinned by starting a read transaction on any of the services. - * * This task will return an object with the transaction reference to be released * and the range of commit counters for the logs to be checked. * @@ -135,7 +134,7 @@ final LogDigestParams params = pinner.submit(new PinLogs(), false).get(); if (log.isInfoEnabled()) - log.info("Pinning startCC: " + params.startCC + ", endCC: " + params.endCC); + log.info("Pinning startCC: " + params.startCC + ", endCC: " + params.endCC + ", last snapshot: " + params.snapshotCC); /** * Now access serviceIDs so that we can use discovery to gain HAGlue interface. @@ -217,18 +216,26 @@ * LogDigestParams with PinLogs and UnpinLogs tasks ensure that * transactions (and logs?) will not be removed while the digests * are computed. - * TODO: Check if this will really pin the logs? + * <p> + * In fact creating a transaction does not protect the halog files, + * which are now protected from removal during a digest computation + * by a protectDigest call on the HALogNexus in GetLogInfo. + * <p> + * TODO: The read transaction is currently retained but should be removed + * if/when it becomes clear that it has no use. */ @SuppressWarnings("serial") static public class LogDigestParams implements Serializable { final public long tx; final public long startCC; final public long endCC; + final public long snapshotCC; - LogDigestParams(final long tx, final long startCC, final long endCC) { + LogDigestParams(final long tx, final long startCC, final long endCC, long sscc) { this.tx = tx; this.startCC = startCC; this.endCC = endCC; + this.snapshotCC = sscc; } } @@ -251,9 +258,14 @@ startCC = logs.next().getCommitCounter(); } else { startCC = endCC; - } + } - return new LogDigestParams(tx, startCC, endCC); + final SnapshotManager ssmgr = ha.getSnapshotManager(); + final ISnapshotRecord rec = ssmgr.getNewestSnapshot(); + final long sscc = rec != null ? rec.getCommitCounter() : -1; + + // return new LogDigestParams(tx, startCC-3, endCC+3); // try asking for more logs than available + return new LogDigestParams(tx, startCC, endCC, sscc); } } @@ -283,7 +295,7 @@ * The GetLogInfo callable is submitted to each service, retrieving a * List of HALogInfo data elements for each commit counter log * requested. - * + * <p> * It is parameterized for start and end commit counters for the logs and * the number of serviceThreads to split the digest computations across. */ @@ -310,6 +322,8 @@ HAJournal ha = (HAJournal) this.getIndexManager(); final HALogNexus nexus = ha.getHALogNexus(); + nexus.protectDigest(startCC); + try { long openCC = nexus.getCommitCounter(); log.warn("Open Commit Counter: " + openCC + ", startCC: " + startCC + ", endCC: " + endCC); @@ -340,11 +354,15 @@ r.computeDigest(digest); - infos.add(new HALogInfo(file.getName(), r.isLive(), digest.digest())); + infos.add(new HALogInfo(cur, r.isLive(), digest.digest())); } catch (FileNotFoundException fnf) { - // half expected + // permitted + infos.add(new HALogInfo(cur, false, null /*digest*/)); } catch (Throwable t) { log.warn("Unexpected error", t); + + // FIXME: what to do here? + infos.add(new HALogInfo(cur, false, "ERROR".getBytes())); } return null; @@ -362,26 +380,40 @@ es.shutdown(); return new ArrayList<HALogInfo>(infos); + } finally { + nexus.releaseProtectDigest(); + } } } @SuppressWarnings("serial") + /** + * If the log does not exist then a null digest is defined. + */ static public class HALogInfo implements Serializable, Comparable<HALogInfo> { - final public String logName; + final public long commitCounter; final public boolean isOpen; final public byte[] digest; - HALogInfo(final String logName, final boolean isOpen, final byte[] bs) { - this.logName = logName; + HALogInfo(final long commitCounter, final boolean isOpen, final byte[] bs) { + this.commitCounter = commitCounter; this.isOpen = isOpen; this.digest = bs; } @Override public int compareTo(HALogInfo other) { - return logName.compareTo(other.logName); + + // should not be same commit counter! + assert commitCounter != other.commitCounter; + + return commitCounter < other.commitCounter ? -1 : 1; } + + public boolean exists() { + return digest != null; + } } /** @@ -406,8 +438,8 @@ StringBuilder sb = new StringBuilder(); sb.append("Service: " + service + "\n"); for (HALogInfo li : logInfos) { - sb.append(li.logName); - sb.append(" " + BytesUtil.toHexString(li.digest)); + sb.append("CC[" + li.commitCounter + "]"); + sb.append(" " + (li.digest == null ? "NOT FOUND" : BytesUtil.toHexString(li.digest))); sb.append(" " + (li.isOpen ? "open" : "closed")); sb.append("\n"); } @@ -436,15 +468,18 @@ } /** - * Two required arguments: - * <HA configuration file> - * <ServiceRoot> - * + * There are two required arguments: + * <li>HA configuration file</li> + * <li>ServiceRoot</li> + * <p> * A third optional argument * "summary" - * which, if present, only generates output for HALogInfo that is different for teh joined services. - * - * The DumpLogDigests dump method returns an Iterator over the halog files. + * which, if present, only generates output for HALogInfo that is different for the joined services. + * <p> + * The DumpLogDigests dump method returns an Iterator over the halog files returning ServiceLog + * objects. + * <p> + * The command line invocation simply writes a string representation of the returned data to standard out. */ static public void main(final String[] args) throws ConfigurationException, IOException, InterruptedException, ExecutionException { if (args.length < 2 || args.length > 3) { @@ -583,6 +618,8 @@ ret.add(haglue); } + client.disconnect(true/*immediate shutdown*/); + return ret; } Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java 2013-09-10 13:50:17 UTC (rev 7391) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java 2013-09-10 13:55:55 UTC (rev 7392) @@ -32,6 +32,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Iterator; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -114,6 +115,12 @@ * IHAWriteMessage, ByteBuffer) */ volatile IHAWriteMessage lastLiveHAWriteMessage = null; + + /* + * Set to protect log files against deletion while a digest is + * computed. This is checked by deleteHALogs. + */ + private final AtomicLong digestLog = new AtomicLong(-1L); /** * Filter visits all HALog files <strong>except</strong> the current HALog @@ -809,6 +816,21 @@ } /** + * Protects logs from removal while a digest is being computed + * @param earliestDigest + */ + void protectDigest(final long earliestDigest) { + digestLog.set(earliestDigest); + } + + /** + * Releases current protection against log removal + */ + void releaseProtectDigest() { + digestLog.set(-1L); + } + + /** * Delete HALogs that are no longer required. * * @param earliestRetainedSnapshotCommitCounter @@ -832,7 +854,13 @@ final long closingCommitCounter = r.getCommitCounter(); - final boolean deleteFile = closingCommitCounter < earliestRetainedSnapshotCommitCounter; + final boolean deleteFile; + if (closingCommitCounter < earliestRetainedSnapshotCommitCounter) { + // now check if protected by the digestLog field (set to -1 if not active) + deleteFile = digestLog.get() == -1 || closingCommitCounter < digestLog.get(); + } else { + deleteFile = false; + } if (!deleteFile) { Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3DumpLogs.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3DumpLogs.java 2013-09-10 13:50:17 UTC (rev 7391) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3DumpLogs.java 2013-09-10 13:55:55 UTC (rev 7392) @@ -25,6 +25,7 @@ import java.io.PrintWriter; import java.io.StringWriter; +import java.util.Calendar; import java.util.Iterator; import java.util.UUID; @@ -32,7 +33,58 @@ public class TestHA3DumpLogs extends AbstractHA3JournalServerTestCase { - public TestHA3DumpLogs() { + @Override + protected String[] getOverrides() { + + /* + * We need to set the time at which the DefaultSnapshotPolicy runs to + * some point in the Future in order to avoid test failures due to + * violated assumptions when the policy runs up self-triggering (based + * on the specified run time) during a CI run. + */ + final String neverRun = getNeverRunSnapshotTime(); + + return new String[]{ + "com.bigdata.journal.jini.ha.HAJournalServer.restorePolicy=new com.bigdata.journal.jini.ha.DefaultRestorePolicy(0L,1,0)", + "com.bigdata.journal.jini.ha.HAJournalServer.snapshotPolicy=new com.bigdata.journal.jini.ha.DefaultSnapshotPolicy("+neverRun+",0)", + "com.bigdata.journal.jini.ha.HAJournalServer.onlineDisasterRecovery=true" + }; + + } + + /** + * We need to set the time at which the {@link DefaultSnapshotPolicy} runs + * to some point in the future in order to avoid test failures due to + * violated assumptions when the policy runs up self-triggering (based on + * the specified run time) during a CI run. + * <p> + * We do this by adding one hour to [now] and then converting it into the + * 'hhmm' format as an integer. + * + * @return The "never run" time as hhmm. + */ + static protected String getNeverRunSnapshotTime() { + + // Right now. + final Calendar c = Calendar.getInstance(); + + // Plus an hour. + c.add(Calendar.HOUR_OF_DAY, 1); + + // Get the hour. + final int hh = c.get(Calendar.HOUR_OF_DAY); + + // And the minutes. + final int mm = c.get(Calendar.MINUTE); + + // Format as hhmm. + final String neverRun = "" + hh + (mm < 10 ? "0" : "") + mm; + + return neverRun; + + } + + public TestHA3DumpLogs() { } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2013-11-19 14:10:13
|
Revision: 7563 http://bigdata.svn.sourceforge.net/bigdata/?rev=7563&view=rev Author: thompsonbry Date: 2013-11-19 14:10:04 +0000 (Tue, 19 Nov 2013) Log Message: ----------- I have added unit tests for a physically empty HALog and a corrupt HALog parallel to the existing unit test for a logically empty HALog. All three conditions are correctly handled when the HALog with the problem is the successor of the last commit point on the journal. This closes out the bug identified above. The other aspects of this ticket remain open. See #775 (HAJournal start()) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestAll.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3JournalServerWithHALogs.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3WORMJournalServer.java Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java 2013-11-18 22:45:25 UTC (rev 7562) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java 2013-11-19 14:10:04 UTC (rev 7563) @@ -70,7 +70,7 @@ */ public class HALogNexus implements IHALogWriter { - private static final Logger log = Logger.getLogger(SnapshotManager.class); + private static final Logger log = Logger.getLogger(HALogNexus.class); /** * Logger for HA events. @@ -249,37 +249,125 @@ * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/679" > * HAJournalServer can not restart due to logically empty log files * </a> + * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/775" > + * HAJournal start() </a> */ { /* - * Used to detect a logically empty HALog (iff it is the last one in - * commit order). + * Data structure used to detect a bad HALog and identify whether or + * not it is the last one in commit order. */ final HALogScanState tmp = new HALogScanState(); // Scan the HALog directory, populating the in-memory index. populateIndexRecursive(haLogDir, IHALogReader.HALOG_FILTER, tmp); - if (tmp.emptyHALogFile != null) { + final long commitCounterOnJournal = journal.getRootBlockView().getCommitCounter(); - /* - * The last HALog file is logically empty. It WAS NOT added to - * the in-memory index. We try to remove it now. + if (tmp.firstBadHALogFile != null) { + + /** + * The only the last HALog file is bad (physically empty, + * logically empty, bad MAGIC, etc), then it WAS NOT added to + * the in-memory index. * + * We try to remove it now and then start up normally. While we + * are short one HALog file, we can obtain it during + * resynchronization from the other nodes in the cluster. + * * Note: It is not critical that we succeed in removing this * HALog file so long as it does not interfere with the correct * startup of the HAJournalServer. */ - final File f = tmp.emptyHALogFile; + final File f = tmp.firstBadHALogFile; - if (!f.delete()) { + /* + * Parse out the closing commit counter for that HALog. This is + * the commit counter that would be assigned to the root block + * if this transaction had been applied to the Journal. + */ + final long closingCommitCounter = CommitCounterUtility + .parseCommitCounterFile(f.getName(), + IHALogReader.HA_LOG_EXT); - log.warn("Could not remove empty HALog: " + f); + if (commitCounterOnJournal + 1 == closingCommitCounter) { + + /* + * This HALog file was for the next commit point to be + * recorded on the Journal. We can safely delete it and + * continue the normal startup. + */ + if (haLog.isInfoEnabled()) + haLog.info("Removing bad/empty HALog file: commitCounterOnJournal=" + + commitCounterOnJournal); + + if (!f.delete()) { + + log.warn("Could not remove empty HALog: " + f); + + } + + } else { + + /* + * This HALog file is bad. The service can not start until + * it has been replaced. + * + * FIXME Automate the replacement of the bad/missing HALog + * file from the quorum leader. + */ + throw new HALogException(tmp.firstBadHALogFile, + tmp.firstCause); + } } + + // Get the most recent HALog record from the index. + final IHALogRecord r = haLogIndex.getNewestEntry(); + + if (r != null) { + + /** + * Note: The logic above makes sure that we have each HALog in + * sequence from some unspecified starting point, but it does + * not verify that the last HALog file corresponds to the last + * durable commit point on the Journal, does not verify the + * number of local HALog files against some target (e.g., as + * specified by the restore policy), and does not verify that + * there are no HALog files for commit points beyond the last + * commit point on the journal (which could happen if someone + * did a point in time restore of the journal from a snapshot + * and failed to remove the HALog files after that point in + * time). + * + * TODO This should be refactored when we address #775. + */ + + if (r.getCommitCounter() < commitCounterOnJournal) { + /* + * Reject start if we are missing the HALog for the most + * recent commit point on the journal. + */ + throw new RuntimeException( + "Missing HALog(s) for committed state on journal: journal@=" + + commitCounterOnJournal + ", lastHALog@" + + r.getCommitCounter()); + } + + /* + * Note: If there are HALog files for commit points beyond the + * most recent commit point on the journal, then those HALog + * files will be applied to roll forward the journal. This is + * done by HAJournalServer in its RESTORE state. Thus is + * necessary to remove any HALog files beyond the desired commit + * point before restarting the service when rolling back to a + * specific point in time. + */ + + } } @@ -309,13 +397,19 @@ */ private static class HALogScanState { /** - * Flag is set the first time an empty HALog file is identified. + * Flag is set the first time bad HALog file is identified. * <p> - * Note: We scan the HALog files in commit counter order. If the last - * file is (logically) empty, then we will silently remove it. However, - * if any other HALog file is logically empty, then this is an error. + * Note: We scan the HALog files in commit counter order. If only the + * last file in the scan is bad, then we will silently remove it - the + * HALog will be replaced when this service attempts to . + * However, if there is more than one bad HALog file, then this is an + * error. */ - File emptyHALogFile = null; + File firstBadHALogFile = null; + /** + * The exception when we first encountered a bad HALog file. + */ + Throwable firstCause = null; } /** @@ -328,11 +422,11 @@ * side-effect using the {@link HALogScanState} and will NOT be added to the * index. The caller SHOULD then remove the logically empty HALog file * - * TODO If an HALog is discovered to have bad checksums or otherwise corrupt - * root blocks and there is a met quorum, then we should re-replicate that - * HALog from the quourm leader. + * FIXME If an HALog is discovered to have bad checksums or otherwise + * corrupt root blocks and there is a met quorum, then we should + * re-replicate that HALog from the quourm leader. * - * TODO For HALog files other than the last HALog file (in commit counter + * FIXME For HALog files other than the last HALog file (in commit counter * sequence) if there are any missing HALog files in the sequence, if any if * the files in the sequence other than the last HALog file is logically * empty, or if any of those HALog files has a bad root bloxks) then we @@ -342,6 +436,27 @@ * we allow the service to start, then it will have limited rollback * capability. All of this could be checked in an index scan once we have * identified all of the HALog files in the file system. + * + * TODO This could be rewritten to generate the filenames by running the + * commit counter from the first discovered HALog file's commit counter up + * through the current commit point on the journal. Alternatively, we could + * just start with the current commit point on the journal and the substract + * one and move backward until we find the first HALog file that is not + * locally available. We could then cross check this with the + * {@link IRestorePolicy} and decide whether we needed to back fill either + * HALog files or snapshots on this service in order to satisify the + * {@link IRestorePolicy}. This has the advantage that we start with the + * most recent HALog file first, so we can immediately diagnose any problems + * with the last commit point on restart. It removes the recursive logic and + * makes it easier to write code that decides whether or not a given HALog + * file being bad poses a problem and what kind of a problem and how to + * resolve that problem. There will be more GC associated with the + * generation of the file names from the commit counters, but we could get + * rid of that GC overhead entirely by supplying a reusable + * {@link StringBuilder}. + * + * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/775" > + * HAJournal start() </a> */ private void populateIndexRecursive(final File f, final FileFilter fileFilter, final HALogScanState state) @@ -370,7 +485,7 @@ } else { - if (state.emptyHALogFile != null) { + if (state.firstBadHALogFile != null) { /* * We already have an empty HALog file. If there are any more @@ -380,8 +495,9 @@ * order). */ - throw new LogicallyEmptyHALogException(state.emptyHALogFile); - + throw new HALogException(state.firstBadHALogFile, + state.firstCause); + } try { @@ -389,16 +505,22 @@ // Attempt to add to the index. addHALog(f); - } catch (LogicallyEmptyHALogException ex) { + } catch (Throwable t) { + if (InnerCause.isInnerCause(t, InterruptedException.class)) { + // propagate interrupt. + throw new RuntimeException(t); + } + // Should be null since we checked this above. - assert state.emptyHALogFile == null; + assert state.firstBadHALogFile == null; /* * The first empty HALog file. There is at most one allowed and * it must be the last HALog file in commit counter order. */ - state.emptyHALogFile = f; + state.firstBadHALogFile = f; + state.firstCause = t; } @@ -430,6 +552,13 @@ final byte[] b0 = new byte[RootBlockView.SIZEOF_ROOT_BLOCK]; final byte[] b1 = new byte[RootBlockView.SIZEOF_ROOT_BLOCK]; + + if (file.length() == 0L) { + /* + * The file is physically empty (zero length). + */ + throw new EmptyHALogException(file); + } final DataInputStream is = new DataInputStream( new FileInputStream(file)); @@ -455,8 +584,8 @@ } catch(IOException ex) { // Wrap exception with the file name. - throw new IOException(ex.getMessage() + ", file=" + file, ex); - + throw new HALogException(file, ex); + } finally { is.close(); @@ -489,18 +618,16 @@ * @throws ChecksumError * if there is a checksum problem with the root blocks. * - * TODO If the root blocks are the same then this is an empty - * HALog. Right now that is an error. [We might want to simply - * remove any such HALog file.] - * <p> - * Likewise, it is an error if any HALog has bad root blocks - * (checksum or other errors). - * * TODO A similar problem exists if any of the HALog files GTE * the earliest snapshot are missing, have bad root blocks, etc. * We will not be able to restore the commit point associated * with that HALog file unless it also happens to correspond to - * a snapshot. + * a snapshot. Such bad/missing HALog files should be + * re-replicated from the quorum leader. This process should be + * automated. + * + * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/775" > + * HAJournal start() </a> */ private void addHALog(final File file) throws IOException, LogicallyEmptyHALogException { @@ -554,28 +681,66 @@ } /** + * Base class for exceptions when we are unable to read an HALog file. + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + */ + private static class HALogException extends IOException { + + private static final long serialVersionUID = 1L; + + public HALogException(final File file) { + + super(file.getAbsolutePath()); + + } + + public HALogException(final File file,final Throwable cause) { + + super(file.getAbsolutePath(), cause); + + } + + } + + /** * Exception raise when an HALog file is logically empty (the opening and * closing root blocks are identicial). * * @author <a href="mailto:tho...@us...">Bryan * Thompson</a> */ - private static class LogicallyEmptyHALogException extends IOException { + private static class LogicallyEmptyHALogException extends HALogException { - /** - * - */ private static final long serialVersionUID = 1L; public LogicallyEmptyHALogException(final File file) { - super(file.getAbsolutePath()); + super(file); } } /** + * Exception raise when an HALog file is physically empty (zero length). + * + * @author <a href="mailto:tho...@us...">Bryan + * Thompson</a> + */ + private static class EmptyHALogException extends HALogException { + + private static final long serialVersionUID = 1L; + + public EmptyHALogException(final File file) { + + super(file); + + } + + } + + /** * Remove an snapshot from the file system and the {@link #haLogIndex}. * * @param file Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestAll.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestAll.java 2013-11-18 22:45:25 UTC (rev 7562) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestAll.java 2013-11-19 14:10:04 UTC (rev 7563) @@ -32,8 +32,6 @@ import junit.framework.TestSuite; import com.bigdata.journal.Journal; -import com.bigdata.journal.WORMStrategy; -import com.bigdata.rwstore.RWStore; /** * Test suite for highly available configurations of the standalone @@ -60,21 +58,6 @@ /** * Returns a test that will run each of the implementation specific test * suites in turn. - * - * FIXME (*) Test {@link WORMStrategy} and {@link RWStore} (through an override?) - * - * FIXME The NSS should transparently proxy mutation requests to the quorum - * leader (and to a global leader if offsite is supported, or maybe that is - * handled at a layer above). The tests need to be modified (A) to NOT only - * write on the leader; and (B) to verify that we can send a write request - * to ANY service that is joined with the met quorum. (And verify for POST, - * DELETE, and PUT since those are all different method.) - * <p> - * Note: We could have services that are not joined with the met quorum - * simply forward read requests to services that ARE joined with the met - * quorum. That way they can begin "accepting" reads and writes immediately. - * This could also be done one level down, using failover reads to reach a - * service joined with the met quorum. */ public static Test suite() { Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3JournalServerWithHALogs.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3JournalServerWithHALogs.java 2013-11-18 22:45:25 UTC (rev 7562) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3JournalServerWithHALogs.java 2013-11-19 14:10:04 UTC (rev 7563) @@ -27,6 +27,9 @@ package com.bigdata.journal.jini.ha; import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; import net.jini.config.Configuration; @@ -37,7 +40,9 @@ import com.bigdata.ha.halog.IHALogReader; import com.bigdata.ha.msg.IHA2PhasePrepareMessage; import com.bigdata.journal.CommitCounterUtility; +import com.bigdata.journal.FileMetadata; import com.bigdata.journal.jini.ha.HAJournalTest.HAGlueTest; +import com.bigdata.util.InnerCause; /** * Test suite when we are using the {@link DefaultSnapshotPolicy} and @@ -104,7 +109,7 @@ * HAJournalServer can not restart due to logically empty log files * </a> */ - public void test_startABC_emptyLogFileDeletedOnRestartC() throws Exception { + public void test_startABC_logicallyEmptyLogFileDeletedOnRestartC() throws Exception { final ABC abc = new ABC(true/* sequential */); @@ -275,6 +280,620 @@ } /** + * This is a unit test for the ability to silently remove a physically empty + * HALog file. Three services are started in sequence (A,B,C). A series of + * small commits are applied to the quorum. (C) is then shutdown. A + * logically empty HALog file should exist on each service for the next + * commit point. We now overwrite that file with a physically empty HALog + * file (zero length). We then do one more update. C is then restarted. We + * verify that C restarts and that the logically empty HALog file has been + * replaced by an HALog file that has the same digest as the HALog file for + * that commit point on (A,B). + * <p> + * Note: We can not reliably observe that the physically HALog file was + * removed during startup. However, this is not critical. What is critical + * is that the physically empty HALog file (a) does not prevent (C) from + * starting; (b) is replaced by the correct HALog data from the quorum + * leader; and (c) that (C) resynchronizes with the met quorum and joins + * causing a fully met quorum. + * + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/679" > + * HAJournalServer can not restart due to logically empty log files + * </a> + * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/775" > + * HAJournal start() </a> + */ + public void test_startABC_physicallyEmptyLogFileDeletedOnRestartC() throws Exception { + + final ABC abc = new ABC(true/* sequential */); + + final HAGlue serverA = abc.serverA, serverB = abc.serverB; + HAGlue serverC = abc.serverC; + + // Verify quorum is FULLY met. + awaitFullyMetQuorum(); + + // await the KB create commit point to become visible on each service. + awaitCommitCounter(1L, new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL journals. + assertDigestsEquals(new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL HALog files. + assertHALogDigestsEquals(1L/* firstCommitCounter */, + 1/* lastCommitCounter */, new HAGlue[] { serverA, serverB, + serverC }); + + /* + * Do a series of small commits. + */ + + final int NSMALL = 5; + + for (int i = 1/* createKB */; i <= NSMALL; i++) { + + simpleTransaction(); + + } + + final long commitCounter1 = 1 + NSMALL; // AKA (6) + + // await the commit points to become visible. + awaitCommitCounter(commitCounter1, + new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL journals. + assertDigestsEquals(new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL HALog files. + assertHALogDigestsEquals(1L/* firstCommitCounter */, commitCounter1, + new HAGlue[] { serverA, serverB, serverC }); + + /* + * Verify the expected #of HALogs on each service. + * + * Note: This is (lastCommitCounter+1) since an empty HALog was created + * for the next commit point. + */ + awaitLogCount(getHALogDirA(), commitCounter1 + 1); + awaitLogCount(getHALogDirB(), commitCounter1 + 1); + awaitLogCount(getHALogDirC(), commitCounter1 + 1); + + /* + * Shutdown C. + * + * Note: This might cause the empty HALog file on (C) to be deleted. + * That is Ok, since we will copy the desired empty HALOg from (A) to + * (C), thus enforcing the desired test condition. + */ + shutdownC(); + + /* + * Verify that there is an empty HALog file on (A) for the next + * commit point. + */ + + // The next commit point. + final long commitCounter2 = commitCounter1 + 1; // AKA (7) + + // The HALog for that next commit point. + final File fileA = CommitCounterUtility.getCommitCounterFile( + getHALogDirA(), commitCounter2, IHALogReader.HA_LOG_EXT); + + // Verify HALog file for next commit point on A is logically empty. + { + assertTrue(fileA.exists()); + final IHALogReader r = new HALogReader(fileA); + assertTrue(r.isEmpty()); + assertFalse(r.isLive()); + r.close(); + assertTrue(fileA.exists()); + } + + // The name of that HALog file on (C). + final File fileC = CommitCounterUtility.getCommitCounterFile( + getHALogDirC(), commitCounter2, IHALogReader.HA_LOG_EXT); + +// // Copy that empty HALog file to (C). +// copyFile(fileA, fileC, false/* append */); + + // delete the logically empty file (if it exists). + if (fileC.exists() && !fileC.delete()) + fail("Could not delete: fileC=" + fileC); + + // create the physically empty file. + if (!fileC.createNewFile()) + fail("Could not create: fileC=" + fileC); + + /* + * Do another transaction. This will cause the HALog file for that + * commit point to be non-empty on A. + */ + simpleTransaction(); + + /* + * Await the commit points to become visible. + * + * Note: This is (lastCommitCounter+1) since an empty HALog was created + * for the next commit point. + */ + awaitCommitCounter(commitCounter2, new HAGlue[] { serverA, serverB }); + + // Verify the expected #of HALogs on each service. + awaitLogCount(getHALogDirA(), commitCounter2 + 1); + awaitLogCount(getHALogDirB(), commitCounter2 + 1); + awaitLogCount(getHALogDirC(), commitCounter2); + + // Verify HALog file for next commit point on A is NOT empty. + { + assertTrue(fileA.exists()); + final IHALogReader r = new HALogReader(fileA); + assertFalse(r.isEmpty()); + assertFalse(r.isLive()); + r.close(); + assertTrue(fileA.exists()); + } + + // Verify HALog file for next commit point on C is phsyically empty. + { + assertTrue(fileC.exists()); + assertEquals(0L, fileC.length()); + } + + /* + * Restart (C). It should start without complaint. The logically empty + * HALog file should be replaced by the corresponding file from (A) by + * the time the quorum fully meets. At this point all services will have + * the same digests for all HALog files. + */ + + // Restart C. + serverC = startC(); + + // Wait until the quorum is fully met. + awaitFullyMetQuorum(); + + // await the commit points to become visible. + awaitCommitCounter(commitCounter2, + new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL journals. + assertDigestsEquals(new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL HALog files. + assertHALogDigestsEquals(1L/* firstCommitCounter */, + commitCounter2 /* lastCommitCounter */, new HAGlue[] { serverA, + serverB, serverC }); + + /* + * Verify the expected #of HALogs on each service. + * + * Note: Each service will have an empty HALog for the next commit + * point. + */ + awaitLogCount(getHALogDirA(), commitCounter2+1); + awaitLogCount(getHALogDirB(), commitCounter2+1); + awaitLogCount(getHALogDirC(), commitCounter2+1); + + } + + /** + * This is a variant test for the ability to silently remove a corrupt HALog + * file on restart when it is the HALog file for the first write set not yet + * committed on the journal. Three services are started in sequence (A,B,C). + * A series of small commits are applied to the quorum. (C) is then + * shutdown. A logically empty HALog file should exist on each service for + * the next commit point. However, since this might have been removed on C + * when it was shutdown, we copy the logically empty HALog file from (A) to + * (C). We then overwrite the root blocks of that logically empty HALog file + * with junk. We then do one more update. C is then restarted. We verify + * that C restarts and that the corrupt HALog file has been replaced + * by an HALog file that has the same digest as the HALog file for that + * commit point on (A,B). + * <p> + * Note: We can not reliably observe that the logically HALog file was + * removed during startup. However, this is not critical. What is critical + * is that the logically empty HALog file (a) does not prevent (C) from + * starting; (b) is replaced by the correct HALog data from the quorum + * leader; and (c) that (C) resynchronizes with the met quorum and joins + * causing a fully met quorum. + * + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/679" > + * HAJournalServer can not restart due to logically empty log files + * </a> + */ + public void test_startABC_corruptLogFileDeletedOnRestartC() throws Exception { + + final ABC abc = new ABC(true/* sequential */); + + final HAGlue serverA = abc.serverA, serverB = abc.serverB; + HAGlue serverC = abc.serverC; + + // Verify quorum is FULLY met. + awaitFullyMetQuorum(); + + // await the KB create commit point to become visible on each service. + awaitCommitCounter(1L, new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL journals. + assertDigestsEquals(new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL HALog files. + assertHALogDigestsEquals(1L/* firstCommitCounter */, + 1/* lastCommitCounter */, new HAGlue[] { serverA, serverB, + serverC }); + + /* + * Do a series of small commits. + */ + + final int NSMALL = 5; + + for (int i = 1/* createKB */; i <= NSMALL; i++) { + + simpleTransaction(); + + } + + final long commitCounter1 = 1 + NSMALL; // AKA (6) + + // await the commit points to become visible. + awaitCommitCounter(commitCounter1, + new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL journals. + assertDigestsEquals(new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL HALog files. + assertHALogDigestsEquals(1L/* firstCommitCounter */, commitCounter1, + new HAGlue[] { serverA, serverB, serverC }); + + /* + * Verify the expected #of HALogs on each service. + * + * Note: This is (lastCommitCounter+1) since an empty HALog was created + * for the next commit point. + */ + awaitLogCount(getHALogDirA(), commitCounter1 + 1); + awaitLogCount(getHALogDirB(), commitCounter1 + 1); + awaitLogCount(getHALogDirC(), commitCounter1 + 1); + + /* + * Shutdown C. + * + * Note: This might cause the empty HALog file on (C) to be deleted. + * That is Ok, since we will copy the desired empty HALOg from (A) to + * (C), thus enforcing the desired test condition. + */ + shutdownC(); + + /* + * Verify that there is an empty HALog file on (A) for the next + * commit point. + */ + + // The next commit point. + final long commitCounter2 = commitCounter1 + 1; // AKA (7) + + // The HALog for that next commit point. + final File fileA = CommitCounterUtility.getCommitCounterFile( + getHALogDirA(), commitCounter2, IHALogReader.HA_LOG_EXT); + + // Verify HALog file for next commit point on A is logically empty. + { + assertTrue(fileA.exists()); + final IHALogReader r = new HALogReader(fileA); + assertTrue(r.isEmpty()); + assertFalse(r.isLive()); + r.close(); + assertTrue(fileA.exists()); + } + + // The name of that HALog file on (C). + final File fileC = CommitCounterUtility.getCommitCounterFile( + getHALogDirC(), commitCounter2, IHALogReader.HA_LOG_EXT); + + // Copy that empty HALog file to (C). + copyFile(fileA, fileC, false/* append */); + /* + * Overwrite the root blocks of the HALog on (C). + */ + { + final OutputStream os = new FileOutputStream(fileC); + try { + final ByteBuffer buf = getRandomData(FileMetadata.headerSize0); + final byte[] b = getBytes(buf); + os.write(b); + os.flush(); + } finally { + os.close(); + } + } + + /* + * Do another transaction. This will cause the HALog file for that + * commit point to be non-empty on A. + */ + simpleTransaction(); + + /* + * Await the commit points to become visible. + * + * Note: This is (lastCommitCounter+1) since an empty HALog was created + * for the next commit point. + */ + awaitCommitCounter(commitCounter2, new HAGlue[] { serverA, serverB }); + + // Verify the expected #of HALogs on each service. + awaitLogCount(getHALogDirA(), commitCounter2 + 1); + awaitLogCount(getHALogDirB(), commitCounter2 + 1); + awaitLogCount(getHALogDirC(), commitCounter2); + + // Verify HALog file for next commit point on A is NOT empty. + { + assertTrue(fileA.exists()); + final IHALogReader r = new HALogReader(fileA); + assertFalse(r.isEmpty()); + assertFalse(r.isLive()); + r.close(); + assertTrue(fileA.exists()); + } + + // Verify HALog file for next commit point on C is corrupt. + { + boolean ok = false; + try { + new HALogReader(fileC); + ok = true; + } catch(Throwable t) { + // Note: Could be IOException, ChecksumError, or + // RuntimeException. + } + if (ok) + fail("HALog is not corrupt: " + fileC); + } + + /* + * Restart (C). It should start without complaint. The logically empty + * HALog file should be replaced by the corresponding file from (A) by + * the time the quorum fully meets. At this point all services will have + * the same digests for all HALog files. + */ + + // Restart C. + serverC = startC(); + + // Wait until the quorum is fully met. + awaitFullyMetQuorum(); + + // await the commit points to become visible. + awaitCommitCounter(commitCounter2, + new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL journals. + assertDigestsEquals(new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL HALog files. + assertHALogDigestsEquals(1L/* firstCommitCounter */, + commitCounter2 /* lastCommitCounter */, new HAGlue[] { serverA, + serverB, serverC }); + + /* + * Verify the expected #of HALogs on each service. + * + * Note: Each service will have an empty HALog for the next commit + * point. + */ + awaitLogCount(getHALogDirA(), commitCounter2+1); + awaitLogCount(getHALogDirB(), commitCounter2+1); + awaitLogCount(getHALogDirC(), commitCounter2+1); + + } + + /** + * This is a unit test for the ability to correctly NOT remove a logically + * empty HALog file when that HALog file is for the last commit point on the + * Journal. Three services are started in sequence (A,B,C). A series of + * small commits are applied to the quorum. (C) is then shutdown. A + * logically empty HALog file should exist on each service for the next + * commit point. We remove the HALog for the next commit point from (C) if + * it exists. We then remove the HALog for the last durable commit point on + * (C) and replace it with a physically empty HALog file. We then do one + * more update. C is then restarted. We verify that C DOES NOT restart and + * that the physically empty HALog file for the last durable commit point on + * C has not been removed or updated. + * + * TODO This is the staring place for adding the capability to automatically + * replicate bad or missing historical HALog files from the quorum leader. + * The tests exists now to ensure that the logic to remove a bad HALog on + * startup will refuse to remove an HALog corresponding to the most recent + * commit point on the Journal. + * + * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/679" > + * HAJournalServer can not restart due to logically empty log files + * </a> + * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/775" > + * HAJournal start() </a> + */ + public void test_startABC_missingHALogFileForLastCommitBlocksRestartC() throws Exception { + + final ABC abc = new ABC(true/* sequential */); + + final HAGlue serverA = abc.serverA, serverB = abc.serverB; + HAGlue serverC = abc.serverC; + + // Verify quorum is FULLY met. + awaitFullyMetQuorum(); + + // await the KB create commit point to become visible on each service. + awaitCommitCounter(1L, new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL journals. + assertDigestsEquals(new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL HALog files. + assertHALogDigestsEquals(1L/* firstCommitCounter */, + 1/* lastCommitCounter */, new HAGlue[] { serverA, serverB, + serverC }); + + /* + * Do a series of small commits. + */ + + final int NSMALL = 5; + + for (int i = 1/* createKB */; i <= NSMALL; i++) { + + simpleTransaction(); + + } + + final long commitCounter1 = 1 + NSMALL; // AKA (6) + + // await the commit points to become visible. + awaitCommitCounter(commitCounter1, + new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL journals. + assertDigestsEquals(new HAGlue[] { serverA, serverB, serverC }); + + // Verify binary equality of ALL HALog files. + assertHALogDigestsEquals(1L/* firstCommitCounter */, commitCounter1, + new HAGlue[] { serverA, serverB, serverC }); + + /* + * Verify the expected #of HALogs on each service. + * + * Note: This is (lastCommitCounter+1) since an empty HALog was created + * for the next commit point. + */ + awaitLogCount(getHALogDirA(), commitCounter1 + 1); + awaitLogCount(getHALogDirB(), commitCounter1 + 1); + awaitLogCount(getHALogDirC(), commitCounter1 + 1); + + /* + * Shutdown C. + * + * Note: This might cause the empty HALog file on (C) to be deleted. + * That is Ok, since we will copy the desired empty HALOg from (A) to + * (C), thus enforcing the desired test condition. + */ + shutdownC(); + + /* + * Verify that there is an empty HALog file on (A) for the next + * commit point. + */ + + // The next commit point. + final long commitCounter2 = commitCounter1 + 1; // AKA (7) + + // The HALog for that next commit point. + final File fileA = CommitCounterUtility.getCommitCounterFile( + getHALogDirA(), commitCounter2, IHALogReader.HA_LOG_EXT); + + // Verify HALog file for next commit point on A is logically empty. + { + assertTrue(fileA.exists()); + final IHALogReader r = new HALogReader(fileA); + assertTrue(r.isEmpty()); + assertFalse(r.isLive()); + r.close(); + assertTrue(fileA.exists()); + } + + // The name of that HALog file on (C). + final File fileC = CommitCounterUtility.getCommitCounterFile( + getHALogDirC(), commitCounter2, IHALogReader.HA_LOG_EXT); + +// // Copy that empty HALog file to (C). +// copyFile(fileA, fileC, false/* append */); + if (fileC.exists()) + if (!fileC.delete()) + fail("Could not remove HALog for open write set: " + fileC); + + // The HALog file on (C) for the last durable commit point on (C). + final File fileCLastCommit = CommitCounterUtility.getCommitCounterFile( + getHALogDirC(), commitCounter1, IHALogReader.HA_LOG_EXT); + + if (!fileCLastCommit.exists()) + fail("HALog for last commit not found: " + fileCLastCommit); + + if (!fileCLastCommit.delete()) + fail("Could not remove HALog for last commit: " + fileCLastCommit); + + /* + * Do another transaction. This will cause the HALog file for that + * commit point to be non-empty on A. + */ + simpleTransaction(); + + /* + * Await the commit points to become visible. + * + * Note: This is (lastCommitCounter+1) since an empty HALog was created + * for the next commit point. + */ + awaitCommitCounter(commitCounter2, new HAGlue[] { serverA, serverB }); + + // Verify the expected #of HALogs on each service. + awaitLogCount(getHALogDirA(), commitCounter2 + 1); + awaitLogCount(getHALogDirB(), commitCounter2 + 1); + awaitLogCount(getHALogDirC(), commitCounter1 - 1); + + // Verify HALog file for next commit point on A is NOT empty. + { + assertTrue(fileA.exists()); + final IHALogReader r = new HALogReader(fileA); + assertFalse(r.isEmpty()); + assertFalse(r.isLive()); + r.close(); + assertTrue(fileA.exists()); + } + + // Verify HALog files for last and next commit point on C are missing. + { + assertFalse(fileC.exists()); + assertFalse(fileCLastCommit.exists()); + } + + /* + * Restart (C). + * + * Note: This restart should fail. The number of HALog files on (C) + * should be unchanged. + */ + + // Restart C. + { + boolean ok = false; + try { + serverC = startC(); + ok = true; + } catch (Throwable t) { + if (InnerCause.isInnerCause(t, InterruptedException.class)) + // test interrupted? propagate interrupt. + throw new RuntimeException(t); + // log message. refused start is expected. + log.warn("C refused to start: " + t, t); + } + if (ok) + fail("C should not have restarted."); + } + + /* + * Verify the expected #of HALogs on each service. + * + * Note: Each service will have an empty HALog for the next commit + * point. + */ + awaitLogCount(getHALogDirA(), commitCounter2 + 1); + awaitLogCount(getHALogDirB(), commitCounter2 + 1); + awaitLogCount(getHALogDirC(), commitCounter1 - 1); + + } + + /** * Unit test for a situation in which A B and C start. A quorum mets and the * third service resyncs with the met quorum. The quorum then fully meets. * Once the fully met quorum is stable, C is then restarted. This test Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3WORMJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3WORMJournalServer.java 2013-11-18 22:45:25 UTC (rev 7562) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3WORMJournalServer.java 2013-11-19 14:10:04 UTC (rev 7563) @@ -1,3 +1,26 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2010. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ package com.bigdata.journal.jini.ha; import com.bigdata.journal.BufferMode; @@ -2,13 +25,19 @@ +/** + * FIXME HAWORM: This test suite is not implemented. It needs to override the + * {@link BufferMode}. + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + */ public class TestHA3WORMJournalServer extends TestHA3JournalServer { - - - public TestHA3WORMJournalServer() {} - - public TestHA3WORMJournalServer(String nme) { - super(nme); - } - + + public TestHA3WORMJournalServer() { + } + + public TestHA3WORMJournalServer(String nme) { + super(nme); + } + protected BufferMode getDiskMode() { - return BufferMode.DiskWORM; + return BufferMode.DiskWORM; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2013-11-22 15:43:48
|
Revision: 7581 http://bigdata.svn.sourceforge.net/bigdata/?rev=7581&view=rev Author: thompsonbry Date: 2013-11-22 15:43:38 +0000 (Fri, 22 Nov 2013) Log Message: ----------- Added ability to parse a comma delimited list of URLs as a LookupLocator[]. Added ability to parse a comma delimited list of groups. Integrated into the test suite. Modified Paths: -------------- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/jini/util/ConfigMath.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/TestAll.java Added Paths: ----------- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/util/ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/util/TestAll.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/util/TestConfigMath.java Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/jini/util/ConfigMath.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/jini/util/ConfigMath.java 2013-11-22 02:09:38 UTC (rev 7580) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/jini/util/ConfigMath.java 2013-11-22 15:43:38 UTC (rev 7581) @@ -28,9 +28,14 @@ package com.bigdata.jini.util; import java.io.File; +import java.net.MalformedURLException; import java.util.concurrent.TimeUnit; +import javax.net.SocketFactory; + import net.jini.config.Configuration; +import net.jini.core.discovery.LookupLocator; +import net.jini.discovery.LookupDiscovery; import com.sun.jini.config.ConfigUtil; @@ -331,5 +336,95 @@ return o != null; } - + + /** + * Parse a comma delimited list of zero or more unicast URIs of the form + * <code>jini://host/</code> or <code>jini://host:port/</code>. + * <p> + * This MAY be an empty array if you want to use multicast discovery + * <strong>and</strong> you have specified the groups as + * {@link LookupDiscovery#ALL_GROUPS} (a <code>null</code>). + * <p> + * Note: This method is intended for overrides expressed from scripts using + * environment variables where we need to parse an interpret the value + * rather than given the value directly in a {@link Configuration} file. As + * a consequence, you can not specify the optional {@link SocketFactory} for + * the {@link LookupLocator} with this method. + * + * @param locators + * The locators, expressed as a comma delimited list of URIs. + * + * @return An array of zero or more {@link LookupLocator}s. + * + * @throws MalformedURLException + * if any of the parse URLs is invalid. + * + * @throws IllegalArgumentException + * if the <i>locators</i> is <code>null</code>. + */ + public static LookupLocator[] getLocators(final String locators) + throws MalformedURLException { + + if (locators == null) + throw new IllegalArgumentException(); + + final String[] a = locators.split(","); + + final LookupLocator[] b = new LookupLocator[a.length]; + + if (a.length == 1 && a[0].trim().length() == 0) { + + return new LookupLocator[0]; + + } + + for (int i = 0; i < a.length; i++) { + + final String urlStr = a[i]; + + final LookupLocator locator = new LookupLocator(urlStr); + + b[i] = locator; + + } + + return b; + + } + + /** + * Return an array of zero or more groups -or- <code>null</code> if the + * given argument is either <code>null</code> or <code>"null"</code>. + * <p> + * Note: a <code>null</code> corresponds to + * {@link LookupDiscovery#ALL_GROUPS}. This option is only permissible when + * you have a single setup and are using multicast discovery. In all other + * cases, you need to specify the group(s). + * + * @param groups + * The groups, expressed as a comma delimited list or zero or + * more groups. + * + * @return A string array parsed out of that argument. + */ + public static String[] getGroups(final String groups) { + + if (groups == null) + return null; + + if (groups.trim().equals("null")) + return null; + + final String[] a = groups.split(","); + + if (a.length == 1 && a[0].trim().length() == 0) { + + return new String[0]; + + } + + return a; + + } + } Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/TestAll.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/TestAll.java 2013-11-22 02:09:38 UTC (rev 7580) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/TestAll.java 2013-11-22 15:43:38 UTC (rev 7581) @@ -79,6 +79,9 @@ final TestSuite suite = new TestSuite("jini"); + // jini configuration helpers. + suite.addTest(com.bigdata.jini.util.TestAll.suite()); + // zookeeper client library (queues, locks, etc). suite.addTest(com.bigdata.zookeeper.TestAll.suite()); Added: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/util/TestAll.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/util/TestAll.java (rev 0) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/util/TestAll.java 2013-11-22 15:43:38 UTC (rev 7581) @@ -0,0 +1,62 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + * Created on Jun 26, 2006 + */ +package com.bigdata.jini.util; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import com.bigdata.service.jini.AbstractServerTestCase; + +/** + * Aggregates tests in dependency order - see {@link AbstractServerTestCase} for + * <strong>required</strong> system properties in order to run this test suite. + * + * @version $Id$ + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + */ +public class TestAll extends TestCase { + + public TestAll() { + } + + public TestAll(String name) { + super(name); + } + + public static Test suite() { + + final TestSuite suite = new TestSuite(TestAll.class.getPackage() + .getName()); + + suite.addTestSuite(TestConfigMath.class); + + return suite; + + } + +} Added: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/util/TestConfigMath.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/util/TestConfigMath.java (rev 0) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/jini/util/TestConfigMath.java 2013-11-22 15:43:38 UTC (rev 7581) @@ -0,0 +1,105 @@ +/** + +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Created on Jun 26, 2006 + */ +package com.bigdata.jini.util; + +import java.net.MalformedURLException; + +import junit.framework.TestCase2; +import net.jini.core.discovery.LookupLocator; + +public class TestConfigMath extends TestCase2 { + + public TestConfigMath() { + } + + public TestConfigMath(final String name) { + super(name); + } + + public void test_getLocators() throws MalformedURLException { + + assertSameArray( + new LookupLocator[] {// + new LookupLocator("jini://bigdata15/"),// + new LookupLocator("jini://bigdata16/"),// + new LookupLocator("jini://bigdata17/"),// + }, + ConfigMath + .getLocators("jini://bigdata15/,jini://bigdata16/,jini://bigdata17/")); + + } + + public void test_getLocators_empty() throws MalformedURLException { + + assertSameArray(new LookupLocator[] {// + }, ConfigMath.getLocators("")); + + } + + public void test_getLocators_null_arg() throws MalformedURLException { + + try { + + ConfigMath.getLocators(null/* locators */); + + fail("Expecting " + IllegalArgumentException.class); + + } catch (IllegalArgumentException ex) { + + // ignore expected exception + + } + + } + + public void test_getGroups1() throws MalformedURLException { + + assertSameArray(new String[] { "a" }, + ConfigMath.getGroups("a")); + + } + + public void test_getGroups3() throws MalformedURLException { + + assertSameArray(new String[] { "a", "b", "c" }, + ConfigMath.getGroups("a,b,c")); + + } + + public void test_getGroups_empty() { + + assertSameArray(new String[] {}, ConfigMath.getGroups("")); + + } + + public void test_getGroups_null_label() { + + assertEquals(null, ConfigMath.getGroups("null")); + + } + +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <tho...@us...> - 2014-01-20 22:27:57
|
Revision: 7816 http://bigdata.svn.sourceforge.net/bigdata/?rev=7816&view=rev Author: thompsonbry Date: 2014-01-20 22:27:50 +0000 (Mon, 20 Jan 2014) Log Message: ----------- I have modified HALogNexus to release the HALog files asynchronously. This causes some problems in the existing unit tests, so I have added a configuration parameter that may be used to specify a synchronous HALog release timeout - this defaults to ZERO, but may be increased as desired (in exchange for latency in 2-phase commits) or as necessary for the existing tests, some of which assume that HALog files are released synchronously at the 2-phase commit. The expected behavior should now be that HALog files are asynchronously release starting with the first 2-phase commit in which the corresponding commit point is reclaimable. The release of HALog files will continue until the specified first commit point that may not be released, or until an invariant is violated. Under sustained commits with concurrent releases, any commit in which the task to release HALog files is longer running will start a new such task with a new earliest commit point that may not be released. Changes are to: * HALogNexus: now submits a task to release the HALog files. There can be at most one instance of this task running for a given HAJournal. The task is wrapped by the FutureTaskInvariantMon and will be cancelled if the service is no longer joined with the met quorum. * HAJournalServer: A new configuration parameter exists to control the maximum delay for releasing the HALog files (default is ZERO milliseconds). * TestHARestorePolicy: This was modified to raise the timeout for the synchronous purge of the HALogs so the test assumptions would remain valid. Note: The HAJournalServer.ConfigurationOptions.HA_LOG_PURGE_TIMEOUT defaults to zero. This is probably what we always want. However, the existence of this option allows us to revert to the old behavior using a configuration change or by changing the default. See #780 Modified Paths: -------------- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3RestorePolicy.java Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2014-01-20 20:29:52 UTC (rev 7815) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2014-01-20 22:27:50 UTC (rev 7816) @@ -347,6 +347,27 @@ String DEFAULT_HA_LOG_DIR = "HALog"; /** + * The maximum amount of time in milliseconds to await the synchronous + * release of older HALog files during a 2-phase commit (default + * {@value #DEFAULT_HA_LOG_PURGE_TIMEOUT}). This MAY be ZERO to not + * wait. Large timeouts can cause significant latency during a 2-phase + * commit if a large number of HALog files should be released + * accordinging to the {@link IRestorePolicy}. + * + * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/780" + * >Incremental or asynchronous purge of HALog files</a> + */ + String HA_LOG_PURGE_TIMEOUT = "HALogPurgeTimeout"; + + /** + * The default is ZERO (0L) milliseconds, which is probably what we + * always want. However, the existence of this option allows us to + * revert to the old behavior using a configuration change or by + * changing the default. + */ + long DEFAULT_HA_LOG_PURGE_TIMEOUT = 0L; // milliseconds + + /** * The name of the directory in which periodic snapshots of the journal * will be written. Each snapshot is a full copy of the journal. * Snapshots are compressed and therefore may be much more compact than Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java 2014-01-20 20:29:52 UTC (rev 7815) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HALogNexus.java 2014-01-20 22:27:50 UTC (rev 7816) @@ -32,7 +32,15 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Iterator; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -43,6 +51,9 @@ import com.bigdata.btree.ITuple; import com.bigdata.btree.ITupleIterator; +import com.bigdata.concurrent.FutureTaskInvariantMon; +import com.bigdata.ha.HAGlue; +import com.bigdata.ha.QuorumService; import com.bigdata.ha.QuorumServiceBase; import com.bigdata.ha.halog.HALogReader; import com.bigdata.ha.halog.HALogWriter; @@ -56,6 +67,7 @@ import com.bigdata.journal.RootBlockView; import com.bigdata.journal.jini.ha.HALogIndex.HALogRecord; import com.bigdata.journal.jini.ha.HALogIndex.IHALogRecord; +import com.bigdata.quorum.Quorum; import com.bigdata.striterator.Resolver; import com.bigdata.striterator.Striterator; import com.bigdata.util.ChecksumError; @@ -206,12 +218,41 @@ */ private final HALogIndex haLogIndex; + /** + * The maximum amount of time in milliseconds to await the synchronous purge + * of HALog files during a 2-phase commit. + * + * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/780" + * >Incremental or asynchronous purge of HALog files</a> + * + * @see HAJournalServer.ConfigurationOptions#HA_LOG_PURGE_TIMEOUT + */ + private final long haLogPurgeTimeout; + public HALogNexus(final HAJournalServer server, final HAJournal journal, final Configuration config) throws IOException, ConfigurationException { this.journal = journal; - + + { + haLogPurgeTimeout = (Long) config + .getEntry( + HAJournalServer.ConfigurationOptions.COMPONENT, + HAJournalServer.ConfigurationOptions.HA_LOG_PURGE_TIMEOUT, + Long.TYPE, + HAJournalServer.ConfigurationOptions.DEFAULT_HA_LOG_PURGE_TIMEOUT); + + if (haLogPurgeTimeout < 0) { + throw new ConfigurationException( + HAJournalServer.ConfigurationOptions.HA_LOG_PURGE_TIMEOUT + + "=" + + haLogPurgeTimeout + + " : must be GTE ZERO"); + } + + } + // Note: This is the effective service directory. final File serviceDir = server.getServiceDir(); @@ -1021,6 +1062,87 @@ } /** + * Class purges all HALog files LT the specified commit counter. This class + * is intended to run asynchronously in order to avoid large latency during + * a commit in which many HALog files may be released. + * + * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/780" + * >Incremental or asynchronous purge of HALog files</a> + * + * @author <a href="mailto:tho...@us...">Bryan + * Thompson</a> + */ + private class DeleteHALogsTask implements Callable<Void> { + + private final long token; + private final long earliestRetainedSnapshotCommitCounter; + + DeleteHALogsTask(final long token, + final long earliestRetainedSnapshotCommitCounter) { + this.token = token; + this.earliestRetainedSnapshotCommitCounter = earliestRetainedSnapshotCommitCounter; + } + + @Override + public Void call() throws Exception { + + final long nfiles = haLogIndex.getEntryCount(); + + long ndeleted = 0L, totalBytes = 0L; + + final Iterator<IHALogRecord> itr = getHALogs(); + + while(itr.hasNext() && logAccessors.get() == 0) { + + final IHALogRecord r = itr.next(); + + final long closingCommitCounter = r.getCommitCounter(); + + final boolean deleteFile = closingCommitCounter < earliestRetainedSnapshotCommitCounter; + + if (!deleteFile) { + + // No more files to delete. + break; + + } + + if (!journal.getQuorum().isQuorumFullyMet(token)) { + /* + * Halt operation. + * + * Note: This is not an error, but we can not remove + * snapshots or HALogs if this invariant is violated. + */ + break; + } + + // The HALog file to be removed. + final File logFile = getHALogFile(closingCommitCounter); + + // Remove that HALog file from the file system and our index. + removeHALog(logFile); + + ndeleted++; + + totalBytes += r.sizeOnDisk(); + + } + + if (haLog.isInfoEnabled()) + haLog.info("PURGED LOGS: nfound=" + nfiles + ", ndeleted=" + + ndeleted + ", totalBytes=" + totalBytes + + ", earliestRetainedSnapshotCommitCounter=" + + earliestRetainedSnapshotCommitCounter); + + // done + return null; + + } + + } // class DeleteHALogsTask + + /** * Delete HALogs that are no longer required. * * @param earliestRetainedSnapshotCommitCounter @@ -1028,60 +1150,126 @@ * retained snapshot. We need to retain any HALogs that are GTE * this commit counter since they will be applied to that * snapshot. + * + * @see <a href="http://sourceforge.net/apps/trac/bigdata/ticket/780" + * >Incremental or asynchronous purge of HALog files</a> */ void deleteHALogs(final long token, final long earliestRetainedSnapshotCommitCounter) { - final long nfiles = haLogIndex.getEntryCount(); - - long ndeleted = 0L, totalBytes = 0L; + synchronized (deleteHALogFuture) { - final Iterator<IHALogRecord> itr = getHALogs(); - - while(itr.hasNext() && logAccessors.get() == 0) { - - final IHALogRecord r = itr.next(); + { + final Future<Void> f = deleteHALogFuture.get(); - final long closingCommitCounter = r.getCommitCounter(); - - final boolean deleteFile = closingCommitCounter < earliestRetainedSnapshotCommitCounter; + if (f != null) { - if (!deleteFile) { + /* + * Existing task. Check to see if done or still running. + */ - // No more files to delete. - break; + if (!f.isDone()) { + // Still releasing some HALogs from a previous request. + return; + } - } + try { + f.get(); + } catch (InterruptedException e) { + // propagate interrupt. + Thread.currentThread().interrupt(); + return; + } catch (CancellationException e) { + /* + * Note: This is not an error. If the invariants are + * violated, the task will be cancelled. The task is + * "safe" as long as the invariants are valid. + */ + log.warn("Cancelled: " + e); + } catch (ExecutionException e) { + log.error(e, e); + } - if (!journal.getQuorum().isQuorumFullyMet(token)) { - /* - * Halt operation. - * - * Note: This is not an error, but we can not remove - * snapshots or HALogs if this invariant is violated. - */ - break; + // clear reference. + deleteHALogFuture.set(null); + + } } - // The HALog file to be removed. - final File logFile = getHALogFile(closingCommitCounter); + /* + * Start new request. + */ - // Remove that HALog file from the file system and our index. - removeHALog(logFile); + final Quorum<HAGlue, QuorumService<HAGlue>> quorum = journal + .getQuorum(); - ndeleted++; + final QuorumService<HAGlue> localService = quorum.getClient(); - totalBytes += r.sizeOnDisk(); + // Task sends an HALog file along the pipeline. + final FutureTask<Void> ft = new FutureTaskInvariantMon<Void>( + new DeleteHALogsTask(token, + earliestRetainedSnapshotCommitCounter), quorum) { - } + @Override + protected void establishInvariants() { + assertQuorumMet(); + assertJoined(localService.getServiceId()); + assertMember(localService.getServiceId()); + } - if (haLog.isInfoEnabled()) - haLog.info("PURGED LOGS: nfound=" + nfiles + ", ndeleted=" - + ndeleted + ", totalBytes=" + totalBytes - + ", earliestRetainedSnapshotCommitCounter=" - + earliestRetainedSnapshotCommitCounter); + }; + // save reference to prevent concurrent execution of this task + deleteHALogFuture.set(ft); + + // Run task. + journal.getExecutorService().submit(ft); + + /* + * Wait up to a deadline for the HALogs to be purged. If this + * operation can not be completed synchronously, then it will + * continus asynchronously while the invariants remain valid. + * + * Note: Some of the unit tests were written to assume that the + * purge of the HALog files was synchronous. This assumption is no + * longer valid since we will purge the HALog files asynchronously + * in order to avoid latency during a commit when a large number of + * HALog files must be purged. + */ + if (haLogPurgeTimeout > 0) { + try { + ft.get(haLogPurgeTimeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + // propagate interrupt. + Thread.currentThread().interrupt(); + return; + } catch (CancellationException e) { + /* + * Note: This is not an error. If the invariants are + * violated, the task will be cancelled. The task is "safe" + * as long as the invariants are valid. + */ + log.warn("Cancelled: " + e); + return; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (TimeoutException e) { + // ignore. + } + } + + } + } + + /** + * Reference is used to avoid concurrent execution of multiple instances of + * the {@link DeleteHALogsTask}. + * <p> + * Note: This {@link AtomicReference} also doubles as a monitor object to + * provide a guard for {@link #deleteHALogs(long, long)}. + */ + private final AtomicReference<Future<Void>> deleteHALogFuture = new AtomicReference<Future<Void>>(); /** * Delete all HALog files (except the current one). The {@link #haLogIndex} Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3RestorePolicy.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3RestorePolicy.java 2014-01-20 20:29:52 UTC (rev 7815) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/TestHA3RestorePolicy.java 2014-01-20 22:27:50 UTC (rev 7816) @@ -97,7 +97,8 @@ return new String[]{ "com.bigdata.journal.jini.ha.HAJournalServer.restorePolicy=new com.bigdata.journal.jini.ha.DefaultRestorePolicy("+restorePolicyMinSnapshotAgeMillis+","+restorePolicyMinSnapshots+","+restorePolicyMinRestorePoints+")", - "com.bigdata.journal.jini.ha.HAJournalServer.snapshotPolicy=new com.bigdata.journal.jini.ha.NoSnapshotPolicy()" + "com.bigdata.journal.jini.ha.HAJournalServer.snapshotPolicy=new com.bigdata.journal.jini.ha.NoSnapshotPolicy()", + "com.bigdata.journal.jini.ha.HAJournalServer.HALogPurgeTimeout="+Long.MAX_VALUE+"L" // force synchronous purge. }; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |