From 74ae8f26373f050f18bc0f0f4b3f0706a1f3f5be Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Thu, 04 Sep 2014 13:01:44 +0000
Subject: [PATCH] Checkpoint commit for OPENDJ-1206 : Create a new ReplicationBackend/ChangelogBackend to support cn=changelog CR-4439
---
opends/src/server/org/opends/server/backends/ChangelogBackend.java | 2
opends/src/server/org/opends/server/replication/server/ReplicationServer.java | 127 ++++++++++++++++++++++---------
opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java | 12 +++
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java | 6 +
opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java | 11 ++
opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java | 12 +++
opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java | 12 +++
7 files changed, 143 insertions(+), 39 deletions(-)
diff --git a/opends/src/server/org/opends/server/backends/ChangelogBackend.java b/opends/src/server/org/opends/server/backends/ChangelogBackend.java
index 11cc90f..9d7613f 100644
--- a/opends/src/server/org/opends/server/backends/ChangelogBackend.java
+++ b/opends/src/server/org/opends/server/backends/ChangelogBackend.java
@@ -1021,7 +1021,7 @@
final MultiDomainServerState state = searchParams.cookie;
if (state != null && !state.isEmpty())
{
- replicationServer.validateServerState(state, searchParams.getExcludedBaseDNs());
+ replicationServer.validateCookie(state, searchParams.getExcludedBaseDNs());
}
}
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationServer.java b/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
index 88ae89c..5c80530 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationServer.java
@@ -581,7 +581,7 @@
* parameter.
*
* @param baseDN
- * The base Dn for which the ReplicationServerDomain must be
+ * The base DN for which the ReplicationServerDomain must be
* returned.
* @return The ReplicationServerDomain associated to the base DN given in
* parameter.
@@ -604,70 +604,123 @@
}
/**
- * Validate that provided state is coherent with this replication server,
+ * Validate that provided cookie is coherent with this replication server,
* when ignoring the provided set of DNs.
* <p>
- * The state is coherent if and only if it exactly has the set of DNs corresponding to
- * the replication domains.
+ * The cookie is coherent if and only if it exactly has the set of DNs corresponding to
+ * the replication domains, and the states in the cookie are not older than oldest states
+ * in the server.
*
- * @param state
+ * @param cookie
* The multi domain state (cookie) to validate.
* @param ignoredBaseDNs
* The set of DNs to ignore when validating
* @throws DirectoryException
- * If the state is not valid
+ * If the cookie is not valid
*/
- public void validateServerState(MultiDomainServerState state, Set<DN> ignoredBaseDNs) throws DirectoryException
+ public void validateCookie(MultiDomainServerState cookie, Set<DN> ignoredBaseDNs) throws DirectoryException
{
- // Build the two sets of DNs to compare
- final Set<DN> activeServerDomains = new HashSet<DN>();
+ final Set<DN> activeDomains = getDNsOfActiveDomainsInServer(ignoredBaseDNs);
+ final Set<DN> cookieDomains = getDNsOfCookie(cookie);
+
+ checkNoActiveDomainIsMissingInCookie(cookie, activeDomains, cookieDomains);
+ checkNoUnknownDomainIsProvidedInCookie(cookie, activeDomains, cookieDomains);
+ checkCookieIsNotOutdated(cookie, activeDomains);
+ }
+
+ private Set<DN> getDNsOfCookie(MultiDomainServerState cookie)
+ {
+ final Set<DN> cookieDomains = new HashSet<DN>();
+ for (final DN dn : cookie)
+ {
+ cookieDomains.add(dn);
+ }
+ return cookieDomains;
+ }
+
+ private Set<DN> getDNsOfActiveDomainsInServer(final Set<DN> ignoredBaseDNs) throws DirectoryException
+ {
+ final Set<DN> activeDomains = new HashSet<DN>();
for (final DN dn : getDomainDNs(ignoredBaseDNs))
{
final ServerState lastServerState = getReplicationServerDomain(dn).getLatestServerState();
if (!lastServerState.isEmpty())
{
- activeServerDomains.add(dn);
+ activeDomains.add(dn);
}
}
- final Set<DN> stateDomains = new HashSet<DN>();
- for (final DN dn : state)
- {
- stateDomains.add(dn);
- }
+ return activeDomains;
+ }
- // The two sets of DNs are expected to be the same. Check this.
- final Set<DN> domainsCopy = new HashSet<DN>(activeServerDomains);
- final Set<DN> stateDomainsCopy = new HashSet<DN>(stateDomains);
- domainsCopy.removeAll(stateDomains);
- if (!domainsCopy.isEmpty())
+ private void checkNoUnknownDomainIsProvidedInCookie(final MultiDomainServerState cookie, final Set<DN> activeDomains,
+ final Set<DN> cookieDomains) throws DirectoryException
+ {
+ if (!activeDomains.containsAll(cookieDomains))
{
- final StringBuilder missingDomains = new StringBuilder();
- for (DN dn : domainsCopy)
- {
- missingDomains.append(dn).append(":;");
- }
- throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
- ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE.get(
- missingDomains, "<" + state.toString() + missingDomains + ">"));
- }
- stateDomainsCopy.removeAll(activeServerDomains);
- if (!stateDomainsCopy.isEmpty())
- {
- final StringBuilder startState = new StringBuilder();
- for (DN dn : activeServerDomains) {
- startState.append(dn).append(":").append(state.getServerState(dn).toString()).append(";");
+ final Set<DN> unknownCookieDomains = new HashSet<DN>(cookieDomains);
+ unknownCookieDomains.removeAll(activeDomains);
+ final StringBuilder currentStartingCookie = new StringBuilder();
+ for (DN domainDN : activeDomains) {
+ currentStartingCookie.append(domainDN).append(":").append(cookie.getServerState(domainDN)).append(";");
}
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
ERR_RESYNC_REQUIRED_UNKNOWN_DOMAIN_IN_PROVIDED_COOKIE.get(
- stateDomainsCopy.toString(), startState));
+ unknownCookieDomains.toString(), currentStartingCookie));
}
}
+ private void checkNoActiveDomainIsMissingInCookie(final MultiDomainServerState cookie, final Set<DN> activeDomains,
+ final Set<DN> cookieDomains) throws DirectoryException
+ {
+ if (!cookieDomains.containsAll(activeDomains))
+ {
+ final Set<DN> missingActiveDomains = new HashSet<DN>(activeDomains);
+ missingActiveDomains.removeAll(cookieDomains);
+ final StringBuilder missingDomains = new StringBuilder();
+ for (DN domainDN : missingActiveDomains)
+ {
+ missingDomains.append(domainDN).append(":;");
+ }
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE.get(
+ missingDomains, "<" + cookie + missingDomains + ">"));
+ }
+ }
+
+ private void checkCookieIsNotOutdated(final MultiDomainServerState cookie, final Set<DN> activeDomains)
+ throws DirectoryException
+ {
+ for (DN dn : activeDomains)
+ {
+ if (isCookieOutdatedForDomain(cookie, dn))
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ ERR_RESYNC_REQUIRED_TOO_OLD_DOMAIN_IN_PROVIDED_COOKIE.get(dn.toString()));
+ }
+ }
+ }
+
+ /** Check that provided cookie is not outdated compared to the oldest state of a domain. */
+ private boolean isCookieOutdatedForDomain(MultiDomainServerState cookie, DN domainDN)
+ {
+ final ServerState domainOldestState = getReplicationServerDomain(domainDN).getOldestState();
+ final ServerState providedState = cookie.getServerState(domainDN);
+ for (final CSN oldestCsn : domainOldestState)
+ {
+ final CSN providedCsn = providedState.getCSN(oldestCsn.getServerId());
+ if (providedCsn != null && providedCsn.isOlderThan(oldestCsn))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Get the ReplicationServerDomain associated to the base DN given in
* parameter.
*
- * @param baseDN The base Dn for which the ReplicationServerDomain must be
+ * @param baseDN The base DN for which the ReplicationServerDomain must be
* returned.
* @param create Specifies whether to create the ReplicationServerDomain if
* it does not already exist.
diff --git a/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java b/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
index 67195ef..4a21ff3 100644
--- a/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
+++ b/opends/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
@@ -2382,6 +2382,17 @@
return attributes;
}
+ /**
+ * Returns the oldest known state for the domain, made of the oldest CSN
+ * stored for each serverId.
+ *
+ * @return the start state of the domain.
+ */
+ public ServerState getOldestState()
+ {
+ return domainDB.getDomainOldestCSNs(baseDN);
+ }
+
private void sendTopologyMsg(String type, ServerHandler handler,
TopologyMsg msg)
{
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java b/opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java
index 32f9fba..f16b149 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/api/ReplicationDomainDB.java
@@ -43,6 +43,18 @@
{
/**
+ * Returns the oldest {@link CSN}s from the replicaDBs for each serverId in
+ * the specified replication domain.
+ *
+ * @param baseDN
+ * the replication domain baseDN
+ * @return a new ServerState object holding the {serverId => oldest CSN}
+ * mapping. If a replica DB is empty or closed, the oldest CSN will be
+ * null for that replica. The caller owns the generated ServerState.
+ */
+ ServerState getDomainOldestCSNs(DN baseDN);
+
+ /**
* Returns the newest {@link CSN}s from the replicaDBs for each serverId in
* the specified replication domain.
*
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java b/opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java
index 6458bac..1801956 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/file/FileChangelogDB.java
@@ -485,6 +485,18 @@
/** {@inheritDoc} */
@Override
+ public ServerState getDomainOldestCSNs(DN baseDN)
+ {
+ final ServerState result = new ServerState();
+ for (FileReplicaDB replicaDB : getDomainMap(baseDN).values())
+ {
+ result.update(replicaDB.getOldestCSN());
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
public ServerState getDomainNewestCSNs(DN baseDN)
{
final ServerState result = new ServerState();
diff --git a/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java b/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java
index 5f5ae2a..7200e00 100644
--- a/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java
+++ b/opends/src/server/org/opends/server/replication/server/changelog/je/JEChangelogDB.java
@@ -512,6 +512,18 @@
/** {@inheritDoc} */
@Override
+ public ServerState getDomainOldestCSNs(DN baseDN)
+ {
+ final ServerState result = new ServerState();
+ for (JEReplicaDB replicaDB : getDomainMap(baseDN).values())
+ {
+ result.update(replicaDB.getOldestCSN());
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
public ServerState getDomainNewestCSNs(DN baseDN)
{
final ServerState result = new ServerState();
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java
index fdade65..c8c2b18 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/ChangelogBackendTestCase.java
@@ -450,12 +450,16 @@
final String cookie2 = lastCookie + "o=test6:";
debugInfo(test, "Search with bad domain in cookie=" + cookie);
searchOp = searchChangelogUsingCookie("(targetDN=*" + test + "*,o=test)", cookie2, 0, UNWILLING_TO_PERFORM, test);
+ // the last cookie value may not match due to order of domain dn which is not guaranteed, so do not test it
+ String expectedError = ERR_RESYNC_REQUIRED_UNKNOWN_DOMAIN_IN_PROVIDED_COOKIE.get("[o=test6]", "")
+ .toString().replaceAll("<>", "");
+ assertThat(searchOp.getErrorMessage().toString()).startsWith(expectedError);
// test missing domain in provided cookie
final String cookie3 = lastCookie.substring(lastCookie.indexOf(';')+1);
debugInfo(test, "Search with bad domain in cookie=" + cookie);
searchOp = searchChangelogUsingCookie("(targetDN=*" + test + "*,o=test)", cookie3, 0, UNWILLING_TO_PERFORM, test);
- String expectedError = ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE
+ expectedError = ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE
.get("o=test:;","<"+ cookie3 + "o=test:;>").toString();
assertThat(searchOp.getErrorMessage().toString()).isEqualToIgnoringCase(expectedError);
}
--
Gitblit v1.10.0