mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

gbellato
03.35.2008 dd9090e630e82d58abdc1bfea933bbe02a3a2485
 fix for 2794 : Delete are sometimes not replayed when they immediately follow a moddn

When a delete is done on the new name of an entry immediately after renaming it,
the dependencies between these 2 operations are not detected and the delete
can therefore be replayed without waiting for the moddn to complete.
This cause the delete to fail and not being replayed.

These changes fix the dependency code and add a unit test case allowing
to reproduce the problem.
3 files modified
151 ■■■■■ changed files
opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java 6 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java 17 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java 128 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/plugin/RemotePendingChanges.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 *      Portions Copyright 2007-2008 Sun Microsystems, Inc.
 */
package org.opends.server.replication.plugin;
@@ -451,11 +451,13 @@
          }
          else if (pendingMsg instanceof ModifyDNMsg)
          {
            ModifyDNMsg pendingModDn = (ModifyDNMsg) pendingChange.getMsg();
            /*
             * Check if the operation to be run is an ModifyDNOperation
             * on a children of the current DeleteOperation
             */
            if (pendingChange.getTargetDN().isDescendantOf(targetDn))
            if ((pendingChange.getTargetDN().isDescendantOf(targetDn)) ||
                (pendingModDn.newDNIsParent(targetDn)))
            {
              hasDependencies = true;
              addDependency(change, pendingChange);
opends/src/server/org/opends/server/replication/protocol/ModifyDNMsg.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2008 Sun Microsystems, Inc.
 */
package org.opends.server.replication.protocol;
@@ -38,6 +38,7 @@
import org.opends.server.types.AbstractOperation;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.RDN;
import org.opends.server.types.operation.PostOperationModifyDNOperation;
/**
@@ -279,8 +280,18 @@
  {
    try
    {
      String newStringDN = newRDN + "," + newSuperior;
      DN newDN = DN.decode(newStringDN);
      DN newDN;
      if (newSuperior == null)
      {
        DN parentDn = DN.decode(this.getDn()).getParent();
        newDN = parentDn.concat(RDN.decode(newRDN));
      }
      else
      {
        String newStringDN = newRDN + "," + newSuperior;
        newDN = DN.decode(newStringDN);
      }
      if (newDN.isAncestorOf(targetDn))
        return true;
opends/tests/unit-tests-testng/src/server/org/opends/server/replication/DependencyTest.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 *      Portions Copyright 2007-2008 Sun Microsystems, Inc.
 */
package org.opends.server.replication;
@@ -250,6 +250,131 @@
  }
  /**
   * Check the dependency between moddn and delete operation
   * when an entry is renamed to a new dn and then deleted.
   */
  @SuppressWarnings("unchecked")
  @Test(enabled=true)
  public void moddnDelDependencyTest() throws Exception
  {
    ReplicationServer replServer = null;
    ReplicationDomain domain = null;
    DN baseDn = DN.decode(BASEDN_STRING);
    SynchronizationProvider replicationPlugin = null;
    short brokerId = 2;
    short serverId = 1;
    short replServerId = 1;
    cleanDB();
    try
    {
      //
      // Create replication server, replication domain and broker.
      //
      String entryldif = "dn:" + BASEDN_STRING + "\n"
      + "objectClass: top\n"
      + "objectClass: domain\n";
      Entry entry = TestCaseUtils.entryFromLdifString(entryldif);
      AttributeType uidType =
        DirectoryServer.getSchema().getAttributeType("entryuuid");
      ChangeNumberGenerator gen = new ChangeNumberGenerator(brokerId, 0L);
      int renamedEntryUuid = 100;
      // find  a free port for the replicationServer
      ServerSocket socket = TestCaseUtils.bindFreePort();
      int replServerPort = socket.getLocalPort();
      socket.close();
      replicationPlugin = new MultimasterReplication();
      DirectoryServer.registerSynchronizationProvider(replicationPlugin);
      ReplServerFakeConfiguration conf =
        new ReplServerFakeConfiguration(replServerPort, "addModDeldependency",
                                        0, replServerId, 0,
                                        200, null);
      replServer = new ReplicationServer(conf);
      // configure and start replication of dc=example,dc=com on the server
      SortedSet<String> replServers = new TreeSet<String>();
      replServers.add("localhost:"+replServerPort);
      DomainFakeCfg domainConf =
        new DomainFakeCfg(baseDn, serverId, replServers);
      domainConf.setHeartbeatInterval(100000);
      Thread.sleep(2000);
      domain = MultimasterReplication.createNewDomain(domainConf);
      ReplicationBroker broker =
        openReplicationSession(baseDn, brokerId, 1000, replServerPort, 1000,
                               false);
      // add an entry to play with.
      entry.removeAttribute(uidType);
      entry.addAttribute(new Attribute("entryuuid",
                         stringUID(renamedEntryUuid)),
                         new LinkedList<AttributeValue>());
      String addDn = "dc=moddndel" + "," + BASEDN_STRING;
      AddMsg addMsg =
        new AddMsg(gen.newChangeNumber(), addDn, stringUID(renamedEntryUuid),
                   stringUID(1),
                   entry.getObjectClassAttribute(),
                   entry.getAttributes(), null );
      broker.publish(addMsg);
      // check that the entry was correctly added
      boolean found =
        checkEntryHasAttribute(DN.decode(addDn), "entryuuid",
                               stringUID(renamedEntryUuid),
                               30000, true);
      assertTrue(found, "The initial entry add failed");
      // disable the domain to make sure that the messages are
      // all sent in a row.
      domain.disable();
      // rename and delete the entry.
      ModifyDNMsg moddnMsg =
        new ModifyDNMsg(addDn, gen.newChangeNumber(),
                        stringUID(renamedEntryUuid),
                        stringUID(1), true, null, "dc=new_name");
      broker.publish(moddnMsg);
      DeleteMsg delMsg =
        new DeleteMsg("dc=new_name" + "," + BASEDN_STRING,
                      gen.newChangeNumber(), stringUID(renamedEntryUuid));
      broker.publish(delMsg);
      // enable back the domain to trigger message replay.
      domain.enable();
      // check that entry does not exist anymore.
      Thread.sleep(10000);
      found = checkEntryHasAttribute(DN.decode("dc=new_name" + "," + BASEDN_STRING),
                                     "entryuuid",
                                     stringUID(renamedEntryUuid),
                                     30000, false);
      assertFalse(found, "The delete dependencies was not correctly enforced");
    }
    finally
    {
      if (replServer != null)
        replServer.shutdown();
      if (domain != null)
        MultimasterReplication.deleteDomain(baseDn);
      if (replicationPlugin != null)
        DirectoryServer.deregisterSynchronizationProvider(replicationPlugin);
    }
  }
  /**
   * Clean the database and replace with a single entry.
   *
   * @throws FileNotFoundException
@@ -286,6 +411,7 @@
          + "ds-task-import-reject-file: " + path + "reject\n");
  }
  /**
   * Check that after a sequence of add/del/add done on the same DN
   * the second entry is in the database.