From 947ae41c50aeb216d43274502eb5fca152351aa4 Mon Sep 17 00:00:00 2001
From: ludovicp <ludovicp@localhost>
Date: Mon, 07 Jun 2010 09:55:29 +0000
Subject: [PATCH] Fix issue #3404. Conflicting entries are now indexed by ds-sync-conflict attribute. This index is only updated when conflicts are detected and searched on for all deletes and modDN operations. Tested successfully against performance regression.
---
opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 156 insertions(+), 7 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index 04094a0..3b45c55 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -2411,6 +2411,148 @@
pendingChanges.pushCommittedChanges();
}
}
+
+ checkForClearedConflict(op);
+ }
+
+ /**
+ * Check if the operation that just happened has cleared a conflict :
+ * Clearing a conflict happens if the operation has free a DN that
+ * for which an other entry was in conflict.
+ */
+ private void checkForClearedConflict(PostOperationOperation op)
+ {
+ OperationType type = op.getOperationType();
+ if (op.getResultCode() != ResultCode.SUCCESS)
+ {
+ // those operations cannot have cleared a conflict
+ return;
+ }
+
+ DN targetDN;
+ if (type == OperationType.DELETE)
+ {
+ targetDN = ((PostOperationDeleteOperation) op).getEntryDN();
+ }
+ else if (type == OperationType.MODIFY_DN)
+ {
+ targetDN = ((PostOperationModifyDNOperation) op).getEntryDN();
+ }
+ else
+ {
+ return;
+ }
+
+ LDAPFilter filter = null;
+ try
+ {
+ filter = LDAPFilter.decode(
+ DS_SYNC_CONFLICT + "=" + targetDN.toNormalizedString());
+ } catch (LDAPException e)
+ {
+ // Not possible. We know the filter just above is correct.
+ }
+
+ LinkedHashSet<String> attrs = new LinkedHashSet<String>(1);
+ attrs.add(Historical.HISTORICALATTRIBUTENAME);
+ attrs.add(Historical.ENTRYUIDNAME);
+ attrs.add("*");
+ InternalSearchOperation searchOp = conn.processSearch(
+ ByteString.valueOf(baseDn.toString()),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES,
+ 0, 0, false, filter,
+ attrs, null);
+
+ LinkedList<SearchResultEntry> entries = searchOp.getSearchEntries();
+ Entry entrytoRename = null;
+ ChangeNumber entrytoRenameDate = null;
+ for (SearchResultEntry entry : entries)
+ {
+ Historical history = Historical.load(entry);
+ if (entrytoRename == null)
+ {
+ entrytoRename = entry;
+ entrytoRenameDate = history.getDNDate();
+ }
+ else if (!history.AddedOrRenamedAfter(entrytoRenameDate))
+ {
+ // this conflict is older than the previous, keep it.
+ entrytoRename = entry;
+ entrytoRenameDate = history.getDNDate();
+ }
+ }
+
+ if (entrytoRename != null)
+ {
+ DN entryDN = entrytoRename.getDN();
+ ModifyDNOperationBasis newOp = renameEntry(
+ entryDN, targetDN.getRDN(), targetDN.getParent(), false);
+
+ ResultCode res = newOp.getResultCode();
+ if (res != ResultCode.SUCCESS)
+ {
+ Message message =
+ ERR_COULD_NOT_SOLVE_CONFLICT.get(entryDN.toString(), res.toString());
+ logError(message);
+ }
+ }
+ }
+
+ /**
+ * Rename an Entry Using a synchronization, non-replicated operation.
+ * This method should be used instead of the InternalConnection methods
+ * when the operation that need to be run must be local only and therefore
+ * not replicated to the RS.
+ *
+ * @param targetDN The DN of the entry to rename.
+ * @param newRDN The new RDN to be used.
+ * @param parentDN The parentDN to be used.
+ * @param markConflict A boolean indicating is this entry should be marked
+ * as a conflicting entry. In such case the
+ * DS_SYNC_CONFLICT attribute will be added to the entry
+ * with the value of its original DN.
+ * If false, the DS_SYNC_CONFLICT attribute will be
+ * cleared.
+ *
+ * @return The operation that was run to rename the entry.
+ */
+ private ModifyDNOperationBasis renameEntry(
+ DN targetDN, RDN newRDN, DN parentDN, boolean markConflict)
+ {
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+
+ ModifyDNOperationBasis newOp =
+ new ModifyDNOperationBasis(
+ conn, InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(), new ArrayList<Control>(0),
+ targetDN, newRDN, false,
+ parentDN);
+ newOp.setInternalOperation(true);
+ newOp.setSynchronizationOperation(true);
+ newOp.setDontSynchronize(true);
+
+ if (markConflict)
+ {
+ AttributeType attrType =
+ DirectoryServer.getAttributeType(DS_SYNC_CONFLICT, true);
+ Attribute attr = Attributes.create(attrType, AttributeValues.create(
+ attrType, targetDN.toString()));
+ Modification mod = new Modification(ModificationType.REPLACE, attr);
+ newOp.addModification(mod);
+ }
+ else
+ {
+ AttributeType attrType =
+ DirectoryServer.getAttributeType(DS_SYNC_CONFLICT, true);
+ Attribute attr = Attributes.empty(attrType);
+ Modification mod = new Modification(ModificationType.DELETE, attr);
+ newOp.addModification(mod);
+ }
+
+ newOp.run();
+ return newOp;
}
/**
@@ -3188,7 +3330,6 @@
* and keep the entry as a conflicting entry,
*/
conflict = true;
- markConflictEntry(conflictOp, entry.getDN(), entryDN);
renameConflictEntry(conflictOp, entry.getDN(),
Historical.getEntryUuid(entry));
}
@@ -3233,11 +3374,8 @@
*/
private void renameConflictEntry(Operation conflictOp, DN dn, String uid)
{
- InternalClientConnection conn =
- InternalClientConnection.getRootConnection();
-
- ModifyDNOperation newOp = conn.processModifyDN(
- dn, generateDeleteConflictDn(uid, dn),false, baseDn);
+ ModifyDNOperation newOp =
+ renameEntry(dn, generateDeleteConflictDn(uid, dn), baseDn, true);
if (newOp.getResultCode() != ResultCode.SUCCESS)
{
@@ -3275,7 +3413,18 @@
List<Modification> mods = new ArrayList<Modification>();
Modification mod = new Modification(ModificationType.REPLACE, attr);
mods.add(mod);
- ModifyOperation newOp = conn.processModify(currentDN, mods);
+
+ ModifyOperationBasis newOp =
+ new ModifyOperationBasis(
+ conn, InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(), new ArrayList<Control>(0),
+ currentDN, mods);
+ newOp.setInternalOperation(true);
+ newOp.setSynchronizationOperation(true);
+ newOp.setDontSynchronize(true);
+
+ newOp.run();
+
if (newOp.getResultCode() != ResultCode.SUCCESS)
{
// Log information for the repair tool.
--
Gitblit v1.10.0