[prev in list] [next in list] [prev in thread] [next in thread]
List: forgerock-opendj-dev
Subject: [Opendj-dev] [11532] branches/opendj3-server-dev: OPENDJ-1690 CR-5724 Fix file-based changelog purgi
From: noreply () forgerock ! org
Date: 2014-12-23 11:39:41
Message-ID: 20141223113941.49D073F87B () sources ! internal ! forgerock ! com
[Download RAW message or body]
[Attachment #2 (text/html)]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[11532] branches/opendj3-server-dev: OPENDJ-1690 CR-5724 Fix file-based \
changelog purging</title> </head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: \
verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: \
bold} #msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: \
bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: \
6px; } #logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em \
0; } #logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg \
h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; } \
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; \
} #logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: \
-1.5em; padding-left: 1.5em; } #logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em \
1em 0 1em; background: white;} #logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid \
#fa0; border-bottom: 1px solid #fa0; background: #fff; } #logmsg table th { \
text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted \
#fa0; } #logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: \
0.2em 0.5em; } #logmsg table thead th { text-align: center; border-bottom: 1px solid \
#fa0; } #logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: \
6px; } #patch { width: 100%; }
#patch h4 {font-family: \
verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, \
#patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins \
{background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del \
{background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, \
.info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a \
href="http://sources.forgerock.org/changelog/opendj/?cs=11532">11532</a></dd> \
<dt>Author</dt> <dd>nicolas.capponi@forgerock.com</dd> <dt>Date</dt> <dd>2014-12-23 \
11:39:41 +0000 (Tue, 23 Dec 2014)</dd> </dl>
<h3>Log Message</h3>
<pre><a href="https://bugster.forgerock.org/jira/browse/OPENDJ-1690">OPENDJ-1690</a> \
CR-5724 Fix file-based changelog purging
- Log file rotation for CN Index DB is based on both size and time
- Time interval before log file rotation is a fraction of replication purge delay
- Last log file rotation time for CN index DB is stored in a file</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#branchesopendj3serverdevsrcmessagesmessagesreplicationproperties">branches/opendj3-server-dev/src/messages/messages/replication.properties</a></li>
<li><a href="#branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchange \
logfileFileChangeNumberIndexDBjava">branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDB.java</a></li>
<li><a href="#branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchange \
logfileFileChangelogDBjava">branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java</a></li>
<li><a href="#branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchange \
logfileLogjava">branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Log.java</a></li>
<li><a href="#branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchange \
logfileRecordjava">branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Record.java</a></li>
<li><a href="#branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchange \
logfileReplicationEnvironmentjava">branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/ReplicationEnvironment.java</a></li>
<li><a href="#branchesopendj3serverdevtestsunitteststestngsrcserverorgopendsserverrep \
licationserverchangelogfileFileChangeNumberIndexDBTestjava">branches/opendj3-server-de \
v/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDBTest.java</a></li>
<li><a href="#branchesopendj3serverdevtestsunitteststestngsrcserverorgopendsserverrep \
licationserverchangelogfileFileReplicaDBTestjava">branches/opendj3-server-dev/tests/un \
it-tests-testng/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDBTest.java</a></li>
<li><a href="#branchesopendj3serverdevtestsunitteststestngsrcserverorgopendsserverrep \
licationserverchangelogfileLogTestjava">branches/opendj3-server-dev/tests/unit-tests-t \
estng/src/server/org/opends/server/replication/server/changelog/file/LogTest.java</a></li>
<li><a href="#branchesopendj3serverdevtestsunitteststestngsrcserverorgopendsserverrep \
licationserverchangelogfileReplicationEnvironmentTestjava">branches/opendj3-server-dev \
/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/ReplicationEnvironmentTest.java</a></li>
</ul>
<h3>Property Changed</h3>
<ul>
<li><a href="#branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchangel \
ogfileRecordjava">branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Record.java</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="branchesopendj3serverdevsrcmessagesmessagesreplicationproperties"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/src/messages/messages/replication.properties (11531 => \
11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/src/messages/messages/replication.properties 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/src/messages/messages/replication.properties 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -626,5 +626,9 @@
</span><span class="cx"> perform a search request on cn=changelog
</span><span class="cx"> ERR_CHANGELOG_BACKEND_SEARCH_286 =An error occurred when \
</span><span class="cx"> searching base DN '%s' with filter '%s' in changelog \
backend : %s </span><del>-ERR_CHANGELOG_BACKEND_ATTRIBUTE_287 =An error occurred when \
\
- retrieving attribute value for attribute '%s' for entry DN '%s' in changelog \
backend : %s </del><span class="cx">\ No newline at end of file
</span><ins>+ERR_CHANGELOG_BACKEND_ATTRIBUTE_287=An error occurred when \
+ retrieving attribute value for attribute '%s' for entry DN '%s' in changelog \
backend : %s +ERR_CHANGELOG_UNABLE_TO_CREATE_LAST_LOG_ROTATION_TIME_FILE_288=Could \
not create \ + file '%s' to store last log rotation time %d
+ERR_CHANGELOG_UNABLE_TO_DELETE_LAST_LOG_ROTATION_TIME_FILE_289=Could not delete \
+ file '%s' that stored the previous last log rotation time
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchangelogfileFileChangeNumberIndexDBjava"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDB.java \
(11531 => 11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDB.java 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDB.java 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -59,6 +59,16 @@
</span><span class="cx"> /** The parser of records stored in this \
ChangeNumberIndexDB. */ </span><span class="cx"> static final RecordParser<Long, \
ChangeNumberIndexRecord> RECORD_PARSER = new ChangeNumberIndexDBParser(); \
</span><span class="cx"> </span><ins>+ static final \
Record.Mapper<ChangeNumberIndexRecord, CSN> MAPPER_TO_CSN = + new \
Record.Mapper<ChangeNumberIndexRecord, CSN>() + {
+ @Override
+ public CSN map(ChangeNumberIndexRecord value)
+ {
+ return value.getCSN();
+ }
+ };
+
</ins><span class="cx"> /** The log in which records are persisted. */
</span><span class="cx"> private final Log<Long, ChangeNumberIndexRecord> \
log; </span><span class="cx">
</span><span class="lines">@@ -231,8 +241,14 @@
</span><span class="cx"> {
</span><span class="cx"> return null;
</span><span class="cx"> }
</span><del>- final Record<Long, ChangeNumberIndexRecord> record = \
log.purgeUpTo(purgeCSN.getTime());
- return record != null ? record.getValue().getCSN() : null;
</del><ins>+ // Retrieve the oldest change number that must not be purged
+ final Long purgeChangeNumber = log.findBoundaryKeyFromRecord(MAPPER_TO_CSN, \
purgeCSN); + if (purgeChangeNumber != null)
+ {
+ final Record<Long, ChangeNumberIndexRecord> record = \
log.purgeUpTo(purgeChangeNumber); + return record != null ? \
record.getValue().getCSN() : null; + }
+ return null;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> /**
</span></span></pre></div>
<a id="branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchangelogfileFileChangelogDBjava"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java \
(11531 => 11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -41,6 +41,7 @@
</span><span class="cx"> import org.forgerock.i18n.LocalizableMessageBuilder;
</span><span class="cx"> import org.forgerock.i18n.slf4j.LocalizedLogger;
</span><span class="cx"> import org.forgerock.opendj.config.server.ConfigException;
</span><ins>+import org.forgerock.util.time.TimeService;
</ins><span class="cx"> import \
org.opends.server.admin.std.server.ReplicationServerCfg; </span><span class="cx"> \
import org.opends.server.api.DirectoryThread; </span><span class="cx"> import \
org.opends.server.backends.ChangelogBackend; </span><span class="lines">@@ -296,7 \
+297,7 @@ </span><span class="cx"> try
</span><span class="cx"> {
</span><span class="cx"> final File dbDir = \
getFileForPath(config.getReplicationDBDirectory()); </span><del>- replicationEnv \
= new ReplicationEnvironment(dbDir.getAbsolutePath(), replicationServer); \
</del><ins>+ replicationEnv = new \
ReplicationEnvironment(dbDir.getAbsolutePath(), replicationServer, \
TimeService.SYSTEM); </ins><span class="cx"> final ChangelogState \
changelogState = replicationEnv.getChangelogState(); </span><span class="cx"> \
initializeToChangelogState(changelogState); </span><span class="cx"> if \
(config.isComputeChangeNumber()) </span><span class="lines">@@ -574,6 +575,12 @@
</span><span class="cx"> public void setPurgeDelay(final long purgeDelayInMillis)
</span><span class="cx"> {
</span><span class="cx"> this.purgeDelayInMillis = purgeDelayInMillis;
</span><ins>+
+ // Rotation time interval for CN Index DB log file
+ // needs to be a fraction of the purge delay
+ // to ensure there is at least one file to purge
+ replicationEnv.setCNIndexDBRotationInterval(purgeDelayInMillis / 2);
+
</ins><span class="cx"> if (purgeDelayInMillis > 0)
</span><span class="cx"> {
</span><span class="cx"> final ChangelogDBPurger newPurger = new \
ChangelogDBPurger(); </span></span></pre></div>
<a id="branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchangelogfileLogjava"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Log.java \
(11531 => 11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Log.java 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Log.java 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -51,6 +51,7 @@
</span><span class="cx"> import org.forgerock.i18n.slf4j.LocalizedLogger;
</span><span class="cx"> import org.forgerock.util.Reject;
</span><span class="cx"> import org.forgerock.util.Utils;
</span><ins>+import org.forgerock.util.time.TimeService;
</ins><span class="cx"> import \
org.opends.server.replication.server.changelog.api.ChangelogException; </span><span \
class="cx"> import org.opends.server.replication.server.changelog.api.DBCursor; \
</span><span class="cx"> import \
org.opends.server.replication.server.changelog.api.DBCursor.KeyMatchingStrategy; \
</span><span class="lines">@@ -74,9 +75,9 @@ </span><span class="cx"> * the string \
representation of lowest and highest key present in the log file.</li> \
</span><span class="cx"> * </ul> </span><span class="cx"> * A read-only log \
file is created each time the head log file has reached the </span><del>- * maximum \
size limit. The head log file is then rotated to the read-only file \
and a
- * new empty head log file is opened. There is no limit on the number of read-only
- * files, but they can be purged.
</del><ins>+ * maximum size limit or the time limit. The head log file is then \
rotated to the + * read-only file and a new empty head log file is opened. There is \
no limit on the + * number of read-only files, but they can be purged.
</ins><span class="cx"> * <p>
</span><span class="cx"> * A log is obtained using the {@code Log.openLog()} method \
and must always be </span><span class="cx"> * released using the {@code close()} \
method. </span><span class="lines">@@ -170,14 +171,23 @@
</span><span class="cx"> private final List<LogCursor<K, V>> \
openCursors = new CopyOnWriteArrayList<LogCursor<K, V>>(); </span><span \
class="cx"> </span><span class="cx"> /**
</span><del>- * A log file is rotated once it has exceeded this size limit. The log \
file can have </del><ins>+ * A log file can be rotated once it has exceeded this \
size limit. The log file can have </ins><span class="cx"> * a size much larger \
than this limit if the last record written has a huge size. </span><span class="cx"> \
* </span><del>- * TODO : to be replaced later by a (or a list of) configurable \
Rotation policy </del><ins>+ * TODO : to be replaced later by a list of \
configurable Rotation policy </ins><span class="cx"> * eg, \
List<RotationPolicy> rotationPolicies = new ArrayList<RotationPolicy>(); \
</span><span class="cx"> */ </span><span class="cx"> private final long \
sizeLimitPerLogFileInBytes; </span><span class="cx">
</span><ins>+ /** The time service used for timing. It is package private so it can \
be modified by test case. */ + TimeService timeService = TimeService.SYSTEM;
+
+ /** A log file can be rotated once it has exceeded a given time interval. No \
rotation happens if equals to zero. */ + private long rotationIntervalInMillis;
+
+ /** The last time a log file was rotated. */
+ private long lastRotationTime;
+
</ins><span class="cx"> /**
</span><span class="cx"> * The exclusive lock used for writes and lifecycle \
operations on this log: </span><span class="cx"> * initialize, clear, sync and \
close. </span><span class="lines">@@ -190,6 +200,12 @@
</span><span class="cx"> private final Lock sharedLock;
</span><span class="cx">
</span><span class="cx"> /**
</span><ins>+ * The replication environment used to create this log. The log is \
notifying it for any change + * that must be persisted.
+ */
+ private final ReplicationEnvironment replicationEnv;
+
+ /**
</ins><span class="cx"> * Open a log with the provided log path, record parser and \
maximum size per </span><span class="cx"> * log file.
</span><span class="cx"> * <p>
</span><span class="lines">@@ -199,25 +215,28 @@
</span><span class="cx"> * Type of the key of a record, which must be \
comparable. </span><span class="cx"> * @param <V>
</span><span class="cx"> * Type of the value of a record.
</span><ins>+ * @param replicationEnv
+ * The replication environment used to create this log.
</ins><span class="cx"> * @param logPath
</span><span class="cx"> * Path of the log.
</span><span class="cx"> * @param parser
</span><span class="cx"> * Parser for encoding/decoding of records.
</span><del>- * @param sizeLimitPerFileInBytes
- * Limit in bytes before rotating the head log file of the log.
</del><ins>+ * @param rotationParameters
+ * Parameters for the log files rotation.
</ins><span class="cx"> * @return a log
</span><span class="cx"> * @throws ChangelogException
</span><span class="cx"> * If a problem occurs during initialization.
</span><span class="cx"> */
</span><del>- static synchronized <K extends Comparable<K>, V> Log<K, \
V> openLog(final File logPath,
- final RecordParser<K, V> parser, final long sizeLimitPerFileInBytes) \
throws ChangelogException </del><ins>+ static synchronized <K extends \
Comparable<K>, V> Log<K, V> openLog(final ReplicationEnvironment \
replicationEnv, + final File logPath, final RecordParser<K, V> parser, \
final LogRotationParameters rotationParameters) + throws ChangelogException
</ins><span class="cx"> {
</span><span class="cx"> Reject.ifNull(logPath, parser);
</span><span class="cx"> @SuppressWarnings("unchecked")
</span><span class="cx"> Log<K, V> log = (Log<K, V>) \
logsCache.get(logPath); </span><span class="cx"> if (log == null)
</span><span class="cx"> {
</span><del>- log = new Log<K, V>(logPath, parser, \
sizeLimitPerFileInBytes); </del><ins>+ log = new Log<K, V>(replicationEnv, \
logPath, parser, rotationParameters); </ins><span class="cx"> \
logsCache.put(logPath, log); </span><span class="cx"> }
</span><span class="cx"> else
</span><span class="lines">@@ -229,7 +248,44 @@
</span><span class="cx"> return log;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ /** Holds the parameters for log files rotation. */
+ static class LogRotationParameters {
+
+ private final long sizeLimitPerFileInBytes;
+ private final long rotationInterval;
+ private final long lastRotationTime;
+
+ /**
+ * Creates rotation parameters.
+ *
+ * @param sizeLimitPerFileInBytes
+ * Size limit before rotating a log file.
+ * @param rotationInterval
+ * Time interval before rotating a log file.
+ * @param lastRotationTime
+ * Last time a log file was rotated.
+ */
+ LogRotationParameters(long sizeLimitPerFileInBytes, long rotationInterval, long \
lastRotationTime) + {
+ this.sizeLimitPerFileInBytes = sizeLimitPerFileInBytes;
+ this.rotationInterval = rotationInterval;
+ this.lastRotationTime = lastRotationTime;
+ }
+
+ }
+
</ins><span class="cx"> /**
</span><ins>+ * Set the time interval for rotation of log file.
+ *
+ * @param rotationIntervalInMillis
+ * time interval before rotation of log file
+ */
+ void setRotationInterval(long rotationIntervalInMillis)
+ {
+ this.rotationIntervalInMillis = rotationIntervalInMillis;
+ }
+
+ /**
</ins><span class="cx"> * Release a reference to the log corresponding to provided \
path. The log is </span><span class="cx"> * closed if this is the last reference.
</span><span class="cx"> */
</span><span class="lines">@@ -256,21 +312,27 @@
</span><span class="cx"> /**
</span><span class="cx"> * Creates a new log.
</span><span class="cx"> *
</span><ins>+ * @param replicationEnv
+ * The replication environment used to create this log.
</ins><span class="cx"> * @param logPath
</span><span class="cx"> * The directory path of the log.
</span><span class="cx"> * @param parser
</span><span class="cx"> * Parser of records.
</span><del>- * @param sizeLimitPerFile
- * Limit in bytes before rotating a log file.
</del><ins>+ * @param rotationParams
+ * Parameters for log-file rotation.
+ *
</ins><span class="cx"> * @throws ChangelogException
</span><span class="cx"> * If a problem occurs during initialization.
</span><span class="cx"> */
</span><del>- private Log(final File logPath, final RecordParser<K, V> parser, \
final long sizeLimitPerFile)
- throws ChangelogException
</del><ins>+ private Log(final ReplicationEnvironment replicationEnv, final File \
logPath, final RecordParser<K, V> parser, + final LogRotationParameters \
rotationParams) throws ChangelogException </ins><span class="cx"> {
</span><ins>+ this.replicationEnv = replicationEnv;
</ins><span class="cx"> this.logPath = logPath;
</span><span class="cx"> this.recordParser = parser;
</span><del>- this.sizeLimitPerLogFileInBytes = sizeLimitPerFile;
</del><ins>+ this.sizeLimitPerLogFileInBytes = \
rotationParams.sizeLimitPerFileInBytes; + this.rotationIntervalInMillis = \
rotationParams.rotationInterval; + this.lastRotationTime = \
rotationParams.lastRotationTime; </ins><span class="cx"> this.referenceCount = 1;
</span><span class="cx">
</span><span class="cx"> final ReadWriteLock lock = new \
ReentrantReadWriteLock(false); </span><span class="lines">@@ -376,9 +438,9 @@
</span><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx"> LogFile<K, V> headLogFile = getHeadLogFile();
</span><del>- if (headLogFile.getSizeInBytes() > sizeLimitPerLogFileInBytes)
</del><ins>+ if (mustRotate(headLogFile))
</ins><span class="cx"> {
</span><del>- \
logger.error(INFO_CHANGELOG_LOG_FILE_ROTATION.get(logPath.getPath(), \
headLogFile.getSizeInBytes())); </del><ins>+ \
logger.info(INFO_CHANGELOG_LOG_FILE_ROTATION.get(logPath.getPath(), \
headLogFile.getSizeInBytes())); </ins><span class="cx">
</span><span class="cx"> rotateHeadLogFile();
</span><span class="cx"> headLogFile = getHeadLogFile();
</span><span class="lines">@@ -392,6 +454,27 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ private boolean mustRotate(LogFile<K, V> headLogFile)
+ {
+ if (lastAppendedKey == null)
+ {
+ // never rotate an empty file
+ return false;
+ }
+ if (headLogFile.getSizeInBytes() > sizeLimitPerLogFileInBytes)
+ {
+ // rotate because file size exceeded threshold
+ return true;
+ }
+ if (rotationIntervalInMillis > 0)
+ {
+ // rotate if time limit is reached
+ final long timeElapsed = timeService.since(lastRotationTime);
+ return timeElapsed > rotationIntervalInMillis;
+ }
+ return false;
+ }
+
</ins><span class="cx"> /**
</span><span class="cx"> * Indicates if the provided record has a key that would \
break the key </span><span class="cx"> * ordering in the log.
</span><span class="lines">@@ -711,6 +794,60 @@
</span><span class="cx"> releaseLog(logPath);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ /**
+ * Find the highest key that corresponds to a record that is the oldest (or
+ * first) of a read-only log file and where value mapped from the record is
+ * lower or equals to provided limit value.
+ * <p>
+ * Example<br>
+ * Given a log with 3 log files, with Record<Int, String> and \
Mapper<String, + * Long> mapping a string to its long value
+ * <ul>
+ * <li>1_10.log where oldest record is (key=1, \
value="50")</li> + * <li>11_20.log where oldest record is \
(key=11, value="150")</li> + * <li>head.log where oldest \
record is (key=25, value="250")</li> + * </ul>
+ * Then
+ * <ul>
+ * <li>findBoundaryKeyFromRecord(mapper, 20) => null</li>
+ * <li>findBoundaryKeyFromRecord(mapper, 50) => 1</li>
+ * <li>findBoundaryKeyFromRecord(mapper, 100) => 1</li>
+ * <li>findBoundaryKeyFromRecord(mapper, 150) => 11</li>
+ * <li>findBoundaryKeyFromRecord(mapper, 200) => 11</li>
+ * <li>findBoundaryKeyFromRecord(mapper, 250) => 25</li>
+ * <li>findBoundaryKeyFromRecord(mapper, 300) => 25</li>
+ * </ul>
+ *
+ * @param <V2>
+ * Type of the value extracted from the record
+ * @param mapper
+ * The mapper to extract a value from a record. It is expected that
+ * extracted values are ordered according to an order consistent with
+ * this log ordering, i.e. for two records, if key(R1) > key(R2) then
+ * extractedValue(R1) > extractedValue(R2).
+ * @param limitValue
+ * The limit value to search for
+ * @return the key or {@code null} if no key corresponds
+ * @throws ChangelogException
+ * If a problem occurs
+ */
+ <V2 extends Comparable<V2>> K \
findBoundaryKeyFromRecord(Record.Mapper<V, V2> mapper, V2 limitValue) + \
throws ChangelogException + {
+ K key = null;
+ for (LogFile<K, V> logFile : logFiles.values())
+ {
+ final Record<K, V> record = logFile.getOldestRecord();
+ final V2 oldestValue = mapper.map(record.getValue());
+ if (oldestValue.compareTo(limitValue) > 0)
+ {
+ return key;
+ }
+ key = record.getKey();
+ }
+ return key;
+ }
+
</ins><span class="cx"> /** Effectively close this log. */
</span><span class="cx"> private void doClose()
</span><span class="cx"> {
</span><span class="lines">@@ -766,6 +903,9 @@
</span><span class="cx">
</span><span class="cx"> // Re-enable cursors previously opened on head, with the \
saved state </span><span class="cx"> \
updateOpenedCursorsOnHeadAfterRotation(cursorsOnHead); </span><ins>+
+ // Notify even if time-based rotation is not enabled, as it could be enabled at \
any time + replicationEnv.notifyLogFileRotation(this);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> private void renameHeadLogFileTo(final File \
rotatedLogFile) throws ChangelogException </span></span></pre></div>
<a id="branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchangelogfileRecordjava"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Record.java \
(11531 => 11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Record.java 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Record.java 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -35,6 +35,18 @@
</span><span class="cx"> */
</span><span class="cx"> class Record<K, V>
</span><span class="cx"> {
</span><ins>+ /** Map the record value to another value. */
+ static interface Mapper<V, V2> {
+ /**
+ * Map a record value to another value.
+ *
+ * @param value
+ * The value to map
+ * @return the new value
+ */
+ V2 map(V value);
+ }
+
</ins><span class="cx"> private final K key;
</span><span class="cx"> private final V value;
</span><span class="cx">
</span><span class="cx">Property changes on: \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/Record.java
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4>Added: svn:eol-style</h4></div>
<a id="branchesopendj3serverdevsrcserverorgopendsserverreplicationserverchangelogfileReplicationEnvironmentjava"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/ReplicationEnvironment.java \
(11531 => 11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/ReplicationEnvironment.java 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/ReplicationEnvironment.java 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -46,13 +46,16 @@
</span><span class="cx"> import java.util.concurrent.CopyOnWriteArrayList;
</span><span class="cx"> import java.util.concurrent.atomic.AtomicBoolean;
</span><span class="cx">
</span><ins>+import org.forgerock.i18n.LocalizableMessage;
</ins><span class="cx"> import org.forgerock.i18n.slf4j.LocalizedLogger;
</span><ins>+import org.forgerock.util.time.TimeService;
</ins><span class="cx"> import org.opends.server.replication.common.CSN;
</span><span class="cx"> import org.opends.server.replication.protocol.UpdateMsg;
</span><span class="cx"> import org.opends.server.replication.server.ChangelogState;
</span><span class="cx"> import \
org.opends.server.replication.server.ReplicationServer; </span><span class="cx"> \
import org.opends.server.replication.server.changelog.api.ChangeNumberIndexRecord; \
</span><span class="cx"> import \
org.opends.server.replication.server.changelog.api.ChangelogException; \
</span><ins>+import org.opends.server.replication.server.changelog.file.Log.LogRotationParameters;
</ins><span class="cx"> import org.opends.server.types.DN;
</span><span class="cx"> import org.opends.server.types.DirectoryException;
</span><span class="cx"> import org.opends.server.util.StaticUtils;
</span><span class="lines">@@ -67,7 +70,8 @@
</span><span class="cx"> * created :
</span><span class="cx"> * <ul>
</span><span class="cx"> * <li>A "changenumberindex" directory \
containing the log files for </span><del>- * ChangeNumberIndexDB</li>
</del><ins>+ * ChangeNumberIndexDB, and a file named \
"rotationtime[millis].last" where [millis] is + * the time of the last log \
file rotation in milliseconds</li> </ins><span class="cx"> * <li>A \
"domains.state" file containing a mapping of each domain DN to an id. The \
</span><span class="cx"> * id is used to name the corresponding domain \
directory.</li> </span><span class="cx"> * <li>One directory per domain, \
named after "[id].domain" where [id] is the id </span><span \
class="lines">@@ -76,7 +80,7 @@ </span><span class="cx"> * <p>
</span><span class="cx"> * Each domain directory contains the following directories \
and files : </span><span class="cx"> * <ul>
</span><del>- * <li>A "generation_[id].id" file, where [id] is the \
generation id</li> </del><ins>+ * <li>A "generation[id].id" \
file, where [id] is the generation id</li> </ins><span class="cx"> * \
<li>One directory per server id, named after "[id].server" where [id] \
is the </span><span class="cx"> * id of the server.</li>
</span><span class="cx"> * </ul>
</span><span class="lines">@@ -100,6 +104,7 @@
</span><span class="cx"> * | \---changenumberindex
</span><span class="cx"> * | \--- head.log [contains last records written]
</span><span class="cx"> * | \--- 1_50.log [contains records with keys in \
interval [1, 50]] </span><ins>+ * | \--- rtime198745512.last
</ins><span class="cx"> * | \---1.domain
</span><span class="cx"> * | \---generation1.id
</span><span class="cx"> * | \---22.server
</span><span class="lines">@@ -119,8 +124,10 @@
</span><span class="cx"> {
</span><span class="cx"> private static final LocalizedLogger logger = \
LocalizedLogger.getLoggerForThisClass(); </span><span class="cx">
</span><del>- private static final long MAX_LOG_FILE_SIZE_IN_BYTES = 10*1024*1024;
</del><ins>+ private static final long CN_INDEX_DB_MAX_LOG_FILE_SIZE_IN_BYTES = 1024 \
* 1024; </ins><span class="cx">
</span><ins>+ private static final long REPLICA_DB_MAX_LOG_FILE_SIZE_IN_BYTES = 10 * \
CN_INDEX_DB_MAX_LOG_FILE_SIZE_IN_BYTES; +
</ins><span class="cx"> private static final int NO_GENERATION_ID = -1;
</span><span class="cx">
</span><span class="cx"> private static final String CN_INDEX_DB_DIRNAME = \
"changenumberindex"; </span><span class="lines">@@ -129,9 +136,13 @@
</span><span class="cx">
</span><span class="cx"> static final String REPLICA_OFFLINE_STATE_FILENAME = \
"offline.state"; </span><span class="cx">
</span><ins>+ static final String LAST_ROTATION_TIME_FILE_PREFIX = \
"rotationtime"; +
+ static final String LAST_ROTATION_TIME_FILE_SUFFIX = ".ms";
+
</ins><span class="cx"> private static final String DOMAIN_STATE_SEPARATOR = \
":"; </span><span class="cx">
</span><del>- private static final String DOMAIN_SUFFIX = ".domain";
</del><ins>+ private static final String DOMAIN_SUFFIX = ".dom";
</ins><span class="cx">
</span><span class="cx"> private static final String SERVER_ID_SUFFIX = \
".server"; </span><span class="cx">
</span><span class="lines">@@ -170,6 +181,17 @@
</span><span class="cx"> }
</span><span class="cx"> };
</span><span class="cx">
</span><ins>+ private static final FileFilter LAST_ROTATION_TIME_FILE_FILTER = new \
FileFilter() + {
+ @Override
+ public boolean accept(File file)
+ {
+ return file.isFile()
+ && file.getName().startsWith(LAST_ROTATION_TIME_FILE_PREFIX)
+ && file.getName().endsWith(LAST_ROTATION_TIME_FILE_SUFFIX);
+ }
+ };
+
</ins><span class="cx"> /** Root path where the replication log is stored. */
</span><span class="cx"> private final String replicationRootPath;
</span><span class="cx"> /**
</span><span class="lines">@@ -181,10 +203,18 @@
</span><span class="cx"> */
</span><span class="cx"> private final ChangelogState changelogState;
</span><span class="cx">
</span><del>- /** The list of logs that are in use. */
- private final List<Log<?, ?>> logs = new \
CopyOnWriteArrayList<Log<?, ?>>(); </del><ins>+ /** The list of logs \
that are in use for Replica DBs. */ + private final List<Log<CSN, \
UpdateMsg>> logsReplicaDB = new CopyOnWriteArrayList<Log<CSN, \
UpdateMsg>>(); </ins><span class="cx">
</span><span class="cx"> /**
</span><ins>+ * The list of logs that are in use for the CN Index DB.
+ * There is a single CN Index DB for a ReplicationServer, but there can be \
multiple references opened on it. + * This is the responsability of Log class to \
handle properly these multiple references. + */
+ private List<Log<Long, ChangeNumberIndexRecord>> logsCNIndexDB =
+ new CopyOnWriteArrayList<Log<Long, ChangeNumberIndexRecord>>();;
+
+ /**
</ins><span class="cx"> * Maps each domain DN to a domain id that is used to name \
directory in file system. </span><span class="cx"> *
</span><span class="cx"> * @GuardedBy("domainsLock")
</span><span class="lines">@@ -205,25 +235,60 @@
</span><span class="cx">
</span><span class="cx"> private final AtomicBoolean isShuttingDown = new \
AtomicBoolean(false); </span><span class="cx">
</span><ins>+ /** The time service used for timing. */
+ private final TimeService timeService;
+
</ins><span class="cx"> /**
</span><ins>+ * For CN Index DB, a log file can be rotated once it has exceeded a \
given time interval. + * <p>
+ * It is disabled if the interval is equals to zero.
+ * The interval can be modified at any time.
+ */
+ private long cnIndexDBRotationInterval;
+
+ /**
+ * For CN Index DB, the last time a log file was rotated.
+ * It is persisted to file each time it changes and read at server start. */
+ private long cnIndexDBLastRotationTime;
+
+ /**
</ins><span class="cx"> * Creates the replication environment.
</span><span class="cx"> *
</span><span class="cx"> * @param rootPath
</span><span class="cx"> * Root path where replication log is stored.
</span><span class="cx"> * @param replicationServer
</span><span class="cx"> * The underlying replication server.
</span><ins>+ * @param timeService
+ * Time service to use for timing.
</ins><span class="cx"> * @throws ChangelogException
</span><span class="cx"> * If an error occurs during initialization.
</span><span class="cx"> */
</span><span class="cx"> ReplicationEnvironment(final String rootPath,
</span><del>- final ReplicationServer replicationServer) throws \
ChangelogException </del><ins>+ final ReplicationServer replicationServer, final \
TimeService timeService) throws ChangelogException </ins><span class="cx"> {
</span><span class="cx"> this.replicationRootPath = rootPath;
</span><span class="cx"> this.replicationServer = replicationServer;
</span><ins>+ this.timeService = timeService;
</ins><span class="cx"> this.changelogState = readOnDiskChangelogState();
</span><ins>+ this.cnIndexDBLastRotationTime = readOnDiskLastRotationTime();
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> /**
</span><ins>+ * Sets the rotation time interval of a log file for the CN Index DB.
+ *
+ * @param timeInterval
+ * time interval for rotation of a log file.
+ */
+ void setCNIndexDBRotationInterval(long timeInterval)
+ {
+ cnIndexDBRotationInterval = timeInterval;
+ for (Log<Long, ChangeNumberIndexRecord> log : logsCNIndexDB)
+ {
+ log.setRotationInterval(cnIndexDBRotationInterval);
+ }
+ }
+
+ /**
</ins><span class="cx"> * Returns the state of the replication changelog.
</span><span class="cx"> *
</span><span class="cx"> * @return the {@link ChangelogState} read from the \
changelogState DB </span><span class="lines">@@ -257,6 +322,16 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> /**
</span><ins>+ * Return the last rotation time for CN Index DB log files.
+ *
+ * @return the last rotation time in millis
+ */
+ long getCnIndexDBLastRotationTime()
+ {
+ return cnIndexDBLastRotationTime;
+ }
+
+ /**
</ins><span class="cx"> * Finds or creates the log used to store changes from the \
replication server </span><span class="cx"> * with the given serverId and the \
given baseDN. </span><span class="cx"> *
</span><span class="lines">@@ -299,7 +374,8 @@
</span><span class="cx"> ensureGenerationIdFileExists(generationIdPath);
</span><span class="cx"> changelogState.setDomainGenerationId(domainDN, \
generationId); </span><span class="cx">
</span><del>- return openLog(serverIdPath, FileReplicaDB.RECORD_PARSER);
</del><ins>+ return openLog(serverIdPath, FileReplicaDB.RECORD_PARSER,
+ new LogRotationParameters(REPLICA_DB_MAX_LOG_FILE_SIZE_IN_BYTES, 0, 0), \
logsReplicaDB); </ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx"> catch (Exception e)
</span><span class="lines">@@ -325,7 +401,9 @@
</span><span class="cx"> final File path = getCNIndexDBPath();
</span><span class="cx"> try
</span><span class="cx"> {
</span><del>- return openLog(path, FileChangeNumberIndexDB.RECORD_PARSER);
</del><ins>+ final LogRotationParameters rotationParams = new \
LogRotationParameters(CN_INDEX_DB_MAX_LOG_FILE_SIZE_IN_BYTES, + \
cnIndexDBRotationInterval, cnIndexDBLastRotationTime); + return openLog(path, \
FileChangeNumberIndexDB.RECORD_PARSER, rotationParams, logsCNIndexDB); </ins><span \
class="cx"> } </span><span class="cx"> catch (Exception e)
</span><span class="cx"> {
</span><span class="lines">@@ -344,7 +422,8 @@
</span><span class="cx"> {
</span><span class="cx"> if (isShuttingDown.compareAndSet(false, true))
</span><span class="cx"> {
</span><del>- logs.clear();
</del><ins>+ logsReplicaDB.clear();
+ logsCNIndexDB.clear();
</ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -409,6 +488,25 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> /**
</span><ins>+ * Notify that log file has been rotated for provided log.
+ *
+ * The last rotation time is persisted to a file and read at startup time.
+ *
+ * @param log
+ * the log that has a file rotated.
+ * @throws ChangelogException
+ * If a problem occurs
+ */
+ void notifyLogFileRotation(Log<?, ?> log) throws ChangelogException
+ {
+ // only CN Index DB log rotation time is persisted
+ if (logsCNIndexDB.contains(log))
+ {
+ updateCNIndexDBLastRotationTime(timeService.now());
+ }
+ }
+
+ /**
</ins><span class="cx"> * Notify that the replica corresponding to provided domain \
and provided CSN </span><span class="cx"> * is offline.
</span><span class="cx"> *
</span><span class="lines">@@ -654,16 +752,17 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> /** Open a log from the provided path and record parser. \
*/ </span><del>- private <K extends Comparable<K>, V> Log<K, V> \
openLog(final File serverIdPath, final RecordParser<K, V> \
parser)
- throws ChangelogException
</del><ins>+ private <K extends Comparable<K>, V> Log<K, V> \
openLog(final File serverIdPath, final RecordParser<K, V> parser, + \
LogRotationParameters rotationParams, List<Log<K, V>> logsCache) throws \
ChangelogException </ins><span class="cx"> {
</span><span class="cx"> checkShutDownBeforeOpening(serverIdPath);
</span><span class="cx">
</span><del>- final Log<K, V> log = Log.openLog(serverIdPath, parser, \
MAX_LOG_FILE_SIZE_IN_BYTES); </del><ins>+ final Log<K, V> log = \
Log.openLog(this, serverIdPath, parser, rotationParams); </ins><span class="cx">
</span><span class="cx"> checkShutDownAfterOpening(serverIdPath, log);
</span><span class="cx">
</span><del>- logs.add(log);
</del><ins>+ logsCache.add(log);
+
</ins><span class="cx"> return log;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -718,6 +817,46 @@
</span><span class="cx"> return (generationIds != null && \
generationIds.length > 0) ? generationIds[0] : null; </span><span class="cx"> }
</span><span class="cx">
</span><ins>+ /**
+ * Retrieve the last rotation time from the disk.
+ *
+ * @return the last rotation time in millis (which is the current time if no
+ * rotation file is found or if a problem occurs).
+ */
+ private long readOnDiskLastRotationTime()
+ {
+ try
+ {
+ final File file = retrieveLastRotationTimeFile();
+ if (file != null)
+ {
+ final String filename = file.getName();
+ final String value = \
filename.substring(LAST_ROTATION_TIME_FILE_PREFIX.length(), + \
filename.length() - LAST_ROTATION_TIME_FILE_SUFFIX.length()); + return \
Long.valueOf(value); + }
+ }
+ catch (Exception e)
+ {
+ logger.trace(LocalizableMessage.raw("Error when retrieving last log file \
rotation time from file"), e); + }
+ // Default to current time
+ return timeService.now();
+ }
+
+ /**
+ * Retrieve the file named after the last rotation time from the provided
+ * directory.
+ *
+ * @return the last rotation time file or {@code null} if the corresponding file
+ * can't be found
+ */
+ private File retrieveLastRotationTimeFile()
+ {
+ File[] files = getCNIndexDBPath().listFiles(LAST_ROTATION_TIME_FILE_FILTER);
+ return (files != null && files.length > 0) ? files[0] : null;
+ }
+
</ins><span class="cx"> private File getDomainPath(final String domainId)
</span><span class="cx"> {
</span><span class="cx"> return new File(replicationRootPath, domainId + \
DOMAIN_SUFFIX); </span><span class="lines">@@ -748,9 +887,16 @@
</span><span class="cx"> return new File(replicationRootPath, \
CN_INDEX_DB_DIRNAME); </span><span class="cx"> }
</span><span class="cx">
</span><ins>+ private File getLastRotationTimePath(long lastRotationTime)
+ {
+ return new File(getCNIndexDBPath(),
+ LAST_ROTATION_TIME_FILE_PREFIX + lastRotationTime + \
LAST_ROTATION_TIME_FILE_SUFFIX); + }
+
</ins><span class="cx"> private void closeLog(final Log<?, ?> log)
</span><span class="cx"> {
</span><del>- logs.remove(log);
</del><ins>+ logsReplicaDB.remove(log);
+ logsCNIndexDB.remove(log);
</ins><span class="cx"> log.close();
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -789,6 +935,30 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ private void updateCNIndexDBLastRotationTime(final long \
lastRotationTime) throws ChangelogException { + final File previousRotationFile = \
retrieveLastRotationTimeFile(); + final File newRotationFile = \
getLastRotationTimePath(lastRotationTime); + try
+ {
+ newRotationFile.createNewFile();
+ }
+ catch (IOException e)
+ {
+ throw new ChangelogException(ERR_CHANGELOG_UNABLE_TO_CREATE_LAST_LOG_ROTATION_TIME_FILE.get(
+ newRotationFile.getPath(), lastRotationTime), e);
+ }
+ if (previousRotationFile != null)
+ {
+ final boolean isDeleted = previousRotationFile.delete();
+ if (!isDeleted)
+ {
+ throw new ChangelogException(ERR_CHANGELOG_UNABLE_TO_DELETE_LAST_LOG_ROTATION_TIME_FILE.get(
+ previousRotationFile.getPath()));
+ }
+ }
+ cnIndexDBLastRotationTime = lastRotationTime;
+ }
+
</ins><span class="cx"> private void ensureGenerationIdFileExists(final File \
generationIdPath) </span><span class="cx"> throws ChangelogException
</span><span class="cx"> {
</span></span></pre></div>
<a id="branchesopendj3serverdevtestsunitteststestngsrcserverorgopendsserverreplicationserverchangelogfileFileChangeNumberIndexDBTestjava"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDBTest.java \
(11531 => 11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDBTest.java 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDBTest.java 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -37,7 +37,6 @@
</span><span class="cx"> import \
org.opends.server.replication.server.changelog.api.ChangelogException; </span><span \
class="cx"> import org.opends.server.replication.server.changelog.api.DBCursor; \
</span><span class="cx"> import org.opends.server.types.DN; </span><del>-import \
org.opends.server.util.StaticUtils; </del><span class="cx"> import \
org.testng.annotations.DataProvider; </span><span class="cx"> import \
org.testng.annotations.Test; </span><span class="cx">
</span><span class="lines">@@ -137,23 +136,12 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><del>-
</del><span class="cx"> /**
</span><del>- * This test makes basic operations of a ChangeNumberIndexDB:
- * <ol>
- * <li>create the db</li>
- * <li>add records</li>
- * <li>read them with a cursor</li>
- * <li>set a very short trim period</li>
- * <li>wait for the db to be trimmed / here since the changes are not stored
- * in the replication changelog, the ChangeNumberIndexDB will be \
cleared.</li>
- * </ol>
</del><ins>+ * This test verifies purge is working by relying on a very short purge \
delay. + * The purge can be done only if there is at least one read-only log file, \
so + * this test ensures the rotation happens, using the rotation based on time.
</ins><span class="cx"> */
</span><del>- // TODO : this works only if we ensure that there is a rotation of \
ahead log file
- // at the right place. First two records are 37 and 76 bytes long,
- // so it means : 37 < max file size < 113 to have the last record alone in \
the ahead log file
- // Re-enable this test when max file size is customizable for log
- @Test(enabled=false)
</del><ins>+ @Test
</ins><span class="cx"> public void testPurge() throws Exception
</span><span class="cx"> {
</span><span class="cx"> ReplicationServer replicationServer = null;
</span><span class="lines">@@ -163,49 +151,32 @@
</span><span class="cx"> final ChangelogDB changelogDB = \
replicationServer.getChangelogDB(); </span><span class="cx"> \
changelogDB.setPurgeDelay(0); // disable purging </span><span class="cx">
</span><del>- // Prepare data to be stored in the db
- DN baseDN1 = DN.valueOf("o=test1");
- DN baseDN2 = DN.valueOf("o=test2");
- DN baseDN3 = DN.valueOf("o=test3");
-
- CSN[] csns = generateCSNs(1, 0, 3);
-
</del><span class="cx"> // Add records
</span><ins>+ DN[] baseDNs = { DN.valueOf("o=test1"), \
DN.valueOf("o=test2"), DN.valueOf("o=test3"), \
DN.valueOf("o=test4") }; + CSN[] csns = generateCSNs(1, 0, 4);
</ins><span class="cx"> final FileChangeNumberIndexDB cnIndexDB = \
getCNIndexDB(replicationServer); </span><del>- long cn1 = addRecord(cnIndexDB, \
baseDN1, csns[0]);
- addRecord(cnIndexDB, baseDN2, csns[1]);
- long cn3 = addRecord(cnIndexDB, baseDN3, csns[2]);
</del><ins>+ long cn0 = addRecord(cnIndexDB, baseDNs[0], csns[0]);
+ addRecord(cnIndexDB, baseDNs[1], csns[1]);
+ long cn2 = addRecord(cnIndexDB, baseDNs[2], csns[2]);
</ins><span class="cx">
</span><del>- // The ChangeNumber should not get purged
- final long oldestCN = cnIndexDB.getOldestRecord().getChangeNumber();
- assertEquals(oldestCN, cn1);
- assertEquals(cnIndexDB.getNewestRecord().getChangeNumber(), cn3);
</del><ins>+ // The CN DB should not be purged at this point
+ assertEquals(cnIndexDB.getOldestRecord().getChangeNumber(), cn0);
+ assertEquals(cnIndexDB.getNewestRecord().getChangeNumber(), cn2);
</ins><span class="cx">
</span><del>- DBCursor<ChangeNumberIndexRecord> cursor = \
cnIndexDB.getCursorFrom(oldestCN);
- try
- {
- assertTrue(cursor.next());
- assertEqualTo(cursor.getRecord(), csns[0], baseDN1);
- assertTrue(cursor.next());
- assertEqualTo(cursor.getRecord(), csns[1], baseDN2);
- assertTrue(cursor.next());
- assertEqualTo(cursor.getRecord(), csns[2], baseDN3);
- assertFalse(cursor.next());
- }
- finally
- {
- StaticUtils.close(cursor);
- }
</del><ins>+ // change the purge delay to a very short time
+ changelogDB.setPurgeDelay(5);
+ Thread.sleep(50);
+ // add a new record to force the rotation of the log
+ addRecord(cnIndexDB, baseDNs[3], csns[3]);
</ins><span class="cx">
</span><del>- // Now test that purging removes all changes but the last one
- changelogDB.setPurgeDelay(1);
</del><ins>+ // Now all changes should have been purged but the last one
</ins><span class="cx"> int count = 0;
</span><span class="cx"> while (cnIndexDB.count() > 1 && count < \
100) </span><span class="cx"> {
</span><span class="cx"> Thread.sleep(10);
</span><span class="cx"> count++;
</span><span class="cx"> }
</span><del>- assertOnlyNewestRecordIsLeft(cnIndexDB, 3);
</del><ins>+ assertOnlyNewestRecordIsLeft(cnIndexDB, 4);
</ins><span class="cx"> }
</span><span class="cx"> finally
</span><span class="cx"> {
</span></span></pre></div>
<a id="branchesopendj3serverdevtestsunitteststestngsrcserverorgopendsserverreplicationserverchangelogfileFileReplicaDBTestjava"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDBTest.java \
(11531 => 11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDBTest.java 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDBTest.java 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> import org.forgerock.i18n.slf4j.LocalizedLogger;
</span><span class="cx"> import org.forgerock.opendj.config.server.ConfigException;
</span><span class="cx"> import org.forgerock.opendj.ldap.ByteString;
</span><ins>+import org.forgerock.util.time.TimeService;
</ins><span class="cx"> import org.opends.server.TestCaseUtils;
</span><span class="cx"> import \
org.opends.server.admin.std.meta.ReplicationServerCfgDefn.ReplicationDBImplementation;
</span><span class="cx"> import \
org.opends.server.admin.std.server.ReplicationServerCfg; </span><span \
class="lines">@@ -423,7 +424,7 @@ </span><span class="cx"> replicationServer = \
configureReplicationServer(100000, 10); </span><span class="cx">
</span><span class="cx"> testRoot = createCleanDir();
</span><del>- dbEnv = new ReplicationEnvironment(testRoot.getPath(), \
replicationServer); </del><ins>+ dbEnv = new \
ReplicationEnvironment(testRoot.getPath(), replicationServer, TimeService.SYSTEM); \
</ins><span class="cx"> replicaDB = new FileReplicaDB(1, TEST_ROOT_DN, \
replicationServer, dbEnv); </span><span class="cx">
</span><span class="cx"> // Populate the db with 'max' msg
</span></span></pre></div>
<a id="branchesopendj3serverdevtestsunitteststestngsrcserverorgopendsserverreplicationserverchangelogfileLogTestjava"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/LogTest.java \
(11531 => 11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/LogTest.java 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/LogTest.java 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> package org.opends.server.replication.server.changelog.file;
</span><span class="cx">
</span><span class="cx"> import static org.assertj.core.api.Assertions.*;
</span><ins>+import static org.mockito.Mockito.*;
</ins><span class="cx"> import static \
org.opends.server.replication.server.changelog.api.DBCursor.KeyMatchingStrategy.*; \
</span><span class="cx"> import static \
org.opends.server.replication.server.changelog.api.DBCursor.PositionStrategy.*; \
</span><span class="cx"> import static \
org.opends.server.replication.server.changelog.file.LogFileTest.*; </span><span \
class="lines">@@ -39,7 +40,9 @@ </span><span class="cx"> import \
org.opends.server.replication.server.changelog.api.DBCursor; </span><span class="cx"> \
import org.opends.server.replication.server.changelog.api.DBCursor.KeyMatchingStrategy;
</span><span class="cx"> import \
org.opends.server.replication.server.changelog.api.DBCursor.PositionStrategy; \
</span><ins>+import org.opends.server.replication.server.changelog.file.Log.LogRotationParameters;
</ins><span class="cx"> import \
org.opends.server.replication.server.changelog.file.LogFileTest.FailingStringRecordParser;
</span><ins>+import \
org.opends.server.replication.server.changelog.file.Record.Mapper; </ins><span \
class="cx"> import org.opends.server.util.StaticUtils; </span><span class="cx"> \
import org.testng.annotations.BeforeMethod; </span><span class="cx"> import \
org.testng.annotations.DataProvider; </span><span class="lines">@@ -52,6 +55,8 @@
</span><span class="cx"> // Use a directory dedicated to this test class
</span><span class="cx"> private static final File LOG_DIRECTORY = new \
File(TestCaseUtils.getUnitTestRootPath(), "changelog-unit"); </span><span \
class="cx"> </span><ins>+ private static final long NO_TIME_BASED_LOG_ROTATION = 0;
+
</ins><span class="cx"> @BeforeMethod
</span><span class="cx"> public void initialize() throws Exception
</span><span class="cx"> {
</span><span class="lines">@@ -79,9 +84,12 @@
</span><span class="cx"> // This allow to ensure rotation mechanism is thoroughly \
tested </span><span class="cx"> // Some tests rely on having 2 records per log \
file (especially the purge tests), so take care </span><span class="cx"> // if \
this value has to be changed </span><del>- int sizeLimitPerFileInBytes = 30;
</del><ins>+ final int sizeLimitPerFileInBytes = 30;
+ final LogRotationParameters rotationParams = new \
LogRotationParameters(sizeLimitPerFileInBytes, + NO_TIME_BASED_LOG_ROTATION, \
NO_TIME_BASED_LOG_ROTATION); + final ReplicationEnvironment replicationEnv = \
mock(ReplicationEnvironment.class); </ins><span class="cx">
</span><del>- return Log.openLog(LOG_DIRECTORY, parser, sizeLimitPerFileInBytes);
</del><ins>+ return Log.openLog(replicationEnv, LOG_DIRECTORY, parser, \
rotationParams); </ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> @Test
</span><span class="lines">@@ -366,8 +374,11 @@
</span><span class="cx"> Log<String, String> writeLog = null;
</span><span class="cx"> try
</span><span class="cx"> {
</span><del>- long sizeOf1MB = 1024*1024;
- writeLog = Log.openLog(LOG_DIRECTORY, LogFileTest.RECORD_PARSER, sizeOf1MB);
</del><ins>+ long sizeOf10MB = 10*1024*1024;
+ final LogRotationParameters rotationParams = new \
LogRotationParameters(sizeOf10MB, NO_TIME_BASED_LOG_ROTATION, + \
NO_TIME_BASED_LOG_ROTATION); + final ReplicationEnvironment replicationEnv = \
mock(ReplicationEnvironment.class); + writeLog = Log.openLog(replicationEnv, \
LOG_DIRECTORY, LogFileTest.RECORD_PARSER, rotationParams); </ins><span class="cx">
</span><span class="cx"> for (int i = 1; i < 1000000; i++)
</span><span class="cx"> {
</span><span class="lines">@@ -514,12 +525,10 @@
</span><span class="cx"> public void testPurge(String purgeKey, \
Record<String,String> firstRecordExpectedAfterPurge, </span><span class="cx"> \
int cursorStartIndex, int cursorEndIndex) throws Exception </span><span class="cx"> \
{ </span><del>- Log<String, String> log = null;
</del><ins>+ Log<String, String> log = openLog(LogFileTest.RECORD_PARSER);
</ins><span class="cx"> DBCursor<Record<String, String>> cursor = \
null; </span><span class="cx"> try
</span><span class="cx"> {
</span><del>- log = openLog(LogFileTest.RECORD_PARSER);
-
</del><span class="cx"> log.purgeUpTo(purgeKey);
</span><span class="cx">
</span><span class="cx"> cursor = log.getCursor();
</span><span class="lines">@@ -533,6 +542,51 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ static final Mapper<String, Integer> MAPPER = new \
Record.Mapper<String, Integer>() + {
+ @Override
+ public Integer map(String value)
+ {
+ // extract numeric value, e.g. from "value10" return 10
+ return Integer.valueOf(value.substring("value".length()));
+ }
+ };
+
+ @DataProvider
+ Object[][] findBoundaryKeyData()
+ {
+ return new Object[][] {
+ // limit value, expected key
+ { 0, null },
+ { 1, "key001" },
+ { 2, "key001" },
+ { 3, "key003" },
+ { 4, "key003" },
+ { 5, "key005" },
+ { 6, "key005" },
+ { 7, "key007" },
+ { 8, "key007" },
+ { 9, "key009" },
+ { 10, "key009" },
+ { 11, "key009" },
+ { 12, "key009" },
+ };
+ }
+
+ @Test(dataProvider = "findBoundaryKeyData")
+ public void testFindBoundaryKeyFromRecord(int limitValue, String expectedKey) \
throws Exception + {
+ Log<String, String> log = openLog(LogFileTest.RECORD_PARSER);
+ try
+ {
+ assertThat(log.findBoundaryKeyFromRecord(MAPPER, \
limitValue)).isEqualTo(expectedKey); + }
+ finally
+ {
+ StaticUtils.close(log);
+ }
+ }
+
</ins><span class="cx"> private void \
advanceCursorUpTo(DBCursor<Record<String, String>> cursor, int fromIndex, \
int endIndex) </span><span class="cx"> throws Exception
</span><span class="cx"> {
</span></span></pre></div>
<a id="branchesopendj3serverdevtestsunitteststestngsrcserverorgopendsserverreplicationserverchangelogfileReplicationEnvironmentTestjava"></a>
<div class="modfile"><h4>Modified: \
branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/ReplicationEnvironmentTest.java \
(11531 => 11532)</h4> <pre class="diff"><span>
<span class="info">--- \
branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/ReplicationEnvironmentTest.java 2014-12-23 \
11:18:12 UTC (rev 11531)
+++ branches/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/server/changelog/file/ReplicationEnvironmentTest.java 2014-12-23 \
11:39:41 UTC (rev 11532) </span><span class="lines">@@ -31,12 +31,14 @@
</span><span class="cx"> import java.util.List;
</span><span class="cx">
</span><span class="cx"> import org.assertj.core.data.MapEntry;
</span><ins>+import org.forgerock.util.time.TimeService;
</ins><span class="cx"> import org.opends.server.DirectoryServerTestCase;
</span><span class="cx"> import org.opends.server.TestCaseUtils;
</span><span class="cx"> import org.opends.server.replication.common.CSN;
</span><span class="cx"> import org.opends.server.replication.common.CSNGenerator;
</span><span class="cx"> import org.opends.server.replication.protocol.UpdateMsg;
</span><span class="cx"> import org.opends.server.replication.server.ChangelogState;
</span><ins>+import org.opends.server.replication.server.ReplicationServer;
</ins><span class="cx"> import \
org.opends.server.replication.server.changelog.api.ChangeNumberIndexRecord; \
</span><span class="cx"> import \
org.opends.server.replication.server.changelog.api.ChangelogException; </span><span \
class="cx"> import org.opends.server.types.DN; </span><span class="lines">@@ -48,6 \
+50,7 @@ </span><span class="cx"> import org.testng.annotations.Test;
</span><span class="cx">
</span><span class="cx"> import static org.assertj.core.api.Assertions.*;
</span><ins>+import static org.mockito.Mockito.*;
</ins><span class="cx"> import static \
org.opends.server.replication.server.changelog.file.ReplicationEnvironment.*; \
</span><span class="cx"> </span><span class="cx"> \
@SuppressWarnings("javadoc") </span><span class="lines">@@ -94,7 +97,7 @@
</span><span class="cx"> {
</span><span class="cx"> final File rootPath = new \
File(TEST_DIRECTORY_CHANGELOG); </span><span class="cx"> final DN domainDN = \
DN.valueOf(DN1_AS_STRING); </span><del>- ReplicationEnvironment environment = \
new ReplicationEnvironment(rootPath.getAbsolutePath(), null); </del><ins>+ \
ReplicationEnvironment environment = createReplicationEnv(rootPath); </ins><span \
class="cx"> cnDB = environment.getOrCreateCNIndexDB(); </span><span class="cx"> \
replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); </span><span \
class="cx"> replicaDB2 = environment.getOrCreateReplicaDB(domainDN, \
SERVER_ID_2, 1); </span><span class="lines">@@ -122,7 +125,7 @@
</span><span class="cx"> {
</span><span class="cx"> File rootPath = new File(TEST_DIRECTORY_CHANGELOG);
</span><span class="cx"> List<DN> domainDNs = \
Arrays.asList(DN.valueOf(DN1_AS_STRING), DN.valueOf(DN2_AS_STRING), \
DN.valueOf(DN3_AS_STRING)); </span><del>- ReplicationEnvironment environment = \
new ReplicationEnvironment(rootPath.getAbsolutePath(), null); </del><ins>+ \
ReplicationEnvironment environment = createReplicationEnv(rootPath); </ins><span \
class="cx"> cnDB = environment.getOrCreateCNIndexDB(); </span><span class="cx"> \
for (int i = 0; i <= 2 ; i++) </span><span class="cx"> {
</span><span class="lines">@@ -163,7 +166,7 @@
</span><span class="cx"> {
</span><span class="cx"> final File rootPath = new \
File(TEST_DIRECTORY_CHANGELOG); </span><span class="cx"> final DN domainDN = \
DN.valueOf(DN1_AS_STRING); </span><del>- ReplicationEnvironment environment = \
new ReplicationEnvironment(rootPath.getAbsolutePath(), null); </del><ins>+ \
ReplicationEnvironment environment = createReplicationEnv(rootPath); </ins><span \
class="cx"> cnDB = environment.getOrCreateCNIndexDB(); </span><span class="cx"> \
replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); </span><span \
class="cx"> </span><span class="lines">@@ -193,7 +196,7 @@
</span><span class="cx"> {
</span><span class="cx"> final File rootPath = new \
File(TEST_DIRECTORY_CHANGELOG); </span><span class="cx"> final DN domainDN = \
DN.valueOf(DN1_AS_STRING); </span><del>- ReplicationEnvironment environment = \
new ReplicationEnvironment(rootPath.getAbsolutePath(), null); </del><ins>+ \
ReplicationEnvironment environment = createReplicationEnv(rootPath); </ins><span \
class="cx"> cnDB = environment.getOrCreateCNIndexDB(); </span><span class="cx"> \
replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); </span><span \
class="cx"> </span><span class="lines">@@ -218,7 +221,7 @@
</span><span class="cx"> final File rootPath = new \
File(TEST_DIRECTORY_CHANGELOG); </span><span class="cx"> final DN domainDN = \
DN.valueOf(DN1_AS_STRING); </span><span class="cx">
</span><del>- ReplicationEnvironment environment = new \
ReplicationEnvironment(rootPath.getAbsolutePath(), null); </del><ins>+ \
ReplicationEnvironment environment = createReplicationEnv(rootPath); </ins><span \
class="cx"> cnDB = environment.getOrCreateCNIndexDB(); </span><span class="cx"> \
replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); </span><span \
class="cx"> </span><span class="lines">@@ -249,7 +252,7 @@
</span><span class="cx"> final File rootPath = new \
File(TEST_DIRECTORY_CHANGELOG); </span><span class="cx"> final DN domainDN = \
DN.valueOf(DN1_AS_STRING); </span><span class="cx">
</span><del>- ReplicationEnvironment environment = new \
ReplicationEnvironment(rootPath.getAbsolutePath(), null); </del><ins>+ \
ReplicationEnvironment environment = createReplicationEnv(rootPath); </ins><span \
class="cx"> cnDB = environment.getOrCreateCNIndexDB(); </span><span class="cx"> \
replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); </span><span \
class="cx"> </span><span class="lines">@@ -278,7 +281,7 @@
</span><span class="cx"> final File rootPath = new \
File(TEST_DIRECTORY_CHANGELOG); </span><span class="cx"> final DN domainDN = \
DN.valueOf(DN1_AS_STRING); </span><span class="cx">
</span><del>- ReplicationEnvironment environment = new \
ReplicationEnvironment(rootPath.getAbsolutePath(), null); </del><ins>+ \
ReplicationEnvironment environment = createReplicationEnv(rootPath); </ins><span \
class="cx"> cnDB = environment.getOrCreateCNIndexDB(); </span><span class="cx"> \
replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); </span><span \
class="cx"> CSN offlineCSN = new CSN(TimeThread.getTime(), 0, SERVER_ID_1); \
</span><span class="lines">@@ -309,13 +312,13 @@ </span><span class="cx"> {
</span><span class="cx"> File rootPath = new File(TEST_DIRECTORY_CHANGELOG);
</span><span class="cx"> DN domainDN = DN.valueOf(DN1_AS_STRING);
</span><del>- ReplicationEnvironment environment = new \
ReplicationEnvironment(rootPath.getAbsolutePath(), null); </del><ins>+ \
ReplicationEnvironment environment = createReplicationEnv(rootPath); </ins><span \
class="cx"> replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, \
1); </span><span class="cx"> replicaDB2 = \
environment.getOrCreateReplicaDB(domainDN, SERVER_ID_2, 1); </span><span class="cx">
</span><span class="cx"> // delete the domain directory created for the 2 \
replica DBs to break the </span><span class="cx"> // consistency with domain \
state file </span><del>- StaticUtils.recursiveDelete(new File(rootPath, \
"1.domain")); </del><ins>+ StaticUtils.recursiveDelete(new \
File(rootPath, "1.dom")); </ins><span class="cx">
</span><span class="cx"> environment.readOnDiskChangelogState();
</span><span class="cx"> }
</span><span class="lines">@@ -324,4 +327,50 @@
</span><span class="cx"> StaticUtils.close(cnDB, replicaDB, replicaDB2);
</span><span class="cx"> }
</span><span class="cx"> }
</span><ins>+
+ private ReplicationEnvironment createReplicationEnv(File rootPath) throws \
ChangelogException + {
+ ReplicationServer unusedReplicationServer = null;
+ return new ReplicationEnvironment(rootPath.getAbsolutePath(), \
unusedReplicationServer, TimeService.SYSTEM); + }
+
+ @Test
+ public void testLastRotationTimeRetrievalWithNoRotationFile() throws Exception
+ {
+ final File rootPath = new File(TEST_DIRECTORY_CHANGELOG);
+ TimeService time = mock(TimeService.class);
+ when(time.now()).thenReturn(100L);
+ ReplicationEnvironment environment = new \
ReplicationEnvironment(rootPath.getAbsolutePath(), null, time); +
+ assertThat(environment.getCnIndexDBLastRotationTime()).isEqualTo(100L);
+ }
+
+ @Test
+ public void testLastRotationTimeRetrievalWithRotationFile() throws Exception
+ {
+ final File rootPath = new File(TEST_DIRECTORY_CHANGELOG);
+ final TimeService time = mock(TimeService.class);
+ when(time.now()).thenReturn(100L, 200L);
+ ReplicationEnvironment environment = new \
ReplicationEnvironment(rootPath.getAbsolutePath(), null, time); + \
Log<Long,ChangeNumberIndexRecord> cnIndexDB = \
environment.getOrCreateCNIndexDB(); +
+ try {
+ environment.notifyLogFileRotation(cnIndexDB);
+
+ // check runtime change of last rotation time is effective
+ // this should also persist the time in a file, but this is checked later in \
the test + assertThat(environment.getCnIndexDBLastRotationTime()).isEqualTo(200L);
+ }
+ finally
+ {
+ cnIndexDB.close();
+ environment.shutdown();
+ }
+
+ // now check last rotation time is correctly read from persisted file when \
re-creating environment + when(time.now()).thenReturn(0L);
+ environment = new ReplicationEnvironment(rootPath.getAbsolutePath(), null, \
time); + assertThat(environment.getCnIndexDBLastRotationTime()).isEqualTo(200L);
+ }
+
</ins><span class="cx"> }
</span></span></pre>
</div>
</div>
<div id="footer">Copyright (c) by ForgeRock. All rights reserved.</div>
</body>
</html>
_______________________________________________
OpenDJ-dev mailing list
OpenDJ-dev@forgerock.org
https://lists.forgerock.org/mailman/listinfo/opendj-dev
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic