| | |
| | | * The name of the attribute in a purge conflicts historical task definition |
| | | * that specifies the maximum duration of the task. |
| | | */ |
| | | public static final String ATTR_TASK_CONFLICTS_HIST_PURGE_FIRST_CN = |
| | | public static final String ATTR_TASK_CONFLICTS_HIST_PURGE_FIRST_CSN = |
| | | NAME_PREFIX_TASK + "purge-conflicts-historical-first-purged-changenumber"; |
| | | |
| | | /** |
| | | * The name of the attribute in a purge conflicts historical task definition |
| | | * that specifies the maximum duration of the task. |
| | | */ |
| | | public static final String ATTR_TASK_CONFLICTS_HIST_PURGE_LAST_CN = |
| | | public static final String ATTR_TASK_CONFLICTS_HIST_PURGE_LAST_CSN = |
| | | NAME_PREFIX_TASK + "purge-conflicts-historical-last-purged-changenumber"; |
| | | |
| | | /** |
| | |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2012 ForgeRock AS |
| | | * Portions Copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.plugins; |
| | | |
| | |
| | | import org.opends.server.api.plugin.PluginResult; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.protocols.asn1.ASN1Writer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.OperationContext; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | import org.opends.server.types.Control; |
| | |
| | | import org.opends.server.types.operation.PostOperationModifyDNOperation; |
| | | import org.opends.server.types.operation.PostOperationModifyOperation; |
| | | import org.opends.server.types.operation.PostOperationOperation; |
| | | |
| | | import static org.opends.messages.PluginMessages.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | |
| | | /** |
| | | * This class implements a Directory Server plugin that will add the |
| | | * replication CSN to a response whenever the CSN control is received. |
| | |
| | | implements ConfigurationChangeListener<ChangeNumberControlPluginCfg> |
| | | { |
| | | |
| | | // The current configuration for this plugin. |
| | | /** The current configuration for this plugin. */ |
| | | private ChangeNumberControlPluginCfg currentConfig; |
| | | |
| | | /** |
| | | * The control used by this plugin. |
| | | */ |
| | | /** The control used by this plugin. */ |
| | | public static class ChangeNumberControl extends Control |
| | | { |
| | | private ChangeNumber cn; |
| | | private CSN csn; |
| | | |
| | | /** |
| | | * Constructs a new change number control. |
| | | * |
| | | * @param isCritical Indicates whether support for this control should be |
| | | * considered a critical part of the server processing. |
| | | * @param cn The change number. |
| | | * @param isCritical Indicates whether support for this control should be |
| | | * considered a critical part of the server processing. |
| | | * @param csn The CSN. |
| | | */ |
| | | public ChangeNumberControl(boolean isCritical, ChangeNumber cn) |
| | | public ChangeNumberControl(boolean isCritical, CSN csn) |
| | | { |
| | | super(OID_CSN_CONTROL, isCritical); |
| | | this.cn = cn; |
| | | this.csn = csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @throws IOException If a problem occurs while writing to the stream. |
| | | */ |
| | | protected void writeValue(ASN1Writer writer) throws IOException { |
| | | writer.writeOctetString(cn.toString()); |
| | | writer.writeOctetString(csn.toString()); |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the change number. |
| | | * Retrieves the CSN. |
| | | * |
| | | * @return The change number. |
| | | * @return The CSN. |
| | | */ |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | return cn; |
| | | return csn; |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the Change number from the synchronization context |
| | | * and sets the control response in the operation. |
| | | * Retrieves the CSN from the synchronization context and sets the control |
| | | * response in the operation. |
| | | * |
| | | * @param operation the operation |
| | | */ |
| | |
| | | OperationContext ctx = (OperationContext) |
| | | operation.getAttachment(OperationContext.SYNCHROCONTEXT); |
| | | if (ctx != null) { |
| | | ChangeNumber cn = ctx.getChangeNumber(); |
| | | CSN cn = ctx.getCSN(); |
| | | if (cn != null) { |
| | | Control responseControl = |
| | | new ChangeNumberControl(c.isCritical(), cn); |
| File was renamed from opends/src/server/org/opends/server/replication/common/ChangeNumber.java |
| | |
| | | */ |
| | | package org.opends.server.replication.common; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | |
| | | import org.opends.server.types.ByteSequence; |
| | |
| | | import org.opends.server.types.ByteStringBuilder; |
| | | |
| | | /** |
| | | * Class used to represent Change Numbers. |
| | | * Class used to represent Change Sequence Numbers. |
| | | */ |
| | | public class ChangeNumber implements java.io.Serializable, |
| | | java.lang.Comparable<ChangeNumber> |
| | | public class CSN implements Serializable, Comparable<CSN> |
| | | { |
| | | /** |
| | | * The number of bytes used by the byte string representation of a change |
| | |
| | | private final int serverId; |
| | | |
| | | /** |
| | | * Parses the provided {@link #toString()} representation of a change number. |
| | | * Parses the provided {@link #toString()} representation of a CSN. |
| | | * |
| | | * @param s |
| | | * The string to be parsed. |
| | | * @return The parsed change number. |
| | | * @return The parsed CSN. |
| | | * @see #toString() |
| | | */ |
| | | public static ChangeNumber valueOf(String s) |
| | | public static CSN valueOf(String s) |
| | | { |
| | | return new ChangeNumber(s); |
| | | return new CSN(s); |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @param bs |
| | | * The byte sequence to be parsed. |
| | | * @return The decoded change number. |
| | | * @return The decoded CSN. |
| | | * @see #toByteString() |
| | | */ |
| | | public static ChangeNumber valueOf(ByteSequence bs) |
| | | public static CSN valueOf(ByteSequence bs) |
| | | { |
| | | ByteSequenceReader reader = bs.asReader(); |
| | | long timeStamp = reader.getLong(); |
| | | int serverId = reader.getShort() & 0xffff; |
| | | int seqnum = reader.getInt(); |
| | | return new ChangeNumber(timeStamp, seqnum, serverId); |
| | | return new CSN(timeStamp, seqnum, serverId); |
| | | } |
| | | |
| | | /** |
| | | * Create a new ChangeNumber from a String. |
| | | * Create a new {@link CSN} from a String. |
| | | * |
| | | * @param str the string from which to create a ChangeNumber |
| | | * @param str the string from which to create a {@link CSN} |
| | | */ |
| | | public ChangeNumber(String str) |
| | | public CSN(String str) |
| | | { |
| | | String temp = str.substring(0, 16); |
| | | timeStamp = Long.parseLong(temp, 16); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Create a new ChangeNumber. |
| | | * Create a new {@link CSN}. |
| | | * |
| | | * @param timeStamp timeStamp for the ChangeNumber |
| | | * @param timeStamp timeStamp for the {@link CSN} |
| | | * @param seqNum sequence number |
| | | * @param serverId identity of server |
| | | */ |
| | | public ChangeNumber(long timeStamp, int seqNum, int serverId) |
| | | public CSN(long timeStamp, int seqNum, int serverId) |
| | | { |
| | | this.serverId = serverId; |
| | | this.timeStamp = timeStamp; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the timestamp associated to this ChangeNumber in seconds. |
| | | * @return timestamp associated to this ChangeNumber in seconds |
| | | * Get the timestamp associated to this {@link CSN} in seconds. |
| | | * @return timestamp associated to this {@link CSN} in seconds |
| | | */ |
| | | public long getTimeSec() |
| | | { |
| | |
| | | @Override |
| | | public boolean equals(Object obj) |
| | | { |
| | | if (obj instanceof ChangeNumber) |
| | | if (obj instanceof CSN) |
| | | { |
| | | ChangeNumber cn = (ChangeNumber) obj; |
| | | return this.seqnum == cn.seqnum && |
| | | this.serverId == cn.serverId && |
| | | this.timeStamp == cn.timeStamp; |
| | | CSN csn = (CSN) obj; |
| | | return this.seqnum == csn.seqnum && |
| | | this.serverId == csn.serverId && |
| | | this.timeStamp == csn.timeStamp; |
| | | } |
| | | return false; |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * Encodes this change number as a byte string. |
| | | * Encodes this CSN as a byte string. |
| | | * <p> |
| | | * NOTE: this representation must not be modified otherwise interop with |
| | | * earlier protocol versions will be broken. |
| | | * |
| | | * @return The encoded representation of this change number. |
| | | * @return The encoded representation of this CSN. |
| | | * @see #valueOf(ByteSequence) |
| | | */ |
| | | public ByteString toByteString() |
| | |
| | | } |
| | | |
| | | /** |
| | | * Encodes this change number into the provided byte string builder. |
| | | * Encodes this CSN into the provided byte string builder. |
| | | * <p> |
| | | * NOTE: this representation must not be modified otherwise interop with |
| | | * earlier protocol versions will be broken. |
| | | * |
| | | * @param builder |
| | | * The byte string builder. |
| | | * @return The byte string builder containing the encoded change number. |
| | | * @return The byte string builder containing the encoded CSN. |
| | | * @see #valueOf(ByteSequence) |
| | | */ |
| | | public ByteStringBuilder toByteString(ByteStringBuilder builder) |
| | |
| | | } |
| | | |
| | | /** |
| | | * Convert the ChangeNumber to a printable String. |
| | | * Convert the {@link CSN} to a printable String. |
| | | * <p> |
| | | * NOTE: this representation must not be modified otherwise interop with |
| | | * earlier protocol versions will be broken. |
| | |
| | | } |
| | | |
| | | /** |
| | | * Convert the ChangeNumber to a printable String with a user friendly |
| | | * Convert the {@link CSN} to a printable String with a user friendly |
| | | * format. |
| | | * |
| | | * @return the string |
| | |
| | | } |
| | | |
| | | /** |
| | | * Compares 2 ChangeNumber. |
| | | * @param CN1 the first ChangeNumber to compare |
| | | * @param CN2 the second ChangeNumber to compare |
| | | * @return value 0 if changeNumber matches, negative if first |
| | | * changeNumber is smaller, positive otherwise |
| | | * Compares 2 {@link CSN}. |
| | | * @param csn1 the first {@link CSN} to compare |
| | | * @param csn2 the second {@link CSN} to compare |
| | | * @return value 0 if CSN matches, negative if first |
| | | * CSN is smaller, positive otherwise |
| | | */ |
| | | public static int compare(ChangeNumber CN1, ChangeNumber CN2) |
| | | public static int compare(CSN csn1, CSN csn2) |
| | | { |
| | | if (CN1 == null) |
| | | if (csn1 == null) |
| | | { |
| | | if (CN2 == null) |
| | | if (csn2 == null) |
| | | return 0; |
| | | return -1; |
| | | } |
| | | else if (CN2 == null) |
| | | else if (csn2 == null) |
| | | return 1; |
| | | else if (CN1.timeStamp < CN2.timeStamp) |
| | | else if (csn1.timeStamp < csn2.timeStamp) |
| | | return -1; |
| | | else if (CN2.timeStamp < CN1.timeStamp) |
| | | else if (csn2.timeStamp < csn1.timeStamp) |
| | | return 1; |
| | | else |
| | | { |
| | | // timestamps are equals compare seqnums |
| | | if (CN1.seqnum < CN2.seqnum) |
| | | if (csn1.seqnum < csn2.seqnum) |
| | | return -1; |
| | | else if (CN2.seqnum < CN1.seqnum) |
| | | else if (csn2.seqnum < csn1.seqnum) |
| | | return 1; |
| | | else |
| | | { |
| | | // timestamp and seqnum are equals compare serverIds |
| | | if (CN1.serverId < CN2.serverId) |
| | | if (csn1.serverId < csn2.serverId) |
| | | return -1; |
| | | else if (CN2.serverId < CN1.serverId) |
| | | else if (csn2.serverId < csn1.serverId) |
| | | return 1; |
| | | |
| | | // if we get here ChangeNumber are equals |
| | | // if we get here {@link CSN} are equals |
| | | return 0; |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Computes the difference in number of changes between 2 |
| | | * change numbers. First one is expected to be newer than second one. If this |
| | | * is not the case, 0 will be returned. |
| | | * @param op1 the first ChangeNumber |
| | | * @param op2 the second ChangeNumber |
| | | * @return the difference |
| | | */ |
| | | public static int diffSeqNum(ChangeNumber op1, ChangeNumber op2) |
| | | /** |
| | | * Computes the difference in number of changes between 2 CSNs. First one is |
| | | * expected to be newer than second one. If this is not the case, 0 will be |
| | | * returned. |
| | | * |
| | | * @param csn1 |
| | | * the first {@link CSN} |
| | | * @param csn2 |
| | | * the second {@link CSN} |
| | | * @return the difference |
| | | */ |
| | | public static int diffSeqNum(CSN csn1, CSN csn2) |
| | | { |
| | | if (op1 == null) |
| | | if (csn1 == null) |
| | | { |
| | | return 0; |
| | | } |
| | | if (op2 == null) |
| | | if (csn2 == null) |
| | | { |
| | | return op1.getSeqnum(); |
| | | return csn1.getSeqnum(); |
| | | } |
| | | if (op2.newerOrEquals(op1)) |
| | | if (csn2.newerOrEquals(csn1)) |
| | | { |
| | | return 0; |
| | | } |
| | | |
| | | int seqnum1 = op1.getSeqnum(); |
| | | long time1 = op1.getTime(); |
| | | int seqnum2 = op2.getSeqnum(); |
| | | long time2 = op2.getTime(); |
| | | int seqnum1 = csn1.getSeqnum(); |
| | | long time1 = csn1.getTime(); |
| | | int seqnum2 = csn2.getSeqnum(); |
| | | long time2 = csn2.getTime(); |
| | | |
| | | if (time2 <= time1) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * check if the current Object is strictly older than ChangeNumber |
| | | * check if the current Object is strictly older than {@link CSN} |
| | | * given in parameter. |
| | | * @param CN the ChangeNumber to compare with |
| | | * @param csn the {@link CSN} to compare with |
| | | * @return true if strictly older, false if younger or same |
| | | */ |
| | | public boolean older(ChangeNumber CN) |
| | | public boolean older(CSN csn) |
| | | { |
| | | return compare(this, CN) < 0; |
| | | return compare(this, csn) < 0; |
| | | } |
| | | |
| | | /** |
| | | * check if the current Object is older than ChangeNumber |
| | | * check if the current Object is older than {@link CSN} |
| | | * given in parameter. |
| | | * @param CN the ChangeNumber to compare with |
| | | * @param csn the {@link CSN} to compare with |
| | | * @return true if older or equal, false if younger |
| | | */ |
| | | public boolean olderOrEqual(ChangeNumber CN) |
| | | public boolean olderOrEqual(CSN csn) |
| | | { |
| | | return compare(this, CN) <= 0; |
| | | return compare(this, csn) <= 0; |
| | | } |
| | | |
| | | /** |
| | | * Check if the current Object is newer than ChangeNumber. |
| | | * @param CN the ChangeNumber to compare with |
| | | * Check if the current Object is newer than {@link CSN}. |
| | | * @param csn the {@link CSN} to compare with |
| | | * @return true if newer |
| | | */ |
| | | public boolean newerOrEquals(ChangeNumber CN) |
| | | public boolean newerOrEquals(CSN csn) |
| | | { |
| | | return compare(this, CN) >= 0; |
| | | return compare(this, csn) >= 0; |
| | | } |
| | | |
| | | /** |
| | | * Check if the current Object is strictly newer than ChangeNumber. |
| | | * @param CN the ChangeNumber to compare with |
| | | * Check if the current Object is strictly newer than {@link CSN}. |
| | | * @param csn the {@link CSN} to compare with |
| | | * @return true if strictly newer |
| | | */ |
| | | public boolean newer(ChangeNumber CN) |
| | | public boolean newer(CSN csn) |
| | | { |
| | | return compare(this, CN) > 0; |
| | | return compare(this, csn) > 0; |
| | | } |
| | | |
| | | /** |
| | | * Compares this object with the specified object for order. |
| | | * @param cn the ChangeNumber to compare with. |
| | | * @param csn the {@link CSN} to compare with. |
| | | * @return a negative integer, zero, or a positive integer as this object |
| | | * is less than, equal to, or greater than the specified object. |
| | | */ |
| | | @Override |
| | | public int compareTo(ChangeNumber cn) |
| | | public int compareTo(CSN csn) |
| | | { |
| | | return compare(this, cn); |
| | | return compare(this, csn); |
| | | } |
| | | } |
| File was renamed from opends/src/server/org/opends/server/replication/common/ChangeNumberGenerator.java |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011 ForgeRock AS |
| | | * Portions Copyright 2011-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.common; |
| | | |
| | | import org.opends.server.util.TimeThread; |
| | | |
| | | /** |
| | | * This class defines a structure that is used for storing the |
| | | * last change numbers generated on this server or received from other servers |
| | | * and generating new changenumbers that are guaranteed to be larger than |
| | | * all the previously seen or generated change numbers. |
| | | * This class defines a structure that is used for storing the last {@link CSN}s |
| | | * generated on this server or received from other servers and generating new |
| | | * {@link CSN}s that are guaranteed to be larger than all the previously seen or |
| | | * generated CSNs. |
| | | */ |
| | | public class ChangeNumberGenerator |
| | | public class CSNGenerator |
| | | { |
| | | private long lastTime; |
| | | private int seqnum; |
| | | private int serverId; |
| | | |
| | | /** |
| | | * Create a new ChangeNumber Generator. |
| | | * @param serverID2 id to use when creating change numbers. |
| | | * Create a new {@link CSNGenerator}. |
| | | * @param serverID2 id to use when creating {@link CSN}s. |
| | | * @param timestamp time to start with. |
| | | */ |
| | | public ChangeNumberGenerator(int serverID2, long timestamp) |
| | | public CSNGenerator(int serverID2, long timestamp) |
| | | { |
| | | this.lastTime = timestamp; |
| | | this.serverId = serverID2; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Create a new ChangeNumber Generator. |
| | | * Create a new {@link CSNGenerator}. |
| | | * |
| | | * @param id id to use when creating change numbers. |
| | | * @param id id to use when creating {@link CSN}s. |
| | | * @param state This generator will be created in a way that makes sure that |
| | | * all change numbers generated will be larger than all the |
| | | * changenumbers currently in state. |
| | | * all {@link CSN}s generated will be larger than all the |
| | | * {@link CSN}s currently in state. |
| | | */ |
| | | public ChangeNumberGenerator(int id, ServerState state) |
| | | public CSNGenerator(int id, ServerState state) |
| | | { |
| | | this.lastTime = TimeThread.getTime(); |
| | | for (int stateId : state) |
| | | { |
| | | if (this.lastTime < state.getChangeNumber(stateId).getTime()) |
| | | this.lastTime = state.getChangeNumber(stateId).getTime(); |
| | | if (this.lastTime < state.getCSN(stateId).getTime()) |
| | | this.lastTime = state.getCSN(stateId).getTime(); |
| | | if (stateId == id) |
| | | this.seqnum = state.getChangeNumber(id).getSeqnum(); |
| | | this.seqnum = state.getCSN(id).getSeqnum(); |
| | | } |
| | | this.serverId = id; |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Generate a new ChangeNumber. |
| | | * Generate a new {@link CSN}. |
| | | * |
| | | * @return the generated ChangeNUmber |
| | | * @return the generated {@link CSN} |
| | | */ |
| | | public ChangeNumber newChangeNumber() |
| | | public CSN newCSN() |
| | | { |
| | | long curTime = TimeThread.getTime(); |
| | | int mySeqnum; |
| | |
| | | myTime = lastTime; |
| | | } |
| | | |
| | | return new ChangeNumber(myTime, mySeqnum, serverId); |
| | | return new CSN(myTime, mySeqnum, serverId); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Adjust the lastTime of this Changenumber generator with |
| | | * a ChangeNumber that we have received from another server. |
| | | * This is necessary because we need that the changenumber generated |
| | | * after processing an update received from other hosts to be larger |
| | | * than the received changenumber |
| | | * Adjust the lastTime of this {@link CSNGenerator} with a {@link CSN} that we |
| | | * have received from another server. |
| | | * <p> |
| | | * This is necessary because we need that the {@link CSN} generated after |
| | | * processing an update received from other hosts to be larger than the |
| | | * received {@link CSN} |
| | | * |
| | | * @param number the ChangeNumber to adjust with |
| | | * @param number |
| | | * the {@link CSN} to adjust with |
| | | */ |
| | | public void adjust(ChangeNumber number) |
| | | public void adjust(CSN number) |
| | | { |
| | | if (number==null) |
| | | { |
| | |
| | | int changeServerId = number.getServerId(); |
| | | int changeSeqNum = number.getSeqnum(); |
| | | |
| | | /* need to synchronize with NewChangeNumber method so that we |
| | | * protect writing lastTime fields |
| | | /* |
| | | * need to synchronize with newCSN method so that we protect writing |
| | | * lastTime fields |
| | | */ |
| | | synchronized(this) |
| | | { |
| | |
| | | { |
| | | for (int localServerId : state) |
| | | { |
| | | adjust(state.getChangeNumber(localServerId)); |
| | | adjust(state.getCSN(localServerId)); |
| | | } |
| | | } |
| | | } |
| | |
| | | ReplicationServer rs = eclwe.getReplicationServer(); |
| | | rs.disableEligibility(excludedDomains); |
| | | int[] limits = rs.getECLDraftCNLimits( |
| | | rs.getEligibleCN(), excludedDomains); |
| | | rs.getEligibleCSN(), excludedDomains); |
| | | |
| | | first = String.valueOf(limits[0]); |
| | | } |
| | |
| | | ReplicationServer rs = eclwe.getReplicationServer(); |
| | | rs.disableEligibility(excludedDomains); |
| | | int[] limits = rs.getECLDraftCNLimits( |
| | | rs.getEligibleCN(), excludedDomains); |
| | | rs.getEligibleCSN(), excludedDomains); |
| | | |
| | | last = String.valueOf(limits[1]); |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * Update the ServerState of the provided baseDN with the |
| | | * replication change number provided. |
| | | * Update the ServerState of the provided baseDN with the replication |
| | | * {@link CSN} provided. |
| | | * |
| | | * @param baseDN The provided baseDN. |
| | | * @param changeNumber The provided ChangeNumber. |
| | | * @param csn The provided CSN. |
| | | * |
| | | * @return a boolean indicating if the update was meaningful. |
| | | */ |
| | | public boolean update(String baseDN, ChangeNumber changeNumber) |
| | | public boolean update(String baseDN, CSN csn) |
| | | { |
| | | if (changeNumber == null) |
| | | if (csn == null) |
| | | return false; |
| | | |
| | | synchronized(this) |
| | | { |
| | | int serverId = changeNumber.getServerId(); |
| | | int serverId = csn.getServerId(); |
| | | ServerState oldServerState = list.get(baseDN); |
| | | if (oldServerState == null) |
| | | oldServerState = new ServerState(); |
| | | |
| | | if (changeNumber.newer(oldServerState.getChangeNumber(serverId))) |
| | | if (csn.newer(oldServerState.getCSN(serverId))) |
| | | { |
| | | oldServerState.update(changeNumber); |
| | | oldServerState.update(csn); |
| | | list.put(baseDN, oldServerState); |
| | | return true; |
| | | } |
| | |
| | | String[] domains = multidomainserverstate.split(";"); |
| | | for (String domain : domains) |
| | | { |
| | | // For each domain, split the changenumbers by server |
| | | // For each domain, split the CSNs by server |
| | | // and build a server state (SHOULD BE OPTIMIZED) |
| | | ServerState serverStateByDomain = new ServerState(); |
| | | |
| | |
| | | if (fields.length > 1) |
| | | { |
| | | String strState = fields[1]; |
| | | String[] strCN = strState.split(" "); |
| | | for (String sr : strCN) |
| | | String[] strCSN = strState.split(" "); |
| | | for (String sr : strCSN) |
| | | { |
| | | ChangeNumber fromChangeNumber = new ChangeNumber(sr); |
| | | serverStateByDomain.update(fromChangeNumber); |
| | | CSN fromCSN = new CSN(sr); |
| | | serverStateByDomain.update(fromCSN); |
| | | } |
| | | } |
| | | startStates.put(domainBaseDN, serverStateByDomain); |
| | |
| | | import org.opends.server.types.ByteString; |
| | | |
| | | /** |
| | | * This class is used to associate serverIds with ChangeNumbers. |
| | | * This class is used to associate serverIds with {@link CSN}s. |
| | | * <p> |
| | | * For example, it is exchanged with the replication servers at connection |
| | | * establishment time to communicate |
| | | * "which ChangeNumbers last seen by a serverId" |
| | | * establishment time to communicate "which CSNs was last seen by a serverId". |
| | | */ |
| | | public class ServerState implements Iterable<Integer> |
| | | { |
| | | |
| | | /** Associates a serverId with a ChangeNumber. */ |
| | | private final Map<Integer, ChangeNumber> serverIdToChangeNumber = |
| | | new HashMap<Integer, ChangeNumber>(); |
| | | /** Associates a serverId with a CSN. */ |
| | | private final Map<Integer, CSN> serverIdToCSN = new HashMap<Integer, CSN>(); |
| | | /** |
| | | * Whether the state has been saved to persistent storage. It starts at true, |
| | | * and moves to false when an update is made to the current object. |
| | |
| | | */ |
| | | public void clear() |
| | | { |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | serverIdToChangeNumber.clear(); |
| | | serverIdToCSN.clear(); |
| | | } |
| | | } |
| | | |
| | |
| | | { |
| | | while (endpos > pos) |
| | | { |
| | | // FIXME JNR: why store the serverId separately from the changeNumber |
| | | // since the changeNumber already contains the serverId? |
| | | // FIXME JNR: why store the serverId separately from the CSN since the |
| | | // CSN already contains the serverId? |
| | | |
| | | // read the ServerId |
| | | int length = getNextLength(in, pos); |
| | |
| | | int serverId = Integer.valueOf(serverIdString); |
| | | pos += length +1; |
| | | |
| | | // read the ChangeNumber |
| | | // read the CSN |
| | | length = getNextLength(in, pos); |
| | | String cnString = new String(in, pos, length, "UTF-8"); |
| | | ChangeNumber cn = new ChangeNumber(cnString); |
| | | String csnString = new String(in, pos, length, "UTF-8"); |
| | | CSN csn = new CSN(csnString); |
| | | pos += length +1; |
| | | |
| | | // Add the serverId |
| | | serverIdToChangeNumber.put(serverId, cn); |
| | | serverIdToCSN.put(serverId, csn); |
| | | } |
| | | } catch (UnsupportedEncodingException e) |
| | | { |
| | |
| | | |
| | | /** |
| | | * Get the length of the next String encoded in the in byte array. |
| | | * This method is used to cut the different parts (server ids, change number) |
| | | * This method is used to cut the different parts (serverIds, CSN) |
| | | * of a server state. |
| | | * |
| | | * @param in the byte array where to calculate the string. |
| | |
| | | } |
| | | |
| | | /** |
| | | * Update the Server State with a ChangeNumber. |
| | | * Update the Server State with a CSN. |
| | | * |
| | | * @param changeNumber The committed ChangeNumber. |
| | | * |
| | | * @param csn The committed CSN. |
| | | * @return a boolean indicating if the update was meaningful. |
| | | */ |
| | | public boolean update(ChangeNumber changeNumber) |
| | | public boolean update(CSN csn) |
| | | { |
| | | if (changeNumber == null) |
| | | if (csn == null) |
| | | return false; |
| | | |
| | | saved = false; |
| | | |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | int serverId = changeNumber.getServerId(); |
| | | ChangeNumber oldCN = serverIdToChangeNumber.get(serverId); |
| | | if (oldCN == null || changeNumber.newer(oldCN)) |
| | | int serverId = csn.getServerId(); |
| | | CSN oldCSN = serverIdToCSN.get(serverId); |
| | | if (oldCSN == null || csn.newer(oldCSN)) |
| | | { |
| | | serverIdToChangeNumber.put(serverId, changeNumber); |
| | | serverIdToCSN.put(serverId, csn); |
| | | return true; |
| | | } |
| | | return false; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Update the Server State with a Server State. Every change number of this |
| | | * object is updated with the change number of the passed server state if |
| | | * it is newer. |
| | | * Update the Server State with a Server State. Every CSN of this object is |
| | | * updated with the CSN of the passed server state if it is newer. |
| | | * |
| | | * @param serverState the server state to use for the update. |
| | | * |
| | | * @return a boolean indicating if the update was meaningful. |
| | | */ |
| | | public boolean update(ServerState serverState) |
| | |
| | | return false; |
| | | |
| | | boolean updated = false; |
| | | for (ChangeNumber cn : serverState.serverIdToChangeNumber.values()) |
| | | for (CSN csn : serverState.serverIdToCSN.values()) |
| | | { |
| | | if (update(cn)) |
| | | if (update(csn)) |
| | | { |
| | | updated = true; |
| | | } |
| | |
| | | return false; |
| | | } |
| | | |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | clear(); |
| | | return update(serverState); |
| | |
| | | { |
| | | Set<String> set = new HashSet<String>(); |
| | | |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | for (ChangeNumber change : serverIdToChangeNumber.values()) |
| | | for (CSN change : serverIdToCSN.values()) |
| | | { |
| | | Date date = new Date(change.getTime()); |
| | | set.add(change + " " + date + " " + change.getTime()); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Return an ArrayList of ANS1OctetString encoding the ChangeNumbers |
| | | * Return an ArrayList of ANS1OctetString encoding the CSNs |
| | | * contained in the ServerState. |
| | | * @return an ArrayList of ANS1OctetString encoding the ChangeNumbers |
| | | * @return an ArrayList of ANS1OctetString encoding the CSNs |
| | | * contained in the ServerState. |
| | | */ |
| | | public ArrayList<ByteString> toASN1ArrayList() |
| | | { |
| | | ArrayList<ByteString> values = new ArrayList<ByteString>(0); |
| | | |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | for (ChangeNumber changeNumber : serverIdToChangeNumber.values()) |
| | | for (CSN csn : serverIdToCSN.values()) |
| | | { |
| | | values.add(ByteString.valueOf(changeNumber.toString())); |
| | | values.add(ByteString.valueOf(csn.toString())); |
| | | } |
| | | } |
| | | return values; |
| | |
| | | public void writeTo(ASN1Writer writer, short protocolVersion) |
| | | throws IOException |
| | | { |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V7) |
| | | { |
| | | for (ChangeNumber cn : serverIdToChangeNumber.values()) |
| | | for (CSN csn : serverIdToCSN.values()) |
| | | { |
| | | writer.writeOctetString(cn.toByteString()); |
| | | writer.writeOctetString(csn.toByteString()); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | for (ChangeNumber cn : serverIdToChangeNumber.values()) |
| | | for (CSN csn : serverIdToCSN.values()) |
| | | { |
| | | writer.writeOctetString(cn.toString()); |
| | | writer.writeOctetString(csn.toString()); |
| | | } |
| | | } |
| | | } |
| | |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | for (ChangeNumber change : serverIdToChangeNumber.values()) |
| | | for (CSN change : serverIdToCSN.values()) |
| | | { |
| | | buffer.append(change).append(" "); |
| | | } |
| | | if (!serverIdToChangeNumber.isEmpty()) |
| | | if (!serverIdToCSN.isEmpty()) |
| | | buffer.deleteCharAt(buffer.length() - 1); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the {@code ChangeNumber} contained in this server state which |
| | | * corresponds to the provided server ID. |
| | | * Returns the {@code CSN} contained in this server state which corresponds to |
| | | * the provided server ID. |
| | | * |
| | | * @param serverId |
| | | * The server ID. |
| | | * @return The {@code ChangeNumber} contained in this server state which |
| | | * @return The {@code CSN} contained in this server state which |
| | | * corresponds to the provided server ID. |
| | | */ |
| | | public ChangeNumber getChangeNumber(int serverId) |
| | | public CSN getCSN(int serverId) |
| | | { |
| | | return serverIdToChangeNumber.get(serverId); |
| | | return serverIdToCSN.get(serverId); |
| | | } |
| | | |
| | | /** |
| | | * Returns the largest (most recent) {@code ChangeNumber} in this server |
| | | * state. |
| | | * Returns the largest (most recent) {@code CSN} in this server state. |
| | | * |
| | | * @return The largest (most recent) {@code ChangeNumber} in this server |
| | | * state. |
| | | * @return The largest (most recent) {@code CSN} in this server state. |
| | | */ |
| | | public ChangeNumber getMaxChangeNumber() |
| | | public CSN getMaxCSN() |
| | | { |
| | | ChangeNumber maxCN = null; |
| | | CSN maxCSN = null; |
| | | |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | for (ChangeNumber tmpMax : serverIdToChangeNumber.values()) |
| | | for (CSN csn : serverIdToCSN.values()) |
| | | { |
| | | if (maxCN == null || tmpMax.newer(maxCN)) |
| | | maxCN = tmpMax; |
| | | if (maxCSN == null || csn.newer(maxCSN)) |
| | | maxCSN = csn; |
| | | } |
| | | } |
| | | return maxCN; |
| | | return maxCSN; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public byte[] getBytes() throws UnsupportedEncodingException |
| | | { |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | final int size = serverIdToChangeNumber.size(); |
| | | final int size = serverIdToCSN.size(); |
| | | List<String> idList = new ArrayList<String>(size); |
| | | List<String> cnList = new ArrayList<String>(size); |
| | | List<String> csnList = new ArrayList<String>(size); |
| | | // calculate the total length needed to allocate byte array |
| | | int length = 0; |
| | | for (Entry<Integer, ChangeNumber> entry : serverIdToChangeNumber |
| | | for (Entry<Integer, CSN> entry : serverIdToCSN |
| | | .entrySet()) |
| | | { |
| | | // serverId is useless, see comment in ServerState ctor |
| | |
| | | idList.add(serverIdStr); |
| | | length += serverIdStr.length() + 1; |
| | | |
| | | String changeNumberStr = entry.getValue().toString(); |
| | | cnList.add(changeNumberStr); |
| | | length += changeNumberStr.length() + 1; |
| | | String csnStr = entry.getValue().toString(); |
| | | csnList.add(csnStr); |
| | | length += csnStr.length() + 1; |
| | | } |
| | | byte[] result = new byte[length]; |
| | | |
| | |
| | | { |
| | | String str = idList.get(i); |
| | | pos = addByteArray(str.getBytes("UTF-8"), result, pos); |
| | | str = cnList.get(i); |
| | | str = csnList.get(i); |
| | | pos = addByteArray(str.getBytes("UTF-8"), result, pos); |
| | | } |
| | | return result; |
| | |
| | | @Override |
| | | public Iterator<Integer> iterator() |
| | | { |
| | | return serverIdToChangeNumber.keySet().iterator(); |
| | | return serverIdToCSN.keySet().iterator(); |
| | | } |
| | | |
| | | /** |
| | | * Check that all the ChangeNumbers in the covered serverState are also in |
| | | * this serverState. |
| | | * Check that all the CSNs in the covered serverState are also in this |
| | | * serverState. |
| | | * |
| | | * @param covered The ServerState that needs to be checked. |
| | | * @return A boolean indicating if this ServerState covers the ServerState |
| | |
| | | */ |
| | | public boolean cover(ServerState covered) |
| | | { |
| | | for (ChangeNumber coveredChange : covered.serverIdToChangeNumber.values()) |
| | | for (CSN coveredChange : covered.serverIdToCSN.values()) |
| | | { |
| | | if (!cover(coveredChange)) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Checks that the ChangeNumber given as a parameter is in this ServerState. |
| | | * Checks that the CSN given as a parameter is in this ServerState. |
| | | * |
| | | * @param covered The ChangeNumber that should be checked. |
| | | * @return A boolean indicating if this ServerState contains the ChangeNumber |
| | | * given in parameter. |
| | | * @param covered The CSN that should be checked. |
| | | * @return A boolean indicating if this ServerState contains the CSN given in |
| | | * parameter. |
| | | */ |
| | | public boolean cover(ChangeNumber covered) |
| | | public boolean cover(CSN covered) |
| | | { |
| | | ChangeNumber change = |
| | | this.serverIdToChangeNumber.get(covered.getServerId()); |
| | | CSN change = |
| | | this.serverIdToCSN.get(covered.getServerId()); |
| | | return change != null && !change.older(covered); |
| | | } |
| | | |
| | |
| | | */ |
| | | public boolean isEmpty() |
| | | { |
| | | return serverIdToChangeNumber.isEmpty(); |
| | | return serverIdToCSN.isEmpty(); |
| | | } |
| | | |
| | | /** |
| | |
| | | public ServerState duplicate() |
| | | { |
| | | ServerState newState = new ServerState(); |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | newState.serverIdToChangeNumber.putAll(serverIdToChangeNumber); |
| | | newState.serverIdToCSN.putAll(serverIdToCSN); |
| | | } |
| | | return newState; |
| | | } |
| | |
| | | } |
| | | |
| | | int diff = 0; |
| | | for (Integer serverId : ss1.serverIdToChangeNumber.keySet()) |
| | | for (Integer serverId : ss1.serverIdToCSN.keySet()) |
| | | { |
| | | ChangeNumber cn1 = ss1.serverIdToChangeNumber.get(serverId); |
| | | if (cn1 != null) |
| | | CSN csn1 = ss1.serverIdToCSN.get(serverId); |
| | | if (csn1 != null) |
| | | { |
| | | ChangeNumber cn2 = ss2.serverIdToChangeNumber.get(serverId); |
| | | if (cn2 != null) |
| | | { |
| | | diff += ChangeNumber.diffSeqNum(cn1, cn2); |
| | | } else { |
| | | // ss2 does not have a change for this server id but ss1, so the |
| | | // server holding ss1 has every changes represented in cn1 in advance |
| | | // compared to server holding ss2, add this amount |
| | | diff += cn1.getSeqnum(); |
| | | } |
| | | CSN csn2 = ss2.serverIdToCSN.get(serverId); |
| | | if (csn2 != null) |
| | | { |
| | | diff += CSN.diffSeqNum(csn1, csn2); |
| | | } |
| | | else |
| | | { |
| | | // ss2 does not have a change for this server id but ss1, so the |
| | | // server holding ss1 has every changes represented in csn1 in advance |
| | | // compared to server holding ss2, add this amount |
| | | diff += csn1.getSeqnum(); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * Build a copy of the ServerState with only ChangeNumbers older than |
| | | * a specific ChangeNumber. This is used when building the initial |
| | | * Build a copy of the ServerState with only CSNs older than |
| | | * a specific CSN. This is used when building the initial |
| | | * Cookie in the External Changelog, to cope with purged changes. |
| | | * @param cn The ChangeNumber to compare the ServerState with |
| | | * @return a copy of the ServerState which only contains the ChangeNumbers |
| | | * older than cn. |
| | | * @param csn The CSN to compare the ServerState with |
| | | * @return a copy of the ServerState which only contains the CSNs older than |
| | | * csn. |
| | | */ |
| | | public ServerState duplicateOnlyOlderThan(ChangeNumber cn) |
| | | public ServerState duplicateOnlyOlderThan(CSN csn) |
| | | { |
| | | ServerState newState = new ServerState(); |
| | | synchronized (serverIdToChangeNumber) |
| | | synchronized (serverIdToCSN) |
| | | { |
| | | for (ChangeNumber change : serverIdToChangeNumber.values()) |
| | | for (CSN change : serverIdToCSN.values()) |
| | | { |
| | | if (change.older(cn)) |
| | | if (change.older(csn)) |
| | | { |
| | | newState.serverIdToChangeNumber.put(change.getServerId(), change); |
| | | newState.serverIdToCSN.put(change.getServerId(), change); |
| | | } |
| | | } |
| | | } |
| | |
| | | import java.util.Iterator; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | |
| | | |
| | | /** |
| | | * This class store historical information for a provided attribute. |
| | | */ |
| | |
| | | * |
| | | * @param modsIterator The iterator on the mods from which the mod is |
| | | * extracted. |
| | | * @param changeNumber The changeNumber associated to the operation. |
| | | * @param csn The CSN associated to the operation. |
| | | * @param modifiedEntry The entry modified by this operation. |
| | | * @param mod The modification. |
| | | * |
| | | * @return a boolean indicating if a conflict was detected. |
| | | */ |
| | | public abstract boolean replayOperation( |
| | | Iterator<Modification> modsIterator, ChangeNumber changeNumber, |
| | | Iterator<Modification> modsIterator, CSN csn, |
| | | Entry modifiedEntry, Modification mod); |
| | | |
| | | /** |
| | |
| | | * It does not check if the operation to process is conflicting or not with |
| | | * previous operations. The caller is responsible for this. |
| | | * |
| | | * @param changeNumber The changeNumber of the operation to process |
| | | * @param csn The CSN of the operation to process |
| | | * @param mod The modify operation to process. |
| | | */ |
| | | public abstract void processLocalOrNonConflictModification( |
| | | ChangeNumber changeNumber, Modification mod); |
| | | CSN csn, Modification mod); |
| | | |
| | | /** |
| | | * Create a new object from a provided attribute type. Historical is empty. |
| | |
| | | * |
| | | * @return the last time when this attribute was deleted |
| | | */ |
| | | public abstract ChangeNumber getDeleteTime(); |
| | | public abstract CSN getDeleteTime(); |
| | | |
| | | /** |
| | | * Assign the provided information to this object. |
| | | * |
| | | * @param histKey the key to assign. |
| | | * @param value the associated value or null if there is no value; |
| | | * @param cn the associated ChangeNumber. |
| | | * @param csn the associated CSN. |
| | | */ |
| | | public abstract void assign( |
| | | HistAttrModificationKey histKey, AttributeValue value, ChangeNumber cn); |
| | | HistAttrModificationKey histKey, AttributeValue value, CSN csn); |
| | | |
| | | } |
| | | |
| | |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeBuilder; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.ModificationType; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.*; |
| | | |
| | | /** |
| | | * This class is used to store historical information for multiple valued |
| | |
| | | public class AttrHistoricalMultiple extends AttrHistorical |
| | | { |
| | | /** Last time when the attribute was deleted. */ |
| | | private ChangeNumber deleteTime; |
| | | private CSN deleteTime; |
| | | /** Last time the attribute was modified. */ |
| | | private ChangeNumber lastUpdateTime; |
| | | private CSN lastUpdateTime; |
| | | /** |
| | | * Change history for the values of this attribute. We are using a |
| | | * LinkedHashMap here because we want: |
| | | * <ol> |
| | | * <li>Fast access for removing/adding a AttrValueHistorical keyed by the |
| | | * AttributeValue => Use a Map</li> |
| | | * <li>Ordering changes according to the changeNumber of each changes => Use a |
| | | * <li>Ordering changes according to the CSN of each changes => Use a |
| | | * LinkedHashMap</li> |
| | | * </ol> |
| | | */ |
| | |
| | | * @param updateTime the last time this attribute was updated |
| | | * @param valuesHist the new attribute values when updated. |
| | | */ |
| | | public AttrHistoricalMultiple(ChangeNumber deleteTime, |
| | | ChangeNumber updateTime, |
| | | public AttrHistoricalMultiple(CSN deleteTime, |
| | | CSN updateTime, |
| | | Map<AttrValueHistorical,AttrValueHistorical> valuesHist) |
| | | { |
| | | this.deleteTime = deleteTime; |
| | |
| | | * Returns the last time when the attribute was updated. |
| | | * @return the last time when the attribute was updated |
| | | */ |
| | | private ChangeNumber getLastUpdateTime() |
| | | private CSN getLastUpdateTime() |
| | | { |
| | | return lastUpdateTime; |
| | | } |
| | |
| | | * @return the last time when the attribute was deleted |
| | | */ |
| | | @Override |
| | | public ChangeNumber getDeleteTime() |
| | | public CSN getDeleteTime() |
| | | { |
| | | return deleteTime; |
| | | } |
| | | |
| | | /** |
| | | * Duplicate an object. |
| | | * ChangeNumber are duplicated by references |
| | | * @return the duplicated object. |
| | | * |
| | | * Duplicate an object. CSNs are duplicated by references. |
| | | * <p> |
| | | * Method only called in tests |
| | | * |
| | | * @return the duplicated object. |
| | | */ |
| | | AttrHistoricalMultiple duplicate() |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Delete all historical information that is older than |
| | | * the provided ChangeNumber for this attribute type. |
| | | * Delete all historical information that is older than the provided CSN for |
| | | * this attribute type. |
| | | * Add the delete attribute state information |
| | | * @param CN time when the delete was done |
| | | * @param csn time when the delete was done |
| | | */ |
| | | protected void delete(ChangeNumber CN) |
| | | protected void delete(CSN csn) |
| | | { |
| | | // iterate through the values in the valuesInfo |
| | | // and suppress all the values that have not been added |
| | | // after the date of this delete. |
| | | // iterate through the values in the valuesInfo and suppress all the values |
| | | // that have not been added after the date of this delete. |
| | | Iterator<AttrValueHistorical> it = valuesHist.keySet().iterator(); |
| | | while (it.hasNext()) |
| | | { |
| | | AttrValueHistorical info = it.next(); |
| | | if (CN.newerOrEquals(info.getValueUpdateTime()) && |
| | | CN.newerOrEquals(info.getValueDeleteTime())) |
| | | if (csn.newerOrEquals(info.getValueUpdateTime()) && |
| | | csn.newerOrEquals(info.getValueDeleteTime())) |
| | | it.remove(); |
| | | } |
| | | |
| | | if (CN.newer(deleteTime)) |
| | | if (csn.newer(deleteTime)) |
| | | { |
| | | deleteTime = CN; |
| | | deleteTime = csn; |
| | | } |
| | | |
| | | if (CN.newer(lastUpdateTime)) |
| | | if (csn.newer(lastUpdateTime)) |
| | | { |
| | | lastUpdateTime = CN; |
| | | lastUpdateTime = csn; |
| | | } |
| | | } |
| | | |
| | |
| | | * Update the historical of this attribute after a delete value. |
| | | * |
| | | * @param val value that was deleted |
| | | * @param CN time when the delete was done |
| | | * @param csn time when the delete was done |
| | | */ |
| | | protected void delete(AttributeValue val, ChangeNumber CN) |
| | | protected void delete(AttributeValue val, CSN csn) |
| | | { |
| | | AttrValueHistorical info = new AttrValueHistorical(val, null, CN); |
| | | AttrValueHistorical info = new AttrValueHistorical(val, null, csn); |
| | | valuesHist.remove(info); |
| | | valuesHist.put(info, info); |
| | | if (CN.newer(lastUpdateTime)) |
| | | if (csn.newer(lastUpdateTime)) |
| | | { |
| | | lastUpdateTime = CN; |
| | | lastUpdateTime = csn; |
| | | } |
| | | } |
| | | |
| | |
| | | * @param attr |
| | | * the attribute containing the set of values that were |
| | | * deleted |
| | | * @param CN |
| | | * @param csn |
| | | * time when the delete was done |
| | | */ |
| | | protected void delete(Attribute attr, ChangeNumber CN) |
| | | protected void delete(Attribute attr, CSN csn) |
| | | { |
| | | for (AttributeValue val : attr) |
| | | { |
| | | AttrValueHistorical info = new AttrValueHistorical(val, null, CN); |
| | | AttrValueHistorical info = new AttrValueHistorical(val, null, csn); |
| | | valuesHist.remove(info); |
| | | valuesHist.put(info, info); |
| | | if (CN.newer(lastUpdateTime)) |
| | | if (csn.newer(lastUpdateTime)) |
| | | { |
| | | lastUpdateTime = CN; |
| | | lastUpdateTime = csn; |
| | | } |
| | | } |
| | | } |
| | |
| | | * |
| | | * @param addedValue |
| | | * values that was added |
| | | * @param CN |
| | | * @param csn |
| | | * time when the value was added |
| | | */ |
| | | protected void add(AttributeValue addedValue, ChangeNumber CN) |
| | | protected void add(AttributeValue addedValue, CSN csn) |
| | | { |
| | | AttrValueHistorical info = new AttrValueHistorical(addedValue, CN, null); |
| | | AttrValueHistorical info = new AttrValueHistorical(addedValue, csn, null); |
| | | valuesHist.remove(info); |
| | | valuesHist.put(info, info); |
| | | if (CN.newer(lastUpdateTime)) |
| | | if (csn.newer(lastUpdateTime)) |
| | | { |
| | | lastUpdateTime = CN; |
| | | lastUpdateTime = csn; |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * @param attr |
| | | * the attribute containing the set of added values |
| | | * @param CN |
| | | * @param csn |
| | | * time when the add is done |
| | | */ |
| | | private void add(Attribute attr, ChangeNumber CN) |
| | | private void add(Attribute attr, CSN csn) |
| | | { |
| | | for (AttributeValue val : attr) |
| | | { |
| | | AttrValueHistorical info = new AttrValueHistorical(val, CN, null); |
| | | AttrValueHistorical info = new AttrValueHistorical(val, csn, null); |
| | | valuesHist.remove(info); |
| | | valuesHist.put(info, info); |
| | | if (CN.newer(lastUpdateTime)) |
| | | if (csn.newer(lastUpdateTime)) |
| | | { |
| | | lastUpdateTime = CN; |
| | | lastUpdateTime = csn; |
| | | } |
| | | } |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean replayOperation( |
| | | Iterator<Modification> modsIterator, ChangeNumber changeNumber, |
| | | public boolean replayOperation(Iterator<Modification> modsIterator, CSN csn, |
| | | Entry modifiedEntry, Modification m) |
| | | { |
| | | // We are replaying an operation that was already done |
| | | // on another master server and this operation has a potential |
| | | // conflict with some more recent operations on this same entry |
| | | // we need to take the more complex path to solve them |
| | | if ((ChangeNumber.compare(changeNumber, getLastUpdateTime()) < 0) || |
| | | if ((CSN.compare(csn, getLastUpdateTime()) < 0) || |
| | | (m.getModificationType() != ModificationType.REPLACE)) |
| | | { |
| | | // the attribute was modified after this change -> conflict |
| | |
| | | switch (m.getModificationType()) |
| | | { |
| | | case DELETE: |
| | | if (changeNumber.older(getDeleteTime())) |
| | | if (csn.older(getDeleteTime())) |
| | | { |
| | | /* this delete is already obsoleted by a more recent delete |
| | | * skip this mod |
| | |
| | | break; |
| | | } |
| | | |
| | | if (!conflictDelete(changeNumber, m, modifiedEntry)) |
| | | if (!conflictDelete(csn, m, modifiedEntry)) |
| | | { |
| | | modsIterator.remove(); |
| | | } |
| | | break; |
| | | |
| | | case ADD: |
| | | conflictAdd(changeNumber, m, modsIterator); |
| | | conflictAdd(csn, m, modsIterator); |
| | | break; |
| | | |
| | | case REPLACE: |
| | | if (changeNumber.older(getDeleteTime())) |
| | | if (csn.older(getDeleteTime())) |
| | | { |
| | | /* this replace is already obsoleted by a more recent delete |
| | | * skip this mod |
| | |
| | | Attribute addedValues = m.getAttribute(); |
| | | m.setAttribute(new AttributeBuilder(addedValues, true).toAttribute()); |
| | | |
| | | conflictDelete(changeNumber, m, modifiedEntry); |
| | | conflictDelete(csn, m, modifiedEntry); |
| | | Attribute keptValues = m.getAttribute(); |
| | | |
| | | m.setAttribute(addedValues); |
| | | conflictAdd(changeNumber, m, modsIterator); |
| | | conflictAdd(csn, m, modsIterator); |
| | | |
| | | AttributeBuilder builder = new AttributeBuilder(keptValues); |
| | | builder.addAll(m.getAttribute()); |
| | |
| | | } |
| | | else |
| | | { |
| | | processLocalOrNonConflictModification(changeNumber, m); |
| | | processLocalOrNonConflictModification(csn, m); |
| | | return false;// the attribute was not modified more recently |
| | | } |
| | | } |
| | |
| | | * It does not check if the operation to process is conflicting or not with |
| | | * previous operations. The caller is responsible for this. |
| | | * |
| | | * @param changeNumber The changeNumber of the operation to process |
| | | * @param csn The CSN of the operation to process |
| | | * @param mod The modify operation to process. |
| | | */ |
| | | @Override |
| | | public void processLocalOrNonConflictModification(ChangeNumber changeNumber, |
| | | Modification mod) |
| | | public void processLocalOrNonConflictModification(CSN csn, Modification mod) |
| | | { |
| | | /* |
| | | * The operation is either a non-conflicting operation or a local |
| | |
| | | case DELETE: |
| | | if (modAttr.isEmpty()) |
| | | { |
| | | delete(changeNumber); |
| | | delete(csn); |
| | | } |
| | | else |
| | | { |
| | | delete(modAttr, changeNumber); |
| | | delete(modAttr, csn); |
| | | } |
| | | break; |
| | | |
| | | case ADD: |
| | | if (type.isSingleValue()) |
| | | { |
| | | delete(changeNumber); |
| | | delete(csn); |
| | | } |
| | | add(modAttr, changeNumber); |
| | | add(modAttr, csn); |
| | | break; |
| | | |
| | | case REPLACE: |
| | | /* TODO : can we replace specific attribute values ????? */ |
| | | delete(changeNumber); |
| | | add(modAttr, changeNumber); |
| | | delete(csn); |
| | | add(modAttr, csn); |
| | | break; |
| | | |
| | | case INCREMENT: |
| | | /* FIXME : we should update ChangeNumber */ |
| | | /* FIXME : we should update CSN */ |
| | | break; |
| | | } |
| | | } |
| | |
| | | * Process a delete attribute values that is conflicting with a previous |
| | | * modification. |
| | | * |
| | | * @param changeNumber The changeNumber of the currently processed change |
| | | * @param csn The CSN of the currently processed change |
| | | * @param m the modification that is being processed |
| | | * @param modifiedEntry the entry that is modified (before current mod) |
| | | * @return false if there is nothing to do |
| | | */ |
| | | private boolean conflictDelete(ChangeNumber changeNumber, Modification m, |
| | | Entry modifiedEntry) |
| | | private boolean conflictDelete(CSN csn, Modification m, Entry modifiedEntry) |
| | | { |
| | | /* |
| | | * We are processing a conflicting DELETE modification |
| | |
| | | { |
| | | AttrValueHistorical valInfo = it.next(); |
| | | |
| | | if (changeNumber.older(valInfo.getValueUpdateTime())) |
| | | if (csn.older(valInfo.getValueUpdateTime())) |
| | | { |
| | | /* |
| | | * this value has been updated after this delete, therefore |
| | |
| | | * information unless it is a Deleted attribute value that is |
| | | * more recent than this DELETE |
| | | */ |
| | | if (changeNumber.newerOrEquals(valInfo.getValueDeleteTime())) |
| | | if (csn.newerOrEquals(valInfo.getValueDeleteTime())) |
| | | { |
| | | it.remove(); |
| | | } |
| | |
| | | |
| | | m.setAttribute(builder.toAttribute()); |
| | | |
| | | if (changeNumber.newer(getDeleteTime())) |
| | | if (csn.newer(getDeleteTime())) |
| | | { |
| | | deleteTime = changeNumber; |
| | | deleteTime = csn; |
| | | } |
| | | if (changeNumber.newer(getLastUpdateTime())) |
| | | if (csn.newer(getLastUpdateTime())) |
| | | { |
| | | lastUpdateTime = changeNumber; |
| | | lastUpdateTime = csn; |
| | | } |
| | | } |
| | | else |
| | |
| | | |
| | | /* update historical information */ |
| | | AttrValueHistorical valInfo = |
| | | new AttrValueHistorical(val, null, changeNumber); |
| | | new AttrValueHistorical(val, null, csn); |
| | | AttrValueHistorical oldValInfo = valuesHist.get(valInfo); |
| | | if (oldValInfo != null) |
| | | { |
| | | /* this value already exist in the historical information */ |
| | | if (changeNumber.equals(oldValInfo.getValueUpdateTime())) |
| | | if (csn.equals(oldValInfo.getValueUpdateTime())) |
| | | { |
| | | // This value was added earlier in the same operation |
| | | // we need to keep the delete. |
| | | addedInCurrentOp = true; |
| | | } |
| | | if (changeNumber.newerOrEquals(oldValInfo.getValueDeleteTime()) && |
| | | changeNumber.newerOrEquals(oldValInfo.getValueUpdateTime())) |
| | | if (csn.newerOrEquals(oldValInfo.getValueDeleteTime()) && |
| | | csn.newerOrEquals(oldValInfo.getValueUpdateTime())) |
| | | { |
| | | valuesHist.remove(oldValInfo); |
| | | valuesHist.put(valInfo, valInfo); |
| | |
| | | |
| | | m.setAttribute(builder.toAttribute()); |
| | | |
| | | if (changeNumber.newer(getLastUpdateTime())) |
| | | if (csn.newer(getLastUpdateTime())) |
| | | { |
| | | lastUpdateTime = changeNumber; |
| | | lastUpdateTime = csn; |
| | | } |
| | | } |
| | | |
| | |
| | | * Process a add attribute values that is conflicting with a previous |
| | | * modification. |
| | | * |
| | | * @param changeNumber the historical info associated to the entry |
| | | * @param csn the historical info associated to the entry |
| | | * @param m the modification that is being processed |
| | | * @param modsIterator iterator on the list of modification |
| | | * @return false if operation becomes empty and must not be processed |
| | | */ |
| | | private boolean conflictAdd(ChangeNumber changeNumber, Modification m, |
| | | private boolean conflictAdd(CSN csn, Modification m, |
| | | Iterator<Modification> modsIterator) |
| | | { |
| | | /* |
| | |
| | | * real entry |
| | | */ |
| | | |
| | | if (changeNumber.older(getDeleteTime())) |
| | | if (csn.older(getDeleteTime())) |
| | | { |
| | | /* A delete has been done more recently than this add |
| | | * forget this MOD ADD |
| | |
| | | for (AttributeValue addVal : m.getAttribute()) |
| | | { |
| | | AttrValueHistorical valInfo = |
| | | new AttrValueHistorical(addVal, changeNumber, null); |
| | | new AttrValueHistorical(addVal, csn, null); |
| | | AttrValueHistorical oldValInfo = valuesHist.get(valInfo); |
| | | if (oldValInfo == null) |
| | | { |
| | |
| | | * in all cases suppress this value from the value list |
| | | * as it is already present in the entry |
| | | */ |
| | | if (changeNumber.newer(oldValInfo.getValueUpdateTime())) |
| | | if (csn.newer(oldValInfo.getValueUpdateTime())) |
| | | { |
| | | valuesHist.remove(oldValInfo); |
| | | valuesHist.put(valInfo, valInfo); |
| | |
| | | /* this value is marked as a deleted value |
| | | * check if this mod is more recent the this delete |
| | | */ |
| | | if (changeNumber.newerOrEquals(oldValInfo.getValueDeleteTime())) |
| | | if (csn.newerOrEquals(oldValInfo.getValueDeleteTime())) |
| | | { |
| | | /* this add is more recent, |
| | | * remove the old delete historical information |
| | |
| | | modsIterator.remove(); |
| | | } |
| | | |
| | | if (changeNumber.newer(getLastUpdateTime())) |
| | | if (csn.newer(getLastUpdateTime())) |
| | | { |
| | | lastUpdateTime = changeNumber; |
| | | lastUpdateTime = csn; |
| | | } |
| | | |
| | | return true; |
| | |
| | | */ |
| | | @Override |
| | | public void assign(HistAttrModificationKey histKey, AttributeValue value, |
| | | ChangeNumber cn) |
| | | CSN csn) |
| | | { |
| | | switch (histKey) |
| | | { |
| | | case ADD: |
| | | if (value != null) |
| | | { |
| | | add(value, cn); |
| | | add(value, csn); |
| | | } |
| | | break; |
| | | |
| | | case DEL: |
| | | if (value != null) |
| | | { |
| | | delete(value, cn); |
| | | delete(value, csn); |
| | | } |
| | | break; |
| | | |
| | | case REPL: |
| | | delete(cn); |
| | | delete(csn); |
| | | if (value != null) |
| | | { |
| | | add(value, cn); |
| | | add(value, csn); |
| | | } |
| | | break; |
| | | |
| | | case DELATTR: |
| | | delete(cn); |
| | | delete(csn); |
| | | break; |
| | | } |
| | | } |
| | |
| | | import java.util.Iterator; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.ModificationType; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.*; |
| | | |
| | | /** |
| | | * This class is used to store historical information for single valued |
| | |
| | | public class AttrHistoricalSingle extends AttrHistorical |
| | | { |
| | | /** Last time when the attribute was deleted. */ |
| | | private ChangeNumber deleteTime = null; |
| | | private CSN deleteTime = null; |
| | | /** Last time when a value was added. */ |
| | | private ChangeNumber addTime = null; |
| | | private CSN addTime = null; |
| | | /** Last added value. */ |
| | | private AttributeValue value = null; |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ChangeNumber getDeleteTime() |
| | | public CSN getDeleteTime() |
| | | { |
| | | return this.deleteTime; |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void processLocalOrNonConflictModification(ChangeNumber changeNumber, |
| | | Modification mod) |
| | | public void processLocalOrNonConflictModification(CSN csn, Modification mod) |
| | | { |
| | | AttributeValue newValue = null; |
| | | Attribute modAttr = mod.getAttribute(); |
| | |
| | | { |
| | | case DELETE: |
| | | this.addTime = null; |
| | | this.deleteTime = changeNumber; |
| | | this.deleteTime = csn; |
| | | this.value = newValue; |
| | | lastMod = HistAttrModificationKey.DEL; |
| | | break; |
| | | |
| | | case ADD: |
| | | this.addTime = changeNumber; |
| | | this.addTime = csn; |
| | | this.value = newValue; |
| | | lastMod = HistAttrModificationKey.ADD; |
| | | break; |
| | |
| | | { |
| | | // REPLACE with null value is actually a DELETE |
| | | this.addTime = null; |
| | | this.deleteTime = changeNumber; |
| | | this.deleteTime = csn; |
| | | this.value = null; |
| | | lastMod = HistAttrModificationKey.DEL; |
| | | } |
| | | else |
| | | { |
| | | this.deleteTime = addTime = changeNumber; |
| | | this.deleteTime = addTime = csn; |
| | | lastMod = HistAttrModificationKey.REPL; |
| | | } |
| | | this.value = newValue; |
| | | break; |
| | | |
| | | case INCREMENT: |
| | | /* FIXME : we should update ChangeNumber */ |
| | | /* FIXME : we should update CSN */ |
| | | break; |
| | | } |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean replayOperation(Iterator<Modification> modsIterator, |
| | | ChangeNumber changeNumber, Entry modifiedEntry, Modification mod) |
| | | public boolean replayOperation(Iterator<Modification> modsIterator, CSN csn, |
| | | Entry modifiedEntry, Modification mod) |
| | | { |
| | | boolean conflict = false; |
| | | |
| | |
| | | switch (mod.getModificationType()) |
| | | { |
| | | case DELETE: |
| | | if (changeNumber.newer(addTime)) |
| | | if (csn.newer(addTime)) |
| | | { |
| | | if (newValue == null || newValue.equals(value) || value == null) |
| | | { |
| | | if (changeNumber.newer(deleteTime)) |
| | | if (csn.newer(deleteTime)) |
| | | { |
| | | deleteTime = changeNumber; |
| | | deleteTime = csn; |
| | | } |
| | | AttributeType type = modAttr.getAttributeType(); |
| | | if (!modifiedEntry.hasAttribute(type)) |
| | |
| | | modsIterator.remove(); |
| | | } |
| | | } |
| | | else if (changeNumber.equals(addTime)) |
| | | else if (csn.equals(addTime)) |
| | | { |
| | | if ((lastMod == HistAttrModificationKey.ADD) |
| | | || (lastMod == HistAttrModificationKey.REPL)) |
| | | { |
| | | if (changeNumber.newer(deleteTime)) |
| | | if (csn.newer(deleteTime)) |
| | | { |
| | | deleteTime = changeNumber; |
| | | deleteTime = csn; |
| | | } |
| | | addTime = null; |
| | | lastMod = HistAttrModificationKey.DEL; |
| | |
| | | break; |
| | | |
| | | case ADD: |
| | | if (changeNumber.newerOrEquals(deleteTime) && changeNumber.older(addTime)) |
| | | if (csn.newerOrEquals(deleteTime) && csn.older(addTime)) |
| | | { |
| | | conflict = true; |
| | | mod.setModificationType(ModificationType.REPLACE); |
| | | addTime = changeNumber; |
| | | addTime = csn; |
| | | value = newValue; |
| | | lastMod = HistAttrModificationKey.REPL; |
| | | } |
| | | else |
| | | { |
| | | if (changeNumber.newerOrEquals(deleteTime) |
| | | if (csn.newerOrEquals(deleteTime) |
| | | && ((addTime == null ) || addTime.older(deleteTime))) |
| | | { |
| | | // no conflict : don't do anything beside setting the addTime |
| | | addTime = changeNumber; |
| | | addTime = csn; |
| | | value = newValue; |
| | | lastMod = HistAttrModificationKey.ADD; |
| | | } |
| | | else |
| | | { |
| | | // Case where changeNumber = addTime = deleteTime |
| | | if (changeNumber.equals(deleteTime) |
| | | && changeNumber.equals(addTime) |
| | | // Case where CSN = addTime = deleteTime |
| | | if (csn.equals(deleteTime) && csn.equals(addTime) |
| | | && (lastMod == HistAttrModificationKey.DEL)) |
| | | { |
| | | // No conflict, record the new value. |
| | |
| | | break; |
| | | |
| | | case REPLACE: |
| | | if (changeNumber.older(deleteTime)) |
| | | if (csn.older(deleteTime)) |
| | | { |
| | | conflict = true; |
| | | modsIterator.remove(); |
| | |
| | | { |
| | | addTime = null; |
| | | value = newValue; |
| | | deleteTime = changeNumber; |
| | | deleteTime = csn; |
| | | lastMod = HistAttrModificationKey.DEL; |
| | | } |
| | | else |
| | | { |
| | | addTime = changeNumber; |
| | | addTime = csn; |
| | | value = newValue; |
| | | deleteTime = changeNumber; |
| | | deleteTime = csn; |
| | | lastMod = HistAttrModificationKey.REPL; |
| | | } |
| | | } |
| | | break; |
| | | |
| | | case INCREMENT: |
| | | /* FIXME : we should update ChangeNumber */ |
| | | /* FIXME : we should update CSN */ |
| | | break; |
| | | } |
| | | return conflict; |
| | |
| | | */ |
| | | @Override |
| | | public void assign(HistAttrModificationKey histKey, |
| | | AttributeValue value, ChangeNumber cn) |
| | | AttributeValue value, CSN csn) |
| | | { |
| | | switch (histKey) |
| | | { |
| | | case ADD: |
| | | this.addTime = cn; |
| | | this.addTime = csn; |
| | | this.value = value; |
| | | break; |
| | | |
| | | case DEL: |
| | | this.deleteTime = cn; |
| | | this.deleteTime = csn; |
| | | if (value != null) |
| | | this.value = value; |
| | | break; |
| | | |
| | | case REPL: |
| | | this.addTime = this.deleteTime = cn; |
| | | this.addTime = this.deleteTime = csn; |
| | | if (value != null) |
| | | this.value = value; |
| | | break; |
| | | |
| | | case DELATTR: |
| | | this.deleteTime = cn; |
| | | this.deleteTime = csn; |
| | | break; |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.AttributeValue; |
| | | |
| | | /** |
| | |
| | | public class AttrValueHistorical |
| | | { |
| | | private AttributeValue value; |
| | | private ChangeNumber valueDeleteTime; |
| | | private ChangeNumber valueUpdateTime; |
| | | private CSN valueDeleteTime; |
| | | private CSN valueUpdateTime; |
| | | |
| | | /** |
| | | * Build an AttrValueHistorical for a provided AttributeValue, providing |
| | | * the last time the provided value is either updated or deleted. |
| | | * @param value the provided attributeValue |
| | | * @param CNupdate last time when this value was updated |
| | | * @param CNdelete last time when this value for deleted |
| | | * @param csnUpdate last time when this value was updated |
| | | * @param csnDelete last time when this value for deleted |
| | | */ |
| | | public AttrValueHistorical(AttributeValue value, |
| | | ChangeNumber CNupdate, |
| | | ChangeNumber CNdelete) |
| | | CSN csnUpdate, |
| | | CSN csnDelete) |
| | | { |
| | | this.value = value; |
| | | this.valueUpdateTime = CNupdate; |
| | | this.valueDeleteTime = CNdelete; |
| | | this.valueUpdateTime = csnUpdate; |
| | | this.valueDeleteTime = csnDelete; |
| | | } |
| | | |
| | | /** |
| | |
| | | AttrValueHistorical objVal = (AttrValueHistorical) obj; |
| | | return (value.equals(objVal.getAttributeValue())); |
| | | } |
| | | else |
| | | { |
| | | return false; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | |
| | | * Get the last time when the value was deleted. |
| | | * @return the last time when the value was deleted |
| | | */ |
| | | public ChangeNumber getValueDeleteTime() |
| | | public CSN getValueDeleteTime() |
| | | { |
| | | return valueDeleteTime; |
| | | } |
| | |
| | | * Get the last time when the value was updated. |
| | | * @return the last time when the value was updated |
| | | */ |
| | | public ChangeNumber getValueUpdateTime() |
| | | public CSN getValueUpdateTime() |
| | | { |
| | | return valueUpdateTime; |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | import java.util.*; |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.OperationContext; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.types.operation.PreOperationAddOperation; |
| | |
| | | import org.opends.server.types.operation.PreOperationModifyOperation; |
| | | import org.opends.server.util.TimeThread; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | /** |
| | | * This class is used to store historical information that is |
| | | * used to resolve modify conflicts |
| | |
| | | |
| | | /** |
| | | * The delay to purge the historical information. |
| | | * This delay indicates the time the domain keeps the historical |
| | | * information necessary to solve conflicts.When a change stored in the |
| | | * historical part of the user entry has a date (from its replication |
| | | * ChangeNumber) older than this delay, it is candidate to be purged. |
| | | * The purge is triggered on 2 events: modify of the entry, dedicated purge |
| | | * task. |
| | | * |
| | | * The purge is done when the historical is encoded. |
| | | * <p> |
| | | * This delay indicates the time the domain keeps the historical information |
| | | * necessary to solve conflicts. When a change stored in the historical part |
| | | * of the user entry has a date (from its replication CSN) older than this |
| | | * delay, it is candidate to be purged. The purge is triggered on 2 events: |
| | | * modify of the entry, dedicated purge task. The purge is done when the |
| | | * historical is encoded. |
| | | */ |
| | | private long purgeDelayInMillisec = -1; |
| | | |
| | | /** |
| | | * The oldest ChangeNumber stored in this entry historical attribute. |
| | | * The oldest CSN stored in this entry historical attribute. |
| | | * null when this historical object has been created from |
| | | * an entry that has no historical attribute and after the last |
| | | * historical has been purged. |
| | | */ |
| | | private ChangeNumber oldestChangeNumber = null; |
| | | private CSN oldestCSN = null; |
| | | |
| | | /** |
| | | * For stats/monitoring purpose, the number of historical values |
| | |
| | | * The in-memory historical information is made of. |
| | | * |
| | | * EntryHistorical ::= ADDDate MODDNDate attributesInfo |
| | | * ADDDate ::= ChangeNumber // the date the entry was added |
| | | * MODDNDate ::= ChangeNumber // the date the entry was last renamed |
| | | * ADDDate ::= CSN // the date the entry was added |
| | | * MODDNDate ::= CSN // the date the entry was last renamed |
| | | * |
| | | * attributesInfo ::= (AttrInfoWithOptions)* |
| | | * one AttrInfoWithOptions by attributeType |
| | |
| | | * AttrValueHistorical is the historical of the |
| | | * the modification of one value |
| | | * |
| | | * AddTime ::= ChangeNumber // last time the attribute was added |
| | | * AddTime ::= CSN // last time the attribute was added |
| | | * // to the entry |
| | | * DeleteTime ::= ChangeNumber // last time the attribute was deleted |
| | | * DeleteTime ::= CSN // last time the attribute was deleted |
| | | * // from the entry |
| | | * |
| | | * AttrValueHistorical ::= AttributeValue valueDeleteTime valueUpdateTime |
| | | * valueDeleteTime ::= ChangeNumber |
| | | * valueUpdateTime ::= ChangeNumber |
| | | * valueDeleteTime ::= CSN |
| | | * valueUpdateTime ::= CSN |
| | | * |
| | | * - a list indexed on AttributeType of AttrInfoWithOptions : |
| | | * each value is the historical for this attribute |
| | |
| | | */ |
| | | |
| | | /** The date when the entry was added. */ |
| | | private ChangeNumber entryADDDate = null; |
| | | private CSN entryADDDate = null; |
| | | |
| | | /** The date when the entry was last renamed. */ |
| | | private ChangeNumber entryMODDNDate = null; |
| | | private CSN entryMODDNDate = null; |
| | | |
| | | /** |
| | | * Contains Historical information for each attribute sorted by attribute |
| | |
| | | { |
| | | boolean bConflict = false; |
| | | List<Modification> mods = modifyOperation.getModifications(); |
| | | ChangeNumber modOpChangeNumber = |
| | | OperationContext.getChangeNumber(modifyOperation); |
| | | CSN modOpCSN = OperationContext.getCSN(modifyOperation); |
| | | |
| | | for (Iterator<Modification> modsIterator = mods.iterator(); |
| | | modsIterator.hasNext(); ) |
| | |
| | | // contained in the mod |
| | | AttrHistorical attrHist = getOrCreateAttrHistorical(m); |
| | | |
| | | if (attrHist.replayOperation(modsIterator, modOpChangeNumber, |
| | | modifiedEntry, m)) |
| | | if (attrHist.replayOperation(modsIterator, modOpCSN, modifiedEntry, m)) |
| | | { |
| | | bConflict = true; |
| | | } |
| | |
| | | { |
| | | List<Modification> mods = modifyOperation.getModifications(); |
| | | Entry modifiedEntry = modifyOperation.getModifiedEntry(); |
| | | ChangeNumber changeNumber = |
| | | OperationContext.getChangeNumber(modifyOperation); |
| | | CSN csn = OperationContext.getCSN(modifyOperation); |
| | | |
| | | /* |
| | | * If this is a local operation we need : |
| | |
| | | // (eventually read from the provided modification) |
| | | AttrHistorical attrHist = getOrCreateAttrHistorical(mod); |
| | | if (attrHist != null) |
| | | attrHist.processLocalOrNonConflictModification(changeNumber, mod); |
| | | attrHist.processLocalOrNonConflictModification(csn, mod); |
| | | } |
| | | } |
| | | |
| | |
| | | public void setHistoricalAttrToOperation( |
| | | PreOperationModifyDNOperation modifyDNOperation) |
| | | { |
| | | // Update this historical information with the operation ChangeNumber. |
| | | this.entryMODDNDate = OperationContext.getChangeNumber(modifyDNOperation); |
| | | // Update this historical information with the operation CSN. |
| | | this.entryMODDNDate = OperationContext.getCSN(modifyDNOperation); |
| | | |
| | | // Update the operations mods and the modified entry so that the |
| | | // historical information gets stored in the DB and indexed accordingly. |
| | |
| | | * from the replication context attached to the provided operation |
| | | * and set this attribute in the operation. |
| | | * |
| | | * For ADD, the historical is made of the changeNumber read from the |
| | | * For ADD, the historical is made of the CSN read from the |
| | | * synchronization context attached to the operation. |
| | | * |
| | | * Called for both local and synchronization ADD preOperation. |
| | |
| | | AttributeType historicalAttrType = |
| | | DirectoryServer.getSchema().getAttributeType(HISTORICAL_ATTRIBUTE_NAME); |
| | | |
| | | // Get the changeNumber from the attached synchronization context |
| | | // Get the CSN from the attached synchronization context |
| | | // Create the attribute (encoded) |
| | | ChangeNumber addCn = OperationContext.getChangeNumber(addOperation); |
| | | AttributeValue attrValue = encodeHistorical(addCn, "add"); |
| | | CSN addCSN = OperationContext.getCSN(addOperation); |
| | | AttributeValue attrValue = encodeHistorical(addCSN, "add"); |
| | | Attribute attr = Attributes.create(historicalAttrType, attrValue); |
| | | |
| | | // Set the created attribute to the operation |
| | |
| | | * operation type . For ADD Operation : "dn:changeNumber:add", for MODDN |
| | | * Operation : "dn:changeNumber:moddn", etc. |
| | | * |
| | | * @param cn |
| | | * @param csn |
| | | * The date when the ADD Operation happened. |
| | | * @param operationType |
| | | * the operation type to encode |
| | | * @return The attribute value containing the historical information for the |
| | | * Operation type. |
| | | */ |
| | | private static AttributeValue encodeHistorical(ChangeNumber cn, |
| | | String operationType) |
| | | private static AttributeValue encodeHistorical(CSN csn, String operationType) |
| | | { |
| | | AttributeType historicalAttrType = |
| | | DirectoryServer.getSchema().getAttributeType(HISTORICAL_ATTRIBUTE_NAME); |
| | | |
| | | String strValue = "dn:" + cn + ":" + operationType; |
| | | String strValue = "dn:" + csn + ":" + operationType; |
| | | return AttributeValues.create(historicalAttrType, strValue); |
| | | } |
| | | |
| | |
| | | optionsString = optionsBuilder.toString(); |
| | | } |
| | | |
| | | ChangeNumber deleteTime = attrHist.getDeleteTime(); |
| | | CSN deleteTime = attrHist.getDeleteTime(); |
| | | /* generate the historical information for deleted attributes */ |
| | | boolean delAttr = deleteTime != null; |
| | | |
| | |
| | | continue; |
| | | } |
| | | |
| | | final ChangeNumber updateTime = attrValHist.getValueUpdateTime(); |
| | | final CSN updateTime = attrValHist.getValueUpdateTime(); |
| | | // FIXME very suspicious use of == in the next if statement, |
| | | // unit tests do not like changing it |
| | | if (delAttr && updateTime == deleteTime && value != null) |
| | |
| | | return builder.toAttribute(); |
| | | } |
| | | |
| | | private boolean needsPurge(ChangeNumber cn, long purgeDate) |
| | | private boolean needsPurge(CSN csn, long purgeDate) |
| | | { |
| | | boolean needsPurge = purgeDelayInMillisec > 0 && cn.getTime() <= purgeDate; |
| | | boolean needsPurge = purgeDelayInMillisec > 0 && csn.getTime() <= purgeDate; |
| | | if (needsPurge) |
| | | { |
| | | // this hist must be purged now, because older than the purge delay |
| | |
| | | } |
| | | |
| | | private String encode(String operation, AttributeType type, |
| | | String optionsString, ChangeNumber changeTime) |
| | | String optionsString, CSN changeTime) |
| | | { |
| | | return type.getNormalizedPrimaryName() + optionsString + ":" + changeTime |
| | | + ":" + operation; |
| | | } |
| | | |
| | | private String encode(String operation, AttributeType type, |
| | | String optionsString, ChangeNumber changeTime, AttributeValue value) |
| | | String optionsString, CSN changeTime, AttributeValue value) |
| | | { |
| | | return type.getNormalizedPrimaryName() + optionsString + ":" + changeTime |
| | | + ":" + operation + ":" + value; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Indicates if the Entry was renamed or added after the ChangeNumber |
| | | * that is given as a parameter. |
| | | * Indicates if the Entry was renamed or added after the CSN that is given as |
| | | * a parameter. |
| | | * |
| | | * @param cn The ChangeNumber with which the ADD or Rename date must be |
| | | * compared. |
| | | * |
| | | * @return A boolean indicating if the Entry was renamed or added after |
| | | * the ChangeNumber that is given as a parameter. |
| | | * @param csn |
| | | * The CSN with which the ADD or Rename date must be compared. |
| | | * @return A boolean indicating if the Entry was renamed or added after the |
| | | * CSN that is given as a parameter. |
| | | */ |
| | | public boolean addedOrRenamedAfter(ChangeNumber cn) |
| | | public boolean addedOrRenamedAfter(CSN csn) |
| | | { |
| | | return cn.older(entryADDDate) || cn.older(entryMODDNDate); |
| | | return csn.older(entryADDDate) || csn.older(entryMODDNDate); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns the lastChangeNumber when the entry DN was modified. |
| | | * Returns the lastCSN when the entry DN was modified. |
| | | * |
| | | * @return The lastChangeNumber when the entry DN was modified. |
| | | * @return The lastCSN when the entry DN was modified. |
| | | */ |
| | | public ChangeNumber getDNDate() |
| | | public CSN getDNDate() |
| | | { |
| | | if (entryADDDate == null) |
| | | return entryMODDNDate; |
| | |
| | | |
| | | AttributeType attrType = histVal.getAttrType(); |
| | | Set<String> options = histVal.getOptions(); |
| | | ChangeNumber cn = histVal.getCn(); |
| | | CSN csn = histVal.getCSN(); |
| | | AttributeValue value = histVal.getAttributeValue(); |
| | | HistAttrModificationKey histKey = histVal.getHistKey(); |
| | | |
| | | // update the oldest ChangeNumber stored in the new entry historical |
| | | newHistorical.updateOldestCN(cn); |
| | | // update the oldest CSN stored in the new entry historical |
| | | newHistorical.updateOldestCSN(csn); |
| | | |
| | | if (histVal.isADDOperation()) |
| | | { |
| | | newHistorical.entryADDDate = cn; |
| | | newHistorical.entryADDDate = csn; |
| | | } |
| | | else if (histVal.isMODDNOperation()) |
| | | { |
| | | newHistorical.entryMODDNDate = cn; |
| | | newHistorical.entryMODDNDate = csn; |
| | | } |
| | | else |
| | | { |
| | |
| | | lastOptions = options; |
| | | } |
| | | |
| | | attrInfo.assign(histKey, value, cn); |
| | | attrInfo.assign(histKey, value, csn); |
| | | } |
| | | } |
| | | } |
| | |
| | | */ |
| | | public static Iterable<FakeOperation> generateFakeOperations(Entry entry) |
| | | { |
| | | TreeMap<ChangeNumber, FakeOperation> operations = |
| | | new TreeMap<ChangeNumber, FakeOperation>(); |
| | | TreeMap<CSN, FakeOperation> operations = |
| | | new TreeMap<CSN, FakeOperation>(); |
| | | List<Attribute> attrs = getHistoricalAttr(entry); |
| | | if (attrs != null) |
| | | { |
| | |
| | | // Found some historical information indicating that this |
| | | // entry was just added. |
| | | // Create the corresponding ADD operation. |
| | | operations.put(histVal.getCn(), |
| | | new FakeAddOperation(histVal.getCn(), entry)); |
| | | operations.put(histVal.getCSN(), |
| | | new FakeAddOperation(histVal.getCSN(), entry)); |
| | | } |
| | | else if (histVal.isMODDNOperation()) |
| | | { |
| | | // Found some historical information indicating that this |
| | | // entry was just renamed. |
| | | // Create the corresponding ADD operation. |
| | | operations.put(histVal.getCn(), |
| | | new FakeModdnOperation(histVal.getCn(), entry)); |
| | | operations.put(histVal.getCSN(), |
| | | new FakeModdnOperation(histVal.getCSN(), entry)); |
| | | } |
| | | else |
| | | { |
| | | // Found some historical information for modify operation. |
| | | // Generate the corresponding ModifyOperation or update |
| | | // the already generated Operation if it can be found. |
| | | ChangeNumber cn = histVal.getCn(); |
| | | CSN csn = histVal.getCSN(); |
| | | Modification mod = histVal.generateMod(); |
| | | FakeOperation fakeOperation = operations.get(cn); |
| | | FakeOperation fakeOperation = operations.get(csn); |
| | | |
| | | if (fakeOperation instanceof FakeModifyOperation) |
| | | { |
| | |
| | | { |
| | | String uuidString = getEntryUUID(entry); |
| | | FakeModifyOperation modifyFakeOperation = |
| | | new FakeModifyOperation(entry.getDN(), cn, uuidString); |
| | | new FakeModifyOperation(entry.getDN(), csn, uuidString); |
| | | modifyFakeOperation.addModification(mod); |
| | | operations.put(histVal.getCn(), modifyFakeOperation); |
| | | operations.put(histVal.getCSN(), modifyFakeOperation); |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * Potentially update the oldest ChangeNumber stored in this entry historical |
| | | * with the provided ChangeNumber when its older than the current oldest. |
| | | * Potentially update the oldest CSN stored in this entry historical |
| | | * with the provided CSN when its older than the current oldest. |
| | | * |
| | | * @param cn the provided ChangeNumber. |
| | | * @param csn the provided CSN. |
| | | */ |
| | | private void updateOldestCN(ChangeNumber cn) |
| | | private void updateOldestCSN(CSN csn) |
| | | { |
| | | if (cn != null |
| | | && (this.oldestChangeNumber == null |
| | | || cn.older(this.oldestChangeNumber))) |
| | | this.oldestChangeNumber = cn; |
| | | if (csn != null |
| | | && (this.oldestCSN == null || csn.older(this.oldestCSN))) |
| | | this.oldestCSN = csn; |
| | | } |
| | | |
| | | /** |
| | | * Returns the oldest ChangeNumber stored in this entry historical attribute. |
| | | * Returns the oldest CSN stored in this entry historical attribute. |
| | | * |
| | | * @return the oldest ChangeNumber stored in this entry historical attribute. |
| | | * @return the oldest CSN stored in this entry historical attribute. |
| | | * Returns null when this historical object has been created from |
| | | * an entry that has no historical attribute and after the last |
| | | * historical has been purged. |
| | | */ |
| | | public ChangeNumber getOldestCN() |
| | | public CSN getOldestCSN() |
| | | { |
| | | return this.oldestChangeNumber; |
| | | return this.oldestCSN; |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * |
| | | * Copyright 2008-2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.AddMsg; |
| | | import org.opends.server.types.Entry; |
| | | |
| | |
| | | /** |
| | | * Creates a new AddFakeOperations. |
| | | * |
| | | * @param cn The ChangeNumber when the entry was created. |
| | | * @param csn The CSN when the entry was created. |
| | | * @param entry The entry that the ADD operation will create. |
| | | */ |
| | | public FakeAddOperation(ChangeNumber cn, Entry entry) |
| | | public FakeAddOperation(CSN csn, Entry entry) |
| | | { |
| | | super(cn); |
| | | super(csn); |
| | | this.entry = entry; |
| | | } |
| | | |
| | |
| | | @Override |
| | | public AddMsg generateMessage() |
| | | { |
| | | return new AddMsg(getChangeNumber(), entry.getDN().toString(), |
| | | return new AddMsg(getCSN(), entry.getDN().toString(), |
| | | EntryHistorical.getEntryUUID(entry), |
| | | LDAPReplicationDomain.findEntryUUID( |
| | | entry.getDN().getParentDNInSuffix()), |
| | |
| | | * |
| | | * |
| | | * Copyright 2009 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.DeleteMsg; |
| | | import org.opends.server.replication.protocol.ReplicationMsg; |
| | | |
| | | /** |
| | | * |
| | | * This class if used to build pseudo DEL Operation from the historical |
| | | * information that stay in the entry in the database. |
| | | * |
| | | * This is useful when a LDAP server can't find a LDAP server that |
| | | * has already seen all its changes and therefore need to retransmit them. |
| | | * |
| | | */ |
| | | public class FakeDelOperation extends FakeOperation |
| | | { |
| | | final private String dn; |
| | | private final String dn; |
| | | private final String entryUUID; |
| | | |
| | | /** |
| | | * Creates a new FakeDelOperation from the provided information. |
| | | * |
| | | * @param dn The dn of the entry that was deleted. |
| | | * @param changeNumber The ChangeNumber of the operation. |
| | | * @param csn The CSN of the operation. |
| | | * @param entryUUID The Unique ID of the deleted entry. |
| | | */ |
| | | public FakeDelOperation(String dn, ChangeNumber changeNumber, |
| | | String entryUUID) |
| | | public FakeDelOperation(String dn, CSN csn, String entryUUID) |
| | | { |
| | | super(changeNumber); |
| | | super(csn); |
| | | this.dn = dn; |
| | | this.entryUUID = entryUUID; |
| | | } |
| | |
| | | @Override |
| | | public ReplicationMsg generateMessage() |
| | | { |
| | | return new DeleteMsg(dn, this.getChangeNumber(), entryUUID); |
| | | return new DeleteMsg(dn, getCSN(), entryUUID); |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * |
| | | * Copyright 2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.ModifyDNMsg; |
| | | import org.opends.server.replication.protocol.ReplicationMsg; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | |
| | | |
| | | /** |
| | | * This class if used to build fake MODDN Operation from the historical |
| | | * information that stay in the entry in the database. |
| | | * |
| | | * This is useful when a LDAP server can't find a LDAP server that |
| | | * has already seen all its changes and therefore need to retransmit them. |
| | | * |
| | | */ |
| | | public class FakeModdnOperation extends FakeOperation |
| | | { |
| | |
| | | /** |
| | | * Creates a new FakeModdnOperation. |
| | | * |
| | | * @param cn The ChangeNumber when the entry was last renamed. |
| | | * @param csn The CSN when the entry was last renamed. |
| | | * @param entry The entry that the MODDN operation will rename. |
| | | */ |
| | | public FakeModdnOperation(ChangeNumber cn, Entry entry) |
| | | public FakeModdnOperation(CSN csn, Entry entry) |
| | | { |
| | | super(cn); |
| | | super(csn); |
| | | this.entry = entry; |
| | | } |
| | | |
| | |
| | | public ReplicationMsg generateMessage() |
| | | { |
| | | DN dn = entry.getDN(); |
| | | return new ModifyDNMsg(dn.toString(), this.getChangeNumber(), |
| | | return new ModifyDNMsg(dn.toString(), getCSN(), |
| | | EntryHistorical.getEntryUUID(entry), |
| | | LDAPReplicationDomain.findEntryUUID(dn.getParent()), |
| | | false, dn.getParent().toString(), dn.getRDN().toString()); |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.ModifyMsg; |
| | | import org.opends.server.replication.protocol.ReplicationMsg; |
| | | import org.opends.server.types.DN; |
| | |
| | | * information that stay in the entry in the database. |
| | | * |
| | | * This is useful when a LDAP server can't find a LDAP server that |
| | | * has already seen all its changes and therefore need to retransmit them |
| | | * |
| | | * has already seen all its changes and therefore need to retransmit them. |
| | | */ |
| | | public class FakeModifyOperation extends FakeOperation |
| | | { |
| | | private ArrayList<Modification> mods = new ArrayList<Modification>(); |
| | | private List<Modification> mods = new ArrayList<Modification>(); |
| | | private DN dn; |
| | | private String entryuuid; |
| | | |
| | |
| | | * Creates a new ModifyFakeOperation with the provided information. |
| | | * |
| | | * @param dn The DN on which the Operation was applied. |
| | | * @param changenumber The ChangeNumber of the operation. |
| | | * @param csn The CSN of the operation. |
| | | * @param entryuuid The unique ID of the entry on which the Operation applies. |
| | | */ |
| | | public FakeModifyOperation(DN dn, ChangeNumber changenumber, String entryuuid) |
| | | public FakeModifyOperation(DN dn, CSN csn, String entryuuid) |
| | | { |
| | | super(changenumber); |
| | | super(csn); |
| | | this.dn = dn; |
| | | this.entryuuid = entryuuid; |
| | | } |
| | |
| | | @Override |
| | | public ReplicationMsg generateMessage() |
| | | { |
| | | return new ModifyMsg(super.getChangeNumber(), dn, mods, entryuuid); |
| | | return new ModifyMsg(getCSN(), dn, mods, entryuuid); |
| | | } |
| | | } |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.ReplicationMsg; |
| | | |
| | | |
| | | /** |
| | | * This class if used to build fake Operation from the historical |
| | | * information that stay in the entry in the database. |
| | | * |
| | | * This is useful when a LDAP server can't find a LDAP server that |
| | | * has already seen all its changes and therefore need to retransmit them |
| | | * |
| | | * has already seen all its changes and therefore need to retransmit them. |
| | | */ |
| | | public abstract class FakeOperation |
| | | { |
| | | private ChangeNumber changeNumber; |
| | | private CSN csn; |
| | | |
| | | /** |
| | | * Creates a new FakeOperation using the provided ChangeNumber. |
| | | * Creates a new FakeOperation using the provided CSN. |
| | | * |
| | | * @param changeNumber The ChangeNumber to use to build the FakeOperation. |
| | | * @param csn The CSN to use to build the FakeOperation. |
| | | */ |
| | | public FakeOperation(ChangeNumber changeNumber) |
| | | public FakeOperation(CSN csn) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | this.csn = csn; |
| | | } |
| | | |
| | | /** |
| | | * Get the ChangeNumber. |
| | | * Get the CSN. |
| | | * |
| | | * @return Returns the changeNumber. |
| | | * @return Returns the CSN. |
| | | */ |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | return changeNumber; |
| | | return csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import java.util.Comparator; |
| | | |
| | | |
| | | /** |
| | | * This Class implements a Comparator that can be used to build TreeSet |
| | | * containing FakeOperations sorted by the ChangeNumber order. |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public int compare(FakeOperation op1, FakeOperation op2) |
| | | { |
| | | if (op1 == null) |
| | | return -1; |
| | | return op1.getChangeNumber().compareTo(op2.getChangeNumber()); |
| | | return op1.getCSN().compareTo(op2.getCSN()); |
| | | } |
| | | } |
| | |
| | | import java.util.Set; |
| | | |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.*; |
| | | |
| | | |
| | | /** |
| | | * This class stores an internal usable representation of the value of |
| | | * the historical related to an entry. |
| | |
| | | * |
| | | * so after split |
| | | * token[0] will contain the attribute name |
| | | * token[1] will contain the change number |
| | | * token[1] will contain the CSN |
| | | * token[2] will contain the type of historical information |
| | | * token[3] will contain the attribute value |
| | | * |
| | |
| | | private AttributeType attrType; |
| | | private String attrString; |
| | | private AttributeValue attributeValue; |
| | | private ChangeNumber cn; |
| | | private LinkedHashSet<String> options; |
| | | private CSN csn; |
| | | private Set<String> options; |
| | | private HistAttrModificationKey histKey; |
| | | private String stringValue; |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | cn = new ChangeNumber(token[1]); |
| | | csn = new CSN(token[1]); |
| | | histKey = HistAttrModificationKey.decodeKey(token[2]); |
| | | stringValue = null; |
| | | if (histKey != HistAttrModificationKey.DELATTR) |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the ChangeNUmber of this HistVal. |
| | | * @return Returns the ChangeNumber of this HistVal. |
| | | * Get the CSN of this HistVal. |
| | | * @return Returns the CSN of this HistVal. |
| | | */ |
| | | public ChangeNumber getCn() |
| | | public CSN getCSN() |
| | | { |
| | | return cn; |
| | | return csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | private class ScanSearchListener implements InternalSearchListener |
| | | { |
| | | private final ChangeNumber startingChangeNumber; |
| | | private final ChangeNumber endChangeNumber; |
| | | private final CSN startCSN; |
| | | private final CSN endCSN; |
| | | |
| | | public ScanSearchListener( |
| | | ChangeNumber startingChangeNumber, |
| | | ChangeNumber endChangeNumber) |
| | | public ScanSearchListener(CSN startCSN, CSN endCSN) |
| | | { |
| | | this.startingChangeNumber = startingChangeNumber; |
| | | this.endChangeNumber = endChangeNumber; |
| | | this.startCSN = startCSN; |
| | | this.endCSN = endCSN; |
| | | } |
| | | |
| | | @Override |
| | |
| | | InternalSearchOperation searchOperation, SearchResultEntry searchEntry) |
| | | throws DirectoryException |
| | | { |
| | | // Build the list of Operations that happened on this entry |
| | | // after startingChangeNumber and before endChangeNumber and |
| | | // add them to the replayOperations list |
| | | // Build the list of Operations that happened on this entry after startCSN |
| | | // and before endCSN and add them to the replayOperations list |
| | | Iterable<FakeOperation> updates = |
| | | EntryHistorical.generateFakeOperations(searchEntry); |
| | | |
| | | for (FakeOperation op : updates) |
| | | { |
| | | ChangeNumber cn = op.getChangeNumber(); |
| | | if ((cn.newer(startingChangeNumber)) && (cn.older(endChangeNumber))) |
| | | CSN csn = op.getCSN(); |
| | | if (csn.newer(startCSN) && csn.older(endCSN)) |
| | | { |
| | | synchronized (replayOperations) |
| | | { |
| | | replayOperations.put(cn, op); |
| | | replayOperations.put(csn, op); |
| | | } |
| | | } |
| | | } |
| | |
| | | private volatile long generationId = -1; |
| | | private volatile boolean generationIdSavedStatus = false; |
| | | |
| | | private final ChangeNumberGenerator generator; |
| | | private final CSNGenerator generator; |
| | | |
| | | /** |
| | | * This object is used to store the list of update currently being |
| | |
| | | * This list is used to temporary store operations that needs to be replayed |
| | | * at session establishment time. |
| | | */ |
| | | private final SortedMap<ChangeNumber, FakeOperation> replayOperations = |
| | | new TreeMap<ChangeNumber, FakeOperation>(); |
| | | private final SortedMap<CSN, FakeOperation> replayOperations = |
| | | new TreeMap<CSN, FakeOperation>(); |
| | | |
| | | /** |
| | | * The isolation policy that this domain is going to use. |
| | |
| | | |
| | | /** |
| | | * This configuration boolean indicates if this ReplicationDomain should log |
| | | * ChangeNumbers. |
| | | * CSNs. |
| | | */ |
| | | private boolean logChangeNumber = false; |
| | | private boolean logCSN = false; |
| | | |
| | | /** |
| | | * This configuration integer indicates the time the domain keeps the |
| | | * historical information necessary to solve conflicts.<br> |
| | | * When a change stored in the historical part of the user entry has a date |
| | | * (from its replication ChangeNumber) older than this delay, it is candidate |
| | | * to be purged. |
| | | * (from its replication CSN) older than this delay, it is candidate to be |
| | | * purged. |
| | | */ |
| | | private long histPurgeDelayInMilliSec = 0; |
| | | |
| | | /** |
| | | * The last change number purged in this domain. Allows to have a continuous |
| | | * purging process from one purge processing (task run) to the next one. |
| | | * Values 0 when the server starts. |
| | | * The last CSN purged in this domain. Allows to have a continuous purging |
| | | * process from one purge processing (task run) to the next one. Values 0 when |
| | | * the server starts. |
| | | */ |
| | | private ChangeNumber lastChangeNumberPurgedFromHist = new ChangeNumber(0,0,0); |
| | | private CSN lastCSNPurgedFromHist = new CSN(0,0,0); |
| | | |
| | | /** |
| | | * The thread that periodically saves the ServerState of this |
| | |
| | | */ |
| | | private class RSUpdater extends DirectoryThread |
| | | { |
| | | private final ChangeNumber startChangeNumber; |
| | | private final CSN startCSN; |
| | | |
| | | |
| | | |
| | | protected RSUpdater(ChangeNumber replServerMaxChangeNumber) |
| | | protected RSUpdater(CSN replServerMaxCSN) |
| | | { |
| | | super("Replica DS(" + serverId |
| | | + ") missing change publisher for domain \"" |
| | | + baseDn.toString() + "\""); |
| | | this.startChangeNumber = replServerMaxChangeNumber; |
| | | this.startCSN = replServerMaxCSN; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | try |
| | | { |
| | | if (buildAndPublishMissingChanges(startChangeNumber, broker)) |
| | | if (buildAndPublishMissingChanges(startCSN, broker)) |
| | | { |
| | | message = DEBUG_CHANGES_SENT.get(); |
| | | logError(message); |
| | |
| | | |
| | | this.isolationPolicy = configuration.getIsolationPolicy(); |
| | | this.configDn = configuration.dn(); |
| | | this.logChangeNumber = configuration.isLogChangenumber(); |
| | | this.logCSN = configuration.isLogChangenumber(); |
| | | this.updateToReplayQueue = updateToReplayQueue; |
| | | this.histPurgeDelayInMilliSec = |
| | | configuration.getConflictsHistoricalPurgeDelay()*60*1000; |
| | |
| | | |
| | | /* |
| | | * Create a new Persistent Server State that will be used to store |
| | | * the last ChangeNumber seen from all LDAP servers in the topology. |
| | | * the last CSN seen from all LDAP servers in the topology. |
| | | */ |
| | | state = new PersistentServerState(baseDn, serverId, getServerState()); |
| | | |
| | | flushThread = new ServerStateFlush(); |
| | | |
| | | /* |
| | | * ChangeNumberGenerator is used to create new unique ChangeNumbers |
| | | * for each operation done on this replication domain. |
| | | * CSNGenerator is used to create new unique CSNs for each operation done on |
| | | * this replication domain. |
| | | * |
| | | * The generator time is adjusted to the time of the last CN received from |
| | | * The generator time is adjusted to the time of the last CSN received from |
| | | * remote other servers. |
| | | */ |
| | | generator = getGenerator(); |
| | |
| | | { |
| | | // There is no replication context attached to the operation |
| | | // so this is not a replication operation. |
| | | ChangeNumber changeNumber = generateChangeNumber(deleteOperation); |
| | | CSN csn = generateCSN(deleteOperation); |
| | | String modifiedEntryUUID = EntryHistorical.getEntryUUID(deletedEntry); |
| | | ctx = new DeleteContext(changeNumber, modifiedEntryUUID); |
| | | ctx = new DeleteContext(csn, modifiedEntryUUID); |
| | | deleteOperation.setAttachment(SYNCHROCONTEXT, ctx); |
| | | |
| | | synchronized (replayOperations) |
| | |
| | | replayOperations.remove(replayOperations.firstKey()); |
| | | } |
| | | replayOperations.put( |
| | | changeNumber, |
| | | csn, |
| | | new FakeDelOperation( |
| | | deleteOperation.getEntryDN().toString(), |
| | | changeNumber,modifiedEntryUUID )); |
| | | csn, modifiedEntryUUID)); |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | EntryHistorical hist = EntryHistorical.newInstanceFromEntry( |
| | | modifyDNOperation.getOriginalEntry()); |
| | | if (hist.addedOrRenamedAfter(ctx.getChangeNumber())) |
| | | if (hist.addedOrRenamedAfter(ctx.getCSN())) |
| | | { |
| | | return new SynchronizationProviderResult.StopProcessing( |
| | | ResultCode.NO_OPERATION, null); |
| | |
| | | { |
| | | // There is no replication context attached to the operation |
| | | // so this is not a replication operation. |
| | | ChangeNumber changeNumber = generateChangeNumber(modifyDNOperation); |
| | | CSN csn = generateCSN(modifyDNOperation); |
| | | String newParentId = null; |
| | | if (modifyDNOperation.getNewSuperior() != null) |
| | | { |
| | |
| | | |
| | | Entry modifiedEntry = modifyDNOperation.getOriginalEntry(); |
| | | String modifiedEntryUUID = EntryHistorical.getEntryUUID(modifiedEntry); |
| | | ctx = new ModifyDnContext(changeNumber, modifiedEntryUUID, newParentId); |
| | | ctx = new ModifyDnContext(csn, modifiedEntryUUID, newParentId); |
| | | modifyDNOperation.setAttachment(SYNCHROCONTEXT, ctx); |
| | | } |
| | | return new SynchronizationProviderResult.ContinueProcessing(); |
| | |
| | | public SynchronizationProviderResult handleConflictResolution( |
| | | PreOperationModifyOperation modifyOperation) |
| | | { |
| | | if ((!modifyOperation.isSynchronizationOperation()) |
| | | && (!brokerIsConnected())) |
| | | if (!modifyOperation.isSynchronizationOperation() && !brokerIsConnected()) |
| | | { |
| | | Message msg = ERR_REPLICATION_COULD_NOT_CONNECT.get(baseDn.toString()); |
| | | return new SynchronizationProviderResult.StopProcessing( |
| | |
| | | if (ctx == null) |
| | | { |
| | | // No replication ctx attached => not a replicated operation |
| | | // - create a ctx with : changeNumber, entryUUID |
| | | // - create a ctx with : CSN, entryUUID |
| | | // - attach the context to the op |
| | | |
| | | ChangeNumber changeNumber = generateChangeNumber(modifyOperation); |
| | | CSN csn = generateCSN(modifyOperation); |
| | | String modifiedEntryUUID = EntryHistorical.getEntryUUID(modifiedEntry); |
| | | ctx = new ModifyContext(changeNumber, modifiedEntryUUID); |
| | | ctx = new ModifyContext(csn, modifiedEntryUUID); |
| | | |
| | | modifyOperation.setAttachment(SYNCHROCONTEXT, ctx); |
| | | } |
| | |
| | | */ |
| | | public void doPreOperation(PreOperationAddOperation addOperation) |
| | | { |
| | | AddContext ctx = new AddContext(generateChangeNumber(addOperation), |
| | | AddContext ctx = new AddContext(generateCSN(addOperation), |
| | | EntryHistorical.getEntryUUID(addOperation), |
| | | findEntryUUID(addOperation.getEntryDN().getParentDNInSuffix())); |
| | | |
| | |
| | | ResultCode result = op.getResultCode(); |
| | | // Note that a failed non-replication operation might not have a change |
| | | // number. |
| | | ChangeNumber curChangeNumber = OperationContext.getChangeNumber(op); |
| | | if ((curChangeNumber != null) && (logChangeNumber)) |
| | | CSN curCSN = OperationContext.getCSN(op); |
| | | if (curCSN != null && logCSN) |
| | | { |
| | | op.addAdditionalLogItem(AdditionalLogItem.unquotedKeyValue(getClass(), |
| | | "replicationCN", curChangeNumber)); |
| | | "replicationCSN", curCSN)); |
| | | } |
| | | |
| | | if (result == ResultCode.SUCCESS) |
| | |
| | | numReplayedPostOpCalled++; |
| | | try |
| | | { |
| | | remotePendingChanges.commit(curChangeNumber); |
| | | remotePendingChanges.commit(curCSN); |
| | | } |
| | | catch (NoSuchElementException e) |
| | | { |
| | | Message message = ERR_OPERATION_NOT_FOUND_IN_PENDING.get( |
| | | op.toString(), curChangeNumber.toString()); |
| | | op.toString(), curCSN.toString()); |
| | | logError(message); |
| | | return; |
| | | } |
| | |
| | | * This is an operation type that we do not know about |
| | | * It should never happen. |
| | | */ |
| | | pendingChanges.remove(curChangeNumber); |
| | | pendingChanges.remove(curCSN); |
| | | Message message = |
| | | ERR_UNKNOWN_TYPE.get(op.getOperationType().toString()); |
| | | logError(message); |
| | |
| | | try |
| | | { |
| | | msg.encode(); |
| | | pendingChanges.commitAndPushCommittedChanges(curChangeNumber, msg); |
| | | pendingChanges.commitAndPushCommittedChanges(curCSN, msg); |
| | | } catch (UnsupportedEncodingException e) |
| | | { |
| | | // will be caught at publish time. |
| | |
| | | catch (NoSuchElementException e) |
| | | { |
| | | Message message = ERR_OPERATION_NOT_FOUND_IN_PENDING.get( |
| | | op.toString(), curChangeNumber.toString()); |
| | | op.toString(), curCSN.toString()); |
| | | logError(message); |
| | | return; |
| | | } |
| | |
| | | { |
| | | // Remove an unsuccessful non-replication operation from the pending |
| | | // changes list. |
| | | if (curChangeNumber != null) |
| | | if (curCSN != null) |
| | | { |
| | | pendingChanges.remove(curChangeNumber); |
| | | pendingChanges.remove(curCSN); |
| | | pendingChanges.pushCommittedChanges(); |
| | | } |
| | | } |
| | |
| | | attrs, null); |
| | | |
| | | Entry entryToRename = null; |
| | | ChangeNumber entryToRenameCN = null; |
| | | CSN entryToRenameCSN = null; |
| | | for (SearchResultEntry entry : searchOp.getSearchEntries()) |
| | | { |
| | | EntryHistorical history = EntryHistorical.newInstanceFromEntry(entry); |
| | | if (entryToRename == null) |
| | | { |
| | | entryToRename = entry; |
| | | entryToRenameCN = history.getDNDate(); |
| | | entryToRenameCSN = history.getDNDate(); |
| | | } |
| | | else if (!history.addedOrRenamedAfter(entryToRenameCN)) |
| | | else if (!history.addedOrRenamedAfter(entryToRenameCSN)) |
| | | { |
| | | // this conflict is older than the previous, keep it. |
| | | entryToRename = entry; |
| | | entryToRenameCN = history.getDNDate(); |
| | | entryToRenameCSN = history.getDNDate(); |
| | | } |
| | | } |
| | | |
| | |
| | | Operation op = null; |
| | | boolean replayDone = false; |
| | | boolean dependency = false; |
| | | ChangeNumber changeNumber = null; |
| | | CSN csn = null; |
| | | int retryCount = 10; |
| | | |
| | | // Try replay the operation, then flush (replaying) any pending operation |
| | |
| | | // are processed locally. |
| | | op.addRequestControl(new LDAPControl(OID_MANAGE_DSAIT_CONTROL)); |
| | | |
| | | changeNumber = OperationContext.getChangeNumber(op); |
| | | csn = OperationContext.getCSN(op); |
| | | op.run(); |
| | | |
| | | ResultCode result = op.getResultCode(); |
| | |
| | | // the update became a dummy update and the result |
| | | // of the conflict resolution phase is to do nothing. |
| | | // however we still need to push this change to the serverState |
| | | updateError(changeNumber); |
| | | updateError(csn); |
| | | } |
| | | else |
| | | { |
| | |
| | | logError(message); |
| | | numUnresolvedNamingConflicts.incrementAndGet(); |
| | | replayErrorMsg = message.toString(); |
| | | updateError(changeNumber); |
| | | updateError(csn); |
| | | } |
| | | } catch (ASN1Exception e) |
| | | { |
| | |
| | | replayErrorMsg = logDecodingOperationError(msg, e); |
| | | } catch (Exception e) |
| | | { |
| | | if (changeNumber != null) |
| | | if (csn != null) |
| | | { |
| | | /* |
| | | * An Exception happened during the replay process. |
| | |
| | | stackTraceToSingleLineString(e), op.toString()); |
| | | logError(message); |
| | | replayErrorMsg = message.toString(); |
| | | updateError(changeNumber); |
| | | updateError(csn); |
| | | } else |
| | | { |
| | | replayErrorMsg = logDecodingOperationError(msg, e); |
| | |
| | | // Prepare restart of loop |
| | | replayDone = false; |
| | | dependency = false; |
| | | changeNumber = null; |
| | | csn = null; |
| | | retryCount = 10; |
| | | |
| | | } while (msg != null); |
| | |
| | | * It is necessary because the postOperation does not always get |
| | | * called when error or Exceptions happen during the operation replay. |
| | | * |
| | | * @param changeNumber the ChangeNumber of the operation with error. |
| | | * @param csn the CSN of the operation with error. |
| | | */ |
| | | public void updateError(ChangeNumber changeNumber) |
| | | public void updateError(CSN csn) |
| | | { |
| | | try |
| | | { |
| | | remotePendingChanges.commit(changeNumber); |
| | | remotePendingChanges.commit(csn); |
| | | } |
| | | catch (NoSuchElementException e) |
| | | { |
| | |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugInfo( |
| | | "LDAPReplicationDomain.updateError: Unable to find remote " |
| | | + "pending change for change number %s", |
| | | changeNumber); |
| | | "LDAPReplicationDomain.updateError: Unable to find remote " |
| | | + "pending change for CSN %s", csn); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Generate a new change number and insert it in the pending list. |
| | | * Generate a new CSN and insert it in the pending list. |
| | | * |
| | | * @param operation The operation for which the change number must be |
| | | * generated. |
| | | * @return The new change number. |
| | | * @param operation |
| | | * The operation for which the CSN must be generated. |
| | | * @return The new CSN. |
| | | */ |
| | | private ChangeNumber generateChangeNumber(PluginOperation operation) |
| | | private CSN generateCSN(PluginOperation operation) |
| | | { |
| | | return pendingChanges.putLocalOperation(operation); |
| | | } |
| | |
| | | // The other type of errors can not be caused by naming conflicts. |
| | | // Log a message for the repair tool. |
| | | Message message = ERR_ERROR_REPLAYING_OPERATION.get( |
| | | op.toString(), ctx.getChangeNumber().toString(), |
| | | op.toString(), ctx.getCSN().toString(), |
| | | result.toString(), op.getErrorMessage().toString()); |
| | | logError(message); |
| | | return true; |
| | |
| | | // The other type of errors can not be caused by naming conflicts. |
| | | // Log a message for the repair tool. |
| | | Message message = ERR_ERROR_REPLAYING_OPERATION.get( |
| | | op.toString(), ctx.getChangeNumber().toString(), |
| | | op.toString(), ctx.getCSN().toString(), |
| | | result.toString(), op.getErrorMessage().toString()); |
| | | logError(message); |
| | | return true; |
| | |
| | | // The other type of errors can not be caused by naming conflicts. |
| | | // Log a message for the repair tool. |
| | | Message message = ERR_ERROR_REPLAYING_OPERATION.get( |
| | | op.toString(), ctx.getChangeNumber().toString(), |
| | | op.toString(), ctx.getCSN().toString(), |
| | | result.toString(), op.getErrorMessage().toString()); |
| | | logError(message); |
| | | return true; |
| | |
| | | // The other type of errors can not be caused by naming conflicts. |
| | | // log a message for the repair tool. |
| | | Message message = ERR_ERROR_REPLAYING_OPERATION.get( |
| | | op.toString(), ctx.getChangeNumber().toString(), |
| | | op.toString(), ctx.getCSN().toString(), |
| | | result.toString(), op.getErrorMessage().toString()); |
| | | logError(message); |
| | | return true; |
| | |
| | | state.clearInMemory(); |
| | | state.loadState(); |
| | | |
| | | generator.adjust(state.getMaxChangeNumber(serverId)); |
| | | generator.adjust(state.getMaxCSN(serverId)); |
| | | // Retrieves the generation ID associated with the data imported |
| | | |
| | | generationId = loadGenerationId(); |
| | |
| | | { |
| | | String includeAttributeStrings[] = |
| | | {"objectclass", "sn", "cn", "entryuuid"}; |
| | | HashSet<AttributeType> includeAttributes; |
| | | includeAttributes = new HashSet<AttributeType>(); |
| | | Set<AttributeType> includeAttributes = new HashSet<AttributeType>(); |
| | | for (String attrName : includeAttributeStrings) |
| | | { |
| | | AttributeType attrType = DirectoryServer.getAttributeType(attrName); |
| | |
| | | |
| | | |
| | | /** |
| | | * Push the modifications contained in the given parameter as |
| | | * a modification that would happen on a local server. |
| | | * The modifications are not applied to the local database, |
| | | * historical information is not updated but a ChangeNumber |
| | | * is generated and the ServerState associated to this domain is |
| | | * updated. |
| | | * @param modifications The modification to push |
| | | * Push the modifications contained in the given parameter as a modification |
| | | * that would happen on a local server. The modifications are not applied to |
| | | * the local database, historical information is not updated but a CSN is |
| | | * generated and the ServerState associated to this domain is updated. |
| | | * |
| | | * @param modifications |
| | | * The modification to push |
| | | */ |
| | | public void synchronizeModifications(List<Modification> modifications) |
| | | { |
| | |
| | | modifications); |
| | | LocalBackendModifyOperation localOp = new LocalBackendModifyOperation(op); |
| | | |
| | | ChangeNumber cn = generateChangeNumber(localOp); |
| | | OperationContext ctx = new ModifyContext(cn, "schema"); |
| | | CSN csn = generateCSN(localOp); |
| | | OperationContext ctx = new ModifyContext(csn, "schema"); |
| | | localOp.setAttachment(SYNCHROCONTEXT, ctx); |
| | | localOp.setResultCode(ResultCode.SUCCESS); |
| | | synchronize(localOp); |
| | |
| | | ReplicationDomainCfg configuration) |
| | | { |
| | | isolationPolicy = configuration.getIsolationPolicy(); |
| | | logChangeNumber = configuration.isLogChangenumber(); |
| | | logCSN = configuration.isLogChangenumber(); |
| | | histPurgeDelayInMilliSec = |
| | | configuration.getConflictsHistoricalPurgeDelay()*60*1000; |
| | | |
| | |
| | | { |
| | | try |
| | | { |
| | | DN eclConfigEntryDN = DN.decode( |
| | | "cn=external changeLog," + configDn); |
| | | |
| | | DN eclConfigEntryDN = DN.decode("cn=external changeLog," + configDn); |
| | | if (DirectoryServer.getConfigHandler().entryExists(eclConfigEntryDN)) |
| | | { |
| | | DirectoryServer.getConfigHandler().deleteEntry(eclConfigEntryDN, null); |
| | |
| | | * Check that the ReplicationServer has seen all our previous |
| | | * changes. |
| | | */ |
| | | ChangeNumber replServerMaxChangeNumber = |
| | | replicationServerState.getChangeNumber(serverId); |
| | | CSN replServerMaxCSN = replicationServerState.getCSN(serverId); |
| | | |
| | | // we don't want to update from here (a DS) an empty RS because |
| | | // normally the RS should have been updated by other RSes except for |
| | |
| | | // ... hence the RS we are connected to should not be empty |
| | | // ... or if it is empty, it is due to a voluntary reset |
| | | // and we don't want to update it with our changes that could be huge. |
| | | if ((replServerMaxChangeNumber != null) && |
| | | (replServerMaxChangeNumber.getSeqnum()!=0)) |
| | | if (replServerMaxCSN != null && replServerMaxCSN.getSeqnum() != 0) |
| | | { |
| | | ChangeNumber ourMaxChangeNumber = state.getMaxChangeNumber(serverId); |
| | | |
| | | if ((ourMaxChangeNumber != null) && |
| | | (!ourMaxChangeNumber.olderOrEqual(replServerMaxChangeNumber))) |
| | | CSN ourMaxCSN = state.getMaxCSN(serverId); |
| | | if (ourMaxCSN != null && !ourMaxCSN.olderOrEqual(replServerMaxCSN)) |
| | | { |
| | | pendingChanges.setRecovering(true); |
| | | broker.setRecoveryRequired(true); |
| | | new RSUpdater(replServerMaxChangeNumber).start(); |
| | | new RSUpdater(replServerMaxCSN).start(); |
| | | } |
| | | } |
| | | } catch (Exception e) |
| | |
| | | } |
| | | |
| | | /** |
| | | * Build the list of changes that have been processed by this server |
| | | * after the ChangeNumber given as a parameter and publish them |
| | | * using the given session. |
| | | * Build the list of changes that have been processed by this server after the |
| | | * CSN given as a parameter and publish them using the given session. |
| | | * |
| | | * @param startingChangeNumber The ChangeNumber where we need to start the |
| | | * search |
| | | * @param session The session to use to publish the changes |
| | | * |
| | | * @return A boolean indicating he success of the |
| | | * operation. |
| | | * @throws Exception if an Exception happens during the search. |
| | | * @param startCSN |
| | | * The CSN where we need to start the search |
| | | * @param session |
| | | * The session to use to publish the changes |
| | | * @return A boolean indicating he success of the operation. |
| | | * @throws Exception |
| | | * if an Exception happens during the search. |
| | | */ |
| | | public boolean buildAndPublishMissingChanges( |
| | | ChangeNumber startingChangeNumber, |
| | | ReplicationBroker session) |
| | | throws Exception |
| | | public boolean buildAndPublishMissingChanges(CSN startCSN, |
| | | ReplicationBroker session) throws Exception |
| | | { |
| | | // Trim the changes in replayOperations that are older than |
| | | // the startingChangeNumber. |
| | | // Trim the changes in replayOperations that are older than the startCSN. |
| | | synchronized (replayOperations) |
| | | { |
| | | Iterator<ChangeNumber> it = replayOperations.keySet().iterator(); |
| | | Iterator<CSN> it = replayOperations.keySet().iterator(); |
| | | while (it.hasNext()) |
| | | { |
| | | if (it.next().olderOrEqual(startingChangeNumber)) |
| | | if (it.next().olderOrEqual(startCSN)) |
| | | { |
| | | it.remove(); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | ChangeNumber lastRetrievedChange; |
| | | CSN lastRetrievedChange; |
| | | InternalSearchOperation op; |
| | | ChangeNumber currentStartChangeNumber = startingChangeNumber; |
| | | CSN currentStartCSN = startCSN; |
| | | do |
| | | { |
| | | lastRetrievedChange = null; |
| | |
| | | // So we search by interval of 10 seconds |
| | | // and store the results in the replayOperations list |
| | | // so that they are sorted before sending them. |
| | | long missingChangesDelta = currentStartChangeNumber.getTime() + 10000; |
| | | ChangeNumber endChangeNumber = |
| | | new ChangeNumber( |
| | | missingChangesDelta, 0xffffffff, serverId); |
| | | long missingChangesDelta = currentStartCSN.getTime() + 10000; |
| | | CSN endCSN = new CSN(missingChangesDelta, 0xffffffff, serverId); |
| | | |
| | | ScanSearchListener listener = |
| | | new ScanSearchListener(currentStartChangeNumber, endChangeNumber); |
| | | op = searchForChangedEntries( |
| | | baseDn, currentStartChangeNumber, endChangeNumber, listener); |
| | | new ScanSearchListener(currentStartCSN, endCSN); |
| | | op = searchForChangedEntries(baseDn, currentStartCSN, endCSN, listener); |
| | | |
| | | // Publish and remove all the changes from the replayOperations list |
| | | // that are older than the endChangeNumber. |
| | | // that are older than the endCSN. |
| | | List<FakeOperation> opsToSend = new LinkedList<FakeOperation>(); |
| | | synchronized (replayOperations) |
| | | { |
| | |
| | | while (itOp.hasNext()) |
| | | { |
| | | FakeOperation fakeOp = itOp.next(); |
| | | if ((fakeOp.getChangeNumber().olderOrEqual(endChangeNumber)) |
| | | && state.cover(fakeOp.getChangeNumber())) |
| | | if ((fakeOp.getCSN().olderOrEqual(endCSN)) |
| | | && state.cover(fakeOp.getCSN())) |
| | | { |
| | | lastRetrievedChange = fakeOp.getChangeNumber(); |
| | | lastRetrievedChange = fakeOp.getCSN(); |
| | | opsToSend.add(fakeOp); |
| | | itOp.remove(); |
| | | } |
| | |
| | | opsToSend.clear(); |
| | | if (lastRetrievedChange != null) |
| | | { |
| | | currentStartChangeNumber = lastRetrievedChange; |
| | | currentStartCSN = lastRetrievedChange; |
| | | } |
| | | else |
| | | { |
| | | currentStartChangeNumber = endChangeNumber; |
| | | currentStartCSN = endCSN; |
| | | } |
| | | |
| | | } while (pendingChanges.recoveryUntil(lastRetrievedChange) && |
| | |
| | | |
| | | |
| | | /** |
| | | * Search for the changes that happened since fromChangeNumber |
| | | * based on the historical attribute. The only changes that will |
| | | * be send will be the one generated on the serverId provided in |
| | | * fromChangeNumber. |
| | | * @param baseDn the base DN |
| | | * @param fromChangeNumber The ChangeNumber from which we want the changes |
| | | * @param lastChangeNumber The max ChangeNumber that the search should return |
| | | * @param resultListener The listener that will process the entries returned |
| | | * Search for the changes that happened since fromCSN based on the historical |
| | | * attribute. The only changes that will be send will be the one generated on |
| | | * the serverId provided in fromCSN. |
| | | * |
| | | * @param baseDn |
| | | * the base DN |
| | | * @param fromCSN |
| | | * The CSN from which we want the changes |
| | | * @param lastCSN |
| | | * The max CSN that the search should return |
| | | * @param resultListener |
| | | * The listener that will process the entries returned |
| | | * @return the internal search operation |
| | | * @throws Exception when raised. |
| | | * @throws Exception |
| | | * when raised. |
| | | */ |
| | | public static InternalSearchOperation searchForChangedEntries( |
| | | DN baseDn, |
| | | ChangeNumber fromChangeNumber, |
| | | ChangeNumber lastChangeNumber, |
| | | InternalSearchListener resultListener) |
| | | throws Exception |
| | | public static InternalSearchOperation searchForChangedEntries(DN baseDn, |
| | | CSN fromCSN, CSN lastCSN, InternalSearchListener resultListener) |
| | | throws Exception |
| | | { |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | Integer serverId = fromChangeNumber.getServerId(); |
| | | Integer serverId = fromCSN.getServerId(); |
| | | |
| | | String maxValueForId; |
| | | if (lastChangeNumber == null) |
| | | if (lastCSN == null) |
| | | { |
| | | maxValueForId = "ffffffffffffffff" + String.format("%04x", serverId) |
| | | + "ffffffff"; |
| | | } |
| | | else |
| | | { |
| | | maxValueForId = lastChangeNumber.toString(); |
| | | maxValueForId = lastCSN.toString(); |
| | | } |
| | | |
| | | LDAPFilter filter = LDAPFilter.decode( |
| | | "(&(" + EntryHistorical.HISTORICAL_ATTRIBUTE_NAME + ">=dummy:" |
| | | + fromChangeNumber + ")(" + EntryHistorical.HISTORICAL_ATTRIBUTE_NAME + |
| | | "<=dummy:" + maxValueForId + "))"); |
| | | "(&(" + HISTORICAL_ATTRIBUTE_NAME + ">=dummy:" + fromCSN + ")" + |
| | | "(" + HISTORICAL_ATTRIBUTE_NAME + "<=dummy:" + maxValueForId + "))"); |
| | | |
| | | Set<String> attrs = new LinkedHashSet<String>(1); |
| | | attrs.add(EntryHistorical.HISTORICAL_ATTRIBUTE_NAME); |
| | | attrs.add(EntryHistorical.ENTRYUUID_ATTRIBUTE_NAME); |
| | | Set<String> attrs = new LinkedHashSet<String>(3); |
| | | attrs.add(HISTORICAL_ATTRIBUTE_NAME); |
| | | attrs.add(ENTRYUUID_ATTRIBUTE_NAME); |
| | | attrs.add("*"); |
| | | return conn.processSearch( |
| | | ByteString.valueOf(baseDn.toString()), |
| | |
| | | } |
| | | |
| | | /** |
| | | * Search for the changes that happened since fromChangeNumber |
| | | * based on the historical attribute. The only changes that will |
| | | * be send will be the one generated on the serverId provided in |
| | | * fromChangeNumber. |
| | | * @param baseDn the base DN |
| | | * @param fromChangeNumber The change number from which we want the changes |
| | | * @param resultListener that will process the entries returned. |
| | | * Search for the changes that happened since fromCSN based on the historical |
| | | * attribute. The only changes that will be send will be the one generated on |
| | | * the serverId provided in fromCSN. |
| | | * |
| | | * @param baseDn |
| | | * the base DN |
| | | * @param fromCSN |
| | | * The CSN from which we want the changes |
| | | * @param resultListener |
| | | * that will process the entries returned. |
| | | * @return the internal search operation |
| | | * @throws Exception when raised. |
| | | * @throws Exception |
| | | * when raised. |
| | | */ |
| | | public static InternalSearchOperation searchForChangedEntries( |
| | | DN baseDn, |
| | | ChangeNumber fromChangeNumber, |
| | | InternalSearchListener resultListener) |
| | | throws Exception |
| | | public static InternalSearchOperation searchForChangedEntries(DN baseDn, |
| | | CSN fromCSN, InternalSearchListener resultListener) throws Exception |
| | | { |
| | | return searchForChangedEntries( |
| | | baseDn, fromChangeNumber, null, resultListener); |
| | | return searchForChangedEntries(baseDn, fromCSN, null, resultListener); |
| | | } |
| | | |
| | | |
| | |
| | | public long countEntries() throws DirectoryException |
| | | { |
| | | Backend backend = retrievesBackend(baseDn); |
| | | |
| | | if (!backend.supportsLDIFExport()) |
| | | { |
| | | Message message = ERR_INIT_EXPORT_NOT_SUPPORTED.get( |
| | |
| | | TRACER.debugInfo("[PURGE] purgeConflictsHistorical " |
| | | + "on domain: " + baseDn |
| | | + "endDate:" + new Date(endDate) |
| | | + "lastChangeNumberPurgedFromHist: " |
| | | + lastChangeNumberPurgedFromHist.toStringUI()); |
| | | + "lastCSNPurgedFromHist: " |
| | | + lastCSNPurgedFromHist.toStringUI()); |
| | | |
| | | LDAPFilter filter = null; |
| | | try |
| | | { |
| | | filter = LDAPFilter.decode( |
| | | "(" + EntryHistorical.HISTORICAL_ATTRIBUTE_NAME + ">=dummy:" |
| | | + lastChangeNumberPurgedFromHist + ")"); |
| | | + lastCSNPurgedFromHist + ")"); |
| | | |
| | | } catch (LDAPException e) |
| | | { |
| | | // Not possible. We know the filter just above is correct. |
| | | } |
| | | |
| | | Set<String> attrs = new LinkedHashSet<String>(1); |
| | | attrs.add(EntryHistorical.HISTORICAL_ATTRIBUTE_NAME); |
| | | attrs.add(EntryHistorical.ENTRYUUID_ATTRIBUTE_NAME); |
| | | Set<String> attrs = new LinkedHashSet<String>(3); |
| | | attrs.add(HISTORICAL_ATTRIBUTE_NAME); |
| | | attrs.add(ENTRYUUID_ATTRIBUTE_NAME); |
| | | attrs.add("*"); |
| | | InternalSearchOperation searchOp = conn.processSearch( |
| | | ByteString.valueOf(baseDn.toString()), |
| | |
| | | int count = 0; |
| | | |
| | | if (task != null) |
| | | task.setProgressStats(lastChangeNumberPurgedFromHist, count); |
| | | task.setProgressStats(lastCSNPurgedFromHist, count); |
| | | |
| | | for (SearchResultEntry entry : searchOp.getSearchEntries()) |
| | | { |
| | |
| | | } |
| | | |
| | | EntryHistorical entryHist = EntryHistorical.newInstanceFromEntry(entry); |
| | | lastChangeNumberPurgedFromHist = entryHist.getOldestCN(); |
| | | lastCSNPurgedFromHist = entryHist.getOldestCSN(); |
| | | entryHist.setPurgeDelay(this.histPurgeDelayInMilliSec); |
| | | Attribute attr = entryHist.encodeAndPurge(); |
| | | count += entryHist.getLastPurgedValuesCount(); |
| | |
| | | } |
| | | else if (task != null) |
| | | { |
| | | task.setProgressStats(lastChangeNumberPurgedFromHist, count); |
| | | task.setProgressStats(lastCSNPurgedFromHist, count); |
| | | } |
| | | } |
| | | } |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.protocol.LDAPUpdateMsg; |
| | | import org.opends.server.types.DN; |
| | |
| | | */ |
| | | public class PendingChange implements Comparable<PendingChange> |
| | | { |
| | | private ChangeNumber changeNumber; |
| | | private CSN changeNumber; |
| | | private boolean committed; |
| | | private LDAPUpdateMsg msg; |
| | | private PluginOperation op; |
| | | private ServerState dependencyState = null; |
| | | private DN targetDN = null; |
| | | private ServerState dependencyState; |
| | | private DN targetDN; |
| | | |
| | | /** |
| | | * Construct a new PendingChange. |
| | |
| | | * @param op the operation to use |
| | | * @param msg the message to use (can be null for local operations) |
| | | */ |
| | | public PendingChange(ChangeNumber changeNumber, |
| | | public PendingChange(CSN changeNumber, |
| | | PluginOperation op, |
| | | LDAPUpdateMsg msg) |
| | | { |
| | |
| | | * Get the ChangeNumber associated to this PendingChange. |
| | | * @return the ChangeNumber |
| | | */ |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | return changeNumber; |
| | | } |
| | |
| | | * @param changeNumber The ChangeNumber to add in the list of dependencies |
| | | * of this PendingChange. |
| | | */ |
| | | public void addDependency(ChangeNumber changeNumber) |
| | | public void addDependency(CSN changeNumber) |
| | | { |
| | | if (dependencyState == null) |
| | | { |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public int compareTo(PendingChange o) |
| | | { |
| | | return this.getChangeNumber().compareTo(o.getChangeNumber()); |
| | | return this.getCSN().compareTo(o.getCSN()); |
| | | } |
| | | } |
| | |
| | | import java.util.SortedMap; |
| | | import java.util.TreeMap; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.protocol.LDAPUpdateMsg; |
| | | import org.opends.server.replication.service.ReplicationDomain; |
| | | import org.opends.server.types.operation.PluginOperation; |
| | |
| | | * in progress and not yet committed in the database. |
| | | * |
| | | * It is used to make sure that operations are sent to the Replication |
| | | * Server in the order defined by their ChangeNumber. |
| | | * Server in the order defined by their CSN. |
| | | * It is also used to update the ServerState at the appropriate time. |
| | | * |
| | | * On object of this class is instantiated for each ReplicationDomain. |
| | |
| | | /** |
| | | * A map used to store the pending changes. |
| | | */ |
| | | private SortedMap<ChangeNumber, PendingChange> pendingChanges = |
| | | new TreeMap<ChangeNumber, PendingChange>(); |
| | | private SortedMap<CSN, PendingChange> pendingChanges = |
| | | new TreeMap<CSN, PendingChange>(); |
| | | |
| | | /** |
| | | * The ChangeNumberGenerator to use to create new unique ChangeNumbers |
| | | * The {@link CSNGenerator} to use to create new unique CSNs |
| | | * for each operation done on the replication domain. |
| | | */ |
| | | private ChangeNumberGenerator changeNumberGenerator; |
| | | private CSNGenerator csnGenerator; |
| | | |
| | | /** |
| | | * The ReplicationDomain that will be used to send UpdateMsg. |
| | |
| | | private boolean recoveringOldChanges = false; |
| | | |
| | | /** |
| | | * Creates a new PendingChanges using the provided ChangeNumberGenerator. |
| | | * Creates a new PendingChanges using the provided CSNGenerator. |
| | | * |
| | | * @param changeNumberGenerator The ChangeNumberGenerator to use to create |
| | | * new unique ChangeNumbers. |
| | | * @param domain The ReplicationDomain that will be used to send |
| | | * UpdateMsg. |
| | | * @param csnGenerator The CSNGenerator to use to create new unique CSNs. |
| | | * @param domain The ReplicationDomain that will be used to send UpdateMsg. |
| | | */ |
| | | public PendingChanges( |
| | | ChangeNumberGenerator changeNumberGenerator, ReplicationDomain domain) |
| | | CSNGenerator csnGenerator, ReplicationDomain domain) |
| | | { |
| | | this.changeNumberGenerator = changeNumberGenerator; |
| | | this.csnGenerator = csnGenerator; |
| | | this.domain = domain; |
| | | } |
| | | |
| | | /** |
| | | * Remove and return an update form the pending changes list. |
| | | * |
| | | * @param changeNumber The ChangeNumber of the update to remove. |
| | | * |
| | | * @param csn |
| | | * The CSN of the update to remove. |
| | | * @return The UpdateMsg that was just removed. |
| | | */ |
| | | public synchronized LDAPUpdateMsg remove(ChangeNumber changeNumber) |
| | | public synchronized LDAPUpdateMsg remove(CSN csn) |
| | | { |
| | | return pendingChanges.remove(changeNumber).getMsg(); |
| | | return pendingChanges.remove(csn).getMsg(); |
| | | } |
| | | |
| | | /** |
| | |
| | | /** |
| | | * Mark an update message as committed. |
| | | * |
| | | * @param changeNumber The ChangeNumber of the update message that must be |
| | | * set as committed. |
| | | * @param csn The CSN of the update message that must be set as committed. |
| | | * @param msg The message associated to the update. |
| | | */ |
| | | public synchronized void commit(ChangeNumber changeNumber, |
| | | LDAPUpdateMsg msg) |
| | | public synchronized void commit(CSN csn, LDAPUpdateMsg msg) |
| | | { |
| | | PendingChange curChange = pendingChanges.get(changeNumber); |
| | | PendingChange curChange = pendingChanges.get(csn); |
| | | if (curChange == null) |
| | | { |
| | | throw new NoSuchElementException(); |
| | |
| | | /** |
| | | * Mark an update message as committed. |
| | | * |
| | | * @param changeNumber The ChangeNumber of the update message that must be |
| | | * set as committed. |
| | | * @param csn The CSN of the update message that must be set as committed. |
| | | */ |
| | | public synchronized void commit(ChangeNumber changeNumber) |
| | | public synchronized void commit(CSN csn) |
| | | { |
| | | PendingChange curChange = pendingChanges.get(changeNumber); |
| | | PendingChange curChange = pendingChanges.get(csn); |
| | | if (curChange == null) |
| | | { |
| | | throw new NoSuchElementException(); |
| | |
| | | * |
| | | * @param operation The local operation for which an UpdateMsg must |
| | | * be added in the pending list. |
| | | * @return The ChangeNumber now associated to the operation. |
| | | * @return The CSN now associated to the operation. |
| | | */ |
| | | public synchronized ChangeNumber putLocalOperation(PluginOperation operation) |
| | | public synchronized CSN putLocalOperation(PluginOperation operation) |
| | | { |
| | | ChangeNumber changeNumber = changeNumberGenerator.newChangeNumber(); |
| | | PendingChange change = new PendingChange(changeNumber, operation, null); |
| | | pendingChanges.put(changeNumber, change); |
| | | return changeNumber; |
| | | CSN csn = csnGenerator.newCSN(); |
| | | PendingChange change = new PendingChange(csn, operation, null); |
| | | pendingChanges.put(csn, change); |
| | | return csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | return numSentUpdates; |
| | | } |
| | | |
| | | // peek the oldest changeNumber |
| | | ChangeNumber firstChangeNumber = pendingChanges.firstKey(); |
| | | PendingChange firstChange = pendingChanges.get(firstChangeNumber); |
| | | // peek the oldest CSN |
| | | CSN firstCSN = pendingChanges.firstKey(); |
| | | PendingChange firstChange = pendingChanges.get(firstCSN); |
| | | |
| | | while (firstChange != null && firstChange.isCommitted()) |
| | | { |
| | |
| | | { |
| | | // do not push updates until the RS catches up. |
| | | // @see #setRecovering(boolean) |
| | | domain.getServerState().update(updateMsg.getChangeNumber()); |
| | | domain.getServerState().update(updateMsg.getCSN()); |
| | | } |
| | | } |
| | | pendingChanges.remove(firstChangeNumber); |
| | | pendingChanges.remove(firstCSN); |
| | | |
| | | if (pendingChanges.isEmpty()) |
| | | { |
| | |
| | | } |
| | | else |
| | | { |
| | | // peek the oldest changeNumber |
| | | firstChangeNumber = pendingChanges.firstKey(); |
| | | firstChange = pendingChanges.get(firstChangeNumber); |
| | | // peek the oldest CSN |
| | | firstCSN = pendingChanges.firstKey(); |
| | | firstChange = pendingChanges.get(firstCSN); |
| | | } |
| | | } |
| | | return numSentUpdates; |
| | |
| | | * in a single atomic operation. |
| | | * |
| | | * |
| | | * @param changeNumber The ChangeNumber of the update message that must be |
| | | * set as committed. |
| | | * @param csn The CSN of the update message that must be set as committed. |
| | | * @param msg The message associated to the update. |
| | | * |
| | | * @return The number of pushed updates. |
| | | */ |
| | | public synchronized int commitAndPushCommittedChanges( |
| | | ChangeNumber changeNumber, LDAPUpdateMsg msg) |
| | | public synchronized int commitAndPushCommittedChanges(CSN csn, |
| | | LDAPUpdateMsg msg) |
| | | { |
| | | commit(changeNumber, msg); |
| | | commit(csn, msg); |
| | | return pushCommittedChanges(); |
| | | } |
| | | |
| | | /** |
| | | * Set the PendingChangesList structure in a mode where it is |
| | | * waiting for the RS to receive all the previous changes to |
| | | * be sent before starting to process the changes normally. |
| | | * In this mode, The Domain does not publish the changes from |
| | | * the pendingChanges because there are older changes that |
| | | * need to be published before. |
| | | * Set the PendingChangesList structure in a mode where it is waiting for the |
| | | * RS to receive all the previous changes to be sent before starting to |
| | | * process the changes normally. In this mode, The Domain does not publish the |
| | | * changes from the pendingChanges because there are older changes that need |
| | | * to be published before. |
| | | * |
| | | * @param b The recovering status that must be set. |
| | | * @param recovering |
| | | * The recovering status that must be set. |
| | | */ |
| | | public void setRecovering(boolean b) |
| | | public void setRecovering(boolean recovering) |
| | | { |
| | | recoveringOldChanges = b; |
| | | recoveringOldChanges = recovering; |
| | | } |
| | | |
| | | /** |
| | | * Allows to update the recovery situation by comparing the ChangeNumber of |
| | | * the last change that was sent to the ReplicationServer with the |
| | | * ChangeNumber of the last operation that was taken out of the |
| | | * PendingChanges list. |
| | | * If he two match then the recovery is completed and normal procedure can |
| | | * restart. Otherwise the RSUpdate thread must continue to look for |
| | | * older changes and no changes can be committed from the pendingChanges list. |
| | | * Allows to update the recovery situation by comparing the CSN of the last |
| | | * change that was sent to the ReplicationServer with the CSN of the last |
| | | * operation that was taken out of the PendingChanges list. If the two match |
| | | * then the recovery is completed and normal procedure can restart. Otherwise |
| | | * the RSUpdate thread must continue to look for older changes and no changes |
| | | * can be committed from the pendingChanges list. |
| | | * |
| | | * @param recovered The ChangeNumber of the last change that was published |
| | | * to the ReplicationServer. |
| | | * |
| | | * @return A boolean indicating if the recovery is completed (false) |
| | | * or must continue (true). |
| | | * @param recovered |
| | | * The CSN of the last change that was published to the |
| | | * ReplicationServer. |
| | | * @return A boolean indicating if the recovery is completed (false) or must |
| | | * continue (true). |
| | | */ |
| | | public synchronized boolean recoveryUntil(ChangeNumber recovered) |
| | | public synchronized boolean recoveryUntil(CSN recovered) |
| | | { |
| | | ChangeNumber lastLocalChange = domain.getLastLocalChange(); |
| | | CSN lastLocalChange = domain.getLastLocalChange(); |
| | | if (recovered != null && recovered.newerOrEquals(lastLocalChange)) |
| | | { |
| | | recoveringOldChanges = false; |
| | |
| | | * Portions copyright 2012-2013 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | import org.opends.messages.Message; |
| | | |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.core.ModifyOperationBasis; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.protocols.ldap.LDAPModification; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DereferencePolicy; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.ModificationType; |
| | | import org.opends.server.types.RawModification; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchResultEntry; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.opends.server.types.*; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | |
| | | /** |
| | | * This class implements a ServerState that is stored in the backend |
| | |
| | | } |
| | | |
| | | /** |
| | | * Checks that the ChangeNumber given as a parameter is in this ServerState. |
| | | * Checks that the CSN given as a parameter is in this ServerState. |
| | | * |
| | | * @param covered The ChangeNumber that should be checked. |
| | | * @return A boolean indicating if this ServerState contains the ChangeNumber |
| | | * @param covered The CSN that should be checked. |
| | | * @return A boolean indicating if this ServerState contains the CSN |
| | | * given in parameter. |
| | | */ |
| | | public boolean cover(ChangeNumber covered) |
| | | public boolean cover(CSN covered) |
| | | { |
| | | return state.cover(covered); |
| | | } |
| | | |
| | | /** |
| | | * Update the Server State with a ChangeNumber. |
| | | * All operations with smaller CSN and the same serverID must be committed |
| | | * before calling this method. |
| | | * Update the Server State with a CSN. All operations with smaller CSN and the |
| | | * same serverID must be committed before calling this method. |
| | | * |
| | | * @param changeNumber The committed ChangeNumber. |
| | | * |
| | | * @param csn |
| | | * The committed CSN. |
| | | * @return a boolean indicating if the update was meaningful. |
| | | */ |
| | | public boolean update(ChangeNumber changeNumber) |
| | | public boolean update(CSN csn) |
| | | { |
| | | return state.update(changeNumber); |
| | | return state.update(csn); |
| | | } |
| | | |
| | | /** |
| | |
| | | Attribute attr = attrs.get(0); |
| | | for (AttributeValue value : attr) |
| | | { |
| | | ChangeNumber changeNumber = new ChangeNumber(value.toString()); |
| | | update(changeNumber); |
| | | update(new CSN(value.toString())); |
| | | } |
| | | } |
| | | } |
| | |
| | | public final void checkAndUpdateServerState() { |
| | | Message message; |
| | | InternalSearchOperation op; |
| | | ChangeNumber serverStateMaxCn; |
| | | ChangeNumber dbMaxCn; |
| | | CSN serverStateMaxCsn; |
| | | CSN dbMaxCsn; |
| | | final AttributeType histType = DirectoryServer.getAttributeType( |
| | | EntryHistorical.HISTORICAL_ATTRIBUTE_NAME); |
| | | |
| | | // Retrieves the entries that have changed since the |
| | | // maxCn stored in the serverState |
| | | // maxCsn stored in the serverState |
| | | synchronized (this) |
| | | { |
| | | serverStateMaxCn = state.getChangeNumber(serverId); |
| | | serverStateMaxCsn = state.getCSN(serverId); |
| | | |
| | | if (serverStateMaxCn == null) |
| | | if (serverStateMaxCsn == null) |
| | | return; |
| | | |
| | | try { |
| | | op = LDAPReplicationDomain.searchForChangedEntries(baseDn, |
| | | serverStateMaxCn, null); |
| | | serverStateMaxCsn, null); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | } |
| | | else |
| | | { |
| | | dbMaxCn = serverStateMaxCn; |
| | | dbMaxCsn = serverStateMaxCsn; |
| | | for (SearchResultEntry resEntry : op.getSearchEntries()) |
| | | { |
| | | for (AttributeValue attrValue : |
| | |
| | | { |
| | | HistoricalAttributeValue histVal = |
| | | new HistoricalAttributeValue(attrValue.toString()); |
| | | ChangeNumber cn = histVal.getCn(); |
| | | |
| | | if ((cn != null) && (cn.getServerId() == serverId)) |
| | | CSN csn = histVal.getCSN(); |
| | | if (csn != null && csn.getServerId() == serverId) |
| | | { |
| | | // compare the csn regarding the maxCn we know and |
| | | // compare the csn regarding the maxCsn we know and |
| | | // store the biggest |
| | | if (ChangeNumber.compare(dbMaxCn, cn) < 0) |
| | | if (CSN.compare(dbMaxCsn, csn) < 0) |
| | | { |
| | | dbMaxCn = cn; |
| | | dbMaxCsn = csn; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (ChangeNumber.compare(dbMaxCn, serverStateMaxCn) > 0) |
| | | if (CSN.compare(dbMaxCsn, serverStateMaxCsn) > 0) |
| | | { |
| | | // Update the serverState with the new maxCn |
| | | // Update the serverState with the new maxCsn |
| | | // present in the database |
| | | this.update(dbMaxCn); |
| | | this.update(dbMaxCsn); |
| | | message = NOTE_SERVER_STATE_RECOVERY.get( |
| | | baseDn.toNormalizedString(), dbMaxCn.toString()); |
| | | baseDn.toNormalizedString(), dbMaxCsn.toString()); |
| | | logError(message); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the largest ChangeNumber seen for a given LDAP server ID. |
| | | * Get the largest CSN seen for a given LDAP server ID. |
| | | * |
| | | * @param serverID The server ID. |
| | | * |
| | | * @return The largest ChangeNumber seen. |
| | | * @param serverId |
| | | * The serverId |
| | | * @return The largest CSN seen |
| | | */ |
| | | public ChangeNumber getMaxChangeNumber(int serverID) |
| | | public CSN getMaxCSN(int serverId) |
| | | { |
| | | return state.getChangeNumber(serverID); |
| | | return state.getCSN(serverId); |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import java.util.NoSuchElementException; |
| | | import java.util.SortedMap; |
| | | import java.util.SortedSet; |
| | | import java.util.TreeMap; |
| | | import java.util.TreeSet; |
| | | import java.util.*; |
| | | |
| | | import org.opends.server.core.AddOperation; |
| | | import org.opends.server.core.DeleteOperation; |
| | | import org.opends.server.core.ModifyDNOperationBasis; |
| | | import org.opends.server.core.ModifyOperation; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.protocol.AddMsg; |
| | | import org.opends.server.replication.protocol.DeleteMsg; |
| | | import org.opends.server.replication.protocol.ModifyDNMsg; |
| | | import org.opends.server.replication.protocol.OperationContext; |
| | | import org.opends.server.replication.protocol.LDAPUpdateMsg; |
| | | import org.opends.server.replication.protocol.*; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Operation; |
| | | |
| | | /** |
| | | * |
| | | * This class is used to store the list of remote changes received |
| | | * from a replication server and that are either currently being replayed |
| | | * or that are waiting for being replayed. |
| | |
| | | * the dependencies between operations. |
| | | * |
| | | * One of this object is instantiated for each ReplicationDomain. |
| | | * |
| | | */ |
| | | public final class RemotePendingChanges |
| | | { |
| | | /** |
| | | * A map used to store the pending changes. |
| | | */ |
| | | private SortedMap<ChangeNumber, PendingChange> pendingChanges = |
| | | new TreeMap<ChangeNumber, PendingChange>(); |
| | | private SortedMap<CSN, PendingChange> pendingChanges = |
| | | new TreeMap<CSN, PendingChange>(); |
| | | |
| | | /** |
| | | * A sorted set containing the list of PendingChanges that have |
| | |
| | | */ |
| | | public synchronized void putRemoteUpdate(LDAPUpdateMsg update) |
| | | { |
| | | ChangeNumber changeNumber = update.getChangeNumber(); |
| | | pendingChanges.put(changeNumber, new PendingChange(changeNumber, null, |
| | | update)); |
| | | CSN csn = update.getCSN(); |
| | | pendingChanges.put(csn, new PendingChange(csn, null, update)); |
| | | } |
| | | |
| | | /** |
| | | * Mark an update message as committed. |
| | | * |
| | | * @param changeNumber The ChangeNumber of the update message that must be |
| | | * set as committed. |
| | | * @param csn |
| | | * The CSN of the update message that must be set as committed. |
| | | */ |
| | | public synchronized void commit(ChangeNumber changeNumber) |
| | | public synchronized void commit(CSN csn) |
| | | { |
| | | PendingChange curChange = pendingChanges.get(changeNumber); |
| | | PendingChange curChange = pendingChanges.get(csn); |
| | | if (curChange == null) |
| | | { |
| | | throw new NoSuchElementException(); |
| | | } |
| | | curChange.setCommitted(true); |
| | | |
| | | ChangeNumber firstChangeNumber = pendingChanges.firstKey(); |
| | | PendingChange firstChange = pendingChanges.get(firstChangeNumber); |
| | | CSN firstCSN = pendingChanges.firstKey(); |
| | | PendingChange firstChange = pendingChanges.get(firstCSN); |
| | | |
| | | while ((firstChange != null) && firstChange.isCommitted()) |
| | | { |
| | | state.update(firstChangeNumber); |
| | | pendingChanges.remove(firstChangeNumber); |
| | | state.update(firstCSN); |
| | | pendingChanges.remove(firstCSN); |
| | | |
| | | if (pendingChanges.isEmpty()) |
| | | { |
| | |
| | | } |
| | | else |
| | | { |
| | | firstChangeNumber = pendingChanges.firstKey(); |
| | | firstChange = pendingChanges.get(firstChangeNumber); |
| | | firstCSN = pendingChanges.firstKey(); |
| | | firstChange = pendingChanges.get(firstCSN); |
| | | } |
| | | } |
| | | } |
| | |
| | | private void addDependency( |
| | | PendingChange dependentChange, PendingChange pendingChange) |
| | | { |
| | | dependentChange.addDependency(pendingChange.getChangeNumber()); |
| | | dependentChange.addDependency(pendingChange.getCSN()); |
| | | dependentChanges.add(dependentChange); |
| | | } |
| | | |
| | |
| | | { |
| | | boolean hasDependencies = false; |
| | | DN targetDn = op.getEntryDN(); |
| | | ChangeNumber changeNumber = OperationContext.getChangeNumber(op); |
| | | PendingChange change = pendingChanges.get(changeNumber); |
| | | CSN csn = OperationContext.getCSN(op); |
| | | PendingChange change = pendingChanges.get(csn); |
| | | if (change == null) |
| | | return false; |
| | | |
| | | for (PendingChange pendingChange : pendingChanges.values()) |
| | | { |
| | | if (pendingChange.getChangeNumber().older(changeNumber)) |
| | | if (pendingChange.getCSN().older(csn)) |
| | | { |
| | | LDAPUpdateMsg pendingMsg = pendingChange.getMsg(); |
| | | if (pendingMsg != null) |
| | |
| | | { |
| | | boolean hasDependencies = false; |
| | | DN targetDn = op.getEntryDN(); |
| | | ChangeNumber changeNumber = OperationContext.getChangeNumber(op); |
| | | PendingChange change = pendingChanges.get(changeNumber); |
| | | CSN csn = OperationContext.getCSN(op); |
| | | PendingChange change = pendingChanges.get(csn); |
| | | if (change == null) |
| | | return false; |
| | | |
| | | for (PendingChange pendingChange : pendingChanges.values()) |
| | | { |
| | | if (pendingChange.getChangeNumber().older(changeNumber)) |
| | | if (pendingChange.getCSN().older(csn)) |
| | | { |
| | | LDAPUpdateMsg pendingMsg = pendingChange.getMsg(); |
| | | if (pendingMsg != null) |
| | |
| | | public synchronized boolean checkDependencies(ModifyDNMsg msg) |
| | | { |
| | | boolean hasDependencies = false; |
| | | ChangeNumber changeNumber = msg.getChangeNumber(); |
| | | PendingChange change = pendingChanges.get(changeNumber); |
| | | CSN csn = msg.getCSN(); |
| | | PendingChange change = pendingChanges.get(csn); |
| | | if (change == null) |
| | | return false; |
| | | |
| | |
| | | |
| | | for (PendingChange pendingChange : pendingChanges.values()) |
| | | { |
| | | if (pendingChange.getChangeNumber().older(changeNumber)) |
| | | if (pendingChange.getCSN().older(csn)) |
| | | { |
| | | LDAPUpdateMsg pendingMsg = pendingChange.getMsg(); |
| | | if (pendingMsg != null) |
| | |
| | | { |
| | | boolean hasDependencies = false; |
| | | DN targetDn = op.getEntryDN(); |
| | | ChangeNumber changeNumber = OperationContext.getChangeNumber(op); |
| | | PendingChange change = pendingChanges.get(changeNumber); |
| | | CSN csn = OperationContext.getCSN(op); |
| | | PendingChange change = pendingChanges.get(csn); |
| | | if (change == null) |
| | | return false; |
| | | |
| | | for (PendingChange pendingChange : pendingChanges.values()) |
| | | { |
| | | if (pendingChange.getChangeNumber().older(changeNumber)) |
| | | if (pendingChange.getCSN().older(csn)) |
| | | { |
| | | LDAPUpdateMsg pendingMsg = pendingChange.getMsg(); |
| | | if (pendingMsg != null) |
| | |
| | | import java.util.List; |
| | | import java.util.zip.DataFormatException; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** |
| | | * AckMsg messages are used for acknowledging an update that has been sent |
| | | * requesting an ack: update sent in Assured Mode, either safe data or safe |
| | | * read sub mode. |
| | | * The change number refers to the update change number that was requested to be |
| | | * acknowledged. |
| | | * The CSN refers to the update CSN that was requested to be acknowledged. |
| | | * If some errors occurred during attempt to acknowledge the update in the path |
| | | * to final servers, errors are marked with the following fields: |
| | | * - hasTimeout: |
| | |
| | | */ |
| | | public class AckMsg extends ReplicationMsg |
| | | { |
| | | // ChangeNumber of the update that was acked. |
| | | private ChangeNumber changeNumber; |
| | | /** CSN of the update that was acked. */ |
| | | private CSN csn; |
| | | |
| | | // Did some servers go in timeout when the matching update (corresponding to |
| | | // change number) was sent ? |
| | | /** |
| | | * Did some servers go in timeout when the matching update (corresponding to |
| | | * CSN) was sent?. |
| | | */ |
| | | private boolean hasTimeout = false; |
| | | |
| | | // Were some servers in wrong status when the matching |
| | | // update (correspondig to change number) was sent ? |
| | | /** |
| | | * Were some servers in wrong status when the matching update (corresponding |
| | | * to CSN) was sent?. |
| | | */ |
| | | private boolean hasWrongStatus = false; |
| | | |
| | | // Did some servers make an error replaying the sent matching update |
| | | // (corresponding to change number) ? |
| | | /** |
| | | * Did some servers make an error replaying the sent matching update |
| | | * (corresponding to CSN)?. |
| | | */ |
| | | private boolean hasReplayError = false; |
| | | |
| | | // The list of server ids that had errors for the sent matching update |
| | | // (corresponding to change number). Each server id of the list had one of the |
| | | // 3 possible errors (timeout/degraded or admin/replay error) |
| | | /** |
| | | * The list of server ids that had errors for the sent matching update |
| | | * (corresponding to CSN). Each server id of the list had one of the 3 |
| | | * possible errors (timeout/degraded or admin/replay error). |
| | | */ |
| | | private List<Integer> failedServers = new ArrayList<Integer>(); |
| | | |
| | | /** |
| | | * Creates a new AckMsg from a ChangeNumber (no errors). |
| | | * Creates a new AckMsg from a CSN (no errors). |
| | | * |
| | | * @param changeNumber The ChangeNumber used to build the AckMsg. |
| | | * @param csn The CSN used to build the AckMsg. |
| | | */ |
| | | public AckMsg(ChangeNumber changeNumber) |
| | | public AckMsg(CSN csn) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | this.csn = csn; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new AckMsg from a ChangeNumber (with specified error info). |
| | | * Creates a new AckMsg from a CSN (with specified error info). |
| | | * |
| | | * @param changeNumber The ChangeNumber used to build the AckMsg. |
| | | * @param csn The CSN used to build the AckMsg. |
| | | * @param hasTimeout The hasTimeout info |
| | | * @param hasWrongStatus The hasWrongStatus info |
| | | * @param hasReplayError The hasReplayError info |
| | | * @param failedServers The list of failed servers |
| | | */ |
| | | public AckMsg(ChangeNumber changeNumber, boolean hasTimeout, |
| | | boolean hasWrongStatus, boolean hasReplayError, List<Integer> failedServers) |
| | | public AckMsg(CSN csn, boolean hasTimeout, boolean hasWrongStatus, |
| | | boolean hasReplayError, List<Integer> failedServers) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | this.csn = csn; |
| | | this.hasTimeout = hasTimeout; |
| | | this.hasWrongStatus = hasWrongStatus; |
| | | this.hasReplayError = hasReplayError; |
| | |
| | | { |
| | | /* |
| | | * The message is stored in the form: |
| | | * <operation type><change number><has timeout><has degraded><has replay |
| | | * <operation type><CSN><has timeout><has degraded><has replay |
| | | * error><failed server ids> |
| | | */ |
| | | |
| | | /* First byte is the type */ |
| | | // First byte is the type |
| | | if (in[0] != MSG_TYPE_ACK) |
| | | { |
| | | throw new DataFormatException("byte[] is not a valid modify msg"); |
| | | } |
| | | int pos = 1; |
| | | |
| | | /* Read the changeNumber */ |
| | | // Read the CSN |
| | | int length = getNextLength(in, pos); |
| | | String changenumberStr = new String(in, pos, length, "UTF-8"); |
| | | changeNumber = new ChangeNumber(changenumberStr); |
| | | String csnStr = new String(in, pos, length, "UTF-8"); |
| | | csn = new CSN(csnStr); |
| | | pos += length + 1; |
| | | |
| | | /* Read the hasTimeout flag */ |
| | | // Read the hasTimeout flag |
| | | hasTimeout = in[pos++] == 1; |
| | | |
| | | /* Read the hasWrongStatus flag */ |
| | | // Read the hasWrongStatus flag |
| | | hasWrongStatus = in[pos++] == 1; |
| | | |
| | | /* Read the hasReplayError flag */ |
| | | // Read the hasReplayError flag |
| | | hasReplayError = in[pos++] == 1; |
| | | |
| | | /* Read the list of failed server ids */ |
| | | // Read the list of failed server ids |
| | | while (pos < in.length) |
| | | { |
| | | length = getNextLength(in, pos); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the ChangeNumber from the message. |
| | | * Get the CSN from the message. |
| | | * |
| | | * @return the ChangeNumber |
| | | * @return the CSN |
| | | */ |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | return changeNumber; |
| | | return csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | { |
| | | /* |
| | | * The message is stored in the form: |
| | | * <operation type><change number><has timeout><has degraded><has replay |
| | | * <operation type><CSN><has timeout><has degraded><has replay |
| | | * error><failed server ids> |
| | | */ |
| | | |
| | | ByteArrayOutputStream oStream = new ByteArrayOutputStream(200); |
| | | |
| | | /* Put the type of the operation */ |
| | | // Put the type of the operation |
| | | oStream.write(MSG_TYPE_ACK); |
| | | |
| | | /* Put the ChangeNumber */ |
| | | byte[] changeNumberByte = changeNumber.toString().getBytes("UTF-8"); |
| | | oStream.write(changeNumberByte); |
| | | // Put the CSN |
| | | byte[] csnByte = csn.toString().getBytes("UTF-8"); |
| | | oStream.write(csnByte); |
| | | oStream.write(0); |
| | | |
| | | /* Put the hasTimeout flag */ |
| | | oStream.write((hasTimeout ? (byte) 1 : (byte) 0)); |
| | | // Put the hasTimeout flag |
| | | oStream.write(hasTimeout ? 1 : 0); |
| | | |
| | | /* Put the hasWrongStatus flag */ |
| | | oStream.write((hasWrongStatus ? (byte) 1 : (byte) 0)); |
| | | // Put the hasWrongStatus flag |
| | | oStream.write(hasWrongStatus ? 1 : 0); |
| | | |
| | | /* Put the hasReplayError flag */ |
| | | oStream.write((hasReplayError ? (byte) 1 : (byte) 0)); |
| | | // Put the hasReplayError flag |
| | | oStream.write(hasReplayError ? 1 : 0); |
| | | |
| | | /* Put the list of server ids */ |
| | | // Put the list of server ids |
| | | for (Integer sid : failedServers) |
| | | { |
| | | byte[] byteServerId = String.valueOf(sid).getBytes("UTF-8"); |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** |
| | | * This class describes the context that is attached to |
| | | * Add Operation. |
| | | * This class describes the context that is attached to Add Operation. |
| | | */ |
| | | public class AddContext extends OperationContext |
| | | { |
| | |
| | | /** |
| | | * Creates a new AddContext with the provided information. |
| | | * |
| | | * @param changeNumber The change number of the add operation. |
| | | * @param csn The CSN of the add operation. |
| | | * @param entryUUID the Unique Id of the added entry. |
| | | * @param parentEntryUUID The unique Id of the parent of the added entry. |
| | | */ |
| | | public AddContext(ChangeNumber changeNumber, String entryUUID, |
| | | String parentEntryUUID) |
| | | public AddContext(CSN csn, String entryUUID, String parentEntryUUID) |
| | | { |
| | | super(changeNumber, entryUUID); |
| | | super(csn, entryUUID); |
| | | this.parentEntryUUID = parentEntryUUID; |
| | | } |
| | | |
| | |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import org.opends.server.core.AddOperationBasis; |
| | | import org.opends.server.core.DirectoryServer; |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | |
| | | import java.util.Map; |
| | | import java.util.zip.DataFormatException; |
| | | |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.protocols.asn1.ASN1Writer; |
| | | import org.opends.server.core.AddOperationBasis; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.protocols.asn1.ASN1; |
| | | import org.opends.server.protocols.asn1.ASN1Exception; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.protocols.asn1.ASN1Writer; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.plugin.EntryHistorical; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.types.operation.PostOperationAddOperation; |
| | | |
| | | import static org.opends.server.replication.protocol.OperationContext.*; |
| | | import static org.opends.server.util.StaticUtils.toLowerCase; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | /** |
| | | * This class is used to exchange Add operation between LDAP servers |
| | |
| | | */ |
| | | public class AddMsg extends LDAPUpdateMsg |
| | | { |
| | | // Attributes are managed encoded |
| | | /** Attributes are managed encoded. */ |
| | | private byte[] encodedAttributes; |
| | | |
| | | // Parent is managed decoded |
| | | /** Parent is managed decoded. */ |
| | | private String parentEntryUUID; |
| | | |
| | | /** |
| | |
| | | /** |
| | | * Creates a new AddMessage. |
| | | * |
| | | * @param cn ChangeNumber of the add. |
| | | * @param csn CSN of the add. |
| | | * @param dn DN of the added entry. |
| | | * @param entryUUID The Unique identifier of the added entry. |
| | | * @param parentEntryUUID The unique Id of the parent of the added |
| | |
| | | * @param userAttributes user attributes of the added entry. |
| | | * @param operationalAttributes operational attributes of the added entry. |
| | | */ |
| | | public AddMsg(ChangeNumber cn, |
| | | public AddMsg(CSN csn, |
| | | String dn, |
| | | String entryUUID, |
| | | String parentEntryUUID, |
| | |
| | | Map<AttributeType,List<Attribute>> userAttributes, |
| | | Map<AttributeType,List<Attribute>> operationalAttributes) |
| | | { |
| | | super (cn, entryUUID, dn); |
| | | super (csn, entryUUID, dn); |
| | | |
| | | // Stores parentUniqueID not encoded |
| | | this.parentEntryUUID = parentEntryUUID; |
| | |
| | | /** |
| | | * Creates a new AddMessage. |
| | | * |
| | | * @param cn ChangeNumber of the add. |
| | | * @param csn CSN of the add. |
| | | * @param dn DN of the added entry. |
| | | * @param uniqueId The Unique identifier of the added entry. |
| | | * @param parentId The unique Id of the parent of the added entry. |
| | |
| | | * @param userAttributes user attributes of the added entry. |
| | | * @param operationalAttributes operational attributes of the added entry. |
| | | */ |
| | | public AddMsg(ChangeNumber cn, |
| | | public AddMsg(CSN csn, |
| | | String dn, |
| | | String uniqueId, |
| | | String parentId, |
| | |
| | | Collection<Attribute> userAttributes, |
| | | Collection<Attribute> operationalAttributes) |
| | | { |
| | | super (cn, uniqueId, dn); |
| | | super (csn, uniqueId, dn); |
| | | |
| | | // Stores parentUniqueID not encoded |
| | | this.parentEntryUUID = parentId; |
| | |
| | | InternalClientConnection.nextOperationID(), |
| | | InternalClientConnection.nextMessageID(), null, |
| | | ByteString.valueOf(newDn), attr); |
| | | AddContext ctx = new AddContext(getChangeNumber(), getEntryUUID(), |
| | | AddContext ctx = new AddContext(getCSN(), getEntryUUID(), |
| | | parentEntryUUID); |
| | | add.setAttachment(SYNCHROCONTEXT, ctx); |
| | | return add; |
| | |
| | | return "AddMsg content: " + |
| | | " protocolVersion: " + protocolVersion + |
| | | " dn: " + dn + |
| | | " changeNumber: " + changeNumber + |
| | | " changeNumber: " + csn + |
| | | " uniqueId: " + entryUUID + |
| | | " assuredFlag: " + assuredFlag; |
| | | } |
| | |
| | | return "AddMsg content: " + |
| | | " protocolVersion: " + protocolVersion + |
| | | " dn: " + dn + |
| | | " changeNumber: " + changeNumber + |
| | | " changeNumber: " + csn + |
| | | " uniqueId: " + entryUUID + |
| | | " assuredFlag: " + assuredFlag + |
| | | " assuredMode: " + assuredMode + |
| | |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | |
| | | |
| | | import java.util.zip.DataFormatException; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.ByteSequenceReader; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.ByteStringBuilder; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Class that define messages sent by a replication domain (DS) to the |
| | | * replication server to let the RS know the DS current change time. |
| | |
| | | public class ChangeTimeHeartbeatMsg extends ReplicationMsg |
| | | { |
| | | /** |
| | | * The ChangeNumber containing the change time. |
| | | * The CSN containing the change time. |
| | | */ |
| | | private final ChangeNumber changeNumber; |
| | | |
| | | |
| | | private final CSN csn; |
| | | |
| | | /** |
| | | * Constructor of a Change Time Heartbeat message providing the change time |
| | | * value in a change number. |
| | | * value in a CSN. |
| | | * |
| | | * @param cn |
| | | * The provided change number. |
| | | * @param csn |
| | | * The provided CSN. |
| | | */ |
| | | public ChangeTimeHeartbeatMsg(ChangeNumber cn) |
| | | public ChangeTimeHeartbeatMsg(CSN csn) |
| | | { |
| | | this.changeNumber = cn; |
| | | this.csn = csn; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Get a change number with the transmitted change time. |
| | | * Get a CSN with the transmitted change time. |
| | | * |
| | | * @return the ChangeNumber |
| | | * @return the CSN |
| | | */ |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | return changeNumber; |
| | | return csn; |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V7) |
| | | { |
| | | changeNumber = ChangeNumber.valueOf(reader |
| | | .getByteSequence(ChangeNumber.BYTE_ENCODING_LENGTH)); |
| | | csn = CSN.valueOf(reader.getByteSequence(CSN.BYTE_ENCODING_LENGTH)); |
| | | } |
| | | else |
| | | { |
| | | changeNumber = ChangeNumber.valueOf(reader |
| | | .getString(ChangeNumber.STRING_ENCODING_LENGTH)); |
| | | csn = CSN.valueOf(reader.getString(CSN.STRING_ENCODING_LENGTH)); |
| | | reader.get(); // Read trailing 0 byte. |
| | | } |
| | | |
| | |
| | | if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V7) |
| | | { |
| | | final ByteStringBuilder builder = new ByteStringBuilder( |
| | | ChangeNumber.BYTE_ENCODING_LENGTH + 1 /* type + csn */); |
| | | CSN.BYTE_ENCODING_LENGTH + 1 /* type + csn */); |
| | | builder.append(MSG_TYPE_CT_HEARTBEAT); |
| | | changeNumber.toByteString(builder); |
| | | csn.toByteString(builder); |
| | | return builder.toByteArray(); |
| | | } |
| | | else |
| | | { |
| | | final ByteStringBuilder builder = new ByteStringBuilder( |
| | | ChangeNumber.STRING_ENCODING_LENGTH + 2 /* type + csn str + nul */); |
| | | CSN.STRING_ENCODING_LENGTH + 2 /* type + csn str + nul */); |
| | | builder.append(MSG_TYPE_CT_HEARTBEAT); |
| | | builder.append(changeNumber.toString()); |
| | | builder.append(csn.toString()); |
| | | builder.append((byte) 0); // For compatibility with earlier protocol |
| | | // versions. |
| | | return builder.toByteArray(); |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** |
| | | * This class is used to describe the context attached to a Delete Operation. |
| | |
| | | /** |
| | | * Creates a new DeleteContext with the provided information. |
| | | * |
| | | * @param changeNumber The change number of the Delete Operation. |
| | | * @param csn The CSN of the Delete Operation. |
| | | * @param entryUUID The unique Id of the deleted entry. |
| | | */ |
| | | public DeleteContext(ChangeNumber changeNumber, String entryUUID) |
| | | public DeleteContext(CSN csn, String entryUUID) |
| | | { |
| | | super(changeNumber, entryUUID); |
| | | super(csn, entryUUID); |
| | | } |
| | | } |
| | |
| | | import org.opends.server.controls.SubtreeDeleteControl; |
| | | import org.opends.server.core.DeleteOperationBasis; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.operation.PostOperationDeleteOperation; |
| | |
| | | * Creates a new delete message. |
| | | * |
| | | * @param dn The dn with which the message must be created. |
| | | * @param changeNumber The change number with which the message must be |
| | | * created. |
| | | * @param csn The CSN with which the message must be created. |
| | | * @param entryUUID The unique id with which the message must be created. |
| | | */ |
| | | public DeleteMsg(String dn, ChangeNumber changeNumber, String entryUUID) |
| | | public DeleteMsg(String dn, CSN csn, String entryUUID) |
| | | { |
| | | super(new DeleteContext(changeNumber, entryUUID), dn); |
| | | super(new DeleteContext(csn, entryUUID), dn); |
| | | } |
| | | |
| | | /** |
| | |
| | | if (isSubtreeDelete) |
| | | del.addRequestControl(new SubtreeDeleteControl(false)); |
| | | |
| | | DeleteContext ctx = new DeleteContext(getChangeNumber(), getEntryUUID()); |
| | | DeleteContext ctx = new DeleteContext(getCSN(), getEntryUUID()); |
| | | del.setAttachment(SYNCHROCONTEXT, ctx); |
| | | return del; |
| | | } |
| | |
| | | return "DeleteMsg content: " + |
| | | " protocolVersion: " + protocolVersion + |
| | | " dn: " + dn + |
| | | " changeNumber: " + changeNumber + |
| | | " changeNumber: " + csn + |
| | | " uniqueId: " + entryUUID + |
| | | " assuredFlag: " + assuredFlag; |
| | | } |
| | |
| | | return "DeleteMsg content: " + |
| | | " protocolVersion: " + protocolVersion + |
| | | " dn: " + dn + |
| | | " changeNumber: " + changeNumber + |
| | | " changeNumber: " + csn + |
| | | " uniqueId: " + entryUUID + |
| | | " assuredFlag: " + assuredFlag + |
| | | " assuredMode: " + assuredMode + |
| | |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.replication.common.AssuredMode; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.ByteSequenceReader; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.ByteStringBuilder; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.RawAttribute; |
| | | import org.opends.server.types.operation.PostOperationAddOperation; |
| | | import org.opends.server.types.operation.PostOperationDeleteOperation; |
| | | import org.opends.server.types.operation.PostOperationModifyDNOperation; |
| | | import org.opends.server.types.operation.PostOperationModifyOperation; |
| | | import org.opends.server.types.operation.PostOperationOperation; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.types.operation.*; |
| | | |
| | | /** |
| | | * Abstract class that must be extended to define a message |
| | |
| | | /** |
| | | * The DN on which the update was originally done. |
| | | */ |
| | | protected String dn = null; |
| | | protected String dn; |
| | | |
| | | /** |
| | | * The entryUUID of the entry that was updated. |
| | |
| | | /** |
| | | * Encoded form of the LDAPUpdateMsg. |
| | | */ |
| | | protected byte[] bytes = null; |
| | | protected byte[] bytes; |
| | | |
| | | /** |
| | | * Encoded form of entry attributes. |
| | |
| | | public LDAPUpdateMsg(OperationContext ctx, String dn) |
| | | { |
| | | this.protocolVersion = ProtocolVersion.getCurrentVersion(); |
| | | this.changeNumber = ctx.getChangeNumber(); |
| | | this.csn = ctx.getCSN(); |
| | | this.entryUUID = ctx.getEntryUUID(); |
| | | this.dn = dn; |
| | | } |
| | |
| | | /** |
| | | * Creates a new UpdateMessage with the given information. |
| | | * |
| | | * @param cn The ChangeNumber of the operation for which the |
| | | * @param csn The CSN of the operation for which the |
| | | * UpdateMessage is created. |
| | | * @param entryUUID The Unique identifier of the entry that is updated |
| | | * by the operation for which the UpdateMessage is created. |
| | | * @param dn The DN of the entry on which the change |
| | | * that caused the creation of this object happened |
| | | */ |
| | | public LDAPUpdateMsg(ChangeNumber cn, String entryUUID, String dn) |
| | | public LDAPUpdateMsg(CSN csn, String entryUUID, String dn) |
| | | { |
| | | this.protocolVersion = ProtocolVersion.getCurrentVersion(); |
| | | this.changeNumber = cn; |
| | | this.csn = csn; |
| | | this.entryUUID = entryUUID; |
| | | this.dn = dn; |
| | | } |
| | |
| | | throws UnsupportedEncodingException |
| | | { |
| | | byte[] byteDn = dn.getBytes("UTF-8"); |
| | | byte[] changeNumberByte = |
| | | this.getChangeNumber().toString().getBytes("UTF-8"); |
| | | byte[] csnByte = getCSN().toString().getBytes("UTF-8"); |
| | | byte[] byteEntryuuid = getEntryUUID().getBytes("UTF-8"); |
| | | |
| | | /* The message header is stored in the form : |
| | | * <operation type><protocol version><changenumber><dn><entryuuid><assured> |
| | | * <operation type><protocol version><CSN><dn><entryuuid><assured> |
| | | * <assured mode> <safe data level> |
| | | * the length of result byte array is therefore : |
| | | * 1 + 1 + change number length + 1 + dn length + 1 + uuid length + 1 + 1 |
| | | * 1 + 1 + CSN length + 1 + dn length + 1 + uuid length + 1 + 1 |
| | | * + 1 + 1 + additional_length |
| | | */ |
| | | int length = 8 + changeNumberByte.length + byteDn.length |
| | | int length = 8 + csnByte.length + byteDn.length |
| | | + byteEntryuuid.length + additionalLength; |
| | | |
| | | byte[] encodedMsg = new byte[length]; |
| | | |
| | | /* put the type of the operation */ |
| | | // put the type of the operation |
| | | encodedMsg[0] = type; |
| | | |
| | | /* put the protocol version */ |
| | | // put the protocol version |
| | | encodedMsg[1] = (byte) version; |
| | | int pos = 2; |
| | | |
| | | /* Put the ChangeNumber */ |
| | | pos = addByteArray(changeNumberByte, encodedMsg, pos); |
| | | // Put the CSN |
| | | pos = addByteArray(csnByte, encodedMsg, pos); |
| | | |
| | | /* Put the DN and a terminating 0 */ |
| | | // Put the DN and a terminating 0 |
| | | pos = addByteArray(byteDn, encodedMsg, pos); |
| | | |
| | | /* Put the entry uuid and a terminating 0 */ |
| | | // Put the entry uuid and a terminating 0 |
| | | pos = addByteArray(byteEntryuuid, encodedMsg, pos); |
| | | |
| | | /* Put the assured flag */ |
| | | // Put the assured flag |
| | | encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0); |
| | | |
| | | /* Put the assured mode */ |
| | | // Put the assured mode |
| | | encodedMsg[pos++] = assuredMode.getValue(); |
| | | |
| | | /* Put the safe data level */ |
| | | // Put the safe data level |
| | | encodedMsg[pos++] = safeDataLevel; |
| | | |
| | | return encodedMsg; |
| | |
| | | throws UnsupportedEncodingException |
| | | { |
| | | byte[] byteDn = dn.getBytes("UTF-8"); |
| | | byte[] changeNumberByte = |
| | | this.getChangeNumber().toString().getBytes("UTF-8"); |
| | | byte[] csnByte = getCSN().toString().getBytes("UTF-8"); |
| | | byte[] byteEntryuuid = getEntryUUID().getBytes("UTF-8"); |
| | | |
| | | /* The message header is stored in the form : |
| | | * <operation type><changenumber><dn><assured><entryuuid><change> |
| | | * <operation type><CSN><dn><assured><entryuuid><change> |
| | | * the length of result byte array is therefore : |
| | | * 1 + change number length + 1 + dn length + 1 + 1 + |
| | | * 1 + CSN length + 1 + dn length + 1 + 1 + |
| | | * uuid length + 1 + additional_length |
| | | */ |
| | | int length = 5 + changeNumberByte.length + byteDn.length |
| | | int length = 5 + csnByte.length + byteDn.length |
| | | + byteEntryuuid.length + additionalLength; |
| | | |
| | | byte[] encodedMsg = new byte[length]; |
| | | |
| | | /* put the type of the operation */ |
| | | // put the type of the operation |
| | | encodedMsg[0] = type; |
| | | int pos = 1; |
| | | |
| | | /* put the ChangeNumber */ |
| | | pos = addByteArray(changeNumberByte, encodedMsg, pos); |
| | | // put the CSN |
| | | pos = addByteArray(csnByte, encodedMsg, pos); |
| | | |
| | | /* put the assured information */ |
| | | // put the assured information |
| | | encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0); |
| | | |
| | | /* put the DN and a terminating 0 */ |
| | | // put the DN and a terminating 0 |
| | | pos = addByteArray(byteDn, encodedMsg, pos); |
| | | |
| | | /* put the entry uuid and a terminating 0 */ |
| | | // put the entry uuid and a terminating 0 |
| | | pos = addByteArray(byteEntryuuid, encodedMsg, pos); |
| | | |
| | | return encodedMsg; |
| | |
| | | public int decodeHeader(byte[] types, byte[] encodedMsg) |
| | | throws DataFormatException |
| | | { |
| | | /* first byte is the type */ |
| | | // first byte is the type |
| | | boolean foundMatchingType = false; |
| | | for (byte type : types) |
| | | { |
| | |
| | | return decodeHeader_V1(encodedMsg); |
| | | } |
| | | |
| | | /* read the protocol version */ |
| | | // read the protocol version |
| | | protocolVersion = encodedMsg[1]; |
| | | |
| | | try |
| | | { |
| | | /* Read the changeNumber */ |
| | | // Read the CSN |
| | | int pos = 2; |
| | | int length = getNextLength(encodedMsg, pos); |
| | | String changeNumberStr = new String(encodedMsg, pos, length, "UTF-8"); |
| | | String csnStr = new String(encodedMsg, pos, length, "UTF-8"); |
| | | pos += length + 1; |
| | | changeNumber = new ChangeNumber(changeNumberStr); |
| | | csn = new CSN(csnStr); |
| | | |
| | | /* Read the dn */ |
| | | // Read the dn |
| | | length = getNextLength(encodedMsg, pos); |
| | | dn = new String(encodedMsg, pos, length, "UTF-8"); |
| | | pos += length + 1; |
| | | |
| | | /* Read the entryuuid */ |
| | | // Read the entryuuid |
| | | length = getNextLength(encodedMsg, pos); |
| | | entryUUID = new String(encodedMsg, pos, length, "UTF-8"); |
| | | pos += length + 1; |
| | | |
| | | /* Read the assured information */ |
| | | // Read the assured information |
| | | assuredFlag = encodedMsg[pos++] == 1; |
| | | |
| | | /* Read the assured mode */ |
| | | // Read the assured mode |
| | | assuredMode = AssuredMode.valueOf(encodedMsg[pos++]); |
| | | |
| | | /* Read the safe data level */ |
| | | // Read the safe data level |
| | | safeDataLevel = encodedMsg[pos++]; |
| | | |
| | | return pos; |
| | |
| | | |
| | | try |
| | | { |
| | | /* read the changeNumber */ |
| | | // read the CSN |
| | | int pos = 1; |
| | | int length = getNextLength(encodedMsg, pos); |
| | | String changeNumberStr = new String(encodedMsg, pos, length, "UTF-8"); |
| | | String csnStr = new String(encodedMsg, pos, length, "UTF-8"); |
| | | pos += length + 1; |
| | | changeNumber = new ChangeNumber(changeNumberStr); |
| | | csn = new CSN(csnStr); |
| | | |
| | | /* read the assured information */ |
| | | // read the assured information |
| | | assuredFlag = encodedMsg[pos++] == 1; |
| | | |
| | | /* read the dn */ |
| | | // read the dn |
| | | length = getNextLength(encodedMsg, pos); |
| | | dn = new String(encodedMsg, pos, length, "UTF-8"); |
| | | pos += length + 1; |
| | | |
| | | /* read the entryuuid */ |
| | | // read the entryuuid |
| | | length = getNextLength(encodedMsg, pos); |
| | | entryUUID = new String(encodedMsg, pos, length, "UTF-8"); |
| | | pos += length + 1; |
| | |
| | | * |
| | | * |
| | | * Copyright 2009-2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2013 ForgeRock AS |
| | | */ |
| | | |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import java.util.ArrayList; |
| | |
| | | import org.opends.server.protocols.asn1.ASN1Writer; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.protocols.ldap.LDAPModification; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.plugin.EntryHistorical; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeUsage; |
| | | import org.opends.server.types.ByteStringBuilder; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.RawModification; |
| | | import org.opends.server.types.*; |
| | | |
| | | /** |
| | | * This class holds every common code for the modify messages (mod, moddn). |
| | |
| | | /** |
| | | * Creates a new ModifyCommonMsg with the given informations. |
| | | * |
| | | * @param cn The ChangeNumber of the operation for which the |
| | | * @param csn The CSN of the operation for which the |
| | | * UpdateMessage is created. |
| | | * @param entryUUID The Unique identifier of the entry that is updated |
| | | * by the operation for which the UpdateMessage is created. |
| | | * @param dn The DN of the entry on which the change |
| | | * that caused the creation of this object happened |
| | | */ |
| | | public ModifyCommonMsg(ChangeNumber cn, String entryUUID, String dn) |
| | | public ModifyCommonMsg(CSN csn, String entryUUID, String dn) |
| | | { |
| | | super(cn, entryUUID, dn); |
| | | super(csn, entryUUID, dn); |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** |
| | | * This class describe the replication context that is attached to |
| | |
| | | /** |
| | | * Creates a new Modify Context with the provided parameters. |
| | | * |
| | | * @param changeNumber The change number of the operation. |
| | | * @param csn The CSN of the operation. |
| | | * @param entryUUID the unique Id of the modified entry. |
| | | */ |
| | | public ModifyContext(ChangeNumber changeNumber, String entryUUID) |
| | | public ModifyContext(CSN csn, String entryUUID) |
| | | { |
| | | super(changeNumber, entryUUID); |
| | | super(csn, entryUUID); |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import static org.opends.server.replication.protocol.OperationContext.*; |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.util.List; |
| | | import java.util.zip.DataFormatException; |
| | |
| | | import org.opends.server.core.ModifyDNOperationBasis; |
| | | import org.opends.server.protocols.asn1.ASN1Exception; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.RDN; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.types.operation.PostOperationModifyDNOperation; |
| | | |
| | | import static org.opends.server.replication.protocol.OperationContext.*; |
| | | |
| | | /** |
| | | * Message used to send Modify DN information. |
| | | */ |
| | |
| | | * using mods. |
| | | * |
| | | * @param dn The dn to use for building the message. |
| | | * @param changeNumber The changeNumberto use for building the message. |
| | | * @param csn The CSN to use for building the message. |
| | | * @param entryUUID The unique id to use for building the message. |
| | | * @param newSuperiorEntryUUID The new parent unique id to use for building |
| | | * the message. |
| | |
| | | * @param newSuperior The new Superior entry to use for building the message. |
| | | * @param newRDN The new Rdn to use for building the message. |
| | | */ |
| | | public ModifyDNMsg(String dn, ChangeNumber changeNumber, String entryUUID, |
| | | public ModifyDNMsg(String dn, CSN csn, String entryUUID, |
| | | String newSuperiorEntryUUID, boolean deleteOldRdn, |
| | | String newSuperior, String newRDN) |
| | | { |
| | | super(new ModifyDnContext(changeNumber, entryUUID, newSuperiorEntryUUID), |
| | | dn); |
| | | super(new ModifyDnContext(csn, entryUUID, newSuperiorEntryUUID), dn); |
| | | |
| | | this.newSuperiorEntryUUID = newSuperiorEntryUUID; |
| | | this.deleteOldRdn = deleteOldRdn; |
| | |
| | | * Construct a new Modify DN message (with mods). |
| | | * |
| | | * @param dn The dn to use for building the message. |
| | | * @param changeNumber The changeNumberto use for building the message. |
| | | * @param csn The CSNto use for building the message. |
| | | * @param entryUUID The unique id to use for building the message. |
| | | * @param newSuperiorEntryUUID The new parent unique id to use for building |
| | | * the message. |
| | |
| | | * @param newRDN The new Rdn to use for building the message. |
| | | * @param mods The mod of the operation. |
| | | */ |
| | | public ModifyDNMsg(String dn, ChangeNumber changeNumber, String entryUUID, |
| | | public ModifyDNMsg(String dn, CSN csn, String entryUUID, |
| | | String newSuperiorEntryUUID, boolean deleteOldRdn, String newSuperior, |
| | | String newRDN, List<Modification> mods) |
| | | { |
| | | this(dn, changeNumber, entryUUID, newSuperiorEntryUUID, deleteOldRdn, |
| | | this(dn, csn, entryUUID, newSuperiorEntryUUID, deleteOldRdn, |
| | | newSuperior, newRDN); |
| | | this.encodedMods = encodeMods(mods); |
| | | } |
| | |
| | | moddn.addModification(mod); |
| | | } |
| | | |
| | | ModifyDnContext ctx = new ModifyDnContext(getChangeNumber(), getEntryUUID(), |
| | | ModifyDnContext ctx = new ModifyDnContext(getCSN(), getEntryUUID(), |
| | | newSuperiorEntryUUID); |
| | | moddn.setAttachment(SYNCHROCONTEXT, ctx); |
| | | return moddn; |
| | |
| | | return "ModifyDNMsg content: " + |
| | | " protocolVersion: " + protocolVersion + |
| | | " dn: " + dn + |
| | | " changeNumber: " + changeNumber + |
| | | " changeNumber: " + csn + |
| | | " uniqueId: " + entryUUID + |
| | | " assuredFlag: " + assuredFlag + |
| | | " newRDN: " + newRDN + |
| | |
| | | return "ModifyDNMsg content: " + |
| | | " protocolVersion: " + protocolVersion + |
| | | " dn: " + dn + |
| | | " changeNumber: " + changeNumber + |
| | | " changeNumber: " + csn + |
| | | " uniqueId: " + entryUUID + |
| | | " newRDN: " + newRDN + |
| | | " newSuperior: " + newSuperior + |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** |
| | | * This class describe the replication context that is attached to |
| | |
| | | /** |
| | | * Creates a new ModifyDN Context with the provided parameters. |
| | | * |
| | | * @param changeNumber The change number of the operation. |
| | | * @param csn The CSN of the operation. |
| | | * @param entryUUID the unique Id of the modified entry. |
| | | * @param newSuperiorEntryUUID The unique Identifier of the new parent, |
| | | * can be null if the entry is to stay below the same |
| | | * parent. |
| | | */ |
| | | public ModifyDnContext(ChangeNumber changeNumber, String entryUUID, |
| | | String newSuperiorEntryUUID) |
| | | public ModifyDnContext(CSN csn, String entryUUID, String newSuperiorEntryUUID) |
| | | { |
| | | super(changeNumber, entryUUID); |
| | | super(csn, entryUUID); |
| | | this.newSuperiorEntryUUID = newSuperiorEntryUUID; |
| | | } |
| | | |
| | |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import static org.opends.server.replication.protocol.OperationContext.*; |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | |
| | | import org.opends.server.core.ModifyOperationBasis; |
| | | import org.opends.server.protocols.asn1.ASN1Exception; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.LDAPException; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.RawModification; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.types.operation.PostOperationModifyOperation; |
| | | |
| | | import static org.opends.server.replication.protocol.OperationContext.*; |
| | | |
| | | /** |
| | | * Message used to send Modify information. |
| | | */ |
| | |
| | | /** |
| | | * Creates a new Modify message using the provided information. |
| | | * |
| | | * @param changeNumber The ChangeNumber for the operation. |
| | | * @param csn The CSN for the operation. |
| | | * @param dn The baseDN of the operation. |
| | | * @param mods The mod of the operation. |
| | | * @param entryUUID The unique id of the entry on which the modification |
| | | * needs to apply. |
| | | */ |
| | | public ModifyMsg(ChangeNumber changeNumber, DN dn, List<Modification> mods, |
| | | String entryUUID) |
| | | public ModifyMsg(CSN csn, DN dn, List<Modification> mods, String entryUUID) |
| | | { |
| | | super(new ModifyContext(changeNumber, entryUUID), |
| | | super(new ModifyContext(csn, entryUUID), |
| | | dn.toNormalizedString()); |
| | | this.encodedMods = encodeMods(mods); |
| | | } |
| | |
| | | InternalClientConnection.nextOperationID(), |
| | | InternalClientConnection.nextMessageID(), null, |
| | | ByteString.valueOf(newDn), ldapmods); |
| | | ModifyContext ctx = new ModifyContext(getChangeNumber(), getEntryUUID()); |
| | | ModifyContext ctx = new ModifyContext(getCSN(), getEntryUUID()); |
| | | mod.setAttachment(SYNCHROCONTEXT, ctx); |
| | | return mod; |
| | | } |
| | |
| | | return "ModifyMsg content: " + |
| | | " protocolVersion: " + protocolVersion + |
| | | " dn: " + dn + |
| | | " changeNumber: " + changeNumber + |
| | | " changeNumber: " + csn + |
| | | " uniqueId: " + entryUUID + |
| | | " assuredFlag: " + assuredFlag; |
| | | } |
| | |
| | | return "ModifyMsg content: " + |
| | | " protocolVersion: " + protocolVersion + |
| | | " dn: " + dn + |
| | | " changeNumber: " + changeNumber + |
| | | " changeNumber: " + csn + |
| | | " uniqueId: " + entryUUID + |
| | | " assuredFlag: " + assuredFlag + |
| | | " assuredMode: " + assuredMode + |
| | |
| | | import org.opends.server.protocols.asn1.ASN1; |
| | | import org.opends.server.protocols.asn1.ASN1Reader; |
| | | import org.opends.server.protocols.asn1.ASN1Writer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.types.ByteSequenceReader; |
| | | import org.opends.server.types.ByteString; |
| | |
| | | boolean isLDAPServer = false; |
| | | |
| | | asn1Reader.readStartSequence(); |
| | | // loop on the list of CN of the state |
| | | // loop on the list of CSN of the state |
| | | while(asn1Reader.hasNextElement()) |
| | | { |
| | | ChangeNumber cn; |
| | | CSN csn; |
| | | if (version >= ProtocolVersion.REPLICATION_PROTOCOL_V7) |
| | | { |
| | | cn = ChangeNumber.valueOf(asn1Reader.readOctetString()); |
| | | csn = CSN.valueOf(asn1Reader.readOctetString()); |
| | | } |
| | | else |
| | | { |
| | | cn = ChangeNumber.valueOf(asn1Reader.readOctetStringAsString()); |
| | | csn = CSN.valueOf(asn1Reader.readOctetStringAsString()); |
| | | } |
| | | |
| | | if ((data.replServerDbState != null) && (serverId == 0)) |
| | | if (data.replServerDbState != null && serverId == 0) |
| | | { |
| | | // we are on the first CN that is a fake CN to store the serverId |
| | | // we are on the first CSN that is a fake CSN to store the serverId |
| | | // and the older update time |
| | | serverId = cn.getServerId(); |
| | | outime = cn.getTime(); |
| | | isLDAPServer = (cn.getSeqnum()>0); |
| | | serverId = csn.getServerId(); |
| | | outime = csn.getTime(); |
| | | isLDAPServer = csn.getSeqnum() > 0; |
| | | } |
| | | else |
| | | { |
| | | // we are on a normal CN |
| | | newState.update(cn); |
| | | // we are on a normal CSN |
| | | newState.update(csn); |
| | | } |
| | | } |
| | | asn1Reader.readEndSequence(); |
| | |
| | | writer.writeStartSequence(); |
| | | { |
| | | /* |
| | | * A fake change number helps storing the LDAP server ID. The sequence |
| | | * number will be used to differentiate between an LDAP server (1) or an |
| | | * RS (0). |
| | | * A fake CSN helps storing the LDAP server ID. The sequence number will |
| | | * be used to differentiate between an LDAP server (1) or an RS (0). |
| | | */ |
| | | ChangeNumber cn = new ChangeNumber( |
| | | CSN csn = new CSN( |
| | | server.getValue().approxFirstMissingDate, seqNum, |
| | | server.getKey()); |
| | | if (protocolVersion >= ProtocolVersion.REPLICATION_PROTOCOL_V7) |
| | | { |
| | | writer.writeOctetString(cn.toByteString()); |
| | | writer.writeOctetString(csn.toByteString()); |
| | | } |
| | | else |
| | | { |
| | | writer.writeOctetString(cn.toString()); |
| | | writer.writeOctetString(csn.toString()); |
| | | } |
| | | |
| | | // the changenumbers that make the state |
| | | // the CSNs that make the state |
| | | server.getValue().state.writeTo(writer, protocolVersion); |
| | | } |
| | | writer.writeEndSequence(); |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | * Portions copyright 2012-2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.operation.PluginOperation; |
| | | |
| | |
| | | public static final String SYNCHROCONTEXT = "replicationContext"; |
| | | |
| | | /** |
| | | * The change Number of the Operation. |
| | | * The CSN of the Operation. |
| | | */ |
| | | private ChangeNumber changeNumber; |
| | | private CSN csn; |
| | | |
| | | /** |
| | | * The unique Id of the entry that was modified in the original operation. |
| | |
| | | |
| | | /** |
| | | * Create a new OperationContext. |
| | | * @param changeNumber The change number of the operation. |
| | | * @param csn The CSN of the operation. |
| | | * @param entryUUID The unique Identifier of the modified entry. |
| | | */ |
| | | protected OperationContext(ChangeNumber changeNumber, String entryUUID) |
| | | protected OperationContext(CSN csn, String entryUUID) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | this.csn = csn; |
| | | this.entryUUID = entryUUID; |
| | | } |
| | | |
| | | /** |
| | | * Gets The change number of the Operation. |
| | | * Gets the CSN of the Operation. |
| | | * |
| | | * @return The change number of the Operation. |
| | | * @return The CSN of the Operation. |
| | | */ |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | return changeNumber; |
| | | return csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the change number of an operation. |
| | | * Get the CSN of an operation. |
| | | * |
| | | * @param op The operation. |
| | | * |
| | | * @return The change number of the provided operation, or null if there is |
| | | * no change number associated with the operation. |
| | | * @return The CSN of the provided operation, or null if there is |
| | | * no CSN associated with the operation. |
| | | */ |
| | | public static ChangeNumber getChangeNumber(Operation op) |
| | | public static CSN getCSN(Operation op) |
| | | { |
| | | OperationContext ctx = (OperationContext)op.getAttachment(SYNCHROCONTEXT); |
| | | if (ctx == null) |
| | | { |
| | | return null; |
| | | } |
| | | return ctx.changeNumber; |
| | | return ctx.csn; |
| | | } |
| | | |
| | | /** |
| | | * Get the change number of an operation from the synchronization context |
| | | * Get the CSN of an operation from the synchronization context |
| | | * attached to the provided operation. |
| | | * |
| | | * @param op The operation. |
| | | * |
| | | * @return The change number of the provided operation, or null if there is |
| | | * no change number associated with the operation. |
| | | * @return The CSN of the provided operation, or null if there is |
| | | * no CSN associated with the operation. |
| | | */ |
| | | public static ChangeNumber getChangeNumber(PluginOperation op) |
| | | public static CSN getCSN(PluginOperation op) |
| | | { |
| | | OperationContext ctx = (OperationContext)op.getAttachment(SYNCHROCONTEXT); |
| | | if (ctx == null) |
| | | { |
| | | return null; |
| | | } |
| | | return ctx.changeNumber; |
| | | return ctx.csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | if (obj instanceof OperationContext) |
| | | { |
| | | OperationContext ctx = (OperationContext) obj; |
| | | return ((this.changeNumber.equals(ctx.getChangeNumber()) && |
| | | (this.entryUUID.equals(ctx.getEntryUUID())))); |
| | | return this.csn.equals(ctx.getCSN()) |
| | | && this.entryUUID.equals(ctx.getEntryUUID()); |
| | | } |
| | | else |
| | | return false; |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | |
| | | @Override |
| | | public int hashCode() |
| | | { |
| | | return changeNumber.hashCode() + entryUUID.hashCode(); |
| | | return csn.hashCode() + entryUUID.hashCode(); |
| | | } |
| | | |
| | | |
| | |
| | | import java.util.Set; |
| | | import java.util.zip.DataFormatException; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | /** |
| | | * This class specifies the parameters of a search request on the ECL. |
| | | * It is used as an interface between the requestor (plugin part) |
| | | * - either as an han |
| | | */ |
| | | public class StartECLSessionMsg extends ReplicationMsg |
| | | { |
| | |
| | | /** |
| | | * This specifies that the ECL is requested from a provided interval |
| | | * of change numbers (as defined by draft-good-ldap-changelog [CHANGELOG] |
| | | * and NOT replication change numbers). |
| | | * and NOT replication CSNs). |
| | | * TODO: not yet implemented |
| | | */ |
| | | public final static short REQUEST_TYPE_FROM_DRAFT_CHANGE_NUMBER = 1; |
| | | |
| | | /** |
| | | * This specifies that the ECL is requested only for the entry that have |
| | | * a repl change number matching the provided one. |
| | | * a CSN matching the provided one. |
| | | * TODO: not yet implemented |
| | | */ |
| | | public final static short REQUEST_TYPE_EQUALS_REPL_CHANGE_NUMBER = 2; |
| | |
| | | |
| | | /** |
| | | * When eclRequestType = EQUALS_REPL_CHANGE_NUMBER, specifies the provided |
| | | * replication change number. |
| | | * replication CSN. |
| | | */ |
| | | private ChangeNumber changeNumber; |
| | | private CSN csn; |
| | | |
| | | /** Specifies whether the search is persistent and changesOnly. */ |
| | | private short isPersistent = NON_PERSISTENT; |
| | |
| | | Integer.valueOf(new String(in, pos, length, "UTF-8")); |
| | | pos += length +1; |
| | | |
| | | // replication changeNumber |
| | | // replication CSN |
| | | length = getNextLength(in, pos); |
| | | String changenumberStr = new String(in, pos, length, "UTF-8"); |
| | | String csnStr = new String(in, pos, length, "UTF-8"); |
| | | pos += length + 1; |
| | | changeNumber = new ChangeNumber(changenumberStr); |
| | | csn = new CSN(csnStr); |
| | | |
| | | // persistentSearch mode |
| | | length = getNextLength(in, pos); |
| | |
| | | crossDomainServerState = ""; |
| | | firstDraftChangeNumber = -1; |
| | | lastDraftChangeNumber = -1; |
| | | changeNumber = new ChangeNumber(0,0,0); |
| | | csn = new CSN(0, 0, 0); |
| | | isPersistent = NON_PERSISTENT; |
| | | operationId = "-1"; |
| | | excludedBaseDNs = new HashSet<String>(); |
| | |
| | | |
| | | try |
| | | { |
| | | byte[] byteMode = |
| | | Short.toString(eclRequestType).getBytes("UTF-8"); |
| | | byte[] byteSequenceNumber = |
| | | String.valueOf(firstDraftChangeNumber).getBytes("UTF-8"); |
| | | byte[] byteStopSequenceNumber = |
| | | String.valueOf(lastDraftChangeNumber).getBytes("UTF-8"); |
| | | byte[] byteChangeNumber = |
| | | changeNumber.toString().getBytes("UTF-8"); |
| | | byte[] bytePsearch = |
| | | Short.toString(isPersistent).getBytes(); |
| | | byte[] byteGeneralizedState = |
| | | String.valueOf(crossDomainServerState).getBytes("UTF-8"); |
| | | byte[] byteOperationId = |
| | | String.valueOf(operationId).getBytes("UTF-8"); |
| | | byte[] byteExcludedDNs = |
| | | String.valueOf(excludedBaseDNsString).getBytes("UTF-8"); |
| | | byte[] byteMode = toBytes(eclRequestType); |
| | | byte[] byteSequenceNumber = toBytes(firstDraftChangeNumber); |
| | | byte[] byteStopSequenceNumber = toBytes(lastDraftChangeNumber); |
| | | byte[] byteCSN = csn.toString().getBytes("UTF-8"); |
| | | byte[] bytePsearch = toBytes(isPersistent); |
| | | byte[] byteGeneralizedState = toBytes(crossDomainServerState); |
| | | byte[] byteOperationId = toBytes(operationId); |
| | | byte[] byteExcludedDNs = toBytes(excludedBaseDNsString); |
| | | |
| | | int length = |
| | | byteMode.length + 1 + |
| | | byteSequenceNumber.length + 1 + |
| | | byteStopSequenceNumber.length + 1 + |
| | | byteChangeNumber.length + 1 + |
| | | byteCSN.length + 1 + |
| | | bytePsearch.length + 1 + |
| | | byteGeneralizedState.length + 1 + |
| | | byteOperationId.length + 1 + |
| | |
| | | pos = addByteArray(byteMode, resultByteArray, pos); |
| | | pos = addByteArray(byteSequenceNumber, resultByteArray, pos); |
| | | pos = addByteArray(byteStopSequenceNumber, resultByteArray, pos); |
| | | pos = addByteArray(byteChangeNumber, resultByteArray, pos); |
| | | pos = addByteArray(byteCSN, resultByteArray, pos); |
| | | pos = addByteArray(bytePsearch, resultByteArray, pos); |
| | | pos = addByteArray(byteGeneralizedState, resultByteArray, pos); |
| | | pos = addByteArray(byteOperationId, resultByteArray, pos); |
| | | pos = addByteArray(byteExcludedDNs, resultByteArray, pos); |
| | | return resultByteArray; |
| | | |
| | | } catch (IOException e) |
| | | { |
| | | // never happens |
| | |
| | | } |
| | | } |
| | | |
| | | private byte[] toBytes(int i) throws UnsupportedEncodingException |
| | | { |
| | | return toBytes(String.valueOf(i)); |
| | | } |
| | | |
| | | private byte[] toBytes(String s) throws UnsupportedEncodingException |
| | | { |
| | | return String.valueOf(s).getBytes("UTF-8"); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | |
| | | return getClass().getCanonicalName() + " [" + |
| | | " requestType="+ eclRequestType + |
| | | " persistentSearch=" + isPersistent + |
| | | " changeNumber=" + changeNumber + |
| | | " csn=" + csn + |
| | | " firstDraftChangeNumber=" + firstDraftChangeNumber + |
| | | " lastDraftChangeNumber=" + lastDraftChangeNumber + |
| | | " generalizedState=" + crossDomainServerState + |
| | |
| | | } |
| | | |
| | | /** |
| | | * Getter on the replication change number. |
| | | * @return the replication change number. |
| | | * Getter on the replication CSN. |
| | | * @return the replication CSN. |
| | | */ |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | return changeNumber; |
| | | return csn; |
| | | } |
| | | |
| | | /** |
| | | * Setter on the replication change number. |
| | | * @param changeNumber the provided replication change number. |
| | | * Setter on the replication CSN. |
| | | * @param csn the provided replication CSN. |
| | | */ |
| | | public void setChangeNumber(ChangeNumber changeNumber) |
| | | public void setCSN(CSN csn) |
| | | { |
| | | this.changeNumber = changeNumber; |
| | | this.csn = csn; |
| | | } |
| | | /** |
| | | * Getter on the type of request. |
| | |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.util.zip.DataFormatException; |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | | import org.opends.server.replication.common.AssuredMode; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** |
| | | * Abstract class that must be extended to define a message |
| | |
| | | protected short protocolVersion; |
| | | |
| | | /** |
| | | * The ChangeNumber of this update. |
| | | * The CSN of this update. |
| | | */ |
| | | protected ChangeNumber changeNumber; |
| | | protected CSN csn; |
| | | |
| | | /** |
| | | * True when the update must use assured replication. |
| | |
| | | // Decode header |
| | | int pos = decodeHeader(MSG_TYPE_GENERIC_UPDATE, bytes); |
| | | |
| | | /* Read the payload : all the remaining bytes but the terminating 0 */ |
| | | // Read the payload : all the remaining bytes but the terminating 0 |
| | | int length = bytes.length - pos; |
| | | payload = new byte[length]; |
| | | try |
| | |
| | | * <p> |
| | | * This constructor is only used for testing. |
| | | * |
| | | * @param changeNumber The ChangeNumber associated with the change |
| | | * encoded in this message. |
| | | * @param csn The CSN associated with the change encoded in this message. |
| | | * @param payload The payload that must be encoded in this message. |
| | | */ |
| | | public UpdateMsg(ChangeNumber changeNumber, byte[] payload) |
| | | public UpdateMsg(CSN csn, byte[] payload) |
| | | { |
| | | this.payload = payload; |
| | | this.protocolVersion = ProtocolVersion.getCurrentVersion(); |
| | | this.changeNumber = changeNumber; |
| | | this.csn = csn; |
| | | } |
| | | |
| | | /** |
| | | * Get the ChangeNumber from the message. |
| | | * @return the ChangeNumber |
| | | * Get the CSN from the message. |
| | | * @return the CSN |
| | | */ |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | return changeNumber; |
| | | return csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | public boolean equals(Object obj) |
| | | { |
| | | return obj != null && obj.getClass() == this.getClass() && |
| | | changeNumber.equals(((UpdateMsg) obj).changeNumber); |
| | | csn.equals(((UpdateMsg) obj).csn); |
| | | } |
| | | |
| | | /** |
| | |
| | | @Override |
| | | public int hashCode() |
| | | { |
| | | return changeNumber.hashCode(); |
| | | return csn.hashCode(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public int compareTo(UpdateMsg msg) |
| | | { |
| | | return changeNumber.compareTo(msg.getChangeNumber()); |
| | | return csn.compareTo(msg.getCSN()); |
| | | } |
| | | |
| | | /** |
| | |
| | | protected byte[] encodeHeader(byte type, int additionalLength, short version) |
| | | throws UnsupportedEncodingException |
| | | { |
| | | byte[] changeNumberByte = |
| | | this.getChangeNumber().toString().getBytes("UTF-8"); |
| | | byte[] csnByte = getCSN().toString().getBytes("UTF-8"); |
| | | |
| | | /* The message header is stored in the form : |
| | | * <operation type><protocol version><changenumber><assured> |
| | | * <operation type><protocol version><CSN><assured> |
| | | * <assured mode> <safe data level> |
| | | * the length of result byte array is therefore : |
| | | * 1 + 1 + change number length + 1 + 1 |
| | | * 1 + 1 + CSN length + 1 + 1 |
| | | * + 1 + 1 + additional_length |
| | | */ |
| | | int length = 6 + changeNumberByte.length + additionalLength; |
| | | int length = 6 + csnByte.length + additionalLength; |
| | | |
| | | byte[] encodedMsg = new byte[length]; |
| | | |
| | | /* put the type of the operation */ |
| | | // put the type of the operation |
| | | encodedMsg[0] = type; |
| | | |
| | | /* put the protocol version */ |
| | | // put the protocol version |
| | | encodedMsg[1] = (byte)ProtocolVersion.getCurrentVersion(); |
| | | int pos = 2; |
| | | |
| | | /* Put the ChangeNumber */ |
| | | pos = addByteArray(changeNumberByte, encodedMsg, pos); |
| | | // Put the CSN |
| | | pos = addByteArray(csnByte, encodedMsg, pos); |
| | | |
| | | /* Put the assured flag */ |
| | | // Put the assured flag |
| | | encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0); |
| | | |
| | | /* Put the assured mode */ |
| | | // Put the assured mode |
| | | encodedMsg[pos++] = assuredMode.getValue(); |
| | | |
| | | /* Put the safe data level */ |
| | | // Put the safe data level |
| | | encodedMsg[pos++] = safeDataLevel; |
| | | |
| | | return encodedMsg; |
| | |
| | | throws DataFormatException |
| | | { |
| | | /* The message header is stored in the form : |
| | | * <operation type><protocol version><changenumber><assured> |
| | | * <operation type><protocol version><CSN><assured> |
| | | * <assured mode> <safe data level> |
| | | */ |
| | | if (!(type == encodedMsg[0])) |
| | | throw new DataFormatException("byte[] is not a valid update msg: " |
| | | + encodedMsg[0]); |
| | | |
| | | /* read the protocol version */ |
| | | protocolVersion = (short)encodedMsg[1]; |
| | | // read the protocol version |
| | | protocolVersion = encodedMsg[1]; |
| | | |
| | | try |
| | | { |
| | | /* Read the changeNumber */ |
| | | // Read the CSN |
| | | int pos = 2; |
| | | int length = getNextLength(encodedMsg, pos); |
| | | String changenumberStr = new String(encodedMsg, pos, length, "UTF-8"); |
| | | String csnStr = new String(encodedMsg, pos, length, "UTF-8"); |
| | | pos += length + 1; |
| | | changeNumber = new ChangeNumber(changenumberStr); |
| | | csn = new CSN(csnStr); |
| | | |
| | | /* Read the assured information */ |
| | | // Read the assured information |
| | | assuredFlag = encodedMsg[pos++] == 1; |
| | | |
| | | /* Read the assured mode */ |
| | | // Read the assured mode |
| | | assuredMode = AssuredMode.valueOf(encodedMsg[pos++]); |
| | | |
| | | /* Read the safe data level */ |
| | | // Read the safe data level |
| | | safeDataLevel = encodedMsg[pos++]; |
| | | |
| | | return pos; |
| | |
| | | public byte[] getBytes(short protocolVersion) |
| | | throws UnsupportedEncodingException |
| | | { |
| | | /* Encode the header in a byte[] large enough to also contain the payload */ |
| | | // Encode the header in a byte[] large enough to also contain the payload |
| | | byte[] resultByteArray = encodeHeader(MSG_TYPE_GENERIC_UPDATE, |
| | | payload.length, ProtocolVersion.getCurrentVersion()); |
| | | |
| | | int pos = resultByteArray.length - payload.length; |
| | | |
| | | /* Add the payload */ |
| | | // Add the payload |
| | | for (int i = 0; i < payload.length; i++, pos++) |
| | | { |
| | | resultByteArray[pos] = payload[i]; |
| | |
| | | import org.opends.messages.Category; |
| | | import org.opends.messages.Message; |
| | | import org.opends.messages.Severity; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.MultiDomainServerState; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.common.ServerStatus; |
| | |
| | | */ |
| | | private String startCookie; |
| | | /** |
| | | * Specifies the value of the cookie before the change currently processed |
| | | * is returned. It is updated with the change number of the change |
| | | * currently processed (thus becoming the "current" cookie just |
| | | * before the change is returned. |
| | | * Specifies the value of the cookie before the change currently processed is |
| | | * returned. It is updated with the CSN of the change currently processed |
| | | * (thus becoming the "current" cookie just before the change is returned. |
| | | */ |
| | | private MultiDomainServerState previousCookie = new MultiDomainServerState(); |
| | | /** |
| | |
| | | private Set<String> excludedBaseDNs = new HashSet<String>(); |
| | | |
| | | /** |
| | | * Eligible changeNumber - only changes older or equal to eligibleCN |
| | | * are published in the ECL. |
| | | * Eligible CSN - only changes older or equal to eligibleCSN * are published |
| | | * in the ECL. |
| | | */ |
| | | private ChangeNumber eligibleCN = null; |
| | | private CSN eligibleCSN; |
| | | |
| | | /** |
| | | * Provides a string representation of this object. |
| | |
| | | .append("] [rsd=").append(rsd) |
| | | .append("] [nextMsg=").append(nextMsg).append("(") |
| | | .append(nextMsg != null ? |
| | | new Date(nextMsg.getChangeNumber().getTime()).toString():"") |
| | | new Date(nextMsg.getCSN().getTime()).toString():"") |
| | | .append(")") |
| | | .append("] [nextNonEligibleMsg=").append(nextNonEligibleMsg) |
| | | .append("] [startState=").append(startState) |
| | |
| | | |
| | | /** |
| | | * Get the next message eligible regarding |
| | | * the crossDomain eligible CN. Put it in the context table. |
| | | * the crossDomain eligible CSN. Put it in the context table. |
| | | * @param opid The operation id. |
| | | */ |
| | | private void getNextEligibleMessageForDomain(String opid) |
| | |
| | | if (nextNonEligibleMsg != null) |
| | | { |
| | | boolean hasBecomeEligible = |
| | | (nextNonEligibleMsg.getChangeNumber().getTime() |
| | | <= eligibleCN.getTime()); |
| | | (nextNonEligibleMsg.getCSN().getTime() |
| | | <= eligibleCSN.getTime()); |
| | | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo(" In ECLServerHandler, for " + mh.getBaseDN() + |
| | | " getNextEligibleMessageForDomain(" + opid+ ") " |
| | | + " stored nonEligibleMsg " + nextNonEligibleMsg |
| | | + " has now become eligible regarding " |
| | | + " the eligibleCN ("+ eligibleCN |
| | | + " the eligibleCSN ("+ eligibleCSN |
| | | + " ):" + hasBecomeEligible); |
| | | |
| | | if (hasBecomeEligible) |
| | |
| | | // to be returned in the external changelog. |
| | | // So let's check if the chg time is older than the trim date |
| | | } while ((newMsg!=null) && |
| | | (newMsg.getChangeNumber().getTime() < domainLatestTrimDate)); |
| | | (newMsg.getCSN().getTime() < domainLatestTrimDate)); |
| | | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo(" In ECLServerHandler, for " + mh.getBaseDN() + |
| | |
| | | // in non blocking mode, return null when no more msg |
| | | if (newMsg != null) |
| | | { |
| | | boolean isEligible = (newMsg.getChangeNumber().getTime() |
| | | <= eligibleCN.getTime()); |
| | | boolean isEligible = (newMsg.getCSN().getTime() |
| | | <= eligibleCSN.getTime()); |
| | | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo(" In ECLServerHandler, for " + mh.getBaseDN() |
| | | + " getNextEligibleMessageForDomain(" + opid+ ") " |
| | | + "newMsg isEligible=" + isEligible + " since " |
| | | + "newMsg=[" + newMsg.getChangeNumber() |
| | | + " " + new Date(newMsg.getChangeNumber().getTime()) |
| | | + "] eligibleCN=[" + eligibleCN |
| | | + " " + new Date(eligibleCN.getTime())+"]" |
| | | + "newMsg=[" + newMsg.getCSN() |
| | | + " " + new Date(newMsg.getCSN().getTime()) |
| | | + "] eligibleCSN=[" + eligibleCSN |
| | | + " " + new Date(eligibleCSN.getTime())+"]" |
| | | + dumpState()); |
| | | |
| | | if (isEligible) |
| | |
| | | // startDraftCN provided in the request IS NOT in the DraftCNDb |
| | | |
| | | /* |
| | | * Get the draftLimits (from the eligibleCN got at the beginning of the |
| | | * Get the draftLimits (from the eligibleCSN got at the beginning of the |
| | | * operation) in order to have the first and possible last DraftCN. |
| | | */ |
| | | final int[] limits = |
| | | replicationServer.getECLDraftCNLimits(eligibleCN, excludedBaseDNs); |
| | | replicationServer.getECLDraftCNLimits(eligibleCSN, excludedBaseDNs); |
| | | final int firstDraftCN = limits[0]; |
| | | final int lastDraftCN = limits[1]; |
| | | |
| | |
| | | // Assign the start state for the domain |
| | | if (isPersistent == PERSISTENT_CHANGES_ONLY) |
| | | { |
| | | newDomainCtxt.startState = rsd.getEligibleState(eligibleCN); |
| | | newDomainCtxt.startState = rsd.getEligibleState(eligibleCSN); |
| | | startStatesFromProvidedCookie.remove(rsd.getBaseDn()); |
| | | } |
| | | else |
| | |
| | | // what we have in the replication changelog |
| | | if (newDomainCtxt.startState == null) |
| | | { |
| | | ChangeNumber latestTrimCN = |
| | | new ChangeNumber(newDomainCtxt.domainLatestTrimDate, 0, 0); |
| | | CSN latestTrimCSN = |
| | | new CSN(newDomainCtxt.domainLatestTrimDate, 0, 0); |
| | | newDomainCtxt.startState = |
| | | rsd.getStartState().duplicateOnlyOlderThan(latestTrimCN); |
| | | rsd.getStartState().duplicateOnlyOlderThan(latestTrimCSN); |
| | | } |
| | | } |
| | | else |
| | |
| | | } |
| | | } |
| | | |
| | | // Set the stop state for the domain from the eligibleCN |
| | | newDomainCtxt.stopState = rsd.getEligibleState(eligibleCN); |
| | | // Set the stop state for the domain from the eligibleCSN |
| | | newDomainCtxt.stopState = rsd.getEligibleState(eligibleCSN); |
| | | } |
| | | newDomainCtxt.currentState = new ServerState(); |
| | | |
| | |
| | | */ |
| | | for (int serverId : rsDomain.getStartState()) |
| | | { |
| | | ChangeNumber dbOldestChange = |
| | | rsDomain.getStartState().getChangeNumber(serverId); |
| | | ChangeNumber providedChange = cookie.getChangeNumber(serverId); |
| | | CSN dbOldestChange = |
| | | rsDomain.getStartState().getCSN(serverId); |
| | | CSN providedChange = cookie.getCSN(serverId); |
| | | if (providedChange != null |
| | | && providedChange.older(dbOldestChange)) |
| | | { |
| | |
| | | |
| | | excludedBaseDNs = startECLSessionMsg.getExcludedBaseDNs(); |
| | | replicationServer.disableEligibility(excludedBaseDNs); |
| | | eligibleCN = replicationServer.getEligibleCN(); |
| | | eligibleCSN = replicationServer.getEligibleCSN(); |
| | | |
| | | initializeChangelogSearch(startECLSessionMsg); |
| | | |
| | |
| | | closeInitPhase(); |
| | | } |
| | | |
| | | /* TODO: From replication changenumber |
| | | /* TODO: From replication CSN |
| | | //-- |
| | | if (startCLMsg.getStartMode()==2) |
| | | { |
| | | if (CLSearchFromProvidedExactCN(startCLMsg.getChangeNumber())) |
| | | if (CLSearchFromProvidedExactCSN(startCLMsg.getCSN())) |
| | | return; |
| | | } |
| | | |
| | |
| | | { |
| | | // to get the CL first and last |
| | | initializeCLDomCtxts(null); // from start |
| | | ChangeNumber crossDomainEligibleCN = computeCrossDomainEligibleCN(); |
| | | CSN crossDomainEligibleCSN = computeCrossDomainEligibleCSN(); |
| | | |
| | | try |
| | | { |
| | | // to get the CL first and last |
| | | // last rely on the crossDomainEligibleCN thus must have been |
| | | // last rely on the crossDomainEligibleCSN thus must have been |
| | | // computed before |
| | | int[] limits = computeCLLimits(crossDomainEligibleCN); |
| | | int[] limits = computeCLLimits(crossDomainEligibleCSN); |
| | | // Send the response |
| | | CLLimitsMsg msg = new CLLimitsMsg(limits[0], limits[1]); |
| | | session.publish(msg); |
| | |
| | | // Set and test the domain of the oldestChange see if we reached |
| | | // the end of the phase for this domain |
| | | oldestContext.currentState.update( |
| | | change.getUpdateMsg().getChangeNumber()); |
| | | change.getUpdateMsg().getCSN()); |
| | | |
| | | if (oldestContext.currentState.cover(oldestContext.stopState)) |
| | | { |
| | |
| | | oldestContext.nextMsg = null; // clean |
| | | |
| | | oldestContext.currentState.update( |
| | | change.getUpdateMsg().getChangeNumber()); |
| | | change.getUpdateMsg().getCSN()); |
| | | |
| | | if (draftCompat) |
| | | { |
| | |
| | | { |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo("getNextECLUpdate updates previousCookie:" |
| | | + oldestChange.getUpdateMsg().getChangeNumber()); |
| | | + oldestChange.getUpdateMsg().getCSN()); |
| | | |
| | | // Update the current state |
| | | previousCookie.update( |
| | | oldestChange.getBaseDN(), |
| | | oldestChange.getUpdateMsg().getChangeNumber()); |
| | | oldestChange.getUpdateMsg().getCSN()); |
| | | |
| | | // Set the current value of global state in the returned message |
| | | oldestChange.setCookie(previousCookie); |
| | |
| | | // The following loop allows to loop until being on the same cn |
| | | // in the 2 dbs |
| | | |
| | | // replogcn : the oldest change from the changelog db |
| | | ChangeNumber cnFromChangelogDb = |
| | | oldestChange.getUpdateMsg().getChangeNumber(); |
| | | // replogCSN : the oldest change from the changelog db |
| | | CSN csnFromChangelogDb = oldestChange.getUpdateMsg().getCSN(); |
| | | String dnFromChangelogDb = oldestChange.getBaseDN(); |
| | | |
| | | while (true) |
| | |
| | | |
| | | |
| | | // the next change from the DraftCN db |
| | | ChangeNumber cnFromDraftCNDb = changelogDBIter.getChangeNumber(); |
| | | CSN csnFromDraftCNDb = changelogDBIter.getCSN(); |
| | | String dnFromDraftCNDb = changelogDBIter.getBaseDN(); |
| | | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo("getNextECLUpdate generating draftCN " |
| | | + " comparing the 2 db DNs :" + dnFromChangelogDb + "?=" |
| | | + cnFromChangelogDb + " timestamps:" |
| | | + new Date(cnFromChangelogDb.getTime()) + " ?older" |
| | | + new Date(cnFromDraftCNDb.getTime())); |
| | | + csnFromChangelogDb + " timestamps:" |
| | | + new Date(csnFromChangelogDb.getTime()) + " ?older" |
| | | + new Date(csnFromDraftCNDb.getTime())); |
| | | |
| | | |
| | | if (areSameChange(cnFromChangelogDb, dnFromChangelogDb, |
| | | cnFromDraftCNDb, dnFromDraftCNDb)) |
| | | if (areSameChange(csnFromChangelogDb, dnFromChangelogDb, |
| | | csnFromDraftCNDb, dnFromDraftCNDb)) |
| | | { |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo("getNextECLUpdate generating draftCN " |
| | |
| | | } |
| | | |
| | | |
| | | if (!cnFromDraftCNDb.older(cnFromChangelogDb)) |
| | | if (!csnFromDraftCNDb.older(csnFromChangelogDb)) |
| | | { |
| | | // the change from the changelogDb is older |
| | | // it should have been stored lately |
| | | // let's continue to traverse the changelogdb |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo("getNextECLUpdate: will skip " + cnFromChangelogDb |
| | | TRACER.debugInfo("getNextECLUpdate: will skip " + csnFromChangelogDb |
| | | + " and read next from the regular changelog."); |
| | | return false; // TO BE CHECKED |
| | | } |
| | |
| | | // found in the changelogDb. |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo("getNextECLUpdate generating draftCN " |
| | | + " will skip " + cnFromDraftCNDb |
| | | + " will skip " + csnFromDraftCNDb |
| | | + " and read next change from the DraftCNDb."); |
| | | |
| | | isEndOfDraftCNReached = !changelogDBIter.next(); |
| | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo("getNextECLUpdate generating draftCN " |
| | | + " has skipped to " + " sn=" + changelogDBIter.getDraftCN() |
| | | + " cn=" + changelogDBIter.getChangeNumber() |
| | | + " csn=" + changelogDBIter.getCSN() |
| | | + " End of draftCNDb ?" + isEndOfDraftCNReached); |
| | | } |
| | | catch (ChangelogException e) |
| | |
| | | } |
| | | } |
| | | |
| | | private boolean areSameChange(ChangeNumber cn1, String dn1, ChangeNumber cn2, |
| | | String dn2) |
| | | private boolean areSameChange(CSN csn1, String dn1, CSN csn2, String dn2) |
| | | { |
| | | boolean sameDN = dn1.compareTo(dn2) == 0; |
| | | boolean sameCN = cn1.compareTo(cn2) == 0; |
| | | return sameDN && sameCN; |
| | | boolean sameCSN = csn1.compareTo(csn2) == 0; |
| | | return sameDN && sameCSN; |
| | | } |
| | | |
| | | private void assignNewDraftCNAndStore(ECLUpdateMsg change) |
| | |
| | | change.getDraftChangeNumber(), |
| | | previousCookie.toString(), |
| | | change.getBaseDN(), |
| | | change.getUpdateMsg().getChangeNumber()); |
| | | change.getUpdateMsg().getCSN()); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Refresh the eligibleCN by requesting the replication server. |
| | | * Refresh the eligibleCSN by requesting the replication server. |
| | | */ |
| | | public void refreshEligibleCN() |
| | | public void refreshEligibleCSN() |
| | | { |
| | | eligibleCN = replicationServer.getEligibleCN(); |
| | | eligibleCSN = replicationServer.getEligibleCSN(); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | try |
| | | { |
| | | handler.refreshEligibleCN(); |
| | | handler.refreshEligibleCSN(); |
| | | update = handler.takeECLUpdate(); |
| | | } |
| | | catch(DirectoryException de) |
| | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.replication.common.AssuredMode; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.AckMsg; |
| | | |
| | | /** |
| | |
| | | */ |
| | | public abstract class ExpectedAcksInfo |
| | | { |
| | | // The server handler of the server that sent the assured update message and |
| | | // to who we want to return the final ack |
| | | /** |
| | | * The server handler of the server that sent the assured update message and |
| | | * to who we want to return the final ack. |
| | | */ |
| | | private ServerHandler requesterServerHandler = null; |
| | | |
| | | // The requested assured mode of matching update message |
| | | /** The requested assured mode of matching update message. */ |
| | | private AssuredMode assuredMode = null; |
| | | |
| | | /** |
| | | * The change number of the assured update message we want acks for. |
| | | */ |
| | | protected ChangeNumber changeNumber = null; |
| | | /** The CSN of the assured update message we want acks for. */ |
| | | protected CSN csn = null; |
| | | |
| | | /** |
| | | * Is the treatment of the acks for the update message completed or not ? |
| | |
| | | |
| | | /** |
| | | * Creates a new ExpectedAcksInfo. |
| | | * @param changeNumber The change number of the assured update message |
| | | * @param csn The CSN of the assured update message |
| | | * @param requesterServerHandler The server handler of the server that sent |
| | | * the assured update message |
| | | * @param assuredMode The assured mode requested by the assured update message |
| | | * @param expectedServers The list of servers we want an ack from |
| | | */ |
| | | protected ExpectedAcksInfo(ChangeNumber changeNumber, |
| | | ServerHandler requesterServerHandler, AssuredMode assuredMode, |
| | | List<Integer> expectedServers) |
| | | protected ExpectedAcksInfo(CSN csn, ServerHandler requesterServerHandler, |
| | | AssuredMode assuredMode, List<Integer> expectedServers) |
| | | { |
| | | this.requesterServerHandler = requesterServerHandler; |
| | | this.assuredMode = assuredMode; |
| | | this.changeNumber = changeNumber; |
| | | this.csn = csn; |
| | | |
| | | // Initialize list of servers we expect acks from |
| | | for (Integer serverId : expectedServers) |
| | |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | import org.opends.server.replication.server.changelog.api.*; |
| | |
| | | do |
| | | { |
| | | msg1 = msgQueue.removeFirst(); |
| | | } while (!msg.getChangeNumber().equals(msg1.getChangeNumber())); |
| | | } while (!msg.getCSN().equals(msg1.getCSN())); |
| | | updateServerState(msg); |
| | | return msg1; |
| | | } |
| | |
| | | * Returns null when the queue is empty. |
| | | * @return The older change number. |
| | | */ |
| | | public ChangeNumber getOlderUpdateCN() |
| | | public CSN getOlderUpdateCSN() |
| | | { |
| | | ChangeNumber result = null; |
| | | CSN result = null; |
| | | synchronized (msgQueue) |
| | | { |
| | | if (following) |
| | |
| | | if (!msgQueue.isEmpty()) |
| | | { |
| | | UpdateMsg msg = msgQueue.first(); |
| | | result = msg.getChangeNumber(); |
| | | result = msg.getCSN(); |
| | | } |
| | | } |
| | | else |
| | |
| | | if (!lateQueue.isEmpty()) |
| | | { |
| | | UpdateMsg msg = lateQueue.first(); |
| | | result = msg.getChangeNumber(); |
| | | result = msg.getCSN(); |
| | | } |
| | | else |
| | | { |
| | |
| | | return result; |
| | | } |
| | | |
| | | private ChangeNumber findOldestChangeNumberFromReplicaDBs() |
| | | private CSN findOldestChangeNumberFromReplicaDBs() |
| | | { |
| | | SortedSet<ReplicaDBCursor> sortedCursors = null; |
| | | try |
| | | { |
| | | sortedCursors = collectAllCursorsWithChanges(); |
| | | UpdateMsg msg = sortedCursors.first().getChange(); |
| | | return msg.getChangeNumber(); |
| | | return msg.getCSN(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | |
| | | /** |
| | | * Collects all the {@link ReplicaDBCursor}s that have changes and sort them |
| | | * with the oldest {@link ChangeNumber} first. |
| | | * with the oldest {@link CSN} first. |
| | | * |
| | | * @return a List of cursors with changes sorted by their {@link ChangeNumber} |
| | | * @return a List of cursors with changes sorted by their {@link CSN} |
| | | * (oldest first) |
| | | */ |
| | | private NavigableSet<ReplicaDBCursor> collectAllCursorsWithChanges() |
| | |
| | | for (int serverId : replicationServerDomain.getServerIds()) |
| | | { |
| | | // get the last already sent CN from that server to get a cursor |
| | | final ChangeNumber lastCsn = serverState.getChangeNumber(serverId); |
| | | final CSN lastCsn = serverState.getCSN(serverId); |
| | | addCursorIfNotEmpty(results, |
| | | replicationServerDomain.getCursorFrom(serverId, lastCsn)); |
| | | } |
| | |
| | | */ |
| | | public boolean updateServerState(UpdateMsg msg) |
| | | { |
| | | return serverState.update(msg.getChangeNumber()); |
| | | return serverState.update(msg.getCSN()); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | package org.opends.server.replication.server; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | |
| | | import java.util.SortedMap; |
| | | import java.util.TreeMap; |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | |
| | | /** |
| | | * This class is used to build ordered lists of UpdateMsg. |
| | | * The order is defined by the order of the ChangeNumber of the UpdateMsg. |
| | | * The order is defined by the order of the CSN of the UpdateMsg. |
| | | */ |
| | | |
| | | public class MsgQueue |
| | | { |
| | | private SortedMap<ChangeNumber, UpdateMsg> map = |
| | | new TreeMap<ChangeNumber, UpdateMsg>(); |
| | | private SortedMap<CSN, UpdateMsg> map = new TreeMap<CSN, UpdateMsg>(); |
| | | private final Object lock = new Object(); |
| | | |
| | | // The total number of bytes for all the message in the queue. |
| | | /** The total number of bytes for all the message in the queue. */ |
| | | private int bytesCount = 0; |
| | | |
| | | /** |
| | |
| | | { |
| | | synchronized (lock) |
| | | { |
| | | UpdateMsg msgSameChangeNumber = map.put(update.getChangeNumber(), update); |
| | | if (msgSameChangeNumber != null) |
| | | UpdateMsg msgSameCSN = map.put(update.getCSN(), update); |
| | | if (msgSameCSN != null) |
| | | { |
| | | try |
| | | { |
| | | if (msgSameChangeNumber.getBytes().length != update.getBytes().length |
| | | || msgSameChangeNumber.isAssured() != update.isAssured() |
| | | || msgSameChangeNumber.getVersion() != update.getVersion()) |
| | | if (msgSameCSN.getBytes().length != update.getBytes().length |
| | | || msgSameCSN.isAssured() != update.isAssured() |
| | | || msgSameCSN.getVersion() != update.getVersion()) |
| | | { |
| | | // Adding 2 msgs with the same ChangeNumber is ok only when |
| | | // Adding 2 msgs with the same CSN is ok only when |
| | | // the 2 msgs are the same |
| | | bytesCount += (update.size() - msgSameChangeNumber.size()); |
| | | bytesCount += (update.size() - msgSameCSN.size()); |
| | | Message errMsg = ERR_RSQUEUE_DIFFERENT_MSGS_WITH_SAME_CN.get( |
| | | msgSameChangeNumber.getChangeNumber().toString(), |
| | | msgSameChangeNumber.toString(), update.toString()); |
| | | msgSameCSN.getCSN().toString(), |
| | | msgSameCSN.toString(), update.toString()); |
| | | logError(errMsg); |
| | | } |
| | | } |
| | |
| | | synchronized (lock) |
| | | { |
| | | UpdateMsg update = map.get(map.firstKey()); |
| | | map.remove(update.getChangeNumber()); |
| | | map.remove(update.getCSN()); |
| | | bytesCount -= update.size(); |
| | | if ((map.size() == 0) && (bytesCount != 0)) |
| | | { |
| | |
| | | |
| | | /** |
| | | * Returns <tt>true</tt> if this map contains an UpdateMsg |
| | | * with the same ChangeNumber as the given UpdateMsg. |
| | | * with the same CSN as the given UpdateMsg. |
| | | * |
| | | * @param msg UpdateMsg whose presence in this queue is to be tested. |
| | | * |
| | | * @return <tt>true</tt> if this map contains an UpdateMsg |
| | | * with the same ChangeNumber as the given UpdateMsg. |
| | | * with the same CSN as the given UpdateMsg. |
| | | * |
| | | */ |
| | | public boolean contains(UpdateMsg msg) |
| | | { |
| | | synchronized (lock) |
| | | { |
| | | return map.containsKey(msg.getChangeNumber()); |
| | | return map.containsKey(msg.getCSN()); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.util.zip.DataFormatException; |
| | | |
| | | import org.opends.server.replication.common.AssuredMode; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.LDAPUpdateMsg; |
| | | import org.opends.server.replication.protocol.ProtocolVersion; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | |
| | | /** |
| | | * This is a facility class that is in fact an hack to optimize replication |
| | |
| | | |
| | | /* Look for assured flag position: |
| | | * The message header is stored in the form : |
| | | * <operation type><changenumber><dn><assured><entryuuid><change> |
| | | * <operation type><CSN><dn><assured><entryuuid><change> |
| | | * the length of result byte array is therefore : |
| | | * 1 + change number length + 1 + dn length + 1 + 1 + |
| | | * 1 + CSN length + 1 + dn length + 1 + 1 + |
| | | * uuid length + 1 + additional_length |
| | | * See LDAPUpdateMsg.encodeHeader_V1() for more information |
| | | */ |
| | | // Find end of change number then end of dn |
| | | // Find end of CSN then end of dn |
| | | for (pos = 1; pos < maxLen; pos++) |
| | | { |
| | | if (bytes[pos] == (byte) 0) |
| | |
| | | } |
| | | } |
| | | if (!found) |
| | | throw new UnsupportedEncodingException("Could not find end of " + |
| | | "change number."); |
| | | throw new UnsupportedEncodingException("Could not find end of CSN."); |
| | | pos++; |
| | | if (pos >= maxLen) |
| | | throw new UnsupportedEncodingException("Reached end of packet."); |
| | | // Force assured flag to false |
| | | bytes[pos] = (byte) 0; |
| | | bytes[pos] = 0; |
| | | |
| | | // Store computed V1 serialized form |
| | | realUpdateMsgNotAssuredBytesV1 = bytes; |
| | |
| | | |
| | | /* Look for assured flag position: |
| | | * The message header is stored in the form : |
| | | * <operation type><protocol version><changenumber><dn><entryuuid> |
| | | * <operation type><protocol version><CSN><dn><entryuuid> |
| | | * <assured> <assured mode> <safe data level> |
| | | * the length of result byte array is therefore : |
| | | * 1 + 1 + change number length + 1 + dn length + 1 + uuid length + |
| | | * 1 + 1 + CSN length + 1 + dn length + 1 + uuid length + |
| | | * 1 + 1 + 1 + 1 + additional_length |
| | | * See LDAPUpdateMsg.encodeHeader() for more information |
| | | */ |
| | | // Find end of change number then end of dn then end of uuid |
| | | // Find end of CSN then end of dn then end of uuid |
| | | for (pos = 2; pos < maxLen; pos++) |
| | | { |
| | | if (bytes[pos] == (byte) 0) |
| | |
| | | } |
| | | } |
| | | if (!found) |
| | | throw new UnsupportedEncodingException("Could not find end of " + |
| | | "change number."); |
| | | throw new UnsupportedEncodingException("Could not find end of CSN."); |
| | | pos++; |
| | | if (pos >= maxLen) |
| | | throw new UnsupportedEncodingException("Reached end of packet."); |
| | | // Force assured flag to false |
| | | bytes[pos] = (byte) 0; |
| | | bytes[pos] = 0; |
| | | |
| | | // Store computed VLATEST serialized form |
| | | realUpdateMsgNotAssuredBytesVLatest = bytes; |
| | |
| | | // This is a generic update message |
| | | /* Look for assured flag position: |
| | | * The message header is stored in the form : |
| | | * <operation type><protocol version><changenumber><assured> |
| | | * <operation type><protocol version><CSN><assured> |
| | | * <assured mode> <safe data level> |
| | | * the length of result byte array is therefore : |
| | | * 1 + 1 + change number length + 1 + 1 |
| | | * 1 + 1 + CSN length + 1 + 1 |
| | | * + 1 + 1 + additional_length |
| | | * See UpdateMsg.encodeHeader() for more information |
| | | */ |
| | | // Find end of change number |
| | | // Find end of CSN |
| | | for (pos = 2; pos < maxLen; pos++) |
| | | { |
| | | if (bytes[pos] == (byte) 0) |
| | |
| | | } |
| | | } |
| | | if (!found) |
| | | throw new UnsupportedEncodingException("Could not find end of " + |
| | | "change number."); |
| | | throw new UnsupportedEncodingException("Could not find end of CSN."); |
| | | pos++; |
| | | if (pos >= maxLen) |
| | | throw new UnsupportedEncodingException("Reached end of packet."); |
| | | // Force assured flag to false |
| | | bytes[pos] = (byte) 0; |
| | | bytes[pos] = 0; |
| | | |
| | | // Store computed VLatest serialized form |
| | | realUpdateMsgNotAssuredBytesVLatest = bytes; |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | return realUpdateMsg.getChangeNumber(); |
| | | return realUpdateMsg.getCSN(); |
| | | } |
| | | |
| | | /** |
| | |
| | | { |
| | | if (obj.getClass() != realUpdateMsg.getClass()) |
| | | return false; |
| | | return realUpdateMsg.getChangeNumber(). |
| | | equals(((UpdateMsg)obj).getChangeNumber()); |
| | | return realUpdateMsg.getCSN(). |
| | | equals(((UpdateMsg)obj).getCSN()); |
| | | } |
| | | else |
| | | { |
| | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.plugin.MultimasterReplication; |
| | | import org.opends.server.replication.plugin.ReplicationServerListener; |
| | |
| | | { |
| | | break; |
| | | } |
| | | writeChangesAfterChangeNumber(exportContainer, exportConfig, |
| | | ldifWriter, null, null); |
| | | writeChangesAfterCSN(exportContainer, exportConfig, ldifWriter, null, |
| | | null); |
| | | } |
| | | } |
| | | finally |
| | |
| | | |
| | | /** |
| | | * Exports or returns all the changes from a ReplicationServerDomain coming |
| | | * after the changeNumber specified in the searchOperation. |
| | | * after the CSN specified in the searchOperation. |
| | | */ |
| | | private void writeChangesAfterChangeNumber(ReplicationServerDomain rsd, |
| | | private void writeChangesAfterCSN(ReplicationServerDomain rsd, |
| | | final LDIFExportConfig exportConfig, LDIFWriter ldifWriter, |
| | | SearchOperation searchOperation, final ChangeNumber previousCN) |
| | | SearchOperation searchOperation, final CSN previousCSN) |
| | | { |
| | | for (int serverId : rsd.getServerIds()) |
| | | { |
| | |
| | | return; |
| | | } |
| | | |
| | | ReplicaDBCursor cursor = rsd.getCursorFrom(serverId, previousCN); |
| | | ReplicaDBCursor cursor = rsd.getCursorFrom(serverId, previousCSN); |
| | | if (cursor != null) |
| | | { |
| | | try |
| | |
| | | } |
| | | } |
| | | |
| | | private ChangeNumber extractChangeNumber(SearchOperation searchOperation) |
| | | private CSN extractCSN(SearchOperation searchOperation) |
| | | { |
| | | if (searchOperation != null) |
| | | { |
| | | return extractChangeNumber(searchOperation.getFilter()); |
| | | return extractCSN(searchOperation.getFilter()); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * Attempt to extract a ChangeNumber from searchFilter like |
| | | * Attempt to extract a CSN from searchFilter like |
| | | * ReplicationChangeNumber=xxxx or ReplicationChangeNumber>=xxxx. |
| | | * |
| | | * @param filter The filter to evaluate. |
| | | * |
| | | * @return The extracted ChangeNumber or null if no ChangeNumber |
| | | * was found. |
| | | * @param filter |
| | | * The filter to evaluate. |
| | | * @return The extracted CSN or null if no CSN was found. |
| | | */ |
| | | private ChangeNumber extractChangeNumber(SearchFilter filter) |
| | | private CSN extractCSN(SearchFilter filter) |
| | | { |
| | | // Try to optimize for filters like replicationChangeNumber>=xxxxx |
| | | // or replicationChangeNumber=xxxxx : |
| | |
| | | { |
| | | try |
| | | { |
| | | ChangeNumber startingCN = |
| | | new ChangeNumber(filter.getAssertionValue().getValue().toString()); |
| | | return new ChangeNumber(startingCN.getTime(), |
| | | startingCN.getSeqnum() - 1, startingCN.getServerId()); |
| | | CSN startingCSN = |
| | | new CSN(filter.getAssertionValue().getValue().toString()); |
| | | return new CSN(startingCSN.getTime(), |
| | | startingCSN.getSeqnum() - 1, startingCSN.getServerId()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | // don't try to optimize the search if the ChangeNumber is |
| | | // not a valid replication ChangeNumber. |
| | | // not a valid replication CSN. |
| | | } |
| | | } |
| | | } |
| | |
| | | { |
| | | for (SearchFilter filterComponent : filter.getFilterComponents()) |
| | | { |
| | | // This code does not expect more than one CN in the search filter. |
| | | // This code does not expect more than one CSN in the search filter. |
| | | // It is ok, since it is only used by developers/testers for debugging. |
| | | final ChangeNumber previousCN = extractChangeNumber(filterComponent); |
| | | if (previousCN != null) |
| | | final CSN previousCSN = extractCSN(filterComponent); |
| | | if (previousCSN != null) |
| | | { |
| | | return previousCN; |
| | | return previousCSN; |
| | | } |
| | | } |
| | | } |
| | |
| | | AddOperation addOperation = (AddOperation)msg.createOperation(conn); |
| | | |
| | | dn = DN.decode("puid=" + addMsg.getParentEntryUUID() + "+" + |
| | | CHANGE_NUMBER + "=" + msg.getChangeNumber() + "+" + |
| | | CHANGE_NUMBER + "=" + msg.getCSN() + "+" + |
| | | msg.getDn() + "," + BASE_DN); |
| | | |
| | | Map<AttributeType,List<Attribute>> attrs = |
| | |
| | | entry.addObjectClass(extensibleObjectOC); |
| | | |
| | | addAttribute(entry.getUserAttributes(), CHANGE_NUMBER, |
| | | msg.getChangeNumber().toString()); |
| | | msg.getCSN().toString()); |
| | | addAttribute(entry.getUserAttributes(), "replicationDomain", baseDN); |
| | | |
| | | // Get the base DN, scope, and filter for the search. |
| | |
| | | private DN computeDN(LDAPUpdateMsg msg) throws DirectoryException |
| | | { |
| | | return DN.decode("uuid=" + msg.getEntryUUID() + "," + CHANGE_NUMBER + "=" |
| | | + msg.getChangeNumber() + "," + msg.getDn() + "," + BASE_DN); |
| | | + msg.getCSN() + "," + msg.getDn() + "," + BASE_DN); |
| | | } |
| | | |
| | | private Entry writeChangeRecord(LDIFWriter ldifWriter, |
| | |
| | | findSearchContainers(searchBaseDN); |
| | | for (ReplicationServerDomain exportContainer : searchContainers) |
| | | { |
| | | final ChangeNumber previousCN = extractChangeNumber(searchOperation); |
| | | writeChangesAfterChangeNumber(exportContainer, null, null, |
| | | searchOperation, previousCN); |
| | | final CSN previousCSN = extractCSN(searchOperation); |
| | | writeChangesAfterCSN(exportContainer, null, null, searchOperation, |
| | | previousCSN); |
| | | } |
| | | } |
| | | |
| | |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.protocol.MonitorMsg; |
| | | import org.opends.server.replication.protocol.MonitorRequestMsg; |
| | |
| | | { |
| | | // Let's process our directly connected DS |
| | | // - in the ServerHandler for a given DS1, the stored state contains : |
| | | // -- the max CN produced by DS1 |
| | | // -- the last CN consumed by DS1 from DS2..n |
| | | // -- the max CSN produced by DS1 |
| | | // -- the last CSN consumed by DS1 from DS2..n |
| | | // - in the RSdomain/dbHandler, the built-in state contains : |
| | | // -- the max CN produced by each server |
| | | // -- the max CSN produced by each server |
| | | // So for a given DS connected we can take the state and the max from |
| | | // the DS/state. |
| | | |
| | |
| | | final int serverId = ds.getServerId(); |
| | | final ServerState dsState = ds.getServerState().duplicate(); |
| | | |
| | | ChangeNumber maxcn = dsState.getChangeNumber(serverId); |
| | | if (maxcn == null) |
| | | CSN maxCSN = dsState.getCSN(serverId); |
| | | if (maxCSN == null) |
| | | { |
| | | // This directly connected LS has never produced any change |
| | | maxcn = new ChangeNumber(0, 0, serverId); |
| | | maxCSN = new CSN(0, 0, serverId); |
| | | } |
| | | pendingMonitorData.setMaxCN(serverId, maxcn); |
| | | pendingMonitorData.setMaxCSN(serverId, maxCSN); |
| | | pendingMonitorData.setLDAPServerState(serverId, dsState); |
| | | pendingMonitorData.setFirstMissingDate(serverId, |
| | | ds.getApproxFirstMissingDate()); |
| | | } |
| | | |
| | | // Then initialize the max CN for the LS that produced something |
| | | // Then initialize the max CSN for the LS that produced something |
| | | // - from our own local db state |
| | | // - whatever they are directly or indirectly connected |
| | | final ServerState dbServerState = domain.getDbServerState(); |
| | | pendingMonitorData.setRSState(domain.getLocalRSServerId(), dbServerState); |
| | | for (int serverId : dbServerState) |
| | | { |
| | | ChangeNumber storedCN = dbServerState.getChangeNumber(serverId); |
| | | pendingMonitorData.setMaxCN(serverId, storedCN); |
| | | CSN storedCSN = dbServerState.getCSN(serverId); |
| | | pendingMonitorData.setMaxCSN(serverId, storedCSN); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | try |
| | | { |
| | | // Here is the RS state : list <serverID, lastChangeNumber> |
| | | // For each LDAP Server, we keep the max CN across the RSes |
| | | // Here is the RS state : list <serverID, lastCSN> |
| | | // For each LDAP Server, we keep the max CSN across the RSes |
| | | ServerState replServerState = msg.getReplServerDbState(); |
| | | pendingMonitorData.setMaxCNs(replServerState); |
| | | pendingMonitorData.setMaxCSNs(replServerState); |
| | | |
| | | // store the remote RS states. |
| | | pendingMonitorData.setRSState(msg.getSenderID(), replServerState); |
| | |
| | | for (int dsServerId : toIterable(msg.ldapIterator())) |
| | | { |
| | | ServerState dsServerState = msg.getLDAPServerState(dsServerId); |
| | | pendingMonitorData.setMaxCNs(dsServerState); |
| | | pendingMonitorData.setMaxCSNs(dsServerState); |
| | | pendingMonitorData.setLDAPServerState(dsServerId, dsServerState); |
| | | pendingMonitorData.setFirstMissingDate(dsServerId, |
| | | msg.getLDAPApproxFirstMissingDate(dsServerId)); |
| | |
| | | import java.util.concurrent.ConcurrentMap; |
| | | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.util.TimeThread; |
| | | |
| | |
| | | |
| | | /** |
| | | * |
| | | * - For each server, the max (most recent) CN produced |
| | | * - For each server, the max (most recent) CSN produced |
| | | * |
| | | * - For each server, its state i.e. the last processed from of each |
| | | * other LDAP server. |
| | |
| | | private ConcurrentMap<Integer, ServerState> rsStates = |
| | | new ConcurrentHashMap<Integer, ServerState>(); |
| | | |
| | | /** For each LDAP server, the last(max) CN it published. */ |
| | | private ConcurrentMap<Integer, ChangeNumber> maxCNs = |
| | | new ConcurrentHashMap<Integer, ChangeNumber>(); |
| | | /** For each LDAP server, the last(max) CSN it published. */ |
| | | private ConcurrentMap<Integer, CSN> maxCSNs = |
| | | new ConcurrentHashMap<Integer, CSN>(); |
| | | |
| | | /** |
| | | * For each LDAP server, an approximation of the date of the first missing |
| | |
| | | |
| | | long lsiMissingChanges = 0; |
| | | if (lsiState != null) { |
| | | for (Entry<Integer, ChangeNumber> entry2 : maxCNs.entrySet()) |
| | | for (Entry<Integer, CSN> entry2 : maxCSNs.entrySet()) |
| | | { |
| | | final Integer lsjServerId = entry2.getKey(); |
| | | final ChangeNumber lsjMaxCN = entry2.getValue(); |
| | | ChangeNumber lsiLastCN = lsiState.getChangeNumber(lsjServerId); |
| | | final CSN lsjMaxCSN = entry2.getValue(); |
| | | CSN lsiLastCSN = lsiState.getCSN(lsjServerId); |
| | | |
| | | int missingChangesLsiLsj = |
| | | ChangeNumber.diffSeqNum(lsjMaxCN, lsiLastCN); |
| | | int missingChangesLsiLsj = CSN.diffSeqNum(lsjMaxCSN, lsiLastCSN); |
| | | |
| | | if (debugEnabled()) { |
| | | mds += "+ diff(" + lsjMaxCN + "-" |
| | | + lsiLastCN + ")=" + missingChangesLsiLsj; |
| | | mds += "+ diff(" + lsjMaxCSN + "-" |
| | | + lsiLastCSN + ")=" + missingChangesLsiLsj; |
| | | } |
| | | /* |
| | | Regarding a DS that is generating changes. If it is a local DS1, |
| | | we get its server state, store it, then retrieve server states of |
| | | remote DSs. When a remote server state is coming, it may contain |
| | | a change number for DS1 which is newer than the one we locally |
| | | a CSN for DS1 which is newer than the one we locally |
| | | stored in the server state of DS1. To prevent seeing DS1 has |
| | | missing changes whereas it is wrong, we replace the value with 0 |
| | | if it is a low value. We cannot overwrite big values as they may be |
| | |
| | | long lsiMissingChanges = 0; |
| | | if (lsiState != null) |
| | | { |
| | | for (Entry<Integer, ChangeNumber> entry2 : maxCNs.entrySet()) |
| | | for (Entry<Integer, CSN> entry2 : maxCSNs.entrySet()) |
| | | { |
| | | final Integer lsjServerId = entry2.getKey(); |
| | | final ChangeNumber lsjMaxCN = entry2.getValue(); |
| | | ChangeNumber lsiLastCN = lsiState.getChangeNumber(lsjServerId); |
| | | final CSN lsjMaxCSN = entry2.getValue(); |
| | | CSN lsiLastCSN = lsiState.getCSN(lsjServerId); |
| | | |
| | | int missingChangesLsiLsj = |
| | | ChangeNumber.diffSeqNum(lsjMaxCN, lsiLastCN); |
| | | int missingChangesLsiLsj = CSN.diffSeqNum(lsjMaxCSN, lsiLastCSN); |
| | | |
| | | if (debugEnabled()) { |
| | | mds += "+ diff(" + lsjMaxCN + "-" |
| | | + lsiLastCN + ")=" + missingChangesLsiLsj; |
| | | mds += "+ diff(" + lsjMaxCSN + "-" |
| | | + lsiLastCSN + ")=" + missingChangesLsiLsj; |
| | | } |
| | | lsiMissingChanges += missingChangesLsiLsj; |
| | | } |
| | |
| | | { |
| | | mds += "=" + lsiMissingChanges; |
| | | } |
| | | this.missingChangesRS.put(lsiServerId,lsiMissingChanges); |
| | | this.missingChangesRS.put(lsiServerId, lsiMissingChanges); |
| | | |
| | | if (debugEnabled()) |
| | | { |
| | |
| | | { |
| | | String mds = "Monitor data=\n"; |
| | | |
| | | // maxCNs |
| | | for (Entry<Integer, ChangeNumber> entry : maxCNs.entrySet()) |
| | | // maxCSNs |
| | | for (Entry<Integer, CSN> entry : maxCSNs.entrySet()) |
| | | { |
| | | final Integer serverId = entry.getKey(); |
| | | final ChangeNumber cn = entry.getValue(); |
| | | mds += "\nmaxCNs(" + serverId + ")= " + cn.toStringUI(); |
| | | final CSN csn = entry.getValue(); |
| | | mds += "\nmaxCSNs(" + serverId + ")= " + csn.toStringUI(); |
| | | } |
| | | |
| | | // LDAP data |
| | |
| | | } |
| | | |
| | | /** |
| | | * From a provided state, sets the max CN of the monitor data. |
| | | * From a provided state, sets the max CSN of the monitor data. |
| | | * @param state the provided state. |
| | | */ |
| | | public void setMaxCNs(ServerState state) |
| | | public void setMaxCSNs(ServerState state) |
| | | { |
| | | for (Integer serverId : state) { |
| | | ChangeNumber newCN = state.getChangeNumber(serverId); |
| | | setMaxCN(serverId, newCN); |
| | | CSN newCSN = state.getCSN(serverId); |
| | | setMaxCSN(serverId, newCSN); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * For the provided serverId, sets the provided CN as the max if |
| | | * For the provided serverId, sets the provided CSN as the max if |
| | | * it is newer than the current max. |
| | | * @param serverId the provided serverId |
| | | * @param newCN the provided new CN |
| | | * @param newCSN the provided new CSN |
| | | */ |
| | | public void setMaxCN(int serverId, ChangeNumber newCN) |
| | | public void setMaxCSN(int serverId, CSN newCSN) |
| | | { |
| | | if (newCN==null) return; |
| | | if (newCSN==null) return; |
| | | |
| | | ChangeNumber currentMaxCN = maxCNs.get(serverId); |
| | | if (currentMaxCN == null) |
| | | CSN currentMaxCSN = maxCSNs.get(serverId); |
| | | if (currentMaxCSN == null) |
| | | { |
| | | maxCNs.put(serverId, newCN); |
| | | maxCSNs.put(serverId, newCSN); |
| | | } |
| | | else if (newCN.newer(currentMaxCN)) |
| | | else if (newCSN.newer(currentMaxCSN)) |
| | | { |
| | | maxCNs.replace(serverId, newCN); |
| | | maxCSNs.replace(serverId, newCSN); |
| | | } |
| | | } |
| | | |
| | |
| | | /** |
| | | * Excluded a list of domain from eligibility computation. |
| | | * @param excludedBaseDNs the provided list of baseDNs excluded from |
| | | * the computation of eligibleCN. |
| | | * the computation of eligibleCSN. |
| | | */ |
| | | public void disableEligibility(Set<String> excludedBaseDNs) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the eligible CN cross domains - relies on the eligible CN from |
| | | * Returns the eligible CSN cross domains - relies on the eligible CSN from |
| | | * each domain. |
| | | * @return the cross domain eligible CN. |
| | | * @return the cross domain eligible CSN. |
| | | */ |
| | | public ChangeNumber getEligibleCN() |
| | | public CSN getEligibleCSN() |
| | | { |
| | | String debugLog = ""; |
| | | |
| | | // traverse the domains and get the eligible CN from each domain |
| | | // store the oldest one as the cross domain eligible CN |
| | | ChangeNumber eligibleCN = null; |
| | | // traverse the domains and get the eligible CSN from each domain |
| | | // store the oldest one as the cross domain eligible CSN |
| | | CSN eligibleCSN = null; |
| | | for (ReplicationServerDomain domain : getReplicationServerDomains()) |
| | | { |
| | | if (contains(excludedBaseDNs, domain.getBaseDn())) |
| | | continue; |
| | | |
| | | final ChangeNumber domainEligibleCN = domain.getEligibleCN(); |
| | | if (eligibleCN == null |
| | | || (domainEligibleCN != null && domainEligibleCN.older(eligibleCN))) |
| | | final CSN domainEligibleCSN = domain.getEligibleCSN(); |
| | | if (eligibleCSN == null |
| | | ||(domainEligibleCSN != null && domainEligibleCSN.older(eligibleCSN))) |
| | | { |
| | | eligibleCN = domainEligibleCN; |
| | | eligibleCSN = domainEligibleCSN; |
| | | } |
| | | |
| | | if (debugEnabled()) |
| | | { |
| | | final String dates = domainEligibleCN == null ? |
| | | "" : new Date(domainEligibleCN.getTime()).toString(); |
| | | final String dates = domainEligibleCSN == null ? |
| | | "" : new Date(domainEligibleCSN.getTime()).toString(); |
| | | debugLog += "[baseDN=" + domain.getBaseDn() |
| | | + "] [eligibleCN=" + domainEligibleCN + ", " + dates + "]"; |
| | | + "] [eligibleCSN=" + domainEligibleCSN + ", " + dates + "]"; |
| | | } |
| | | } |
| | | |
| | | if (eligibleCN==null ) |
| | | if (eligibleCSN==null ) |
| | | { |
| | | eligibleCN = new ChangeNumber(TimeThread.getTime(), 0, 0); |
| | | eligibleCSN = new CSN(TimeThread.getTime(), 0, 0); |
| | | } |
| | | |
| | | if (debugEnabled()) { |
| | | TRACER.debugInfo("In " + this + " getEligibleCN() ends with " + |
| | | " the following domainEligibleCN for each domain :" + debugLog + |
| | | " thus CrossDomainEligibleCN=" + eligibleCN + |
| | | " ts=" + new Date(eligibleCN.getTime()).toString()); |
| | | TRACER.debugInfo("In " + this + " getEligibleCSN() ends with " + |
| | | " the following domainEligibleCSN for each domain :" + debugLog + |
| | | " thus CrossDomainEligibleCSN=" + eligibleCSN + |
| | | " ts=" + new Date(eligibleCSN.getTime()).toString()); |
| | | } |
| | | return eligibleCN; |
| | | return eligibleCSN; |
| | | } |
| | | |
| | | private boolean contains(Set<String> col, String elem) |
| | |
| | | /** |
| | | * Get first and last DraftCN. |
| | | * |
| | | * @param crossDomainEligibleCN The provided crossDomainEligibleCN used as |
| | | * @param crossDomainEligibleCSN The provided crossDomainEligibleCSN used as |
| | | * the upper limit for the lastDraftCN |
| | | * @param excludedBaseDNs The baseDNs that are excluded from the ECL. |
| | | * @return The first and last draftCN. |
| | | * @throws DirectoryException When it happens. |
| | | */ |
| | | public int[] getECLDraftCNLimits(ChangeNumber crossDomainEligibleCN, |
| | | public int[] getECLDraftCNLimits(CSN crossDomainEligibleCSN, |
| | | Set<String> excludedBaseDNs) throws DirectoryException |
| | | { |
| | | /* The content of the DraftCNdb depends on the SEARCH operations done before |
| | |
| | | * - initialized with the last record from the DraftCNdb (0 if none) |
| | | * and consider the genState associated |
| | | * - to the last DraftCN, we add the count of updates in the replchangelog |
| | | * FROM that genState TO the crossDomainEligibleCN |
| | | * FROM that genState TO the crossDomainEligibleCSN |
| | | * (this diff is done domain by domain) |
| | | */ |
| | | |
| | |
| | | |
| | | int firstDraftCN = changelogDB.getFirstDraftCN(); |
| | | Map<String,ServerState> domainsServerStateForLastSeqnum = null; |
| | | ChangeNumber changeNumberForLastSeqnum = null; |
| | | CSN csnForLastSeqnum = null; |
| | | String domainForLastSeqnum = null; |
| | | if (firstDraftCN < 1) |
| | | { |
| | |
| | | splitGenStateToServerStates(lastSeqnumGenState); |
| | | } |
| | | |
| | | // Get the changeNumber associated with the current last DraftCN |
| | | changeNumberForLastSeqnum = changelogDB.getChangeNumber(lastDraftCN); |
| | | // Get the CSN associated with the current last DraftCN |
| | | csnForLastSeqnum = changelogDB.getCSN(lastDraftCN); |
| | | |
| | | // Get the domain associated with the current last DraftCN |
| | | domainForLastSeqnum = changelogDB.getBaseDN(lastDraftCN); |
| | |
| | | if (domainsServerStateForLastSeqnum == null) |
| | | { |
| | | // Count changes of this domain from the beginning of the changelog |
| | | ChangeNumber trimCN = |
| | | new ChangeNumber(rsd.getLatestDomainTrimDate(), 0,0); |
| | | CSN trimCSN = new CSN(rsd.getLatestDomainTrimDate(), 0, 0); |
| | | ec = rsd.getEligibleCount( |
| | | rsd.getStartState().duplicateOnlyOlderThan(trimCN), |
| | | crossDomainEligibleCN); |
| | | rsd.getStartState().duplicateOnlyOlderThan(trimCSN), |
| | | crossDomainEligibleCSN); |
| | | } |
| | | else |
| | | { |
| | |
| | | // the date of the most recent change from this last draft record |
| | | if (newestDate == 0) |
| | | { |
| | | newestDate = changeNumberForLastSeqnum.getTime(); |
| | | newestDate = csnForLastSeqnum.getTime(); |
| | | } |
| | | |
| | | // And count changes of this domain from the date of the |
| | | // lastseqnum record (that does not refer to this domain) |
| | | ChangeNumber cnx = new ChangeNumber(newestDate, |
| | | changeNumberForLastSeqnum.getSeqnum(), 0); |
| | | ec = rsd.getEligibleCount(cnx, crossDomainEligibleCN); |
| | | CSN csnx = new CSN(newestDate, csnForLastSeqnum.getSeqnum(), 0); |
| | | ec = rsd.getEligibleCount(csnx, crossDomainEligibleCSN); |
| | | |
| | | if (domainForLastSeqnum.equalsIgnoreCase(rsd.getBaseDn())) |
| | | ec--; |
| | |
| | | || rsd.getDbServerState().isEmpty()) |
| | | continue; |
| | | |
| | | result.update(rsd.getBaseDn(), rsd.getEligibleState(getEligibleCN())); |
| | | result.update(rsd.getBaseDn(), rsd.getEligibleState(getEligibleCSN())); |
| | | } |
| | | return result; |
| | | } |
| | |
| | | * The needed info for each received assured update message we are waiting |
| | | * acks for. |
| | | * <p> |
| | | * Key: a change number matching a received update message which requested |
| | | * Key: a CSN matching a received update message which requested |
| | | * assured mode usage (either safe read or safe data mode) |
| | | * <p> |
| | | * Value: The object holding every info needed about the already received acks |
| | |
| | | * @see ExpectedAcksInfo For more details, see ExpectedAcksInfo and its sub |
| | | * classes javadoc. |
| | | */ |
| | | private final ConcurrentHashMap<ChangeNumber, ExpectedAcksInfo> waitingAcks = |
| | | new ConcurrentHashMap<ChangeNumber, ExpectedAcksInfo>(); |
| | | private final Map<CSN, ExpectedAcksInfo> waitingAcks = |
| | | new ConcurrentHashMap<CSN, ExpectedAcksInfo>(); |
| | | |
| | | /** |
| | | * The timer used to run the timeout code (timer tasks) for the assured update |
| | |
| | | public void put(UpdateMsg update, ServerHandler sourceHandler) |
| | | throws IOException |
| | | { |
| | | ChangeNumber cn = update.getChangeNumber(); |
| | | int serverId = cn.getServerId(); |
| | | CSN csn = update.getCSN(); |
| | | int serverId = csn.getServerId(); |
| | | |
| | | sourceHandler.updateServerState(update); |
| | | sourceHandler.incrementInCount(); |
| | |
| | | // The following timer will time out and send an timeout ack to the |
| | | // requester if the acks are not received in time. The timer will also |
| | | // remove the object from this map. |
| | | waitingAcks.put(cn, preparedAssuredInfo.expectedAcksInfo); |
| | | waitingAcks.put(csn, preparedAssuredInfo.expectedAcksInfo); |
| | | |
| | | // Arm timer for this assured update message (wait for acks until it |
| | | // times out) |
| | | AssuredTimeoutTask assuredTimeoutTask = new AssuredTimeoutTask(cn); |
| | | AssuredTimeoutTask assuredTimeoutTask = new AssuredTimeoutTask(csn); |
| | | assuredTimeoutTimer.schedule(assuredTimeoutTask, |
| | | localReplicationServer.getAssuredTimeout()); |
| | | // Purge timer every 100 treated messages |
| | |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debug("update " + update.getChangeNumber() |
| | | debug("update " + update.getCSN() |
| | | + " will not be sent to replication server " |
| | | + rsHandler.getServerId() + " with generation id " |
| | | + rsHandler.getGenerationId() + " different from local " |
| | |
| | | { |
| | | if (dsStatus == ServerStatus.BAD_GEN_ID_STATUS) |
| | | { |
| | | debug("update " + update.getChangeNumber() |
| | | debug("update " + update.getCSN() |
| | | + " will not be sent to directory server " |
| | | + dsHandler.getServerId() + " with generation id " |
| | | + dsHandler.getGenerationId() + " different from local " |
| | |
| | | } |
| | | if (dsStatus == ServerStatus.FULL_UPDATE_STATUS) |
| | | { |
| | | debug("update " + update.getChangeNumber() |
| | | debug("update " + update.getCSN() |
| | | + " will not be sent to directory server " |
| | | + dsHandler.getServerId() + " as it is in full update"); |
| | | } |
| | |
| | | private PreparedAssuredInfo processSafeReadUpdateMsg( |
| | | UpdateMsg update, ServerHandler sourceHandler) throws IOException |
| | | { |
| | | ChangeNumber cn = update.getChangeNumber(); |
| | | CSN csn = update.getCSN(); |
| | | byte groupId = localReplicationServer.getGroupId(); |
| | | byte sourceGroupId = sourceHandler.getGroupId(); |
| | | List<Integer> expectedServers = new ArrayList<Integer>(); |
| | |
| | | if (expectedServers.size() > 0) |
| | | { |
| | | // Some other acks to wait for |
| | | preparedAssuredInfo.expectedAcksInfo = new SafeReadExpectedAcksInfo(cn, |
| | | preparedAssuredInfo.expectedAcksInfo = new SafeReadExpectedAcksInfo(csn, |
| | | sourceHandler, expectedServers, wrongStatusServers); |
| | | preparedAssuredInfo.expectedServers = expectedServers; |
| | | } |
| | |
| | | if (preparedAssuredInfo.expectedServers == null) |
| | | { |
| | | // No eligible servers found, send the ack immediately |
| | | sourceHandler.send(new AckMsg(cn)); |
| | | sourceHandler.send(new AckMsg(csn)); |
| | | } |
| | | |
| | | return preparedAssuredInfo; |
| | |
| | | private PreparedAssuredInfo processSafeDataUpdateMsg( |
| | | UpdateMsg update, ServerHandler sourceHandler) throws IOException |
| | | { |
| | | ChangeNumber cn = update.getChangeNumber(); |
| | | CSN csn = update.getCSN(); |
| | | boolean interestedInAcks = false; |
| | | byte safeDataLevel = update.getSafeDataLevel(); |
| | | byte groupId = localReplicationServer.getGroupId(); |
| | |
| | | * mode with safe data level 1, coming from a DS. No need to wait |
| | | * for more acks |
| | | */ |
| | | sourceHandler.send(new AckMsg(cn)); |
| | | sourceHandler.send(new AckMsg(csn)); |
| | | } else |
| | | { |
| | | /** |
| | |
| | | */ |
| | | if (safeDataLevel > (byte) 1) |
| | | { |
| | | sourceHandler.send(new AckMsg(cn)); |
| | | sourceHandler.send(new AckMsg(csn)); |
| | | } |
| | | } |
| | | } |
| | |
| | | byte finalSdl = (nExpectedServers >= neededAdditionalServers) ? |
| | | (byte)sdl : // Keep level as it was |
| | | (byte)(nExpectedServers+1); // Change level to match what's available |
| | | preparedAssuredInfo.expectedAcksInfo = new SafeDataExpectedAcksInfo(cn, |
| | | preparedAssuredInfo.expectedAcksInfo = new SafeDataExpectedAcksInfo(csn, |
| | | sourceHandler, finalSdl, expectedServers); |
| | | preparedAssuredInfo.expectedServers = expectedServers; |
| | | } else |
| | | { |
| | | // level > 1 and source is a DS but no eligible servers found, send the |
| | | // ack immediately |
| | | sourceHandler.send(new AckMsg(cn)); |
| | | sourceHandler.send(new AckMsg(csn)); |
| | | } |
| | | } |
| | | |
| | |
| | | { |
| | | // Retrieve the expected acks info for the update matching the original |
| | | // sent update. |
| | | ChangeNumber cn = ack.getChangeNumber(); |
| | | ExpectedAcksInfo expectedAcksInfo = waitingAcks.get(cn); |
| | | CSN csn = ack.getCSN(); |
| | | ExpectedAcksInfo expectedAcksInfo = waitingAcks.get(csn); |
| | | |
| | | if (expectedAcksInfo != null) |
| | | { |
| | |
| | | if (expectedAcksInfo.processReceivedAck(ackingServer, ack)) |
| | | { |
| | | // Remove the object from the map as no more needed |
| | | waitingAcks.remove(cn); |
| | | waitingAcks.remove(csn); |
| | | AckMsg finalAck = expectedAcksInfo.createAck(false); |
| | | ServerHandler origServer = expectedAcksInfo.getRequesterServer(); |
| | | try |
| | |
| | | mb.append(ERR_RS_ERROR_SENDING_ACK.get( |
| | | Integer.toString(localReplicationServer.getServerId()), |
| | | Integer.toString(origServer.getServerId()), |
| | | cn.toString(), baseDn)); |
| | | csn.toString(), baseDn)); |
| | | mb.append(stackTraceToSingleLineString(e)); |
| | | logError(mb.toMessage()); |
| | | stopServer(origServer, false); |
| | |
| | | } |
| | | } |
| | | } |
| | | /* Else the timeout occurred for the update matching this change number |
| | | /* Else the timeout occurred for the update matching this CSN |
| | | * and the ack with timeout error has probably already been sent. |
| | | */ |
| | | } |
| | |
| | | */ |
| | | private class AssuredTimeoutTask extends TimerTask |
| | | { |
| | | private ChangeNumber cn = null; |
| | | private CSN csn = null; |
| | | |
| | | /** |
| | | * Constructor for the timer task. |
| | | * @param cn The changeNumber of the assured update we are waiting acks for |
| | | * @param csn The CSN of the assured update we are waiting acks for |
| | | */ |
| | | public AssuredTimeoutTask(ChangeNumber cn) |
| | | public AssuredTimeoutTask(CSN csn) |
| | | { |
| | | this.cn = cn; |
| | | this.csn = csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | @Override |
| | | public void run() |
| | | { |
| | | ExpectedAcksInfo expectedAcksInfo = waitingAcks.get(cn); |
| | | ExpectedAcksInfo expectedAcksInfo = waitingAcks.get(csn); |
| | | |
| | | if (expectedAcksInfo != null) |
| | | { |
| | |
| | | return; |
| | | } |
| | | // Remove the object from the map as no more needed |
| | | waitingAcks.remove(cn); |
| | | waitingAcks.remove(csn); |
| | | // Create the timeout ack and send him to the server the assured |
| | | // update message came from |
| | | AckMsg finalAck = expectedAcksInfo.createAck(true); |
| | | ServerHandler origServer = expectedAcksInfo.getRequesterServer(); |
| | | if (debugEnabled()) |
| | | { |
| | | debug("sending timeout for assured update with change number " + cn |
| | | debug("sending timeout for assured update with CSN " + csn |
| | | + " to serverId=" + origServer.getServerId()); |
| | | } |
| | | try |
| | |
| | | mb.append(ERR_RS_ERROR_SENDING_ACK.get( |
| | | Integer.toString(localReplicationServer.getServerId()), |
| | | Integer.toString(origServer.getServerId()), |
| | | cn.toString(), baseDn)); |
| | | csn.toString(), baseDn)); |
| | | mb.append(stackTraceToSingleLineString(e)); |
| | | logError(mb.toMessage()); |
| | | stopServer(origServer, false); |
| | |
| | | * |
| | | * @param serverId |
| | | * Identifier of the server for which the cursor is created. |
| | | * @param startAfterCN |
| | | * @param startAfterCSN |
| | | * Starting point for the cursor. |
| | | * @return the created {@link ReplicaDBCursor}. Null when no DB is |
| | | * available or the DB is empty for the provided serverId . |
| | | * @return the created {@link ReplicaDBCursor}. Null when no DB is available |
| | | * or the DB is empty for the provided serverId . |
| | | */ |
| | | public ReplicaDBCursor getCursorFrom(int serverId, |
| | | ChangeNumber startAfterCN) |
| | | public ReplicaDBCursor getCursorFrom(int serverId, CSN startAfterCSN) |
| | | { |
| | | DbHandler dbHandler = sourceDbHandlers.get(serverId); |
| | | if (dbHandler == null) |
| | |
| | | ReplicaDBCursor cursor; |
| | | try |
| | | { |
| | | cursor = dbHandler.generateCursorFrom(startAfterCN); |
| | | cursor = dbHandler.generateCursorFrom(startAfterCSN); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | |
| | | /** |
| | | * Count the number of changes in the replication changelog for the provided |
| | | * serverID, between 2 provided changenumbers. |
| | | * serverID, between 2 provided CSNs. |
| | | * @param serverId Identifier of the server for which to compute the count. |
| | | * @param from lower limit changenumber. |
| | | * @param to upper limit changenumber. |
| | | * @param from lower limit CSN. |
| | | * @param to upper limit CSN. |
| | | * @return the number of changes. |
| | | */ |
| | | public long getCount(int serverId, ChangeNumber from, ChangeNumber to) |
| | | public long getCount(int serverId, CSN from, CSN to) |
| | | { |
| | | DbHandler dbHandler = sourceDbHandlers.get(serverId); |
| | | if (dbHandler != null) |
| | |
| | | * <pre> |
| | | * s1 s2 s3 |
| | | * -- -- -- |
| | | * cn31 |
| | | * cn15 |
| | | * csn31 |
| | | * csn15 |
| | | * |
| | | * ----------------------------------------- eligibleCN |
| | | * cn14 |
| | | * cn26 |
| | | * cn13 |
| | | * ----------------------------------------- eligibleCSN |
| | | * csn14 |
| | | * csn26 |
| | | * csn13 |
| | | * </pre> |
| | | * |
| | | * The eligibleState is : s1;cn14 / s2;cn26 / s3;cn31 |
| | | * The eligibleState is : s1;csn14 / s2;csn26 / s3;csn31 |
| | | * |
| | | * @param eligibleCN The provided eligibleCN. |
| | | * @param eligibleCSN |
| | | * The provided eligible CSN. |
| | | * @return The computed eligible server state. |
| | | */ |
| | | public ServerState getEligibleState(ChangeNumber eligibleCN) |
| | | public ServerState getEligibleState(CSN eligibleCSN) |
| | | { |
| | | ServerState dbState = getDbServerState(); |
| | | |
| | | // The result is initialized from the dbState. |
| | | // From it, we don't want to keep the changes newer than eligibleCN. |
| | | // From it, we don't want to keep the changes newer than eligibleCSN. |
| | | ServerState result = dbState.duplicate(); |
| | | |
| | | if (eligibleCN != null) |
| | | if (eligibleCSN != null) |
| | | { |
| | | for (int serverId : dbState) |
| | | { |
| | | DbHandler h = sourceDbHandlers.get(serverId); |
| | | ChangeNumber mostRecentDbCN = dbState.getChangeNumber(serverId); |
| | | CSN mostRecentDbCSN = dbState.getCSN(serverId); |
| | | try { |
| | | // Is the most recent change in the Db newer than eligible CN ? |
| | | // if yes (like cn15 in the example above, then we have to go back |
| | | // to the Db and look for the change older than eligible CN (cn14) |
| | | if (eligibleCN.olderOrEqual(mostRecentDbCN)) { |
| | | // let's try to seek the first change <= eligibleCN |
| | | // Is the most recent change in the Db newer than eligible CSN ? |
| | | // if yes (like csn15 in the example above, then we have to go back |
| | | // to the Db and look for the change older than eligible CSN (csn14) |
| | | if (eligibleCSN.olderOrEqual(mostRecentDbCSN)) |
| | | { |
| | | // let's try to seek the first change <= eligibleCSN |
| | | ReplicaDBCursor cursor = null; |
| | | try { |
| | | cursor = h.generateCursorFrom(eligibleCN); |
| | | cursor = h.generateCursorFrom(eligibleCSN); |
| | | if (cursor != null && cursor.getChange() != null) { |
| | | ChangeNumber newCN = cursor.getChange().getChangeNumber(); |
| | | result.update(newCN); |
| | | CSN newCSN = cursor.getChange().getCSN(); |
| | | result.update(newCSN); |
| | | } |
| | | } catch (ChangelogException e) { |
| | | // there's no change older than eligibleCN (case of s3/cn31) |
| | | result.update(new ChangeNumber(0, 0, serverId)); |
| | | // there's no change older than eligibleCSN (case of s3/csn31) |
| | | result.update(new CSN(0, 0, serverId)); |
| | | } finally { |
| | | close(cursor); |
| | | } |
| | | } else { |
| | | // for this serverId, all changes in the ChangelogDb are holder |
| | | // than eligibleCN , the most recent in the db is our guy. |
| | | result.update(mostRecentDbCN); |
| | | // than eligibleCSN, the most recent in the db is our guy. |
| | | result.update(mostRecentDbCSN); |
| | | } |
| | | } catch (Exception e) { |
| | | logError(ERR_WRITER_UNEXPECTED_EXCEPTION |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the eligibleCN for that domain - relies on the ChangeTimeHeartbeat |
| | | * state. |
| | | * For each DS, take the oldest CN from the changetime heartbeat state |
| | | * and from the changelog db last CN. Can be null. |
| | | * @return the eligible CN. |
| | | * Returns the eligible CSN for that domain - relies on the |
| | | * ChangeTimeHeartbeat state. |
| | | * <p> |
| | | * For each DS, take the oldest CSN from the changetime heartbeat state and |
| | | * from the changelog db last CSN. Can be null. |
| | | * |
| | | * @return the eligible CSN. |
| | | */ |
| | | public ChangeNumber getEligibleCN() |
| | | public CSN getEligibleCSN() |
| | | { |
| | | ChangeNumber eligibleCN = null; |
| | | CSN eligibleCSN = null; |
| | | |
| | | for (DbHandler db : sourceDbHandlers.values()) |
| | | { |
| | |
| | | int serverId = db.getServerId(); |
| | | |
| | | // Should it be considered for eligibility ? |
| | | ChangeNumber heartbeatLastCN = |
| | | getChangeTimeHeartbeatState().getChangeNumber(serverId); |
| | | CSN heartbeatLastCSN = |
| | | getChangeTimeHeartbeatState().getCSN(serverId); |
| | | |
| | | // If the most recent UpdateMsg or CLHeartbeatMsg received is very old |
| | | // then the domain is considered down and not considered for eligibility |
| | |
| | | continue; |
| | | } |
| | | |
| | | ChangeNumber changelogLastCN = db.getLastChange(); |
| | | if (changelogLastCN != null |
| | | && (eligibleCN == null || changelogLastCN.newer(eligibleCN))) |
| | | CSN changelogLastCSN = db.getLastChange(); |
| | | if (changelogLastCSN != null |
| | | && (eligibleCSN == null || changelogLastCSN.newer(eligibleCSN))) |
| | | { |
| | | eligibleCN = changelogLastCN; |
| | | eligibleCSN = changelogLastCSN; |
| | | } |
| | | if (heartbeatLastCN != null |
| | | && (eligibleCN == null || heartbeatLastCN.newer(eligibleCN))) |
| | | if (heartbeatLastCSN != null |
| | | && (eligibleCSN == null || heartbeatLastCSN.newer(eligibleCSN))) |
| | | { |
| | | eligibleCN = heartbeatLastCN; |
| | | eligibleCSN = heartbeatLastCSN; |
| | | } |
| | | } |
| | | |
| | | if (debugEnabled()) |
| | | { |
| | | debug("getEligibleCN() returns result =" + eligibleCN); |
| | | debug("getEligibleCSN() returns result =" + eligibleCSN); |
| | | } |
| | | return eligibleCN; |
| | | return eligibleCSN; |
| | | } |
| | | |
| | | private boolean isServerConnected(int serverId) |
| | |
| | | |
| | | |
| | | /** |
| | | * Processes a ChangeTimeHeartbeatMsg received, by storing the CN (timestamp) |
| | | * Processes a ChangeTimeHeartbeatMsg received, by storing the CSN (timestamp) |
| | | * value received, and forwarding the message to the other RSes. |
| | | * @param senderHandler The handler for the server that sent the heartbeat. |
| | | * @param msg The message to process. |
| | |
| | | |
| | | try |
| | | { |
| | | storeReceivedCTHeartbeat(msg.getChangeNumber()); |
| | | storeReceivedCTHeartbeat(msg.getCSN()); |
| | | if (senderHandler.isDataServer()) |
| | | { |
| | | // If we are the first replication server warned, |
| | |
| | | |
| | | /** |
| | | * Store a change time value received from a data server. |
| | | * @param cn The provided change time. |
| | | * @param csn The provided change time. |
| | | */ |
| | | public void storeReceivedCTHeartbeat(ChangeNumber cn) |
| | | public void storeReceivedCTHeartbeat(CSN csn) |
| | | { |
| | | // TODO:May be we can spare processing by only storing CN (timestamp) |
| | | // TODO:May be we can spare processing by only storing CSN (timestamp) |
| | | // instead of a server state. |
| | | getChangeTimeHeartbeatState().update(cn); |
| | | getChangeTimeHeartbeatState().update(csn); |
| | | } |
| | | |
| | | /** |
| | | * This methods count the changes, server by server : |
| | | * - from a serverState start point |
| | | * - to (inclusive) an end point (the provided endCN). |
| | | * - to (inclusive) an end point (the provided endCSN). |
| | | * @param startState The provided start server state. |
| | | * @param endCN The provided end change number. |
| | | * @return The number of changes between startState and endCN. |
| | | * @param endCSN The provided end CSN. |
| | | * @return The number of changes between startState and endCSN. |
| | | */ |
| | | public long getEligibleCount(ServerState startState, ChangeNumber endCN) |
| | | public long getEligibleCount(ServerState startState, CSN endCSN) |
| | | { |
| | | long res = 0; |
| | | |
| | | for (int serverId : getDbServerState()) |
| | | { |
| | | ChangeNumber startCN = startState.getChangeNumber(serverId); |
| | | long serverIdRes = getCount(serverId, startCN, endCN); |
| | | CSN startCSN = startState.getCSN(serverId); |
| | | long serverIdRes = getCount(serverId, startCSN, endCSN); |
| | | |
| | | // The startPoint is excluded when counting the ECL eligible changes |
| | | if (startCN != null && serverIdRes > 0) |
| | | if (startCSN != null && serverIdRes > 0) |
| | | { |
| | | serverIdRes--; |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * This methods count the changes, server by server : |
| | | * - from a start CN |
| | | * - to (inclusive) an end point (the provided endCN). |
| | | * @param startCN The provided start changeNumber. |
| | | * @param endCN The provided end change number. |
| | | * @return The number of changes between startTime and endCN. |
| | | * This methods count the changes, server by server: |
| | | * - from a start CSN |
| | | * - to (inclusive) an end point (the provided endCSN). |
| | | * @param startCSN The provided start CSN. |
| | | * @param endCSN The provided end CSN. |
| | | * @return The number of changes between startTime and endCSN. |
| | | */ |
| | | public long getEligibleCount(ChangeNumber startCN, ChangeNumber endCN) |
| | | public long getEligibleCount(CSN startCSN, CSN endCSN) |
| | | { |
| | | long res = 0; |
| | | for (int serverId : getDbServerState()) { |
| | | ChangeNumber lStartCN = |
| | | new ChangeNumber(startCN.getTime(), startCN.getSeqnum(), serverId); |
| | | res += getCount(serverId, lStartCN, endCN); |
| | | CSN lStartCSN = |
| | | new CSN(startCSN.getTime(), startCSN.getSeqnum(), serverId); |
| | | res += getCount(serverId, lStartCSN, endCSN); |
| | | } |
| | | return res; |
| | | } |
| | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.AssuredMode; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.AckMsg; |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | /** |
| | | * This class holds every info needed about the expected acks for a received |
| | | * update message requesting assured replication with Safe Data sub-mode. |
| | |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | // Requested level of safe data when the update message was received. |
| | | private byte safeDataLevel = (byte)-1; |
| | | /** Requested level of safe data when the update message was received. */ |
| | | private byte safeDataLevel = -1; |
| | | |
| | | // Number of received acks for the matching update message, up to now |
| | | // Already set to 1 as the local RS receiving the message from a DS counts. |
| | | private byte numReceivedAcks = (byte)1; |
| | | /** |
| | | * Number of received acks for the matching update message, up to now Already |
| | | * set to 1 as the local RS receiving the message from a DS counts. |
| | | */ |
| | | private byte numReceivedAcks = 1; |
| | | |
| | | /** |
| | | * Creates a new SafeDataExpectedAcksInfo. |
| | | * @param changeNumber The change number of the assured update message |
| | | * @param csn The CSN of the assured update message |
| | | * @param requesterServerHandler The server that sent the assured update |
| | | * message |
| | | * @param safeDataLevel The Safe Data level requested for the assured |
| | | * update message |
| | | * @param expectedServers The list of servers we want an ack from |
| | | */ |
| | | public SafeDataExpectedAcksInfo(ChangeNumber changeNumber, |
| | | public SafeDataExpectedAcksInfo(CSN csn, |
| | | ServerHandler requesterServerHandler, byte safeDataLevel, |
| | | List<Integer> expectedServers) |
| | | { |
| | | super(changeNumber, requesterServerHandler, AssuredMode.SAFE_DATA_MODE, |
| | | super(csn, requesterServerHandler, AssuredMode.SAFE_DATA_MODE, |
| | | expectedServers); |
| | | this.safeDataLevel = safeDataLevel; |
| | | } |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean processReceivedAck(ServerHandler ackingServer, AckMsg ackMsg) |
| | | @Override |
| | | public boolean processReceivedAck(ServerHandler ackingServer, AckMsg ackMsg) |
| | | { |
| | | /* |
| | | * Security: although a DS should not respond to an update message sent to |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public AckMsg createAck(boolean timeout) |
| | | { |
| | | AckMsg ack = new AckMsg(changeNumber); |
| | | AckMsg ack = new AckMsg(csn); |
| | | |
| | | if (timeout) |
| | | { |
| | |
| | | * |
| | | * |
| | | * Copyright 2008-2009 Sun Microsystems, Inc. |
| | | * Portions copyright 2013 ForgeRock AS |
| | | */ |
| | | |
| | | package org.opends.server.replication.server; |
| | | |
| | | import java.util.ArrayList; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.AssuredMode; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.AckMsg; |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | /** |
| | | * This class holds every info needed about the expected acks for a received |
| | | * update message requesting assured replication with Safe Read sub-mode. |
| | |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | // Did some servers go in timeout when the matching update was sent ? |
| | | /** Did some servers go in timeout when the matching update was sent ?. */ |
| | | private boolean hasTimeout = false; |
| | | |
| | | // Were some servers in wrong status when the matching update was sent ? |
| | | /** Were some servers in wrong status when the matching update was sent ?. */ |
| | | private boolean hasWrongStatus = false; |
| | | |
| | | // Did some servers make an error replaying the sent matching update ? |
| | | /** Did some servers make an error replaying the sent matching update ?. */ |
| | | private boolean hasReplayError = false; |
| | | |
| | | // The list of server ids that had errors for the sent matching update |
| | | // Each server id of the list had one of the |
| | | // 3 possible errors (timeout, wrong status or replay error) |
| | | /** |
| | | * The list of server ids that had errors for the sent matching update Each |
| | | * server id of the list had one of the 3 possible errors (timeout, wrong |
| | | * status or replay error). |
| | | */ |
| | | private List<Integer> failedServers = new ArrayList<Integer>(); |
| | | |
| | | /** |
| | |
| | | |
| | | /** |
| | | * Creates a new SafeReadExpectedAcksInfo. |
| | | * @param changeNumber The change number of the assured update message |
| | | * @param csn The CSN of the assured update message |
| | | * @param requesterServerHandler The server that sent the assured update |
| | | * message |
| | | * @param expectedServers The list of servers we want an ack from (they are |
| | |
| | | * wrongStatus (degraded status) to keep trace of the error for the future |
| | | * returning ack we gonna compute |
| | | */ |
| | | public SafeReadExpectedAcksInfo(ChangeNumber changeNumber, |
| | | public SafeReadExpectedAcksInfo(CSN csn, |
| | | ServerHandler requesterServerHandler, List<Integer> expectedServers, |
| | | List<Integer> wrongStatusServers) |
| | | { |
| | | super(changeNumber, requesterServerHandler, AssuredMode.SAFE_READ_MODE, |
| | | super(csn, requesterServerHandler, AssuredMode.SAFE_READ_MODE, |
| | | expectedServers); |
| | | |
| | | // Keep track of potential servers detected in wrong status |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean processReceivedAck(ServerHandler ackingServer, AckMsg ackMsg) |
| | | { |
| | | // Get the ack status for the matching server |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public AckMsg createAck(boolean timeout) |
| | | { |
| | | AckMsg ack = new AckMsg(changeNumber); |
| | | AckMsg ack = new AckMsg(csn); |
| | | |
| | | // Fill collected errors info |
| | | ack.setHasTimeout(hasTimeout); |
| | |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.common.AssuredMode; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.RSInfo; |
| | | import org.opends.server.replication.common.ServerStatus; |
| | | import org.opends.server.replication.protocol.*; |
| | |
| | | */ |
| | | public long getApproxFirstMissingDate() |
| | | { |
| | | // Get the older CN received |
| | | ChangeNumber olderUpdateCN = getOlderUpdateCN(); |
| | | if (olderUpdateCN != null) |
| | | // Get the older CSN received |
| | | CSN olderUpdateCSN = getOlderUpdateCSN(); |
| | | if (olderUpdateCSN != null) |
| | | { |
| | | // If not present in the local RS db, |
| | | // then approximate with the older update time |
| | | return olderUpdateCN.getTime(); |
| | | return olderUpdateCSN.getTime(); |
| | | } |
| | | return 0; |
| | | } |
| | |
| | | if (dsStatus == BAD_GEN_ID_STATUS) |
| | | logError(WARN_IGNORING_UPDATE_FROM_DS_BADGENID.get( |
| | | handler.getReplicationServerId(), |
| | | updateMsg.getChangeNumber().toString(), |
| | | updateMsg.getCSN().toString(), |
| | | handler.getBaseDN(), handler.getServerId(), |
| | | session.getReadableRemoteAddress(), |
| | | handler.getGenerationId(), |
| | |
| | | if (dsStatus == FULL_UPDATE_STATUS) |
| | | logError(WARN_IGNORING_UPDATE_FROM_DS_FULLUP.get( |
| | | handler.getReplicationServerId(), |
| | | updateMsg.getChangeNumber().toString(), |
| | | updateMsg.getCSN().toString(), |
| | | handler.getBaseDN(), handler.getServerId(), |
| | | session.getReadableRemoteAddress())); |
| | | filtered = true; |
| | |
| | | logError( |
| | | WARN_IGNORING_UPDATE_FROM_RS.get( |
| | | handler.getReplicationServerId(), |
| | | updateMsg.getChangeNumber().toString(), |
| | | updateMsg.getCSN().toString(), |
| | | handler.getBaseDN(), |
| | | handler.getServerId(), |
| | | session.getReadableRemoteAddress(), |
| | |
| | | if (dsStatus == ServerStatus.BAD_GEN_ID_STATUS) |
| | | logError(WARN_IGNORING_UPDATE_TO_DS_BADGENID.get( |
| | | handler.getReplicationServerId(), |
| | | update.getChangeNumber().toString(), |
| | | update.getCSN().toString(), |
| | | handler.getBaseDN(), handler.getServerId(), |
| | | session.getReadableRemoteAddress(), |
| | | handler.getGenerationId(), |
| | |
| | | if (dsStatus == ServerStatus.FULL_UPDATE_STATUS) |
| | | logError(WARN_IGNORING_UPDATE_TO_DS_FULLUP.get( |
| | | handler.getReplicationServerId(), |
| | | update.getChangeNumber().toString(), |
| | | update.getCSN().toString(), |
| | | handler.getBaseDN(), handler.getServerId(), |
| | | session.getReadableRemoteAddress())); |
| | | continue; |
| | |
| | | logError( |
| | | WARN_IGNORING_UPDATE_TO_RS.get( |
| | | handler.getReplicationServerId(), |
| | | update.getChangeNumber().toString(), |
| | | update.getCSN().toString(), |
| | | handler.getBaseDN(), |
| | | handler.getServerId(), |
| | | session.getReadableRemoteAddress(), |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Portions copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.server; |
| | | |
| | | import java.util.Comparator; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | |
| | | /** |
| | |
| | | * 0 if msg1 == msg2 |
| | | * 1 if msg1 > msg2 |
| | | */ |
| | | @Override |
| | | public int compare(UpdateMsg msg1, UpdateMsg msg2) |
| | | { |
| | | return ChangeNumber.compare(msg1.getChangeNumber(), msg2.getChangeNumber()); |
| | | return CSN.compare(msg1.getCSN(), msg2.getCSN()); |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.replication.server.changelog.api; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** |
| | | * This class stores the changelog information into a database. |
| | |
| | | { |
| | | |
| | | /** |
| | | * Get the CN associated to a provided draft change number. |
| | | * Get the CSN associated to a provided draft change number. |
| | | * |
| | | * @param draftCN |
| | | * the provided draft change number. |
| | | * @return the associated CN, null when none. |
| | | * @return the associated CSN, null when none. |
| | | */ |
| | | public ChangeNumber getChangeNumber(int draftCN); |
| | | public CSN getCSN(int draftCN); |
| | | |
| | | /** |
| | | * Get the baseDN associated to a provided draft change number. |
| | |
| | | * The value of the previous cookie. |
| | | * @param baseDN |
| | | * The associated baseDN. |
| | | * @param changeNumber |
| | | * The associated replication change number. |
| | | * @param csn |
| | | * The associated replication CSN. |
| | | */ |
| | | void add(int draftCN, String previousCookie, String baseDN, |
| | | ChangeNumber changeNumber); |
| | | void add(int draftCN, String previousCookie, String baseDN, CSN csn); |
| | | |
| | | /** |
| | | * Generate a new {@link ChangelogDBIterator} that allows to browse the db |
| | |
| | | |
| | | import java.io.Closeable; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** |
| | | * Iterator into the changelog database. Once it is not used anymore, a |
| | |
| | | { |
| | | |
| | | /** |
| | | * Getter for the replication change number field. |
| | | * Getter for the replication CSN field. |
| | | * |
| | | * @return The replication change number field. |
| | | * @return The replication CSN field. |
| | | */ |
| | | ChangeNumber getChangeNumber(); |
| | | CSN getCSN(); |
| | | |
| | | /** |
| | | * Getter for the baseDN field. |
| | |
| | | |
| | | import java.util.Comparator; |
| | | |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** |
| | | * This class defines a {@link Comparator} that allows to know which |
| | | * {@link ReplicaDBCursor} contain the next {@link UpdateMsg} in the order |
| | | * defined by the {@link ChangeNumber} of the {@link UpdateMsg}. |
| | | * defined by the {@link CSN} of the {@link UpdateMsg}. |
| | | */ |
| | | public class ReplicaDBCursorComparator implements Comparator<ReplicaDBCursor> |
| | | { |
| | | /** |
| | | * Compare the {@link ChangeNumber} of the {@link ReplicaDBCursor}. |
| | | * Compare the {@link CSN} of the {@link ReplicaDBCursor}. |
| | | * |
| | | * @param o1 |
| | | * first cursor. |
| | |
| | | @Override |
| | | public int compare(ReplicaDBCursor o1, ReplicaDBCursor o2) |
| | | { |
| | | ChangeNumber csn1 = o1.getChange().getChangeNumber(); |
| | | ChangeNumber csn2 = o2.getChange().getChangeNumber(); |
| | | CSN csn1 = o1.getChange().getCSN(); |
| | | CSN csn2 = o2.getChange().getCSN(); |
| | | |
| | | return ChangeNumber.compare(csn1, csn2); |
| | | return CSN.compare(csn1, csn2); |
| | | } |
| | | } |
| | |
| | | import org.opends.server.api.MonitorProvider; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | import org.opends.server.replication.server.ReplicationServer; |
| | | import org.opends.server.replication.server.ReplicationServerDomain; |
| | |
| | | * It is responsible for efficiently saving the updates that is received from |
| | | * each master server into stable storage. |
| | | * This class is also able to generate a {@link ReplicaDBCursor} that can be |
| | | * used to read all changes from a given {@link ChangeNumber}. |
| | | * used to read all changes from a given {@link CSN}. |
| | | * |
| | | * This class publish some monitoring information below cn=monitor. |
| | | */ |
| | |
| | | private int queueByteSize = 0; |
| | | |
| | | private ReplicationDB db; |
| | | private ChangeNumber firstChange = null; |
| | | private ChangeNumber lastChange = null; |
| | | private CSN firstChange = null; |
| | | private CSN lastChange = null; |
| | | private int serverId; |
| | | private String baseDn; |
| | | private DbMonitorProvider dbMonitor = new DbMonitorProvider(); |
| | |
| | | |
| | | queueByteSize += update.size(); |
| | | msgQueue.add(update); |
| | | if (lastChange == null || lastChange.older(update.getChangeNumber())) |
| | | if (lastChange == null || lastChange.older(update.getCSN())) |
| | | { |
| | | lastChange = update.getChangeNumber(); |
| | | lastChange = update.getCSN(); |
| | | } |
| | | if (firstChange == null) |
| | | { |
| | | firstChange = update.getChangeNumber(); |
| | | firstChange = update.getCSN(); |
| | | } |
| | | } |
| | | } |
| | |
| | | * Get the firstChange. |
| | | * @return Returns the firstChange. |
| | | */ |
| | | public ChangeNumber getFirstChange() |
| | | public CSN getFirstChange() |
| | | { |
| | | return firstChange; |
| | | } |
| | |
| | | * Get the lastChange. |
| | | * @return Returns the lastChange. |
| | | */ |
| | | public ChangeNumber getLastChange() |
| | | public CSN getLastChange() |
| | | { |
| | | return lastChange; |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * Generate a new {@link ReplicaDBCursor} that allows to browse the db |
| | | * managed by this dbHandler and starting at the position defined by a given |
| | | * changeNumber. |
| | | * Generate a new {@link ReplicaDBCursor} that allows to browse the db managed |
| | | * by this dbHandler and starting at the position defined by a given CSN. |
| | | * |
| | | * @param startAfterCN |
| | | * @param startAfterCSN |
| | | * The position where the cursor must start. |
| | | * @return a new {@link ReplicaDBCursor} that allows to browse the db |
| | | * managed by this dbHandler and starting at the position defined by a |
| | | * given changeNumber. |
| | | * @return a new {@link ReplicaDBCursor} that allows to browse the db managed |
| | | * by this dbHandler and starting at the position defined by a given |
| | | * CSN. |
| | | * @throws ChangelogException |
| | | * if a database problem happened. |
| | | */ |
| | | public ReplicaDBCursor generateCursorFrom(ChangeNumber startAfterCN) |
| | | public ReplicaDBCursor generateCursorFrom(CSN startAfterCSN) |
| | | throws ChangelogException |
| | | { |
| | | if (startAfterCN == null) |
| | | if (startAfterCSN == null) |
| | | { |
| | | flush(); |
| | | } |
| | | return new JEReplicaDBCursor(db, startAfterCN, this); |
| | | return new JEReplicaDBCursor(db, startAfterCSN, this); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | latestTrimDate = TimeThread.getTime() - trimAge; |
| | | |
| | | ChangeNumber trimDate = new ChangeNumber(latestTrimDate, 0, 0); |
| | | CSN trimDate = new CSN(latestTrimDate, 0, 0); |
| | | |
| | | // Find the last changeNumber before the trimDate, in the Database. |
| | | ChangeNumber lastBeforeTrimDate = db |
| | | .getPreviousChangeNumber(trimDate); |
| | | // Find the last CSN before the trimDate, in the Database. |
| | | CSN lastBeforeTrimDate = db.getPreviousCSN(trimDate); |
| | | if (lastBeforeTrimDate != null) |
| | | { |
| | | // If we found it, we want to stop trimming when reaching it. |
| | |
| | | { |
| | | for (int j = 0; j < 50; j++) |
| | | { |
| | | ChangeNumber changeNumber = cursor.nextChangeNumber(); |
| | | if (changeNumber == null) |
| | | CSN csn = cursor.nextCSN(); |
| | | if (csn == null) |
| | | { |
| | | cursor.close(); |
| | | done = true; |
| | | return; |
| | | } |
| | | |
| | | if (!changeNumber.equals(lastChange) |
| | | && changeNumber.older(trimDate)) |
| | | if (!csn.equals(lastChange) && csn.older(trimDate)) |
| | | { |
| | | cursor.delete(); |
| | | } |
| | | else |
| | | { |
| | | firstChange = changeNumber; |
| | | firstChange = csn; |
| | | cursor.close(); |
| | | done = true; |
| | | return; |
| | |
| | | return attributes; |
| | | } |
| | | |
| | | private String encode(ChangeNumber changeNumber) |
| | | private String encode(CSN csn) |
| | | { |
| | | return changeNumber + " " + new Date(changeNumber.getTime()); |
| | | return csn + " " + new Date(csn.getTime()); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Return the number of changes between 2 provided change numbers. |
| | | * Return the number of changes between 2 provided CSNs. |
| | | * This a alternative to traverseAndCount, expected to be much more efficient |
| | | * when there is a huge number of changes in the Db. |
| | | * @param from The lower (older) change number. |
| | | * @param to The upper (newer) change number. |
| | | * @param from The lower (older) CSN. |
| | | * @param to The upper (newer) CSN. |
| | | * @return The computed number of changes. |
| | | */ |
| | | public long getCount(ChangeNumber from, ChangeNumber to) |
| | | public long getCount(CSN from, CSN to) |
| | | { |
| | | // Now that we always keep the last ChangeNumber in the DB to avoid |
| | | // expiring cookies too quickly, we need to check if the "to" |
| | | // is older than the trim date. |
| | | if (to == null || !to.older(new ChangeNumber(latestTrimDate, 0, 0))) |
| | | // Now that we always keep the last CSN in the DB to avoid expiring cookies |
| | | // too quickly, we need to check if the "to" is older than the trim date. |
| | | if (to == null || !to.older(new CSN(latestTrimDate, 0, 0))) |
| | | { |
| | | flush(); |
| | | return db.count(from, to); |
| | |
| | | import org.opends.messages.Message; |
| | | import org.opends.messages.MessageBuilder; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.server.changelog.api.ChangelogException; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | |
| | |
| | | * with this draftCN. |
| | | * @param domainBaseDN the provided domainBaseDn to be stored associated |
| | | * with this draftCN. |
| | | * @param changeNumber the provided replication change number to be |
| | | * @param csn the provided replication CSN to be |
| | | * stored associated with this draftCN. |
| | | */ |
| | | public void addEntry(int draftCN, String value, String domainBaseDN, |
| | | ChangeNumber changeNumber) |
| | | CSN csn) |
| | | { |
| | | try |
| | | { |
| | | DatabaseEntry key = new ReplicationDraftCNKey(draftCN); |
| | | DatabaseEntry data = new DraftCNData(value, domainBaseDN, changeNumber); |
| | | DatabaseEntry data = new DraftCNData(value, domainBaseDN, csn); |
| | | |
| | | // Use a transaction so that we can override durability. |
| | | Transaction txn = null; |
| | |
| | | { |
| | | if (localCursor.getSearchKey(key, entry, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | // We could not move the cursor to the expected startingChangeNumber |
| | | // We could not move the cursor to the expected startingDraftCN |
| | | if (localCursor.getSearchKeyRange(key, entry, DEFAULT) != SUCCESS) |
| | | { |
| | | // We could not even move the cursor closed to it => failure |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns the replication changeNumber associated with the current key. |
| | | * @return the replication changeNumber |
| | | * Returns the replication CSN associated with the current key. |
| | | * @return the replication CSN |
| | | */ |
| | | public ChangeNumber currentChangeNumber() |
| | | public CSN currentCSN() |
| | | { |
| | | if (isClosed) |
| | | { |
| | |
| | | { |
| | | if (seqnumData != null) |
| | | { |
| | | return seqnumData.getChangeNumber(); |
| | | return seqnumData.getCSN(); |
| | | } |
| | | } |
| | | catch(Exception e) |
| | |
| | | import java.io.UnsupportedEncodingException; |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.server.changelog.api.ChangelogException; |
| | | |
| | | import com.sleepycat.je.DatabaseEntry; |
| | |
| | | |
| | | private String value; |
| | | private String baseDN; |
| | | private ChangeNumber changeNumber; |
| | | private CSN csn; |
| | | |
| | | /** |
| | | * Creates a record to be stored in the DraftCNDB. |
| | | * @param value The value (cookie). |
| | | * @param baseDN The baseDN (domain DN). |
| | | * @param changeNumber The replication change number. |
| | | * @param csn The replication CSN. |
| | | */ |
| | | public DraftCNData(String value, String baseDN, ChangeNumber changeNumber) |
| | | public DraftCNData(String value, String baseDN, CSN csn) |
| | | { |
| | | String record = value |
| | | + FIELD_SEPARATOR + baseDN |
| | | + FIELD_SEPARATOR + changeNumber; |
| | | String record = value + FIELD_SEPARATOR + baseDN + FIELD_SEPARATOR + csn; |
| | | setData(getBytes(record)); |
| | | } |
| | | |
| | |
| | | String[] str = stringData.split(FIELD_SEPARATOR, 3); |
| | | value = str[0]; |
| | | baseDN = str[1]; |
| | | changeNumber = new ChangeNumber(str[2]); |
| | | csn = new CSN(str[2]); |
| | | } |
| | | catch (UnsupportedEncodingException e) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Getter for the replication change number. |
| | | * Getter for the replication CSN. |
| | | * |
| | | * @return the replication change number. |
| | | * @throws ChangelogException when a problem occurs. |
| | | * @return the replication CSN. |
| | | * @throws ChangelogException |
| | | * when a problem occurs. |
| | | */ |
| | | public ChangeNumber getChangeNumber() throws ChangelogException |
| | | public CSN getCSN() throws ChangelogException |
| | | { |
| | | if (value == null) |
| | | decodeData(getData()); |
| | | return this.changeNumber; |
| | | return this.csn; |
| | | } |
| | | |
| | | /** |
| | |
| | | { |
| | | buffer.append("DraftCNData : [value=").append(value); |
| | | buffer.append("] [serviceID=").append(baseDN); |
| | | buffer.append("] [changeNumber=").append(changeNumber).append("]"); |
| | | buffer.append("] [csn=").append(csn).append("]"); |
| | | } |
| | | } |
| | |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.MultiDomainServerState; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.server.ReplicationServer; |
| | |
| | | * server in the topology. |
| | | * It is responsible for efficiently saving the updates that is received from |
| | | * each master server into stable storage. |
| | | * This class is also able to generate a ChangelogDBIterator that can be |
| | | * used to read all changes from a given ChangeNumber. |
| | | * This class is also able to generate a {@link ChangelogDBIterator} that can be |
| | | * used to read all changes from a given draft ChangeNumber. |
| | | * <p> |
| | | * This class publishes some monitoring information below <code> |
| | | * cn=monitor</code>. |
| | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public synchronized void add(int draftCN, String value, String baseDN, |
| | | ChangeNumber cn) |
| | | CSN csn) |
| | | { |
| | | db.addEntry(draftCN, value, baseDN, cn); |
| | | db.addEntry(draftCN, value, baseDN, csn); |
| | | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo( |
| | |
| | | + " key=" + draftCN |
| | | + " value=" + value |
| | | + " baseDN=" + baseDN |
| | | + " cn=" + cn); |
| | | + " csn=" + csn); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | |
| | | return; |
| | | } |
| | | |
| | | // From the draftCNDb change record, get the domain and changeNumber |
| | | final ChangeNumber cn = cursor.currentChangeNumber(); |
| | | // From the draftCNDb change record, get the domain and CSN |
| | | final CSN csn = cursor.currentCSN(); |
| | | final String baseDN = cursor.currentBaseDN(); |
| | | if (baseDNToClear != null && baseDNToClear.equalsIgnoreCase(baseDN)) |
| | | { |
| | |
| | | } |
| | | |
| | | final ServerState startState = domain.getStartState(); |
| | | final ChangeNumber fcn = startState.getChangeNumber(cn.getServerId()); |
| | | final CSN fcsn = startState.getCSN(csn.getServerId()); |
| | | |
| | | final int currentDraftCN = cursor.currentKey(); |
| | | |
| | | if (cn.older(fcn)) |
| | | if (csn.older(fcsn)) |
| | | { |
| | | cursor.delete(); |
| | | continue; |
| | | } |
| | | |
| | | ServerState cnVector; |
| | | ServerState csnVector; |
| | | try |
| | | { |
| | | Map<String,ServerState> cnStartStates = |
| | | Map<String, ServerState> csnStartStates = |
| | | MultiDomainServerState.splitGenStateToServerStates( |
| | | cursor.currentValue()); |
| | | cnVector = cnStartStates.get(baseDN); |
| | | csnVector = csnStartStates.get(baseDN); |
| | | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo("DraftCNDBHandler:clear() - ChangeVector:" + |
| | | cnVector + " -- StartState:" + startState); |
| | | TRACER.debugInfo("DraftCNDBHandler:clear() - ChangeVector:" |
| | | + csnVector + " -- StartState:" + startState); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | |
| | | continue; |
| | | } |
| | | |
| | | if ((cnVector == null) |
| | | || (cnVector.getChangeNumber(cn.getServerId()) != null |
| | | && !cnVector.cover(startState))) |
| | | if ((csnVector == null) |
| | | || (csnVector.getCSN(csn.getServerId()) != null && !csnVector |
| | | .cover(startState))) |
| | | { |
| | | cursor.delete(); |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo("DraftCNDBHandler:clear() - deleted " + |
| | | cn + "Not covering startState"); |
| | | TRACER.debugInfo("DraftCNDBHandler:clear() - deleted " + csn |
| | | + "Not covering startState"); |
| | | continue; |
| | | } |
| | | |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public ChangeNumber getChangeNumber(int draftCN) |
| | | public CSN getCSN(int draftCN) |
| | | { |
| | | DraftCNDBCursor draftCNDBCursor = null; |
| | | try |
| | | { |
| | | draftCNDBCursor = db.openReadCursor(draftCN); |
| | | return draftCNDBCursor.currentChangeNumber(); |
| | | return draftCNDBCursor.currentCSN(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.server.changelog.api.ChangelogException; |
| | | import org.opends.server.replication.server.changelog.api.ChangelogDBIterator; |
| | | import org.opends.server.replication.server.changelog.je.DraftCNDB |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public ChangeNumber getChangeNumber() |
| | | public CSN getCSN() |
| | | { |
| | | try |
| | | { |
| | | return this.draftCNDbCursor.currentChangeNumber(); |
| | | return this.draftCNDbCursor.currentCSN(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | |
| | | package org.opends.server.replication.server.changelog.je; |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | import org.opends.server.replication.server.changelog.api.ChangelogException; |
| | | import org.opends.server.replication.server.changelog.api.ReplicaDBCursor; |
| | |
| | | private ReplServerDBCursor cursor = null; |
| | | private DbHandler dbHandler; |
| | | private ReplicationDB db; |
| | | private ChangeNumber lastNonNullCurrentCN; |
| | | private CSN lastNonNullCurrentCSN; |
| | | |
| | | /** |
| | | * Creates a new {@link JEReplicaDBCursor}. All created cursor must be |
| | |
| | | * |
| | | * @param db |
| | | * The db where the cursor must be created. |
| | | * @param startAfterCN |
| | | * The ChangeNumber after which the cursor must start. |
| | | * @param startAfterCSN |
| | | * The CSN after which the cursor must start. |
| | | * @param dbHandler |
| | | * The associated DbHandler. |
| | | * @throws ChangelogException |
| | | * if a database problem happened. |
| | | */ |
| | | public JEReplicaDBCursor(ReplicationDB db, ChangeNumber startAfterCN, |
| | | public JEReplicaDBCursor(ReplicationDB db, CSN startAfterCSN, |
| | | DbHandler dbHandler) throws ChangelogException |
| | | { |
| | | this.db = db; |
| | | this.dbHandler = dbHandler; |
| | | this.lastNonNullCurrentCN = startAfterCN; |
| | | this.lastNonNullCurrentCSN = startAfterCSN; |
| | | |
| | | try |
| | | { |
| | | cursor = db.openReadCursor(startAfterCN); |
| | | cursor = db.openReadCursor(startAfterCSN); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | |
| | | dbHandler.flush(); |
| | | |
| | | // look again in the db |
| | | cursor = db.openReadCursor(startAfterCN); |
| | | cursor = db.openReadCursor(startAfterCSN); |
| | | if (cursor == null) |
| | | { |
| | | throw new ChangelogException(Message.raw("no new change")); |
| | |
| | | |
| | | if (currentChange != null) |
| | | { |
| | | lastNonNullCurrentCN = currentChange.getChangeNumber(); |
| | | lastNonNullCurrentCSN = currentChange.getCSN(); |
| | | } |
| | | else |
| | | { |
| | |
| | | dbHandler.flush(); |
| | | try |
| | | { |
| | | cursor = db.openReadCursor(lastNonNullCurrentCN); |
| | | cursor = db.openReadCursor(lastNonNullCurrentCSN); |
| | | currentChange = cursor.next(); |
| | | if (currentChange != null) |
| | | { |
| | | lastNonNullCurrentCN = currentChange.getChangeNumber(); |
| | | lastNonNullCurrentCSN = currentChange.getCSN(); |
| | | } |
| | | } |
| | | catch(Exception e) |
| | |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.messages.MessageBuilder; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | import org.opends.server.replication.server.ReplicationServer; |
| | | import org.opends.server.replication.server.ReplicationServerDomain; |
| | |
| | | private static final int START = 0; |
| | | private static final int STOP = 1; |
| | | |
| | | private Database db = null; |
| | | private ReplicationDbEnv dbenv = null; |
| | | private Database db; |
| | | private ReplicationDbEnv dbenv; |
| | | private ReplicationServer replicationServer; |
| | | private int serverId; |
| | | private String baseDn; |
| | |
| | | // Change counter management |
| | | // The Db itself does not allow to count records between a start and an end |
| | | // change. And we cannot rely on the replication seqnum that is part of the |
| | | // changenumber, since there can be holes (when an operation is canceled). |
| | | // CSN, since there can be holes (when an operation is canceled). |
| | | // And traversing all the records from the start one to the end one works |
| | | // fine but can be very long (ECL:lastChangeNumber). |
| | | // |
| | |
| | | // - a counter value : count of changes since previous counter record. |
| | | // |
| | | // A counter record has to follow the order of the db, so it needs to have |
| | | // a changenumber key that follows the order. |
| | | // A counter record must have its own changenumber key since the Db does not |
| | | // support duplicate keys (it is a compatibility breaker character of the DB). |
| | | // a CSN key that follows the order. |
| | | // A counter record must have its own CSN key since the Db does not support |
| | | // duplicate keys (it is a compatibility breaker character of the DB). |
| | | // |
| | | // We define 2 conditions to store a counter record : |
| | | // 1/- at least 'counterWindowSize' changes have been stored in the Db |
| | |
| | | OperationStatus status = cursor.getLast(key, data, LockMode.DEFAULT); |
| | | while (status == OperationStatus.SUCCESS) |
| | | { |
| | | ChangeNumber cn = toChangeNumber(key.getData()); |
| | | if (isACounterRecord(cn)) |
| | | CSN csn = toCSN(key.getData()); |
| | | if (isACounterRecord(csn)) |
| | | { |
| | | counterCurrValue = decodeCounterValue(data.getData()) + 1; |
| | | counterTsLimit = cn.getTime(); |
| | | counterTsLimit = csn.getTime(); |
| | | break; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private static ChangeNumber toChangeNumber(byte[] data) |
| | | private static CSN toCSN(byte[] data) |
| | | { |
| | | return new ChangeNumber(decodeUTF8(data)); |
| | | return new CSN(decodeUTF8(data)); |
| | | } |
| | | |
| | | |
| | |
| | | for (UpdateMsg change : changes) |
| | | { |
| | | final DatabaseEntry key = |
| | | createReplicationKey(change.getChangeNumber()); |
| | | createReplicationKey(change.getCSN()); |
| | | final DatabaseEntry data = new ReplicationData(change); |
| | | |
| | | insertCounterRecordIfNeeded(change.getChangeNumber()); |
| | | insertCounterRecordIfNeeded(change.getCSN()); |
| | | db.put(null, key, data); |
| | | counterCurrValue++; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private void insertCounterRecordIfNeeded(ChangeNumber changeNumber) |
| | | throws DatabaseException |
| | | private void insertCounterRecordIfNeeded(CSN csn) throws DatabaseException |
| | | { |
| | | if (counterCurrValue != 0 && (counterCurrValue % counterWindowSize == 0)) |
| | | { |
| | | // enough changes to generate a counter record |
| | | // wait for the next change of time |
| | | counterTsLimit = changeNumber.getTime(); |
| | | counterTsLimit = csn.getTime(); |
| | | } |
| | | if (counterTsLimit != 0 && changeNumber.getTime() != counterTsLimit) |
| | | if (counterTsLimit != 0 && csn.getTime() != counterTsLimit) |
| | | { |
| | | // Write the counter record |
| | | final ChangeNumber counterRecord = newCounterRecord(changeNumber); |
| | | final CSN counterRecord = newCounterRecord(csn); |
| | | DatabaseEntry counterKey = createReplicationKey(counterRecord); |
| | | DatabaseEntry counterValue = encodeCounterValue(counterCurrValue - 1); |
| | | db.put(null, counterKey, counterValue); |
| | |
| | | } |
| | | } |
| | | |
| | | private DatabaseEntry createReplicationKey(ChangeNumber changeNumber) |
| | | private DatabaseEntry createReplicationKey(CSN csn) |
| | | { |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | | try |
| | | { |
| | | key.setData(changeNumber.toString().getBytes("UTF-8")); |
| | | key.setData(csn.toString().getBytes("UTF-8")); |
| | | } |
| | | catch (UnsupportedEncodingException e) |
| | | { |
| | |
| | | * Create a cursor that can be used to search or iterate on this |
| | | * ReplicationServer DB. |
| | | * |
| | | * @param changeNumber The ChangeNumber from which the cursor must start. |
| | | * @param startCSN |
| | | * The CSN from which the cursor must start. |
| | | * @throws ChangelogException |
| | | * When a problem occurs or the startingChangeNumber does not exist. |
| | | * When a problem occurs or the startCSN does not exist. |
| | | * @return The ReplServerDBCursor. |
| | | */ |
| | | public ReplServerDBCursor openReadCursor(ChangeNumber changeNumber) |
| | | public ReplServerDBCursor openReadCursor(CSN startCSN) |
| | | throws ChangelogException |
| | | { |
| | | return new ReplServerDBCursor(changeNumber); |
| | | return new ReplServerDBCursor(startCSN); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | /** |
| | | * Read the first Change from the database. |
| | | * @return the first ChangeNumber. |
| | | * |
| | | * @return the first CSN. |
| | | */ |
| | | public ChangeNumber readFirstChange() |
| | | public CSN readFirstChange() |
| | | { |
| | | dbCloseLock.readLock().lock(); |
| | | |
| | |
| | | return null; |
| | | } |
| | | |
| | | final ChangeNumber cn = toChangeNumber(key.getData()); |
| | | if (!isACounterRecord(cn)) |
| | | final CSN csn = toCSN(key.getData()); |
| | | if (!isACounterRecord(csn)) |
| | | { |
| | | return cn; |
| | | return csn; |
| | | } |
| | | |
| | | // First record is a counter record .. go next |
| | |
| | | } |
| | | // There cannot be 2 counter record next to each other, |
| | | // it is safe to return this record |
| | | return toChangeNumber(key.getData()); |
| | | return toCSN(key.getData()); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | |
| | | /** |
| | | * Read the last Change from the database. |
| | | * |
| | | * @return the last ChangeNumber. |
| | | * @return the last CSN. |
| | | */ |
| | | public ChangeNumber readLastChange() |
| | | public CSN readLastChange() |
| | | { |
| | | dbCloseLock.readLock().lock(); |
| | | |
| | |
| | | return null; |
| | | } |
| | | |
| | | final ChangeNumber cn = toChangeNumber(key.getData()); |
| | | if (!isACounterRecord(cn)) |
| | | final CSN csn = toCSN(key.getData()); |
| | | if (!isACounterRecord(csn)) |
| | | { |
| | | return cn; |
| | | return csn; |
| | | } |
| | | |
| | | if (cursor.getPrev(key, data, LockMode.DEFAULT) != SUCCESS) |
| | |
| | | } |
| | | // There cannot be 2 counter record next to each other, |
| | | // it is safe to return this record |
| | | return toChangeNumber(key.getData()); |
| | | return toCSN(key.getData()); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Try to find in the DB, the change number right before the one |
| | | * passed as a parameter. |
| | | * Try to find in the DB, the CSN right before the one passed as a parameter. |
| | | * |
| | | * @param changeNumber |
| | | * The changeNumber from which we start searching. |
| | | * @return the changeNumber right before the one passed as a parameter. |
| | | * Can return null if there is none. |
| | | * @param csn |
| | | * The CSN from which we start searching. |
| | | * @return the CSN right before the one passed as a parameter. Can return null |
| | | * if there is none. |
| | | */ |
| | | public ChangeNumber getPreviousChangeNumber(ChangeNumber changeNumber) |
| | | public CSN getPreviousCSN(CSN csn) |
| | | { |
| | | if (changeNumber == null) |
| | | if (csn == null) |
| | | { |
| | | return null; |
| | | } |
| | |
| | | return null; |
| | | } |
| | | |
| | | DatabaseEntry key = createReplicationKey(changeNumber); |
| | | DatabaseEntry key = createReplicationKey(csn); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | cursor = db.openCursor(null, null); |
| | | if (cursor.getSearchKeyRange(key, data, LockMode.DEFAULT) == SUCCESS) |
| | | { |
| | | // We can move close to the changeNumber. |
| | | // We can move close to the CSN. |
| | | // Let's move to the previous change. |
| | | if (cursor.getPrev(key, data, LockMode.DEFAULT) == SUCCESS) |
| | | { |
| | | return getRegularRecord(cursor, key, data); |
| | | } |
| | | // else, there was no change previous to our changeNumber. |
| | | // else, there was no change previous to our CSN. |
| | | } |
| | | else |
| | | { |
| | | // We could not move the cursor past to the changeNumber |
| | | // Check if the last change is older than changeNumber |
| | | // We could not move the cursor past to the CSN |
| | | // Check if the last change is older than CSN |
| | | if (cursor.getLast(key, data, LockMode.DEFAULT) == SUCCESS) |
| | | { |
| | | return getRegularRecord(cursor, key, data); |
| | |
| | | return null; |
| | | } |
| | | |
| | | private ChangeNumber getRegularRecord(Cursor cursor, DatabaseEntry key, |
| | | private CSN getRegularRecord(Cursor cursor, DatabaseEntry key, |
| | | DatabaseEntry data) throws DatabaseException |
| | | { |
| | | final ChangeNumber cn = toChangeNumber(key.getData()); |
| | | if (!isACounterRecord(cn)) |
| | | final CSN csn = toCSN(key.getData()); |
| | | if (!isACounterRecord(csn)) |
| | | { |
| | | return cn; |
| | | return csn; |
| | | } |
| | | |
| | | // There cannot be 2 counter record next to each other, |
| | | // it is safe to return previous record which must exist |
| | | if (cursor.getPrev(key, data, LockMode.DEFAULT) == SUCCESS) |
| | | { |
| | | return toChangeNumber(key.getData()); |
| | | return toCSN(key.getData()); |
| | | } |
| | | |
| | | // database only contain a counter record, which should not be possible |
| | | // let's just say no changeNumber |
| | | // let's just say no CSN |
| | | return null; |
| | | } |
| | | |
| | |
| | | * Creates a ReplServerDBCursor that can be used for browsing a |
| | | * replicationServer db. |
| | | * |
| | | * @param startingChangeNumber |
| | | * The ChangeNumber from which the cursor must start. |
| | | * @param startCSN |
| | | * The CSN from which the cursor must start. |
| | | * @throws ChangelogException |
| | | * When the startingChangeNumber does not exist. |
| | | * When the startCSN does not exist. |
| | | */ |
| | | private ReplServerDBCursor(ChangeNumber startingChangeNumber) |
| | | throws ChangelogException |
| | | private ReplServerDBCursor(CSN startCSN) throws ChangelogException |
| | | { |
| | | if (startingChangeNumber != null) |
| | | if (startCSN != null) |
| | | { |
| | | key = createReplicationKey(startingChangeNumber); |
| | | key = createReplicationKey(startCSN); |
| | | } |
| | | else |
| | | { |
| | |
| | | } |
| | | |
| | | localCursor = db.openCursor(txn, null); |
| | | if (startingChangeNumber != null) |
| | | if (startCSN != null) |
| | | { |
| | | if (localCursor.getSearchKey(key, data, LockMode.DEFAULT) != SUCCESS) |
| | | { |
| | | // We could not move the cursor to the expected startingChangeNumber |
| | | // We could not move the cursor to the expected startCSN |
| | | if (localCursor.getSearchKeyRange(key, data, DEFAULT) != SUCCESS) |
| | | { |
| | | // We could not even move the cursor closed to it => failure |
| | | throw new ChangelogException( |
| | | Message.raw("ChangeNumber not available")); |
| | | // We could not even move the cursor close to it => failure |
| | | throw new ChangelogException(Message.raw("CSN not available")); |
| | | } |
| | | |
| | | // We can move close to the startingChangeNumber. |
| | | // We can move close to the startCSN. |
| | | // Let's create a cursor from that point. |
| | | DatabaseEntry aKey = new DatabaseEntry(); |
| | | DatabaseEntry aData = new DatabaseEntry(); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the next ChangeNumber in the database from this Cursor. |
| | | * Get the next CSN in the database from this Cursor. |
| | | * |
| | | * @return The next ChangeNumber in the database from this cursor. |
| | | * @throws ChangelogException In case of underlying database problem. |
| | | * @return The next CSN in the database from this cursor. |
| | | * @throws ChangelogException |
| | | * In case of underlying database problem. |
| | | */ |
| | | public ChangeNumber nextChangeNumber() throws ChangelogException |
| | | public CSN nextCSN() throws ChangelogException |
| | | { |
| | | if (isClosed) |
| | | { |
| | |
| | | { |
| | | return null; |
| | | } |
| | | return toChangeNumber(key.getData()); |
| | | return toCSN(key.getData()); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | |
| | | return null; |
| | | } |
| | | |
| | | ChangeNumber cn = null; |
| | | CSN csn = null; |
| | | try |
| | | { |
| | | cn = toChangeNumber(key.getData()); |
| | | if (isACounterRecord(cn)) |
| | | csn = toCSN(key.getData()); |
| | | if (isACounterRecord(csn)) |
| | | { |
| | | continue; |
| | | } |
| | |
| | | */ |
| | | Message message = ERR_REPLICATIONDB_CANNOT_PROCESS_CHANGE_RECORD |
| | | .get(replicationServer.getServerId(), |
| | | (cn == null ? "" : cn.toString()), |
| | | (csn == null ? "" : csn.toString()), |
| | | e.getMessage()); |
| | | logError(message); |
| | | } |
| | |
| | | * Count the number of changes between 2 changes numbers (inclusive). |
| | | * @param start The lower limit of the count. |
| | | * @param stop The higher limit of the count. |
| | | * @return The number of changes between provided start and stop changeNumber. |
| | | * @return The number of changes between provided start and stop CSN. |
| | | * Returns 0 when an error occurs. |
| | | */ |
| | | public long count(ChangeNumber start, ChangeNumber stop) |
| | | public long count(CSN start, CSN stop) |
| | | { |
| | | dbCloseLock.readLock().lock(); |
| | | try |
| | |
| | | } |
| | | |
| | | |
| | | private void findFirstCounterRecordAfterStartPoint(ChangeNumber start, |
| | | ChangeNumber stop, int[] counterValues, int[] distanceToCounterRecords) |
| | | private void findFirstCounterRecordAfterStartPoint(CSN start, |
| | | CSN stop, int[] counterValues, int[] distanceToCounterRecords) |
| | | throws DatabaseException |
| | | { |
| | | Cursor cursor = db.openCursor(null, null); |
| | |
| | | while (status == OperationStatus.SUCCESS) |
| | | { |
| | | // test whether the record is a regular change or a counter |
| | | final ChangeNumber cn = toChangeNumber(key.getData()); |
| | | if (isACounterRecord(cn)) |
| | | final CSN csn = toCSN(key.getData()); |
| | | if (isACounterRecord(csn)) |
| | | { |
| | | // we have found the counter record |
| | | counterValues[START] = decodeCounterValue(data.getData()); |
| | |
| | | |
| | | // reached a regular change record |
| | | // test whether we reached the 'stop' target |
| | | if (!cn.newer(stop)) |
| | | if (!csn.newer(stop)) |
| | | { |
| | | // let's loop |
| | | distanceToCounterRecords[START]++; |
| | |
| | | } |
| | | } |
| | | |
| | | private boolean findFirstCounterRecordBeforeStopPoint(ChangeNumber start, |
| | | ChangeNumber stop, int[] counterValues, int[] distanceToCounterRecords) |
| | | private boolean findFirstCounterRecordBeforeStopPoint(CSN start, |
| | | CSN stop, int[] counterValues, int[] distanceToCounterRecords) |
| | | throws DatabaseException |
| | | { |
| | | Cursor cursor = db.openCursor(null, null); |
| | |
| | | |
| | | while (status == OperationStatus.SUCCESS) |
| | | { |
| | | final ChangeNumber cn = toChangeNumber(key.getData()); |
| | | if (isACounterRecord(cn)) |
| | | final CSN csn = toCSN(key.getData()); |
| | | if (isACounterRecord(csn)) |
| | | { |
| | | // we have found the counter record |
| | | counterValues[STOP] = decodeCounterValue(data.getData()); |
| | |
| | | } |
| | | |
| | | // it is a regular change record |
| | | if (!cn.older(start)) |
| | | if (!csn.older(start)) |
| | | { |
| | | distanceToCounterRecords[STOP]++; |
| | | status = cursor.getPrev(key, data, LockMode.DEFAULT); |
| | |
| | | |
| | | /** |
| | | * The diagram below shows a visual description of how the distance between |
| | | * two change numbers in the database is computed. |
| | | * two CSNs in the database is computed. |
| | | * |
| | | * <pre> |
| | | * +--------+ +--------+ |
| | |
| | | * Explanation of the terms used: |
| | | * <dl> |
| | | * <dt>START</dt> |
| | | * <dd>Start change number for the count</dd> |
| | | * <dd>Start CSN for the count</dd> |
| | | * <dt>STOP</dt> |
| | | * <dd>Stop change number for the count</dd> |
| | | * <dd>Stop CSN for the count</dd> |
| | | * <dt>dist</dt> |
| | | * <dd>Distance from START (or STOP) to the counter record</dd> |
| | | * <dt>CSN</dt> |
| | |
| | | * database is ordered.</dd> |
| | | * <dt>CR</dt> |
| | | * <dd>Stands for "Counter Record". Counter Records are inserted in the |
| | | * database along with real change numbers, but they are not real changes. |
| | | * They are only used to speed up calculating the distance between 2 change |
| | | * numbers without the need to scan the whole database in between.</dd> |
| | | * database along with real CSNs, but they are not real changes. They are only |
| | | * used to speed up calculating the distance between 2 CSNs without the need |
| | | * to scan the whole database in between.</dd> |
| | | * </dl> |
| | | */ |
| | | private long computeDistance(int[] counterValues, |
| | |
| | | } |
| | | |
| | | /** |
| | | * Whether a provided changeNumber represents a counter record. A counter |
| | | * record is used to store TODO. |
| | | * Whether a provided CSN represents a counter record. A counter record is |
| | | * used to store the time. |
| | | * |
| | | * @param cn |
| | | * The changeNumber to test |
| | | * @return true if the provided changenumber is a counter, false otherwise |
| | | * @param csn |
| | | * The CSN to test |
| | | * @return true if the provided CSN is a counter, false otherwise |
| | | */ |
| | | private static boolean isACounterRecord(ChangeNumber cn) |
| | | private static boolean isACounterRecord(CSN csn) |
| | | { |
| | | return cn.getServerId() == 0 && cn.getSeqnum() == 0; |
| | | return csn.getServerId() == 0 && csn.getSeqnum() == 0; |
| | | } |
| | | |
| | | private static ChangeNumber newCounterRecord(ChangeNumber changeNumber) |
| | | private static CSN newCounterRecord(CSN csn) |
| | | { |
| | | return new ChangeNumber(changeNumber.getTime(), 0, 0); |
| | | return new CSN(csn.getTime(), 0, 0); |
| | | } |
| | | |
| | | /** |
| | |
| | | * Copyright 2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2013 ForgeRock AS |
| | | */ |
| | | |
| | | package org.opends.server.replication.service; |
| | | |
| | | import org.opends.server.api.DirectoryThread; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import java.io.IOException; |
| | | |
| | | import org.opends.server.api.DirectoryThread; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.ChangeTimeHeartbeatMsg; |
| | | import org.opends.server.replication.protocol.Session; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.util.TimeThread; |
| | | |
| | | import java.io.IOException; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | /** |
| | | * This thread publishes a heartbeat message on a given protocol session at |
| | |
| | | long now = System.currentTimeMillis(); |
| | | ChangeTimeHeartbeatMsg ctHeartbeatMsg = |
| | | new ChangeTimeHeartbeatMsg( |
| | | new ChangeNumber(TimeThread.getTime(),0, serverId)); |
| | | new CSN(TimeThread.getTime(),0, serverId)); |
| | | |
| | | if (now > session.getLastPublishTime() + heartbeatInterval) |
| | | { |
| | |
| | | */ |
| | | package org.opends.server.replication.service; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.loggers.debug.DebugLogger.getTracer; |
| | | import static org.opends.server.replication.protocol.ProtocolVersion.*; |
| | | import static org.opends.server.replication.server.ReplicationServer.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.math.MathContext; |
| | | import java.math.RoundingMode; |
| | | import java.net.ConnectException; |
| | | import java.net.InetAddress; |
| | | import java.net.InetSocketAddress; |
| | | import java.net.Socket; |
| | | import java.net.SocketException; |
| | | import java.net.SocketTimeoutException; |
| | | import java.net.UnknownHostException; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.net.*; |
| | | import java.util.*; |
| | | import java.util.Map.Entry; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.Semaphore; |
| | |
| | | import org.opends.messages.MessageBuilder; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.DSInfo; |
| | | import org.opends.server.replication.common.MutableBoolean; |
| | | import org.opends.server.replication.common.RSInfo; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.common.ServerStatus; |
| | | import org.opends.server.replication.common.*; |
| | | import org.opends.server.replication.plugin.MultimasterReplication; |
| | | import org.opends.server.replication.protocol.*; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.util.ServerConstants; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.replication.protocol.ProtocolVersion.*; |
| | | import static org.opends.server.replication.server.ReplicationServer.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | /** |
| | | * The broker for Multi-master Replication. |
| | | */ |
| | |
| | | private volatile Collection<String> replicationServerUrls; |
| | | private volatile boolean connected = false; |
| | | /** |
| | | * String reported under cn=monitor when there is no connected RS. |
| | | * String reported under CSN=monitor when there is no connected RS. |
| | | */ |
| | | public final static String NO_CONNECTED_SERVER = "Not connected"; |
| | | private volatile String replicationServer = NO_CONNECTED_SERVER; |
| | |
| | | * @param groupId The group id of our domain. |
| | | * @param changeTimeHeartbeatInterval The interval (in ms) between Change |
| | | * time heartbeats are sent to the RS, |
| | | * or zero if no CN heartbeat should be sent. |
| | | * or zero if no CSN heartbeat should be sent. |
| | | */ |
| | | public ReplicationBroker(ReplicationDomain replicationDomain, |
| | | ServerState state, String baseDn, int serverID2, int window, |
| | |
| | | /** |
| | | * Now apply the choice base on the weight to the best servers list |
| | | */ |
| | | if (bestServers.size() > 1) |
| | | if (bestServers.size() == 1) |
| | | { |
| | | return bestServers.values().iterator().next(); |
| | | } |
| | | |
| | | if (firstConnection) |
| | | { |
| | | // We are not connected to a server yet |
| | |
| | | return computeBestServerForWeight(bestServers, rsServerId, |
| | | localServerId); |
| | | } |
| | | } else |
| | | { |
| | | return bestServers.values().iterator().next(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | Map<Integer, ReplicationServerInfo> moreUpToDateServers = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // Extract the change number of the latest change generated by the local |
| | | // server |
| | | ChangeNumber myChangeNumber = localState.getChangeNumber(localServerId); |
| | | if (myChangeNumber == null) |
| | | // Extract the CSN of the latest change generated by the local server |
| | | CSN myCSN = localState.getCSN(localServerId); |
| | | if (myCSN == null) |
| | | { |
| | | myChangeNumber = new ChangeNumber(0, 0, localServerId); |
| | | myCSN = new CSN(0, 0, localServerId); |
| | | } |
| | | |
| | | /** |
| | |
| | | * if for instance we failed and restarted, having sent some changes to the |
| | | * RS but without having time to store our own state) regarding our own |
| | | * server id. If some servers more up to date, prefer this list but take |
| | | * only the latest change number. |
| | | * only the latest CSN. |
| | | */ |
| | | ChangeNumber latestRsChangeNumber = null; |
| | | CSN latestRsCSN = null; |
| | | for (Integer rsId : bestServers.keySet()) |
| | | { |
| | | ReplicationServerInfo replicationServerInfo = bestServers.get(rsId); |
| | | ServerState rsState = replicationServerInfo.getServerState(); |
| | | ChangeNumber rsChangeNumber = rsState.getChangeNumber(localServerId); |
| | | if (rsChangeNumber == null) |
| | | CSN rsCSN = rsState.getCSN(localServerId); |
| | | if (rsCSN == null) |
| | | { |
| | | rsChangeNumber = new ChangeNumber(0, 0, localServerId); |
| | | rsCSN = new CSN(0, 0, localServerId); |
| | | } |
| | | |
| | | // Has this replication server the latest local change ? |
| | | if (myChangeNumber.olderOrEqual(rsChangeNumber)) |
| | | if (myCSN.olderOrEqual(rsCSN)) |
| | | { |
| | | if (myChangeNumber.equals(rsChangeNumber)) |
| | | if (myCSN.equals(rsCSN)) |
| | | { |
| | | // This replication server has exactly the latest change from the |
| | | // local server |
| | |
| | | { |
| | | // This replication server is even more up to date than the local |
| | | // server |
| | | if (latestRsChangeNumber == null) |
| | | if (latestRsCSN == null) |
| | | { |
| | | // Initialize the latest change number |
| | | latestRsChangeNumber = rsChangeNumber; |
| | | // Initialize the latest CSN |
| | | latestRsCSN = rsCSN; |
| | | } |
| | | if (rsChangeNumber.newerOrEquals(latestRsChangeNumber)) |
| | | if (rsCSN.newerOrEquals(latestRsCSN)) |
| | | { |
| | | if (rsChangeNumber.equals(latestRsChangeNumber)) |
| | | if (rsCSN.equals(latestRsCSN)) |
| | | { |
| | | moreUpToDateServers.put(rsId, replicationServerInfo); |
| | | } else |
| | |
| | | // new RS |
| | | moreUpToDateServers.clear(); |
| | | moreUpToDateServers.put(rsId, replicationServerInfo); |
| | | latestRsChangeNumber = rsChangeNumber; |
| | | latestRsCSN = rsCSN; |
| | | } |
| | | } |
| | | } |
| | |
| | | { |
| | | // Prefer servers more up to date than local server |
| | | return moreUpToDateServers; |
| | | } else |
| | | { |
| | | return upToDateServers; |
| | | } |
| | | return upToDateServers; |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | public void startChangeTimeHeartBeatPublishing() |
| | | { |
| | | // Start a CN heartbeat thread. |
| | | // Start a CSN heartbeat thread. |
| | | if (changeTimeHeartbeatSendInterval > 0) |
| | | { |
| | | String threadName = "Replica DS(" |
| | |
| | | { |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo(this |
| | | + " is not configured to send CN heartbeat interval"); |
| | | + " is not configured to send CSN heartbeat interval"); |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | package org.opends.server.replication.service; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.replication.common.StatusMachine.*; |
| | | |
| | | import java.io.BufferedOutputStream; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.replication.common.StatusMachine.*; |
| | | |
| | | /** |
| | | * This class should be used as a base for Replication implementations. |
| | | * <p> |
| | |
| | | * to be able to correlate all the coming back acks to the original |
| | | * operation. |
| | | */ |
| | | private final Map<ChangeNumber, UpdateMsg> waitingAckMsgs = |
| | | new ConcurrentHashMap<ChangeNumber, UpdateMsg>(); |
| | | private final Map<CSN, UpdateMsg> waitingAckMsgs = |
| | | new ConcurrentHashMap<CSN, UpdateMsg>(); |
| | | |
| | | |
| | | /** |
| | |
| | | private final ServerState state; |
| | | |
| | | /** |
| | | * The generator that will be used to generate {@link ChangeNumber} |
| | | * The generator that will be used to generate {@link CSN} |
| | | * for this domain. |
| | | */ |
| | | private final ChangeNumberGenerator generator; |
| | | private final CSNGenerator generator; |
| | | |
| | | private final Object eclIncludesLock = new Object(); |
| | | private final Map<Integer, Set<String>> eclIncludesByServer = |
| | |
| | | private final Object sessionLock = new Object(); |
| | | |
| | | /** |
| | | * Returns the {@link ChangeNumberGenerator} that will be used to |
| | | * generate {@link ChangeNumber} for this domain. |
| | | * Returns the {@link CSNGenerator} that will be used to |
| | | * generate {@link CSN} for this domain. |
| | | * |
| | | * @return The {@link ChangeNumberGenerator} that will be used to |
| | | * generate {@link ChangeNumber} for this domain. |
| | | * @return The {@link CSNGenerator} that will be used to |
| | | * generate {@link CSN} for this domain. |
| | | */ |
| | | public ChangeNumberGenerator getGenerator() |
| | | public CSNGenerator getGenerator() |
| | | { |
| | | return generator; |
| | | } |
| | |
| | | this.serverID = serverID; |
| | | this.initWindow = initWindow; |
| | | this.state = new ServerState(); |
| | | this.generator = new ChangeNumberGenerator(serverID, state); |
| | | this.generator = new CSNGenerator(serverID, state); |
| | | |
| | | domains.put(baseDN, this); |
| | | } |
| | |
| | | this.baseDN = baseDN; |
| | | this.serverID = serverID; |
| | | this.state = serverState; |
| | | this.generator = new ChangeNumberGenerator(serverID, state); |
| | | this.generator = new CSNGenerator(serverID, state); |
| | | |
| | | domains.put(baseDN, this); |
| | | } |
| | |
| | | else if (msg instanceof UpdateMsg) |
| | | { |
| | | update = (UpdateMsg) msg; |
| | | generator.adjust(update.getChangeNumber()); |
| | | generator.adjust(update.getCSN()); |
| | | } |
| | | else if (msg instanceof InitializeRcvAckMsg) |
| | | { |
| | |
| | | */ |
| | | private void receiveAck(AckMsg ack) |
| | | { |
| | | UpdateMsg update; |
| | | ChangeNumber changeNumber = ack.getChangeNumber(); |
| | | CSN csn = ack.getCSN(); |
| | | |
| | | // Remove the message for pending ack list (this may already make the thread |
| | | // that is waiting for the ack be aware of its reception) |
| | | update = waitingAckMsgs.remove(changeNumber); |
| | | UpdateMsg update = waitingAckMsgs.remove(csn); |
| | | |
| | | // Signal waiting thread ack has been received |
| | | if (update != null) |
| | |
| | | is used the ack is sent without error at replay flag. |
| | | */ |
| | | processUpdateDone(msg, null); |
| | | state.update(msg.getChangeNumber()); |
| | | state.update(msg.getCSN()); |
| | | } |
| | | |
| | | /** |
| | |
| | | if (rsGroupId == groupId) |
| | | { |
| | | // Send the ack |
| | | AckMsg ackMsg = new AckMsg(msg.getChangeNumber()); |
| | | AckMsg ackMsg = new AckMsg(msg.getCSN()); |
| | | if (replayErrorMsg != null) |
| | | { |
| | | // Mark the error in the ack |
| | |
| | | msg.setSafeDataLevel(assuredSdLevel); |
| | | |
| | | // Add the assured message to the list of update that are waiting for acks |
| | | waitingAckMsgs.put(msg.getChangeNumber(), msg); |
| | | waitingAckMsgs.put(msg.getCSN(), msg); |
| | | } |
| | | } |
| | | |
| | |
| | | long startTime = System.currentTimeMillis(); |
| | | synchronized (msg) |
| | | { |
| | | ChangeNumber cn = msg.getChangeNumber(); |
| | | while (waitingAckMsgs.containsKey(cn)) |
| | | CSN csn = msg.getCSN(); |
| | | while (waitingAckMsgs.containsKey(csn)) |
| | | { |
| | | try |
| | | { |
| | |
| | | remove the update from the wait list, log the timeout error and |
| | | also update assured monitoring counters |
| | | */ |
| | | UpdateMsg update; |
| | | update = waitingAckMsgs.remove(cn); |
| | | UpdateMsg update = waitingAckMsgs.remove(csn); |
| | | |
| | | if (update != null) |
| | | { |
| | |
| | | // Should not happen |
| | | } |
| | | |
| | | throw new TimeoutException("No ack received for message cn: " + cn + |
| | | " and replication servceID: " + baseDN + " after " + |
| | | assuredTimeout + " ms."); |
| | | throw new TimeoutException("No ack received for message csn: " |
| | | + csn + " and replication servceID: " + baseDN + " after " |
| | | + assuredTimeout + " ms."); |
| | | } else |
| | | { |
| | | // Ack received just before timeout limit: we can exit |
| | |
| | | { |
| | | // Publish the update |
| | | broker.publish(msg); |
| | | state.update(msg.getChangeNumber()); |
| | | state.update(msg.getCSN()); |
| | | numSentUpdates.incrementAndGet(); |
| | | } |
| | | |
| | |
| | | UpdateMsg update; |
| | | synchronized (this) |
| | | { |
| | | update = new UpdateMsg(generator.newChangeNumber(), msg); |
| | | update = new UpdateMsg(generator.newCSN(), msg); |
| | | /* |
| | | If assured replication is configured, this will prepare blocking |
| | | mechanism. If assured replication is disabled, this returns |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns the ChangeNUmber of the last Change that was fully processed |
| | | * by this ReplicationDomain. |
| | | * Returns the CSN of the last Change that was fully processed by this |
| | | * ReplicationDomain. |
| | | * |
| | | * @return The ChangeNUmber of the last Change that was fully processed |
| | | * by this ReplicationDomain. |
| | | * @return The CSN of the last Change that was fully processed by this |
| | | * ReplicationDomain. |
| | | */ |
| | | public ChangeNumber getLastLocalChange() |
| | | public CSN getLastLocalChange() |
| | | { |
| | | return state.getChangeNumber(serverID); |
| | | return state.getCSN(serverID); |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.tasks; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.core.DirectoryServer.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | import java.util.List; |
| | | |
| | | import org.opends.messages.*; |
| | | import org.opends.server.backends.task.Task; |
| | | import org.opends.server.backends.task.TaskState; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.plugin.LDAPReplicationDomain; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.util.TimeThread; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.core.DirectoryServer.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | /** |
| | | * This class provides an implementation of a Directory Server task that can |
| | | * be used to purge the replication historical informations stored in the |
| | |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | private String domainString = null; |
| | | private LDAPReplicationDomain domain = null; |
| | | private String domainString; |
| | | private LDAPReplicationDomain domain; |
| | | |
| | | /** |
| | | * current historical purge delay |
| | |
| | | * -----------------------------------------------------------------> t |
| | | * | | | |
| | | * current task task |
| | | * CN being purged start date max end date |
| | | * CSN being purged start date max end date |
| | | * <------------> |
| | | * config.purgeMaxDuration |
| | | * |
| | | * The task will start purging the change with the oldest CN found. |
| | | * The task will start purging the change with the oldest CSN found. |
| | | * The task run as long as : |
| | | * - the end date (computed from the configured max duration) is not reached |
| | | * - the CN purged is oldest than the configured historical purge delay |
| | | * |
| | | * |
| | | * - the CSN purged is oldest than the configured historical purge delay |
| | | */ |
| | | |
| | | private int purgeTaskMaxDurationInSec = DEFAULT_MAX_DURATION; |
| | | |
| | | TaskState initState; |
| | | private TaskState initState; |
| | | |
| | | |
| | | private static final void debugInfo(String s) |
| | |
| | | // sets in the attributes the last stats values |
| | | replaceAttributeValue(ATTR_TASK_CONFLICTS_HIST_PURGE_COUNT, |
| | | String.valueOf(this.purgeCount)); |
| | | replaceAttributeValue(ATTR_TASK_CONFLICTS_HIST_PURGE_LAST_CN, |
| | | this.lastCN.toStringUI()); |
| | | replaceAttributeValue(ATTR_TASK_CONFLICTS_HIST_PURGE_LAST_CSN, |
| | | this.lastCSN.toStringUI()); |
| | | debugInfo("[PURGE] PurgeConflictsHistoricalTask write attrs "); |
| | | } |
| | | catch(Exception e) |
| | |
| | | return initState; |
| | | } |
| | | |
| | | int updateAttrPeriod = 0; |
| | | ChangeNumber lastCN; |
| | | int purgeCount; |
| | | private int updateAttrPeriod = 0; |
| | | private CSN lastCSN; |
| | | private int purgeCount; |
| | | |
| | | /** |
| | | * Set the last changenumber purged and the count of purged values in order |
| | | * to monitor the historical purge. |
| | | * @param lastCN the last changeNumber purged. |
| | | * @param purgeCount the count of purged values. |
| | | * Set the last CSN purged and the count of purged values in order to monitor |
| | | * the historical purge. |
| | | * |
| | | * @param lastCSN |
| | | * the last CSN purged. |
| | | * @param purgeCount |
| | | * the count of purged values. |
| | | */ |
| | | public void setProgressStats(ChangeNumber lastCN, int purgeCount) |
| | | public void setProgressStats(CSN lastCSN, int purgeCount) |
| | | { |
| | | try |
| | | { |
| | | if (purgeCount == 0) |
| | | replaceAttributeValue(ATTR_TASK_CONFLICTS_HIST_PURGE_FIRST_CN, |
| | | lastCN.toStringUI()); |
| | | replaceAttributeValue(ATTR_TASK_CONFLICTS_HIST_PURGE_FIRST_CSN, |
| | | lastCSN.toStringUI()); |
| | | |
| | | // we don't want the update of the task to overload too much task duration |
| | | this.purgeCount = purgeCount; |
| | | this.lastCN = lastCN; |
| | | this.lastCSN = lastCSN; |
| | | if (++updateAttrPeriod % 100 == 0) |
| | | { |
| | | replaceAttributeValue(ATTR_TASK_CONFLICTS_HIST_PURGE_COUNT, |
| | | String.valueOf(purgeCount)); |
| | | |
| | | replaceAttributeValue(ATTR_TASK_CONFLICTS_HIST_PURGE_LAST_CN, |
| | | lastCN.toStringUI()); |
| | | replaceAttributeValue(ATTR_TASK_CONFLICTS_HIST_PURGE_LAST_CSN, |
| | | lastCSN.toStringUI()); |
| | | debugInfo("[PURGE] PurgeConflictsHistoricalTask write attrs " |
| | | + purgeCount); |
| | | } |
| | |
| | | { |
| | | out.println(INFO_CHANGE_NUMBER_CONTROL_RESULT.get(operationType, |
| | | ((ChangeNumberControlPlugin.ChangeNumberControl)c). |
| | | getChangeNumber().toString())); |
| | | getCSN().toString())); |
| | | } |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.workflowelement.externalchangelog; |
| | | |
| | | import static org.opends.messages.CoreMessages.*; |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.util.LDIFWriter.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | |
| | |
| | | import org.opends.server.controls.*; |
| | | import org.opends.server.core.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ExternalChangeLogSession; |
| | | import org.opends.server.replication.common.MultiDomainServerState; |
| | | import org.opends.server.replication.plugin.MultimasterReplication; |
| | |
| | | import org.opends.server.types.operation.SearchReferenceSearchOperation; |
| | | import org.opends.server.util.ServerConstants; |
| | | |
| | | import static org.opends.messages.CoreMessages.*; |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.util.LDIFWriter.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | /** |
| | | * This class defines an operation used to search for entries in a local backend |
| | | * of the Directory Server. |
| | |
| | | |
| | | clEntry = createChangelogEntry(eclmsg.getBaseDN(), eclmsg |
| | | .getCookie().toString(), DN.decode(addMsg.getDn()), |
| | | addMsg.getChangeNumber(), ldifChanges, // entry as created (in LDIF |
| | | addMsg.getCSN(), ldifChanges, // entry as created (in LDIF |
| | | // format) |
| | | addMsg.getEntryUUID(), |
| | | eclAttributes, // entry attributes |
| | |
| | | |
| | | clEntry = createChangelogEntry(eclmsg.getBaseDN(), eclmsg |
| | | .getCookie().toString(), DN.decode(modifyMsg.getDn()), |
| | | modifyMsg.getChangeNumber(), ldifChanges, |
| | | modifyMsg.getCSN(), ldifChanges, |
| | | modifyMsg.getEntryUUID(), |
| | | modifyMsg.getEclIncludes(), // entry attributes |
| | | eclmsg.getDraftChangeNumber(), changeType, |
| | |
| | | |
| | | clEntry = createChangelogEntry(eclmsg.getBaseDN(), eclmsg |
| | | .getCookie().toString(), DN.decode(delMsg.getDn()), |
| | | delMsg.getChangeNumber(), |
| | | delMsg.getCSN(), |
| | | null, // no changes |
| | | delMsg.getEntryUUID(), |
| | | delMsg.getEclIncludes(), // entry attributes |
| | |
| | | * @param baseDN The provided baseDN value. |
| | | * @param cookie The provided cookie value. |
| | | * @param targetDN The provided targetDN. |
| | | * @param changeNumber The provided replication changeNumber. |
| | | * @param csn The provided replication CSN. |
| | | * @param clearLDIFchanges The provided LDIF changes for ADD and MODIFY |
| | | * @param targetUUID The provided targetUUID. |
| | | * @param includedAttributes The provided attributes to include |
| | |
| | | String baseDN, |
| | | String cookie, |
| | | DN targetDN, |
| | | ChangeNumber changeNumber, |
| | | CSN csn, |
| | | String clearLDIFchanges, |
| | | String targetUUID, |
| | | List<RawAttribute> includedAttributes, |
| | |
| | | if (draftChangenumber == 0) |
| | | { |
| | | // Draft uncompat mode |
| | | dnString = "replicationCSN=" + changeNumber + "," + baseDN + "," |
| | | dnString = "replicationCSN=" + csn + "," + baseDN + "," |
| | | + ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT; |
| | | } |
| | | else |
| | |
| | | |
| | | SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME); |
| | | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); // ?? |
| | | String format = dateFormat.format(new Date(changeNumber.getTime())); |
| | | String format = dateFormat.format(new Date(csn.getTime())); |
| | | addAttributeByType("changetime", "changeTime", format, uAttrs, |
| | | operationalAttrs); |
| | | |
| | |
| | | |
| | | // NON REQUESTED attributes |
| | | |
| | | addAttributeByType("replicationcsn", "replicationCSN", changeNumber |
| | | addAttributeByType("replicationcsn", "replicationCSN", csn |
| | | .toString(), uAttrs, operationalAttrs); |
| | | |
| | | addAttributeByType("replicaidentifier", "replicaIdentifier", Integer |
| | | .toString(changeNumber.getServerId()), uAttrs, operationalAttrs); |
| | | .toString(csn.getServerId()), uAttrs, operationalAttrs); |
| | | |
| | | if (clearLDIFchanges != null) |
| | | { |
| | |
| | | StartECLSessionMsg msg = evaluateSearchParameters2(sf); |
| | | startCLmsg.setFirstDraftChangeNumber(msg.getFirstDraftChangeNumber()); |
| | | startCLmsg.setLastDraftChangeNumber(msg.getLastDraftChangeNumber()); |
| | | startCLmsg.setChangeNumber(msg.getChangeNumber()); |
| | | startCLmsg.setCSN(msg.getCSN()); |
| | | } |
| | | |
| | | private static StartECLSessionMsg evaluateSearchParameters2(SearchFilter sf) |
| | |
| | | StartECLSessionMsg startCLmsg = new StartECLSessionMsg(); |
| | | startCLmsg.setFirstDraftChangeNumber(-1); |
| | | startCLmsg.setLastDraftChangeNumber(-1); |
| | | startCLmsg.setChangeNumber(new ChangeNumber(0,0,(short)0)); |
| | | startCLmsg.setCSN(new CSN(0, 0, 0)); |
| | | |
| | | // If there's no filter, just return |
| | | if (sf == null) |
| | |
| | | } |
| | | else if (matches(sf, FilterType.EQUALITY, "replicationcsn")) |
| | | { |
| | | // == exact changenumber |
| | | ChangeNumber cn = new ChangeNumber(sf.getAssertionValue().toString()); |
| | | startCLmsg.setChangeNumber(cn); |
| | | // == exact CSN |
| | | startCLmsg.setCSN(new CSN(sf.getAssertionValue().toString())); |
| | | return startCLmsg; |
| | | } |
| | | else if (matches(sf, FilterType.EQUALITY, "changenumber")) |
| | |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.backends.MemoryBackend; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.plugin.DomainFakeCfg; |
| | | import org.opends.server.replication.plugin.LDAPReplicationDomain; |
| | | import org.opends.server.replication.plugin.MultimasterReplication; |
| | |
| | | // send a sequence of add operation |
| | | |
| | | String addDn = TEST_ROOT_DN_STRING; |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(brokerId, 0L); |
| | | CSNGenerator gen = new CSNGenerator(brokerId, 0L); |
| | | |
| | | int sequence; |
| | | for (sequence = 1; sequence<=AddSequenceLength; sequence ++) |
| | |
| | | new LinkedList<AttributeValue>()); |
| | | addDn = "dc=dependency" + sequence + "," + addDn; |
| | | AddMsg addMsg = |
| | | new AddMsg(gen.newChangeNumber(), addDn, stringUID(sequence+1), |
| | | new AddMsg(gen.newCSN(), addDn, stringUID(sequence+1), |
| | | stringUID(sequence), |
| | | entry.getObjectClassAttribute(), |
| | | entry.getAttributes(), null ); |
| | | broker.publish(addMsg); |
| | | |
| | | ModifyMsg modifyMsg = |
| | | new ModifyMsg(gen.newChangeNumber(), DN.decode(addDn), |
| | | new ModifyMsg(gen.newCSN(), DN.decode(addDn), |
| | | generatemods("description", "test"), |
| | | stringUID(sequence+1)); |
| | | broker.publish(modifyMsg); |
| | |
| | | while (sequence-->1) |
| | | { |
| | | DeleteMsg delMsg = new DeleteMsg(deleteDN.toString(), |
| | | gen.newChangeNumber(), |
| | | gen.newCSN(), |
| | | stringUID(sequence + 1)); |
| | | broker.publish(delMsg); |
| | | deleteDN = deleteDN.getParent(); |
| | |
| | | Entry entry = TestCaseUtils.entryFromLdifString(entryldif); |
| | | AttributeType uidType = |
| | | DirectoryServer.getSchema().getAttributeType("entryuuid"); |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(brokerId, 0L); |
| | | CSNGenerator gen = new CSNGenerator(brokerId, 0L); |
| | | int renamedEntryUuid = 100; |
| | | |
| | | int replServerPort = TestCaseUtils.findFreePort(); |
| | |
| | | new LinkedList<AttributeValue>()); |
| | | String addDn = "dc=moddndel" + "," + TEST_ROOT_DN_STRING; |
| | | AddMsg addMsg = |
| | | new AddMsg(gen.newChangeNumber(), addDn, stringUID(renamedEntryUuid), |
| | | new AddMsg(gen.newCSN(), addDn, stringUID(renamedEntryUuid), |
| | | stringUID(1), |
| | | entry.getObjectClassAttribute(), |
| | | entry.getAttributes(), null ); |
| | |
| | | |
| | | // rename and delete the entry. |
| | | ModifyDNMsg moddnMsg = |
| | | new ModifyDNMsg(addDn, gen.newChangeNumber(), |
| | | new ModifyDNMsg(addDn, gen.newCSN(), |
| | | stringUID(renamedEntryUuid), |
| | | stringUID(1), true, null, "dc=new_name"); |
| | | broker.publish(moddnMsg); |
| | | DeleteMsg delMsg = |
| | | new DeleteMsg("dc=new_name" + "," + TEST_ROOT_DN_STRING, |
| | | gen.newChangeNumber(), stringUID(renamedEntryUuid)); |
| | | gen.newCSN(), stringUID(renamedEntryUuid)); |
| | | broker.publish(delMsg); |
| | | |
| | | // enable back the domain to trigger message replay. |
| | |
| | | false, CLEAN_DB_GENERATION_ID); |
| | | |
| | | // send a sequence of add/del/add operations |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(brokerId, 0L); |
| | | CSNGenerator gen = new CSNGenerator(brokerId, 0L); |
| | | |
| | | int sequence; |
| | | for (sequence = 1; sequence<=AddSequenceLength; sequence ++) |
| | |
| | | new LinkedList<AttributeValue>()); |
| | | String addDn = "dc=dependency" + sequence + "," + TEST_ROOT_DN_STRING; |
| | | AddMsg addMsg = |
| | | new AddMsg(gen.newChangeNumber(), addDn, stringUID(sequence+1), |
| | | new AddMsg(gen.newCSN(), addDn, stringUID(sequence+1), |
| | | stringUID(1), |
| | | entry.getObjectClassAttribute(), |
| | | entry.getAttributes(), null ); |
| | | broker.publish(addMsg); |
| | | |
| | | // delete the entry |
| | | DeleteMsg delMsg = new DeleteMsg(addDn, gen.newChangeNumber(), |
| | | DeleteMsg delMsg = new DeleteMsg(addDn, gen.newCSN(), |
| | | stringUID(sequence+1)); |
| | | broker.publish(delMsg); |
| | | |
| | |
| | | entry.addAttribute(Attributes.create("entryuuid", stringUID(sequence+1025)), |
| | | new LinkedList<AttributeValue>()); |
| | | addMsg = |
| | | new AddMsg(gen.newChangeNumber(), addDn, stringUID(sequence+1025), |
| | | new AddMsg(gen.newCSN(), addDn, stringUID(sequence+1025), |
| | | stringUID(1), |
| | | entry.getObjectClassAttribute(), |
| | | entry.getAttributes(), null ); |
| | |
| | | { |
| | | String deleteDN = "dc=dependency" + sequence + "," + TEST_ROOT_DN_STRING; |
| | | DeleteMsg delMsg = new DeleteMsg(deleteDN, |
| | | gen.newChangeNumber(), |
| | | gen.newCSN(), |
| | | stringUID(sequence + 1025)); |
| | | broker.publish(delMsg); |
| | | } |
| | |
| | | |
| | | |
| | | String addDn = TEST_ROOT_DN_STRING; |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(brokerId, 0L); |
| | | CSNGenerator gen = new CSNGenerator(brokerId, 0L); |
| | | |
| | | // send a sequence of add/modrdn operations |
| | | int sequence; |
| | |
| | | new LinkedList<AttributeValue>()); |
| | | addDn = "dc=dependency" + sequence + "," + TEST_ROOT_DN_STRING; |
| | | AddMsg addMsg = |
| | | new AddMsg(gen.newChangeNumber(), addDn, stringUID(sequence+1), |
| | | new AddMsg(gen.newCSN(), addDn, stringUID(sequence+1), |
| | | stringUID(1), |
| | | entry.getObjectClassAttribute(), |
| | | entry.getAttributes(), null ); |
| | |
| | | |
| | | // rename the entry |
| | | ModifyDNMsg moddnMsg = |
| | | new ModifyDNMsg(addDn, gen.newChangeNumber(), stringUID(sequence+1), |
| | | new ModifyDNMsg(addDn, gen.newCSN(), stringUID(sequence+1), |
| | | stringUID(1), true, null, "dc=new_dep" + sequence); |
| | | broker.publish(moddnMsg); |
| | | } |
| | |
| | | { |
| | | addDn = "dc=new_dep" + sequence + "," + TEST_ROOT_DN_STRING; |
| | | DeleteMsg delMsg = new DeleteMsg(addDn.toString(), |
| | | gen.newChangeNumber(), |
| | | gen.newCSN(), |
| | | stringUID(sequence + 1)); |
| | | broker.publish(delMsg); |
| | | } |
| | |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.protocols.ldap.*; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.common.MultiDomainServerState; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.plugin.DomainFakeCfg; |
| | |
| | | /** The LDAPStatistics object associated with the LDAP connection handler. */ |
| | | private LDAPStatistics ldapStatistics; |
| | | |
| | | private ChangeNumber gblCN; |
| | | private CSN gblCSN; |
| | | |
| | | private List<Control> NO_CONTROL = null; |
| | | |
| | |
| | | // create and publish 1 change on each suffix |
| | | long time = TimeThread.getTime(); |
| | | int ts = 1; |
| | | ChangeNumber cn1 = new ChangeNumber(time, ts++, SERVER_ID_1); |
| | | CSN csn1 = new CSN(time, ts++, SERVER_ID_1); |
| | | DeleteMsg delMsg1 = |
| | | new DeleteMsg("o=" + tn + "1," + TEST_ROOT_DN_STRING, cn1, "ECLBasicMsg1uid"); |
| | | new DeleteMsg("o=" + tn + "1," + TEST_ROOT_DN_STRING, csn1, |
| | | "ECLBasicMsg1uid"); |
| | | server01.publish(delMsg1); |
| | | debugInfo(tn, "publishes:" + delMsg1); |
| | | |
| | | ChangeNumber cn2 = new ChangeNumber(time, ts++, SERVER_ID_2); |
| | | CSN csn2 = new CSN(time, ts++, SERVER_ID_2); |
| | | DeleteMsg delMsg2 = |
| | | new DeleteMsg("o=" + tn + "2," + TEST_ROOT_DN_STRING2, cn2, "ECLBasicMsg2uid"); |
| | | new DeleteMsg("o=" + tn + "2," + TEST_ROOT_DN_STRING2, csn2, |
| | | "ECLBasicMsg2uid"); |
| | | server02.publish(delMsg2); |
| | | debugInfo(tn, "publishes:" + delMsg2); |
| | | |
| | |
| | | msg = serverECL.receive(); |
| | | ECLUpdateMsg eclu = (ECLUpdateMsg)msg; |
| | | UpdateMsg u = eclu.getUpdateMsg(); |
| | | debugInfo(tn, "RESULT:" + u.getChangeNumber() + " " + eclu.getCookie()); |
| | | assertTrue(u.getChangeNumber().equals(cn1), "RESULT:" + u.getChangeNumber()); |
| | | debugInfo(tn, "RESULT:" + u.getCSN() + " " + eclu.getCookie()); |
| | | assertTrue(u.getCSN().equals(csn1), "RESULT:" + u.getCSN()); |
| | | assertTrue(eclu.getCookie().equalsTo(new MultiDomainServerState( |
| | | "o=test:"+delMsg1.getChangeNumber()+";o=test2:;"))); |
| | | "o=test:"+delMsg1.getCSN()+";o=test2:;"))); |
| | | |
| | | // receive change 2 from suffix 2 |
| | | msg = serverECL.receive(); |
| | | eclu = (ECLUpdateMsg)msg; |
| | | u = eclu.getUpdateMsg(); |
| | | debugInfo(tn, "RESULT:" + u.getChangeNumber()); |
| | | assertTrue(u.getChangeNumber().equals(cn2), "RESULT:" + u.getChangeNumber()); |
| | | debugInfo(tn, "RESULT:" + u.getCSN()); |
| | | assertTrue(u.getCSN().equals(csn2), "RESULT:" + u.getCSN()); |
| | | assertTrue(eclu.getCookie().equalsTo(new MultiDomainServerState( |
| | | "o=test2:"+delMsg2.getChangeNumber()+";"+ |
| | | "o=test:"+delMsg1.getChangeNumber()+";"))); |
| | | "o=test2:"+delMsg2.getCSN()+";"+ |
| | | "o=test:"+delMsg1.getCSN()+";"))); |
| | | |
| | | // receive Done |
| | | msg = serverECL.receive(); |
| | |
| | | // create and publish 1 change on each suffix |
| | | long time = TimeThread.getTime(); |
| | | |
| | | ChangeNumber cn1 = new ChangeNumber(time, 1, SERVER_ID_1); |
| | | CSN csn1 = new CSN(time, 1, SERVER_ID_1); |
| | | DeleteMsg delMsg1 = |
| | | new DeleteMsg("o=" + tn + "1," + TEST_ROOT_DN_STRING, cn1, "ECLBasicMsg1uid"); |
| | | new DeleteMsg("o=" + tn + "1," + TEST_ROOT_DN_STRING, csn1, |
| | | "ECLBasicMsg1uid"); |
| | | server01.publish(delMsg1); |
| | | debugInfo(tn, "publishes:" + delMsg1); |
| | | |
| | |
| | | // Test lastExternalChangelogCookie attribute of the ECL |
| | | // (does only refer to non private backend) |
| | | MultiDomainServerState expectedLastCookie = |
| | | new MultiDomainServerState("o=test:"+cn1+";"); |
| | | new MultiDomainServerState("o=test:" + csn1 + ";"); |
| | | |
| | | String lastCookie = readLastCookie(); |
| | | |
| | |
| | | // Produce updates |
| | | long time = TimeThread.getTime(); |
| | | int ts = 1; |
| | | ChangeNumber cn = new ChangeNumber(time, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, cn, tn, 1); |
| | | CSN csn = new CSN(time, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, csn, tn, 1); |
| | | |
| | | cn = new ChangeNumber(time++, ts++, s2test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s2test2, cn, tn, 2); |
| | | csn = new CSN(time++, ts++, s2test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s2test2, csn, tn, 2); |
| | | |
| | | ChangeNumber cn3 = new ChangeNumber(time++, ts++, s2test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s2test2, cn3, tn, 3); |
| | | CSN csn3 = new CSN(time++, ts++, s2test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s2test2, csn3, tn, 3); |
| | | |
| | | cn = new ChangeNumber(time++, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, cn, tn, 4); |
| | | csn = new CSN(time++, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, csn, tn, 4); |
| | | sleep(1500); |
| | | |
| | | // Changes are : |
| | |
| | | cookie = getCookie(searchOp.getSearchEntries(), 1, tn, ldifWriter, cookie); |
| | | |
| | | // Now publishes a new change and search from the previous cookie |
| | | ChangeNumber cn5 = new ChangeNumber(time++, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, cn5, tn, 5); |
| | | CSN csn5 = new CSN(time++, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, csn5, tn, 5); |
| | | sleep(500); |
| | | |
| | | // Changes are : |
| | |
| | | sleep(500); |
| | | |
| | | time = TimeThread.getTime(); |
| | | cn = new ChangeNumber(time++, ts++, s1test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s1test2, cn, tn, 6); |
| | | csn = new CSN(time++, ts++, s1test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s1test2, csn, tn, 6); |
| | | |
| | | cn = new ChangeNumber(time++, ts++, s2test.getServerId()); |
| | | publishDeleteMsgInOTest(s2test, cn, tn, 7); |
| | | csn = new CSN(time++, ts++, s2test.getServerId()); |
| | | publishDeleteMsgInOTest(s2test, csn, tn, 7); |
| | | |
| | | ChangeNumber cn8 = new ChangeNumber(time++, ts++, s1test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s1test2, cn8, tn, 8); |
| | | CSN csn8 = new CSN(time++, ts++, s1test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s1test2, csn8, tn, 8); |
| | | |
| | | ChangeNumber cn9 = new ChangeNumber(time++, ts++, s2test.getServerId()); |
| | | publishDeleteMsgInOTest(s2test, cn9, tn, 9); |
| | | CSN csn9 = new CSN(time++, ts++, s2test.getServerId()); |
| | | publishDeleteMsgInOTest(s2test, csn9, tn, 9); |
| | | sleep(500); |
| | | |
| | | ReplicationServerDomain rsd = replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING); |
| | | ServerState startState = rsd.getStartState(); |
| | | assertEquals(startState.getChangeNumber(s1test.getServerId()).getSeqnum(), 1); |
| | | assertTrue(startState.getChangeNumber(s2test.getServerId()) != null); |
| | | assertEquals(startState.getChangeNumber(s2test.getServerId()).getSeqnum(), 7); |
| | | assertEquals(startState.getCSN(s1test.getServerId()).getSeqnum(), 1); |
| | | assertTrue(startState.getCSN(s2test.getServerId()) != null); |
| | | assertEquals(startState.getCSN(s2test.getServerId()).getSeqnum(), 7); |
| | | |
| | | rsd = replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING2); |
| | | startState = rsd.getStartState(); |
| | | assertEquals(startState.getChangeNumber(s2test2.getServerId()).getSeqnum(), 2); |
| | | assertEquals(startState.getChangeNumber(s1test2.getServerId()).getSeqnum(), 6); |
| | | assertEquals(startState.getCSN(s2test2.getServerId()).getSeqnum(), 2); |
| | | assertEquals(startState.getCSN(s1test2.getServerId()).getSeqnum(), 6); |
| | | |
| | | // Test lastExternalChangelogCookie attribute of the ECL |
| | | MultiDomainServerState expectedLastCookie = |
| | | new MultiDomainServerState("o=test:"+cn5+" "+cn9+";o=test2:"+cn3+" "+cn8+";"); |
| | | new MultiDomainServerState("o=test:" + csn5 + " " + csn9 |
| | | + ";o=test2:" + csn3 + " " + csn8 + ";"); |
| | | |
| | | String lastCookie = readLastCookie(); |
| | | |
| | |
| | | return cookie; |
| | | } |
| | | |
| | | private void publishDeleteMsgInOTest(ReplicationBroker broker, |
| | | ChangeNumber cn, String tn, int i) |
| | | private void publishDeleteMsgInOTest(ReplicationBroker broker, CSN csn, |
| | | String tn, int i) |
| | | { |
| | | publishDeleteMsg(broker, cn, tn, i, TEST_ROOT_DN_STRING); |
| | | publishDeleteMsg(broker, csn, tn, i, TEST_ROOT_DN_STRING); |
| | | } |
| | | |
| | | private void publishDeleteMsgInOTest2(ReplicationBroker broker, |
| | | ChangeNumber cn, String tn, int i) |
| | | private void publishDeleteMsgInOTest2(ReplicationBroker broker, CSN csn, |
| | | String tn, int i) |
| | | { |
| | | publishDeleteMsg(broker, cn, tn, i, TEST_ROOT_DN_STRING2); |
| | | publishDeleteMsg(broker, csn, tn, i, TEST_ROOT_DN_STRING2); |
| | | } |
| | | |
| | | private void publishDeleteMsg(ReplicationBroker broker, ChangeNumber cn, |
| | | String tn, int i, String baseDn) |
| | | private void publishDeleteMsg(ReplicationBroker broker, CSN csn, String tn, |
| | | int i, String baseDn) |
| | | { |
| | | String dn = "uid=" + tn + i + "," + baseDn; |
| | | DeleteMsg delMsg = new DeleteMsg(dn, cn, tn + "uuid" + i); |
| | | DeleteMsg delMsg = new DeleteMsg(dn, csn, tn + "uuid" + i); |
| | | broker.publish(delMsg); |
| | | debugInfo(tn, " publishes " + delMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + delMsg.getCSN()); |
| | | } |
| | | |
| | | private InternalSearchOperation searchOnCookieChangelog(String filterString, |
| | |
| | | DN.decode(TEST_ROOT_DN_STRING), SERVER_ID_1, |
| | | 100, replicationServerPort, brokerSessionTimeout, true); |
| | | |
| | | final ChangeNumber[] cns = generateChangeNumbers(4, SERVER_ID_1); |
| | | publishDeleteMsgInOTest(server01, cns[0], tn, 1); |
| | | final CSN[] csns = generateCSNs(4, SERVER_ID_1); |
| | | publishDeleteMsgInOTest(server01, csns[0], tn, 1); |
| | | |
| | | Thread.sleep(1000); |
| | | |
| | |
| | | String cookieNotEmpty = readLastCookie(); |
| | | debugInfo(tn, "Store cookie not empty=\"" + cookieNotEmpty + "\""); |
| | | |
| | | publishDeleteMsgInOTest(server01, cns[1], tn, 2); |
| | | publishDeleteMsgInOTest(server01, cns[2], tn, 3); |
| | | publishDeleteMsgInOTest(server01, csns[1], tn, 2); |
| | | publishDeleteMsgInOTest(server01, csns[2], tn, 3); |
| | | |
| | | // Sleep longer than this delay - the changelog will be trimmed |
| | | Thread.sleep(1000); |
| | |
| | | // 5. Assert that a request with an "old" cookie - one that refers to |
| | | // changes that have been removed by the replication changelog trimming |
| | | // returns the appropriate error. |
| | | publishDeleteMsgInOTest(server01, cns[3], tn, 1); |
| | | publishDeleteMsgInOTest(server01, csns[3], tn, 1); |
| | | |
| | | debugInfo(tn, "d1 trimdate" + d1.getStartState()); |
| | | debugInfo(tn, "d2 trimdate" + d2.getStartState()); |
| | |
| | | String user1entryUUID = "11111111-1111-1111-1111-111111111111"; |
| | | String baseUUID = "22222222-2222-2222-2222-222222222222"; |
| | | |
| | | ChangeNumber[] cns = generateChangeNumbers(4, SERVER_ID_1); |
| | | CSN[] csns = generateCSNs(4, SERVER_ID_1); |
| | | |
| | | // Publish DEL |
| | | int cnCounter = 0; |
| | | publishDeleteMsgInOTest(server01, cns[cnCounter], tn, cnCounter + 1); |
| | | int csnCounter = 0; |
| | | publishDeleteMsgInOTest(server01, csns[csnCounter], tn, csnCounter + 1); |
| | | |
| | | // Publish ADD |
| | | cnCounter++; |
| | | csnCounter++; |
| | | String lentry = "dn: uid="+tn+"2," + TEST_ROOT_DN_STRING + "\n" |
| | | + "objectClass: top\n" + "objectClass: domain\n" |
| | | + "entryUUID: "+user1entryUUID+"\n"; |
| | | Entry entry = TestCaseUtils.entryFromLdifString(lentry); |
| | | AddMsg addMsg = new AddMsg( |
| | | cns[cnCounter], |
| | | csns[csnCounter], |
| | | "uid="+tn+"2," + TEST_ROOT_DN_STRING, |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | entry.getAttributes(), |
| | | new ArrayList<Attribute>()); |
| | | server01.publish(addMsg); |
| | | debugInfo(tn, " publishes " + addMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + addMsg.getCSN()); |
| | | |
| | | // Publish MOD |
| | | cnCounter++; |
| | | csnCounter++; |
| | | DN baseDN = DN.decode("uid=" + tn + "3," + TEST_ROOT_DN_STRING); |
| | | List<Modification> mods = createMods("description", "new value"); |
| | | ModifyMsg modMsg = new ModifyMsg(cns[cnCounter], baseDN, mods, tn + "uuid3"); |
| | | ModifyMsg modMsg = new ModifyMsg(csns[csnCounter], baseDN, mods, tn + "uuid3"); |
| | | server01.publish(modMsg); |
| | | debugInfo(tn, " publishes " + modMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + modMsg.getCSN()); |
| | | |
| | | // Publish modDN |
| | | cnCounter++; |
| | | csnCounter++; |
| | | final DN newSuperior = DN.decode(TEST_ROOT_DN_STRING2); |
| | | ModifyDNOperation op = new ModifyDNOperationBasis(connection, 1, 1, null, |
| | | DN.decode("uid="+tn+"4," + TEST_ROOT_DN_STRING), // entryDN |
| | | RDN.decode("uid="+tn+"new4"), // new rdn |
| | | true, // deleteoldrdn |
| | | newSuperior); |
| | | op.setAttachment(SYNCHROCONTEXT, new ModifyDnContext(cns[cnCounter], |
| | | op.setAttachment(SYNCHROCONTEXT, new ModifyDnContext(csns[csnCounter], |
| | | tn + "uuid4", "newparentId")); |
| | | LocalBackendModifyDNOperation localOp = new LocalBackendModifyDNOperation(op); |
| | | ModifyDNMsg modDNMsg = new ModifyDNMsg(localOp); |
| | | server01.publish(modDNMsg); |
| | | debugInfo(tn, " publishes " + modDNMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + modDNMsg.getCSN()); |
| | | sleep(1000); |
| | | |
| | | String cookie= ""; |
| | |
| | | final String[] cookies = new String[4]; |
| | | for (int j = 0; j < cookies.length; j++) |
| | | { |
| | | cookies[j] = "o=test:" + cns[j] + ";"; |
| | | cookies[j] = "o=test:" + csns[j] + ";"; |
| | | } |
| | | |
| | | assertEquals(searchOp.getSearchEntries().size(), 4); |
| | |
| | | for (SearchResultEntry resultEntry : entries) |
| | | { |
| | | i++; |
| | | checkDn(cns[i - 1], resultEntry); |
| | | checkDn(csns[i - 1], resultEntry); |
| | | checkValue(resultEntry, "targetdn", "uid=" + tn + i + "," + TEST_ROOT_DN_STRING); |
| | | checkValue(resultEntry, "replicationcsn", cns[i - 1].toString()); |
| | | checkValue(resultEntry, "replicationcsn", csns[i - 1].toString()); |
| | | checkValue(resultEntry, "replicaidentifier", String.valueOf(SERVER_ID_1)); |
| | | checkValue(resultEntry, "changelogcookie", cookies[i - 1]); |
| | | checkValue(resultEntry, "changenumber", "0"); |
| | |
| | | debugInfo(tn, "Ending test with success"); |
| | | } |
| | | |
| | | private ChangeNumber[] generateChangeNumbers(int nb, int serverId) |
| | | private CSN[] generateCSNs(int nb, int serverId) |
| | | { |
| | | long startTime = TimeThread.getTime(); |
| | | |
| | | ChangeNumber[] cns = new ChangeNumber[nb]; |
| | | CSN[] csns = new CSN[nb]; |
| | | for (int i = 0; i < nb; i++) |
| | | { |
| | | // seqNum must be greater than 0, so start at 1 |
| | | cns[i] = new ChangeNumber(startTime + i, i + 1, serverId); |
| | | csns[i] = new CSN(startTime + i, i + 1, serverId); |
| | | } |
| | | return cns; |
| | | return csns; |
| | | } |
| | | |
| | | private void checkDn(ChangeNumber cn, SearchResultEntry resultEntry) |
| | | private void checkDn(CSN csn, SearchResultEntry resultEntry) |
| | | { |
| | | String actualDN = resultEntry.getDN().toNormalizedString(); |
| | | String expectedDN = |
| | | "replicationcsn=" + cn + "," + TEST_ROOT_DN_STRING + ",cn=changelog"; |
| | | "replicationcsn=" + csn + "," + TEST_ROOT_DN_STRING + ",cn=changelog"; |
| | | assertThat(actualDN).isEqualToIgnoringCase(expectedDN); |
| | | } |
| | | |
| | |
| | | DN.decode(TEST_ROOT_DN_STRING), SERVER_ID_1, |
| | | 100, replicationServerPort, brokerSessionTimeout, true); |
| | | |
| | | ChangeNumber[] cns = generateChangeNumbers(2, SERVER_ID_1); |
| | | CSN[] csns = generateCSNs(2, SERVER_ID_1); |
| | | |
| | | // Produce update on this suffix |
| | | DeleteMsg delMsg = |
| | | new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, cns[0], |
| | | new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, csns[0], |
| | | "11111111-1112-1113-1114-111111111114"); |
| | | debugInfo(tn, " publishing " + delMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishing " + delMsg.getCSN()); |
| | | server01.publish(delMsg); |
| | | sleep(500); // let's be sure the message is in the RS |
| | | |
| | |
| | | } |
| | | |
| | | // Produces change 2 |
| | | final ChangeNumber cn = cns[1]; |
| | | final CSN csn = csns[1]; |
| | | String expectedDn = "uid=" + tn + "2," + TEST_ROOT_DN_STRING; |
| | | delMsg = new DeleteMsg(expectedDn, cn, |
| | | delMsg = new DeleteMsg(expectedDn, csn, |
| | | "11111111-1112-1113-1114-111111111115"); |
| | | debugInfo(tn, " publishing " + delMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishing " + delMsg.getCSN()); |
| | | server01.publish(delMsg); |
| | | this.gblCN = cn; |
| | | this.gblCSN = csn; |
| | | sleep(1000); |
| | | |
| | | debugInfo(tn, delMsg.getChangeNumber() + |
| | | debugInfo(tn, delMsg.getCSN() + |
| | | " published , psearch will now wait for new entries"); |
| | | |
| | | // wait for the 1 new entry |
| | |
| | | server02.setChangeTimeHeartbeatInterval(100); //ms |
| | | |
| | | // Produce update 1 |
| | | ChangeNumber cn1 = |
| | | new ChangeNumber(TimeThread.getTime(), ts++, SERVER_ID_1); |
| | | CSN csn1 = |
| | | new CSN(TimeThread.getTime(), ts++, SERVER_ID_1); |
| | | DeleteMsg delMsg1 = |
| | | new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, cn1, |
| | | new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, csn1, |
| | | "11111111-1111-1111-1111-111111111111"); |
| | | debugInfo(tn, " publishing " + delMsg1); |
| | | server01.publish(delMsg1); |
| | | sleep(500); // let's be sure the message is in the RS |
| | | |
| | | // Produce update 2 |
| | | ChangeNumber cn2 = |
| | | new ChangeNumber(TimeThread.getTime(), ts++, SERVER_ID_2); |
| | | CSN csn2 = |
| | | new CSN(TimeThread.getTime(), ts++, SERVER_ID_2); |
| | | DeleteMsg delMsg2 = |
| | | new DeleteMsg("uid=" + tn + "2," + TEST_ROOT_DN_STRING2, cn2, |
| | | new DeleteMsg("uid=" + tn + "2," + TEST_ROOT_DN_STRING2, csn2, |
| | | "22222222-2222-2222-2222-222222222222"); |
| | | debugInfo(tn, " publishing " + delMsg2); |
| | | server02.publish(delMsg2); |
| | | sleep(500); // let's be sure the message is in the RS |
| | | |
| | | // Produce update 3 |
| | | ChangeNumber cn3 = |
| | | new ChangeNumber(TimeThread.getTime(), ts++, SERVER_ID_2); |
| | | CSN csn3 = |
| | | new CSN(TimeThread.getTime(), ts++, SERVER_ID_2); |
| | | DeleteMsg delMsg3 = |
| | | new DeleteMsg("uid=" + tn + "3," + TEST_ROOT_DN_STRING2, cn3, |
| | | new DeleteMsg("uid=" + tn + "3," + TEST_ROOT_DN_STRING2, csn3, |
| | | "33333333-3333-3333-3333-333333333333"); |
| | | debugInfo(tn, " publishing " + delMsg3); |
| | | server02.publish(delMsg3); |
| | |
| | | |
| | | InvocationCounterPlugin.resetAllCounters(); |
| | | |
| | | long searchRequests = ldapStatistics.getSearchRequests(); |
| | | ldapStatistics.getSearchRequests(); |
| | | long searchEntries = ldapStatistics.getSearchResultEntries(); |
| | | long searchReferences = ldapStatistics.getSearchResultReferences(); |
| | | ldapStatistics.getSearchResultReferences(); |
| | | long searchesDone = ldapStatistics.getSearchResultsDone(); |
| | | |
| | | LDAPMessage message; |
| | |
| | | if (!changesOnly) |
| | | { |
| | | debugInfo(tn, "Search1 Persistent filter=" + searchRequest1.getFilter() |
| | | + " expected to return change " + cn1); |
| | | + " expected to return change " + csn1); |
| | | searchEntries = 0; |
| | | message = null; |
| | | |
| | |
| | | searchEntries++; |
| | | if (searchEntries==1) |
| | | { |
| | | checkValue(searchResultEntry.toSearchResultEntry(),"replicationcsn",cn1.toString()); |
| | | checkValue(searchResultEntry.toSearchResultEntry(),"replicationcsn",csn1.toString()); |
| | | checkValue(searchResultEntry.toSearchResultEntry(),"changenumber", |
| | | (compatMode?"10":"0")); |
| | | } |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE: |
| | | searchReferences++; |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE: |
| | |
| | | message = null; |
| | | { |
| | | debugInfo(tn, "Search 2 Persistent filter=" + searchRequest2.getFilter() |
| | | + " expected to return change " + cn2 + " & " + cn3); |
| | | + " expected to return change " + csn2 + " & " + csn3); |
| | | while (searchEntries < 2 && (message = r2.readMessage()) != null) |
| | | { |
| | | debugInfo(tn, "Search 2 Result=" + |
| | |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE: |
| | | searchReferences++; |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE: |
| | |
| | | message = null; |
| | | { |
| | | debugInfo(tn, "Search3 Persistent filter=" + searchRequest3.getFilter() |
| | | + " expected to return change top + " + cn1 + " & " + cn2 + " & " + cn3); |
| | | + " expected to return change top + " + csn1 + " & " + csn2 + " & " + csn3); |
| | | while (searchEntries < 4 && (message = r3.readMessage()) != null) |
| | | { |
| | | debugInfo(tn, "Search3 Result=" + |
| | |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE: |
| | | searchReferences++; |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE: |
| | |
| | | } |
| | | |
| | | // Produces additional change |
| | | ChangeNumber cn11 = new ChangeNumber(TimeThread.getTime(), 11, SERVER_ID_1); |
| | | CSN csn11 = new CSN(TimeThread.getTime(), 11, SERVER_ID_1); |
| | | String expectedDn11 = "uid=" + tn + "11," + TEST_ROOT_DN_STRING; |
| | | DeleteMsg delMsg11 = new DeleteMsg(expectedDn11, cn11, |
| | | DeleteMsg delMsg11 = new DeleteMsg(expectedDn11, csn11, |
| | | "44444444-4444-4444-4444-444444444444"); |
| | | debugInfo(tn, " publishing " + delMsg11); |
| | | server01.publish(delMsg11); |
| | | sleep(500); |
| | | debugInfo(tn, delMsg11.getChangeNumber() + " published additionally "); |
| | | debugInfo(tn, delMsg11.getCSN() + " published additionally "); |
| | | |
| | | // Produces additional change |
| | | ChangeNumber cn12 = new ChangeNumber(TimeThread.getTime(), 12, SERVER_ID_2); |
| | | CSN csn12 = new CSN(TimeThread.getTime(), 12, SERVER_ID_2); |
| | | String expectedDn12 = "uid=" + tn + "12," + TEST_ROOT_DN_STRING2; |
| | | DeleteMsg delMsg12 = new DeleteMsg(expectedDn12, cn12, |
| | | DeleteMsg delMsg12 = new DeleteMsg(expectedDn12, csn12, |
| | | "55555555-5555-5555-5555-555555555555"); |
| | | debugInfo(tn, " publishing " + delMsg12 ); |
| | | server02.publish(delMsg12); |
| | | sleep(500); |
| | | debugInfo(tn, delMsg12.getChangeNumber() + " published additionally "); |
| | | debugInfo(tn, delMsg12.getCSN() + " published additionally "); |
| | | |
| | | // Produces additional change |
| | | ChangeNumber cn13 = new ChangeNumber(TimeThread.getTime(), 13, SERVER_ID_2); |
| | | CSN csn13 = new CSN(TimeThread.getTime(), 13, SERVER_ID_2); |
| | | String expectedDn13 = "uid=" + tn + "13," + TEST_ROOT_DN_STRING2; |
| | | DeleteMsg delMsg13 = new DeleteMsg(expectedDn13, cn13, |
| | | DeleteMsg delMsg13 = new DeleteMsg(expectedDn13, csn13, |
| | | "66666666-6666-6666-6666-666666666666"); |
| | | debugInfo(tn, " publishing " + delMsg13); |
| | | server02.publish(delMsg13); |
| | | sleep(500); |
| | | debugInfo(tn, delMsg13.getChangeNumber() + " published additionally "); |
| | | debugInfo(tn, delMsg13.getCSN() + " published additionally "); |
| | | |
| | | // wait 11 |
| | | searchEntries = 0; |
| | |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE: |
| | | searchReferences++; |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE: |
| | |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE: |
| | | searchReferences++; |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE: |
| | |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_REFERENCE: |
| | | searchReferences++; |
| | | break; |
| | | |
| | | case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE: |
| | |
| | | // Produce updates |
| | | long time = TimeThread.getTime(); |
| | | int ts = 1; |
| | | ChangeNumber cn = new ChangeNumber(time, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, cn, tn, 1); |
| | | CSN csn = new CSN(time, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, csn, tn, 1); |
| | | |
| | | cn = new ChangeNumber(time++, ts++, s2test2.getServerId()); |
| | | publishDeleteMsgInOTest(s2test2, cn, tn, 2); |
| | | csn = new CSN(time++, ts++, s2test2.getServerId()); |
| | | publishDeleteMsgInOTest(s2test2, csn, tn, 2); |
| | | |
| | | ChangeNumber cn3 = new ChangeNumber(time++, ts++, s2test2.getServerId()); |
| | | publishDeleteMsgInOTest(s2test2, cn3, tn, 3); |
| | | CSN csn3 = new CSN(time++, ts++, s2test2.getServerId()); |
| | | publishDeleteMsgInOTest(s2test2, csn3, tn, 3); |
| | | |
| | | cn = new ChangeNumber(time++, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, cn, tn, 4); |
| | | csn = new CSN(time++, ts++, s1test.getServerId()); |
| | | publishDeleteMsgInOTest(s1test, csn, tn, 4); |
| | | sleep(500); |
| | | |
| | | // -- |
| | |
| | | |
| | | // Test startState ("first cookie") of the ECL |
| | | time = TimeThread.getTime(); |
| | | cn = new ChangeNumber(time++, ts++, s1test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s1test2, cn, tn, 6); |
| | | csn = new CSN(time++, ts++, s1test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s1test2, csn, tn, 6); |
| | | |
| | | cn = new ChangeNumber(time++, ts++, s2test.getServerId()); |
| | | publishDeleteMsgInOTest(s2test, cn, tn, 7); |
| | | csn = new CSN(time++, ts++, s2test.getServerId()); |
| | | publishDeleteMsgInOTest(s2test, csn, tn, 7); |
| | | |
| | | ChangeNumber cn8 = new ChangeNumber(time++, ts++, s1test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s1test2, cn8, tn, 8); |
| | | CSN csn8 = new CSN(time++, ts++, s1test2.getServerId()); |
| | | publishDeleteMsgInOTest2(s1test2, csn8, tn, 8); |
| | | |
| | | ChangeNumber cn9 = new ChangeNumber(time++, ts++, s2test.getServerId()); |
| | | publishDeleteMsgInOTest(s2test, cn9, tn, 9); |
| | | CSN csn9 = new CSN(time++, ts++, s2test.getServerId()); |
| | | publishDeleteMsgInOTest(s2test, csn9, tn, 9); |
| | | sleep(500); |
| | | |
| | | ReplicationServerDomain rsd1 = replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING); |
| | |
| | | debugInfo(tn, rsd1.getBaseDn() |
| | | + " DbServerState=" + rsd1.getDbServerState() |
| | | + " ChangeTimeHeartBeatState=" + rsd1.getChangeTimeHeartbeatState() |
| | | + " eligibleCN=" + rsd1.getEligibleCN() |
| | | + " rs eligibleCN=" + replicationServer.getEligibleCN()); |
| | | + " eligibleCSN=" + rsd1.getEligibleCSN() |
| | | + " rs eligibleCSN=" + replicationServer.getEligibleCSN()); |
| | | // FIXME:ECL Enable this test by adding an assert on the right value |
| | | |
| | | ReplicationServerDomain rsd2 = replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING2); |
| | |
| | | debugInfo(tn, rsd2.getBaseDn() |
| | | + " DbServerState=" + rsd2.getDbServerState() |
| | | + " ChangeTimeHeartBeatState=" + rsd2.getChangeTimeHeartbeatState() |
| | | + " eligibleCN=" + rsd2.getEligibleCN() |
| | | + " rs eligibleCN=" + replicationServer.getEligibleCN()); |
| | | + " eligibleCSN=" + rsd2.getEligibleCSN() |
| | | + " rs eligibleCSN=" + replicationServer.getEligibleCSN()); |
| | | // FIXME:ECL Enable this test by adding an assert on the right value |
| | | } |
| | | finally |
| | |
| | | String user1entryUUID = "11111111-1112-1113-1114-111111111115"; |
| | | String baseUUID = "22222222-2222-2222-2222-222222222222"; |
| | | |
| | | ChangeNumber[] cns = generateChangeNumbers(nbChanges, SERVER_ID_1); |
| | | gblCN = cns[1]; |
| | | CSN[] csns = generateCSNs(nbChanges, SERVER_ID_1); |
| | | gblCSN = csns[1]; |
| | | |
| | | // Publish DEL |
| | | DeleteMsg delMsg = |
| | | new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, cns[0], |
| | | new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, csns[0], |
| | | user1entryUUID); |
| | | server01.publish(delMsg); |
| | | debugInfo(tn, " publishes " + delMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + delMsg.getCSN()); |
| | | |
| | | // Publish ADD |
| | | String lentry = |
| | |
| | | + "entryUUID: "+user1entryUUID+"\n"; |
| | | Entry entry = TestCaseUtils.entryFromLdifString(lentry); |
| | | AddMsg addMsg = new AddMsg( |
| | | gblCN, |
| | | gblCSN, |
| | | "uid="+tn+"2," + TEST_ROOT_DN_STRING, |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | entry.getAttributes(), |
| | | new ArrayList<Attribute>()); |
| | | server01.publish(addMsg); |
| | | debugInfo(tn, " publishes " + addMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + addMsg.getCSN()); |
| | | |
| | | // Publish MOD |
| | | DN baseDN = DN.decode("uid="+tn+"3," + TEST_ROOT_DN_STRING); |
| | | List<Modification> mods = createMods("description", "new value"); |
| | | ModifyMsg modMsg = new ModifyMsg(cns[2], baseDN, mods, user1entryUUID); |
| | | ModifyMsg modMsg = new ModifyMsg(csns[2], baseDN, mods, user1entryUUID); |
| | | server01.publish(modMsg); |
| | | debugInfo(tn, " publishes " + modMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + modMsg.getCSN()); |
| | | |
| | | // Publish modDN |
| | | ModifyDNOperation op = new ModifyDNOperationBasis(connection, 1, 1, null, |
| | |
| | | RDN.decode("uid="+tn+"new4"), // new rdn |
| | | true, // deleteoldrdn |
| | | DN.decode(TEST_ROOT_DN_STRING2)); // new superior |
| | | op.setAttachment(SYNCHROCONTEXT, new ModifyDnContext(cns[3], user1entryUUID, "newparentId")); |
| | | op.setAttachment(SYNCHROCONTEXT, new ModifyDnContext(csns[3], user1entryUUID, "newparentId")); |
| | | LocalBackendModifyDNOperation localOp = new LocalBackendModifyDNOperation(op); |
| | | ModifyDNMsg modDNMsg = new ModifyDNMsg(localOp); |
| | | server01.publish(modDNMsg); |
| | | debugInfo(tn, " publishes " + modDNMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + modDNMsg.getCSN()); |
| | | sleep(1000); |
| | | |
| | | String filter = "(targetdn=*"+tn.toLowerCase()+"*,o=test)"; |
| | |
| | | |
| | | // test 4 entries returned |
| | | assertEntries(searchOp.getSearchEntries(), firstDraftChangeNumber, tn, |
| | | ldifWriter, user1entryUUID, cns[0], gblCN, cns[2], cns[3]); |
| | | ldifWriter, user1entryUUID, csns[0], gblCSN, csns[2], csns[3]); |
| | | |
| | | stop(server01); |
| | | |
| | |
| | | searchOp = searchOnChangelog(filter, tn, SUCCESS); |
| | | |
| | | assertEntries(searchOp.getSearchEntries(), firstDraftChangeNumber, tn, |
| | | ldifWriter, user1entryUUID, cns[0], gblCN, cns[2], cns[3]); |
| | | ldifWriter, user1entryUUID, csns[0], gblCSN, csns[2], csns[3]); |
| | | assertEquals(searchOp.getSearchEntries().size(), nbChanges); |
| | | } |
| | | debugInfo(tn, "Ending test with success"); |
| | |
| | | |
| | | private void assertEntries(List<SearchResultEntry> entries, |
| | | int firstDraftChangeNumber, String tn, LDIFWriter ldifWriter, |
| | | String user1entryUUID, ChangeNumber... cns) throws Exception |
| | | String user1entryUUID, CSN... csns) throws Exception |
| | | { |
| | | debugAndWriteEntries(ldifWriter, entries, tn); |
| | | assertEquals(entries.size(), 4); |
| | |
| | | checkValue(resultEntry, "changenumber", String.valueOf(firstDraftChangeNumber + i - 1)); |
| | | checkValue(resultEntry, "targetentryuuid", user1entryUUID); |
| | | checkValue(resultEntry, "replicaidentifier", String.valueOf(SERVER_ID_1)); |
| | | final ChangeNumber cn = cns[i - 1]; |
| | | checkValue(resultEntry, "replicationcsn", cn.toString()); |
| | | checkValue(resultEntry, "changelogcookie", "o=test:" + cn + ";"); |
| | | final CSN csn = csns[i - 1]; |
| | | checkValue(resultEntry, "replicationcsn", csn.toString()); |
| | | checkValue(resultEntry, "changelogcookie", "o=test:" + csn + ";"); |
| | | checkValue(resultEntry, "targetdn", "uid=" + tn + i + "," + TEST_ROOT_DN_STRING); |
| | | |
| | | if (i==1) |
| | |
| | | // check the entry has the right content |
| | | SearchResultEntry resultEntry = entries.get(0); |
| | | assertTrue("changenumber=6,cn=changelog".equalsIgnoreCase(resultEntry.getDN().toNormalizedString())); |
| | | checkValue(resultEntry, "replicationcsn", gblCN.toString()); |
| | | checkValue(resultEntry, "replicationcsn", gblCSN.toString()); |
| | | checkValue(resultEntry, "replicaidentifier", String.valueOf(SERVER_ID_1)); |
| | | checkValue(resultEntry, "changetype", "add"); |
| | | checkValue(resultEntry, "changelogcookie", "o=test:" + gblCN + ";"); |
| | | checkValue(resultEntry, "changelogcookie", "o=test:" + gblCSN + ";"); |
| | | checkValue(resultEntry, "targetentryuuid", user1entryUUID); |
| | | checkValue(resultEntry, "changenumber", "6"); |
| | | |
| | |
| | | |
| | | LDIFWriter ldifWriter = getLDIFWriter(); |
| | | |
| | | String filter = "(replicationcsn=" + this.gblCN + ")"; |
| | | String filter = "(replicationcsn=" + this.gblCSN + ")"; |
| | | InternalSearchOperation searchOp = searchOnChangelog(filter, tn, SUCCESS); |
| | | assertEquals(searchOp.getSearchEntries().size(), 1); |
| | | |
| | |
| | | |
| | | // check the DEL entry has the right content |
| | | SearchResultEntry resultEntry = entries.get(0); |
| | | checkValue(resultEntry, "replicationcsn", gblCN.toString()); |
| | | checkValue(resultEntry, "replicationcsn", gblCSN.toString()); |
| | | // TODO:ECL check values of the other attributes |
| | | |
| | | debugInfo(tn, "Ending test with success"); |
| | |
| | | evaluateSearchParameters(baseDN, 8, 8, "(changenumber=8)"); |
| | | |
| | | // |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator( 1, 0); |
| | | ChangeNumber changeNumber1 = gen.newChangeNumber(); |
| | | CSNGenerator gen = new CSNGenerator( 1, 0); |
| | | CSN changeNumber1 = gen.newCSN(); |
| | | final StartECLSessionMsg startCLmsg = |
| | | evaluateSearchParameters(baseDN, -1, -1, |
| | | "(replicationcsn=" + changeNumber1 + ")"); |
| | | assertEquals(startCLmsg.getChangeNumber(), changeNumber1); |
| | | assertEquals(startCLmsg.getCSN(), changeNumber1); |
| | | |
| | | // Use change number as base object. |
| | | baseDN = DN.decode("changeNumber=8,cn=changelog"); |
| | |
| | | } |
| | | |
| | | private StartECLSessionMsg evaluateSearchParameters(DN baseDN, |
| | | int firstDraftCN, |
| | | int lastDraftCN, String filterString) throws Exception |
| | | int firstDraftCN, int lastDraftCN, String filterString) throws Exception |
| | | { |
| | | final StartECLSessionMsg startCLmsg = new StartECLSessionMsg(); |
| | | ECLSearchOperation.evaluateSearchParameters(startCLmsg, baseDN, |
| | |
| | | String user1entryUUID = "11111111-1112-1113-1114-111111111115"; |
| | | |
| | | // Publish DEL |
| | | ChangeNumber cn1 = new ChangeNumber(TimeThread.getTime(), ts++, SERVER_ID_1); |
| | | CSN csn1 = new CSN(TimeThread.getTime(), ts++, SERVER_ID_1); |
| | | DeleteMsg delMsg = new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, |
| | | cn1, user1entryUUID); |
| | | csn1, user1entryUUID); |
| | | server01.publish(delMsg); |
| | | debugInfo(tn, " publishes " + delMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + delMsg.getCSN()); |
| | | sleep(500); |
| | | |
| | | stop(server01); |
| | |
| | | debugInfo(tn, "Starting test\n\n"); |
| | | String user1entryUUID = "11111111-1112-1113-1114-111111111115"; |
| | | |
| | | final ChangeNumber[] cns = generateChangeNumbers(4, SERVER_ID_1); |
| | | final ChangeNumber cn1 = cns[0]; |
| | | final ChangeNumber cn2 = cns[1]; |
| | | final ChangeNumber cn3 = cns[2]; |
| | | final CSN[] csns = generateCSNs(4, SERVER_ID_1); |
| | | final CSN csn1 = csns[0]; |
| | | final CSN csn2 = csns[1]; |
| | | final CSN csn3 = csns[2]; |
| | | |
| | | ReplicationServerDomain rsdtest = replicationServer.getReplicationServerDomain(TEST_ROOT_DN_STRING); |
| | | // this empty state will force to count from the start of the DB |
| | | final ServerState fromStart = new ServerState(); |
| | | |
| | | // The replication changelog is empty |
| | | assertEquals(rsdtest.getEligibleCount(fromStart, cns[0]), 0); |
| | | assertEquals(rsdtest.getEligibleCount(fromStart, csns[0]), 0); |
| | | |
| | | // Creates broker on o=test |
| | | ReplicationBroker server01 = openReplicationSession( |
| | |
| | | 1000, replicationServerPort, brokerSessionTimeout, true); |
| | | |
| | | // Publish one first message |
| | | DeleteMsg delMsg = new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, cn1, |
| | | DeleteMsg delMsg = new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, csn1, |
| | | user1entryUUID); |
| | | server01.publish(delMsg); |
| | | debugInfo(tn, " publishes " + delMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + delMsg.getCSN()); |
| | | sleep(300); |
| | | |
| | | // From begin to now : 1 change |
| | | assertEquals(rsdtest.getEligibleCount(fromStart, now()), 1); |
| | | |
| | | // Publish one second message |
| | | delMsg = new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, cn2, |
| | | user1entryUUID); |
| | | delMsg = new DeleteMsg("uid=" + tn + "1," + TEST_ROOT_DN_STRING, csn2, user1entryUUID); |
| | | server01.publish(delMsg); |
| | | debugInfo(tn, " publishes " + delMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + delMsg.getCSN()); |
| | | sleep(300); |
| | | |
| | | // From begin to now : 2 changes |
| | | assertEquals(rsdtest.getEligibleCount(fromStart, now()), 2); |
| | | |
| | | // From begin to first change (inclusive) : 1 change = cn1 |
| | | assertEquals(rsdtest.getEligibleCount(fromStart, cn1), 1); |
| | | // From begin to first change (inclusive) : 1 change = csn1 |
| | | assertEquals(rsdtest.getEligibleCount(fromStart, csn1), 1); |
| | | |
| | | final ServerState fromStateBeforeCN1 = new ServerState(); |
| | | fromStateBeforeCN1.update(cn1); |
| | | final ServerState fromStateBeforeCSN1 = new ServerState(); |
| | | fromStateBeforeCSN1.update(csn1); |
| | | |
| | | // From state/cn1(exclusive) to cn1 (inclusive) : 0 change |
| | | assertEquals(rsdtest.getEligibleCount(fromStateBeforeCN1, cn1), 0); |
| | | // From state/csn1(exclusive) to csn1 (inclusive) : 0 change |
| | | assertEquals(rsdtest.getEligibleCount(fromStateBeforeCSN1, csn1), 0); |
| | | |
| | | // From state/cn1(exclusive) to cn2 (inclusive) : 1 change = cn2 |
| | | assertEquals(rsdtest.getEligibleCount(fromStateBeforeCN1, cn2), 1); |
| | | // From state/csn1(exclusive) to csn2 (inclusive) : 1 change = csn2 |
| | | assertEquals(rsdtest.getEligibleCount(fromStateBeforeCSN1, csn2), 1); |
| | | |
| | | final ServerState fromStateBeforeCN2 = new ServerState(); |
| | | fromStateBeforeCN2.update(cn2); |
| | | final ServerState fromStateBeforeCSN2 = new ServerState(); |
| | | fromStateBeforeCSN2.update(csn2); |
| | | |
| | | // From state/cn2(exclusive) to now (inclusive) : 0 change |
| | | assertEquals(rsdtest.getEligibleCount(fromStateBeforeCN2, now()), 0); |
| | | // From state/csn2(exclusive) to now (inclusive) : 0 change |
| | | assertEquals(rsdtest.getEligibleCount(fromStateBeforeCSN2, now()), 0); |
| | | |
| | | // Publish one third message |
| | | delMsg = new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, cn3, |
| | | user1entryUUID); |
| | | delMsg = new DeleteMsg("uid="+tn+"1," + TEST_ROOT_DN_STRING, csn3, user1entryUUID); |
| | | server01.publish(delMsg); |
| | | debugInfo(tn, " publishes " + delMsg.getChangeNumber()); |
| | | debugInfo(tn, " publishes " + delMsg.getCSN()); |
| | | sleep(300); |
| | | |
| | | fromStateBeforeCN2.update(cn2); |
| | | fromStateBeforeCSN2.update(csn2); |
| | | |
| | | // From state/cn2(exclusive) to now : 1 change = cn3 |
| | | assertEquals(rsdtest.getEligibleCount(fromStateBeforeCN2, now()), 1); |
| | | // From state/csn2(exclusive) to now : 1 change = csn3 |
| | | assertEquals(rsdtest.getEligibleCount(fromStateBeforeCSN2, now()), 1); |
| | | |
| | | boolean perfs=false; |
| | | if (perfs) |
| | |
| | | debugInfo(tn, "Perf test in compat mode - will generate " + maxMsg + " msgs."); |
| | | for (int i=4; i<=maxMsg; i++) |
| | | { |
| | | ChangeNumber cnx = new ChangeNumber(TimeThread.getTime(), i, SERVER_ID_1); |
| | | delMsg = new DeleteMsg("uid="+tn+i+"," + TEST_ROOT_DN_STRING, cnx, |
| | | user1entryUUID); |
| | | CSN csnx = new CSN(TimeThread.getTime(), i, SERVER_ID_1); |
| | | delMsg = new DeleteMsg("uid="+tn+i+"," + TEST_ROOT_DN_STRING, csnx, user1entryUUID); |
| | | server01.publish(delMsg); |
| | | } |
| | | sleep(1000); |
| | |
| | | rs.disableEligibility(excludedDomains); |
| | | long t1 = TimeThread.getTime(); |
| | | int[] limitss = replicationServer.getECLDraftCNLimits( |
| | | replicationServer.getEligibleCN(), excludedDomains); |
| | | replicationServer.getEligibleCSN(), excludedDomains); |
| | | assertEquals(limitss[1], maxMsg); |
| | | long t2 = TimeThread.getTime(); |
| | | debugInfo(tn, "Perfs - " + maxMsg + " counted in (ms):" + (t2 - t1)); |
| | |
| | | debugInfo(tn, "Ending test with success"); |
| | | } |
| | | |
| | | private ChangeNumber now() |
| | | private CSN now() |
| | | { |
| | | return new ChangeNumber(TimeThread.getTime(), 1, SERVER_ID_1); |
| | | return new CSN(TimeThread.getTime(), 1, SERVER_ID_1); |
| | | } |
| | | |
| | | /** |
| | |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.common.ServerStatus; |
| | | import org.opends.server.replication.plugin.LDAPReplicationDomain; |
| | | import org.opends.server.replication.protocol.*; |
| | |
| | | String user1dn; |
| | | |
| | | /* |
| | | * Create a Change number generator to generate new changenumbers |
| | | * when we need to send operation messages to the replicationServer. |
| | | * Create a CSN generator to generate new CSNs when we need to send |
| | | * operation messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(2, 0); |
| | | CSNGenerator gen = new CSNGenerator(2, 0); |
| | | |
| | | user1entryUUID = "33333333-3333-3333-3333-333333333333"; |
| | | user1dn = "uid=user1,ou=People," + baseDnStr; |
| | |
| | | } |
| | | |
| | | // Create and publish an update message to add an entry. |
| | | return new AddMsg(gen.newChangeNumber(), |
| | | return new AddMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN().toString(), |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | |
| | | /* expected */ |
| | | AddMsg rcvmsg = (AddMsg)msg; |
| | | assertEquals(rcvmsg.getChangeNumber(), emsg.getChangeNumber()); |
| | | assertEquals(rcvmsg.getCSN(), emsg.getCSN()); |
| | | } |
| | | catch(SocketTimeoutException e) |
| | | { |
| | |
| | | */ |
| | | package org.opends.server.replication; |
| | | |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.util.ArrayList; |
| | |
| | | import org.opends.server.core.ModifyOperation; |
| | | import org.opends.server.core.ModifyOperationBasis; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.plugin.EntryHistorical; |
| | | import org.opends.server.replication.protocol.ModifyMsg; |
| | | import org.opends.server.replication.protocol.ReplicationMsg; |
| | |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test for the schema replication. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class SchemaReplicationTest extends ReplicationTestCase |
| | | { |
| | | |
| | | private ArrayList<Modification> rcvdMods = null; |
| | | private List<Modification> rcvdMods; |
| | | |
| | | private int replServerPort; |
| | | |
| | |
| | | |
| | | try |
| | | { |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator( 2, 0); |
| | | CSNGenerator gen = new CSNGenerator( 2, 0); |
| | | |
| | | ModifyMsg modMsg = new ModifyMsg(gen.newChangeNumber(), baseDn, rcvdMods, |
| | | ModifyMsg modMsg = new ModifyMsg(gen.newCSN(), baseDn, rcvdMods, |
| | | EntryHistorical.getEntryUUID(DirectoryServer.getEntry(baseDn))); |
| | | broker.publish(modMsg); |
| | | |
| | |
| | | "The received mod does not contain the original change"); |
| | | |
| | | // check that the schema files were updated with the new ServerState. |
| | | // by checking that the ChangeNUmber of msg we just received has been |
| | | // by checking that the CSN of msg we just received has been |
| | | // added to the user schema file. |
| | | |
| | | // build the string to find in the schema file |
| | | String stateStr = modMsg.getChangeNumber().toString(); |
| | | String stateStr = modMsg.getCSN().toString(); |
| | | |
| | | // open the schema file |
| | | String buildRoot = System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT); |
| | |
| | | if (fileStr.indexOf(stateStr) != -1) |
| | | { |
| | | break; |
| | | } else |
| | | { |
| | | if (count++ > 50) |
| | | { |
| | | fail("The Schema persistentState (changenumber:" + stateStr + |
| | | ") has not been saved to " + path + " : " + fileStr); |
| | | } else |
| | | TestCaseUtils.sleep(100); |
| | | } |
| | | if (count++ > 50) |
| | | { |
| | | fail("The Schema persistentState (CSN:" + stateStr |
| | | + ") has not been saved to " + path + " : " + fileStr); |
| | | } |
| | | TestCaseUtils.sleep(100); |
| | | } |
| | | } finally |
| | | { |
| | |
| | | */ |
| | | package org.opends.server.replication; |
| | | |
| | | import static org.opends.server.TestCaseUtils.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.ldap.LDAPAttribute; |
| | | import org.opends.server.protocols.ldap.LDAPModification; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.plugin.LDAPReplicationDomain; |
| | | import org.opends.server.replication.protocol.*; |
| | | import org.opends.server.replication.service.ReplicationBroker; |
| | |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.opends.server.TestCaseUtils.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test synchronization of update operations on the directory server and through |
| | | * the replication server broker interface. |
| | |
| | | * Add an entry in the database |
| | | * |
| | | */ |
| | | private ChangeNumber addEntry(Entry entry) throws Exception |
| | | private CSN addEntry(Entry entry) throws Exception |
| | | { |
| | | AddOperationBasis addOp = new AddOperationBasis(connection, |
| | | InternalClientConnection.nextOperationID(), InternalClientConnection |
| | |
| | | |
| | | assertEquals(addOp.getResultCode(), ResultCode.SUCCESS); |
| | | assertNotNull(getEntry(entry.getDN(), 1000, true)); |
| | | return OperationContext.getChangeNumber((Operation) addOp); |
| | | return OperationContext.getCSN((Operation) addOp); |
| | | } |
| | | |
| | | /** |
| | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new changenumbers |
| | | * when we need to send operation messages to the replicationServer. |
| | | * Create a CSN generator to generate new CSNs when we need to send |
| | | * operation messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(2, 0); |
| | | CSNGenerator gen = new CSNGenerator(2, 0); |
| | | |
| | | |
| | | // Disable the directory server receive status. |
| | |
| | | |
| | | |
| | | // Create and publish an update message to add an entry. |
| | | AddMsg addMsg = new AddMsg(gen.newChangeNumber(), |
| | | AddMsg addMsg = new AddMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN().toString(), |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | setReceiveStatus(synchroServerEntry.getDN().toString(), true); |
| | | |
| | | // Create and publish another update message to add an entry. |
| | | addMsg = new AddMsg(gen.newChangeNumber(), |
| | | addMsg = new AddMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN().toString(), |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | // Delete the entries to clean the database. |
| | | DeleteMsg delMsg = |
| | | new DeleteMsg(personWithUUIDEntry.getDN().toString(), |
| | | gen.newChangeNumber(), user1entryUUID); |
| | | gen.newCSN(), user1entryUUID); |
| | | broker.publish(delMsg); |
| | | resultEntry = getEntry(personWithUUIDEntry.getDN(), 10000, false); |
| | | |
| | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new changenumbers |
| | | * when we need to send operation messages to the replicationServer. |
| | | * Create a CSN generator to generate new CSNs when we need to send |
| | | * operation messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(2, 0); |
| | | CSNGenerator gen = new CSNGenerator(2, 0); |
| | | |
| | | |
| | | // Create and publish an update message to add an entry. |
| | | AddMsg addMsg = new AddMsg(gen.newChangeNumber(), |
| | | AddMsg addMsg = new AddMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN().toString(), |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | |
| | | // Send a first modify operation message. |
| | | List<Modification> mods = generatemods("telephonenumber", "01 02 45"); |
| | | ModifyMsg modMsg = new ModifyMsg(gen.newChangeNumber(), |
| | | ModifyMsg modMsg = new ModifyMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN(), mods, |
| | | user1entryUUID); |
| | | broker.publish(modMsg); |
| | |
| | | |
| | | // Send a second modify operation message. |
| | | mods = generatemods("description", "Description was changed"); |
| | | modMsg = new ModifyMsg(gen.newChangeNumber(), |
| | | modMsg = new ModifyMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN(), mods, |
| | | user1entryUUID); |
| | | broker.publish(modMsg); |
| | |
| | | // Delete the entries to clean the database. |
| | | DeleteMsg delMsg = |
| | | new DeleteMsg(personWithUUIDEntry.getDN().toString(), |
| | | gen.newChangeNumber(), user1entryUUID); |
| | | gen.newCSN(), user1entryUUID); |
| | | broker.publish(delMsg); |
| | | resultEntry = getEntry(personWithUUIDEntry.getDN(), 10000, false); |
| | | |
| | |
| | | |
| | | // A change on a first server. |
| | | long changeTime = TimeThread.getTime(); |
| | | ChangeNumber t1 = new ChangeNumber(changeTime, 0, 3); |
| | | CSN t1 = new CSN(changeTime, 0, 3); |
| | | |
| | | // A change on a second server. |
| | | changeTime++; |
| | | ChangeNumber t2 = new ChangeNumber(changeTime, 0, 4); |
| | | CSN t2 = new CSN(changeTime, 0, 4); |
| | | |
| | | // Simulate the ordering t2:replace:B followed by t1:add:A that |
| | | updateMonitorCount(baseDn, monitorAttr); |
| | |
| | | // t1:replace:displayname |
| | | // A change on a first server. |
| | | changeTime++; |
| | | t1 = new ChangeNumber(changeTime, 0, 3); |
| | | t1 = new CSN(changeTime, 0, 3); |
| | | |
| | | // A change on a second server. |
| | | changeTime++; |
| | | t2 = new ChangeNumber(changeTime, 0, 4); |
| | | t2 = new CSN(changeTime, 0, 4); |
| | | |
| | | // Simulate the ordering t2:delete:displayname followed by t1:replace:A |
| | | updateMonitorCount(baseDn, monitorAttr); |
| | |
| | | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new changenumbers |
| | | * when we need to send operations messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator( 2, 0); |
| | | /* |
| | | * Create a CSN generator to generate new CSNs when we need to send |
| | | * operations messages to the replicationServer. |
| | | */ |
| | | CSNGenerator gen = new CSNGenerator( 2, 0); |
| | | |
| | | /* |
| | | * Test that the conflict resolution code is able to find entries |
| | |
| | | * Finally check that the modify operation has been applied. |
| | | */ |
| | | // create the entry with a given DN |
| | | AddMsg addMsg = new AddMsg(gen.newChangeNumber(), |
| | | AddMsg addMsg = new AddMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN().toString(), |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | |
| | | // send a modify operation with the correct unique ID but another DN |
| | | List<Modification> mods = generatemods("telephonenumber", "01 02 45"); |
| | | ModifyMsg modMsg = new ModifyMsg(gen.newChangeNumber(), |
| | | ModifyMsg modMsg = new ModifyMsg(gen.newCSN(), |
| | | DN.decode("cn=something,ou=People," + TEST_ROOT_DN_STRING), mods, |
| | | user1entryUUID); |
| | | updateMonitorCount(baseDn, resolvedMonitorAttr); |
| | |
| | | // send a modify operation attempting to replace the RDN entry |
| | | // with a new value |
| | | mods = generatemods("uid", "AnotherUid"); |
| | | modMsg = new ModifyMsg(gen.newChangeNumber(), |
| | | modMsg = new ModifyMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN(), mods, |
| | | user1entryUUID); |
| | | |
| | |
| | | */ |
| | | |
| | | // create the entry with a given DN and unique ID |
| | | addMsg = new AddMsg(gen.newChangeNumber(), |
| | | addMsg = new AddMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN().toString(), |
| | | user1entryUUID, baseUUID, |
| | | personWithUUIDEntry.getObjectClassAttribute(), |
| | |
| | | |
| | | // send a modify operation with a wrong unique ID but the same DN |
| | | mods = generatemods("telephonenumber", "02 01 03 05"); |
| | | modMsg = new ModifyMsg(gen.newChangeNumber(), |
| | | modMsg = new ModifyMsg(gen.newCSN(), |
| | | DN.decode(user1dn), mods, "10000000-9abc-def0-1234-1234567890ab"); |
| | | updateMonitorCount(baseDn, resolvedMonitorAttr); |
| | | AlertCount = DummyAlertHandler.getAlertCount(); |
| | |
| | | // used above |
| | | DeleteMsg delMsg = |
| | | new DeleteMsg("cn=anotherdn,ou=People," + TEST_ROOT_DN_STRING, |
| | | gen.newChangeNumber(), user1entryUUID); |
| | | gen.newCSN(), user1entryUUID); |
| | | updateMonitorCount(baseDn, resolvedMonitorAttr); |
| | | AlertCount = DummyAlertHandler.getAlertCount(); |
| | | broker.publish(delMsg); |
| | |
| | | */ |
| | | |
| | | // create an entry with a given DN and unique ID |
| | | addMsg = new AddMsg(gen.newChangeNumber(), |
| | | addMsg = new AddMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN().toString(), |
| | | user1entryUUID, baseUUID, |
| | | personWithUUIDEntry.getObjectClassAttribute(), |
| | |
| | | "The ADD replication message was not applied"); |
| | | |
| | | // create an entry with the same DN and another unique ID |
| | | addMsg = new AddMsg(gen.newChangeNumber(), |
| | | addMsg = new AddMsg(gen.newCSN(), |
| | | personWithSecondUniqueID.getDN().toString(), |
| | | user1entrysecondUUID, baseUUID, |
| | | personWithSecondUniqueID.getObjectClassAttribute(), |
| | |
| | | // delete the entries to clean the database. |
| | | delMsg = |
| | | new DeleteMsg(personWithUUIDEntry.getDN().toString(), |
| | | gen.newChangeNumber(), user1entryUUID); |
| | | gen.newCSN(), user1entryUUID); |
| | | broker.publish(delMsg); |
| | | delMsg = |
| | | new DeleteMsg(personWithSecondUniqueID.getDN().toString(), |
| | | gen.newChangeNumber(), user1entrysecondUUID); |
| | | gen.newCSN(), user1entrysecondUUID); |
| | | broker.publish(delMsg); |
| | | resultEntry = getEntry(personWithUUIDEntry.getDN(), 10000, false); |
| | | resultEntry = getEntry(personWithSecondUniqueID.getDN(), 10000, false); |
| | |
| | | * Simulate this by trying to add an entry below a DN that does not |
| | | * exist but with a parent ID that exist. |
| | | */ |
| | | addMsg = new AddMsg(gen.newChangeNumber(), |
| | | addMsg = new AddMsg(gen.newCSN(), |
| | | "uid=new person,o=nothere,o=below,ou=People," + TEST_ROOT_DN_STRING, |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | |
| | | delMsg = |
| | | new DeleteMsg("uid=new person,ou=People," + TEST_ROOT_DN_STRING, |
| | | gen.newChangeNumber(), "11111111-9abc-def0-1234-1234567890ab"); |
| | | gen.newCSN(), "11111111-9abc-def0-1234-1234567890ab"); |
| | | updateMonitorCount(baseDn, resolvedMonitorAttr); |
| | | AlertCount = DummyAlertHandler.getAlertCount(); |
| | | broker.publish(delMsg); |
| | |
| | | */ |
| | | |
| | | ModifyDNMsg modDnMsg = new ModifyDNMsg( |
| | | "uid=new person,ou=People," + TEST_ROOT_DN_STRING, gen.newChangeNumber(), |
| | | "uid=new person,ou=People," + TEST_ROOT_DN_STRING, gen.newCSN(), |
| | | user1entryUUID, baseUUID, false, |
| | | "uid=wrong, ou=people," + TEST_ROOT_DN_STRING, |
| | | "uid=newrdn"); |
| | |
| | | */ |
| | | |
| | | modDnMsg = new ModifyDNMsg( |
| | | "uid=wrong,ou=People," + TEST_ROOT_DN_STRING, gen.newChangeNumber(), |
| | | "uid=wrong,ou=People," + TEST_ROOT_DN_STRING, gen.newCSN(), |
| | | user1entryUUID, null, false, null, "uid=reallynewrdn"); |
| | | updateMonitorCount(baseDn, resolvedMonitorAttr); |
| | | AlertCount = DummyAlertHandler.getAlertCount(); |
| | |
| | | */ |
| | | |
| | | // add a second entry |
| | | addMsg = new AddMsg(gen.newChangeNumber(), |
| | | addMsg = new AddMsg(gen.newCSN(), |
| | | user1dn, |
| | | user1entrysecondUUID, |
| | | baseUUID, |
| | |
| | | assertNotNull(resultEntry, "The add operation was not replayed"); |
| | | |
| | | // try to rename the first entry |
| | | modDnMsg = new ModifyDNMsg(user1dn, gen.newChangeNumber(), |
| | | modDnMsg = new ModifyDNMsg(user1dn, gen.newCSN(), |
| | | user1entrysecondUUID, baseUUID, false, |
| | | baseDn.toString(), "uid=reallynewrdn"); |
| | | updateMonitorCount(baseDn, unresolvedMonitorAttr); |
| | |
| | | new DeleteMsg("entryUUID = " + user1entrysecondUUID + "+" + |
| | | DN.decode(user1dn).getRDN().toString() + |
| | | ",ou=People," + TEST_ROOT_DN_STRING, |
| | | gen.newChangeNumber(), user1entrysecondUUID); |
| | | gen.newCSN(), user1entrysecondUUID); |
| | | broker.publish(delMsg); |
| | | resultEntry = getEntry( |
| | | DN.decode("entryUUID = " + user1entrysecondUUID + "+" + |
| | |
| | | |
| | | delMsg = |
| | | new DeleteMsg("uid=reallynewrdn,ou=People," + TEST_ROOT_DN_STRING, |
| | | gen.newChangeNumber(), user1entryUUID); |
| | | gen.newCSN(), user1entryUUID); |
| | | broker.publish(delMsg); |
| | | resultEntry = getEntry( |
| | | DN.decode("uid=reallynewrdn,ou=People," + TEST_ROOT_DN_STRING), 10000, false); |
| | |
| | | "Entry not added: ou=baseDn1,"+baseDn); |
| | | |
| | | // - create Add Msg for user1 with parent entry 1 UUID |
| | | addMsg = new AddMsg(gen.newChangeNumber(), |
| | | addMsg = new AddMsg(gen.newCSN(), |
| | | "uid=new person,ou=baseDn1,"+baseDn, |
| | | user1entryUUID, |
| | | getEntryUUID(DN.decode("ou=baseDn1,"+baseDn)), |
| | |
| | | |
| | | // add domain1 entry with 2 children : domain2 and domain3 |
| | | addEntry(domain1); |
| | | ChangeNumber olderCn = gen.newChangeNumber(); |
| | | CSN olderCSN = gen.newCSN(); |
| | | Thread.sleep(1000); |
| | | domain1uid = getEntryUUID(DN.decode(domain1dn)); |
| | | addEntry(domain2); |
| | |
| | | AlertCount = DummyAlertHandler.getAlertCount(); |
| | | |
| | | // delete domain1 |
| | | delMsg = new DeleteMsg(domain1dn, olderCn, domain1uid); |
| | | delMsg = new DeleteMsg(domain1dn, olderCSN, domain1uid); |
| | | broker.publish(delMsg); |
| | | |
| | | // check that the domain1 has correctly been deleted |
| | |
| | | domain1uid = getEntryUUID(DN.decode(domain1dn)); |
| | | addEntry(domain2); |
| | | domain2uid = getEntryUUID(DN.decode(domain2dn)); |
| | | ChangeNumber addCn = addEntry(domain3); |
| | | gen.adjust(addCn); |
| | | CSN addCSN = addEntry(domain3); |
| | | gen.adjust(addCSN); |
| | | domain3uid = getEntryUUID(DN.decode(domain3dn)); |
| | | |
| | | updateMonitorCount(baseDn, unresolvedMonitorAttr); |
| | | AlertCount = DummyAlertHandler.getAlertCount(); |
| | | |
| | | // delete domain1 |
| | | delMsg = new DeleteMsg(domain1dn, gen.newChangeNumber(), domain1uid); |
| | | delMsg = new DeleteMsg(domain1dn, gen.newCSN(), domain1uid); |
| | | broker.publish(delMsg); |
| | | |
| | | // check that the domain1 has correctly been deleted |
| | |
| | | // that is currently deleted on another master, the replay of the |
| | | // add on the second master cause the added entry to be renamed |
| | | // |
| | | addMsg = new AddMsg(gen.newChangeNumber(), domain2dn, domain2uid, |
| | | addMsg = new AddMsg(gen.newCSN(), domain2dn, domain2uid, |
| | | domain1uid, |
| | | domain2.getObjectClassAttribute(), |
| | | domain2.getAttributes(), new ArrayList<Attribute>()); |
| | |
| | | // this is correctly detected as a resolved conflict. |
| | | // To simulate this simply try a modifyDN on a non existent uid. |
| | | modDnMsg = new ModifyDNMsg( |
| | | "uid=new person,ou=People," + TEST_ROOT_DN_STRING, gen.newChangeNumber(), |
| | | "uid=new person,ou=People," + TEST_ROOT_DN_STRING, gen.newCSN(), |
| | | "33343333-3533-3633-3373-333333833333", baseUUID, false, |
| | | "uid=wrong, ou=people," + TEST_ROOT_DN_STRING, |
| | | "uid=newrdn"); |
| | |
| | | updateMonitorCount(baseDn, unresolvedMonitorAttr); |
| | | AlertCount = DummyAlertHandler.getAlertCount(); |
| | | modDnMsg = new ModifyDNMsg( |
| | | "uid=new person,ou=People," + TEST_ROOT_DN_STRING, gen.newChangeNumber(), |
| | | "uid=new person,ou=People," + TEST_ROOT_DN_STRING, gen.newCSN(), |
| | | "33333333-3333-3333-3333-333333333333", |
| | | "12343333-3533-3633-3333-333333833333" , false, |
| | | "uid=wrong, ou=people," + TEST_ROOT_DN_STRING, |
| | |
| | | openReplicationSession(baseDn, 27, 100, replServerPort, 2000, true); |
| | | |
| | | try { |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator( 27, 0); |
| | | CSNGenerator gen = new CSNGenerator( 27, 0); |
| | | |
| | | /* |
| | | * Test that operations done on this server are sent to the |
| | |
| | | * |
| | | * Start by testing the Add message reception |
| | | */ |
| | | AddMsg addMsg = new AddMsg(gen.newChangeNumber(), |
| | | AddMsg addMsg = new AddMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN().toString(), |
| | | user1entryUUID, baseUUID, |
| | | personWithUUIDEntry.getObjectClassAttribute(), |
| | |
| | | /* |
| | | * Test the reception of Modify Msg |
| | | */ |
| | | modMsg = new ModifyMsg(gen.newChangeNumber(), personWithUUIDEntry.getDN(), |
| | | modMsg = new ModifyMsg(gen.newCSN(), personWithUUIDEntry.getDN(), |
| | | mods, user1entryUUID); |
| | | if (assured) |
| | | modMsg.setAssured(true); |
| | |
| | | // Test that replication is able to add attribute that do |
| | | // not exist in the schema. |
| | | List<Modification> invalidMods = generatemods("badattribute", "value"); |
| | | modMsg = new ModifyMsg(gen.newChangeNumber(), personWithUUIDEntry.getDN(), |
| | | modMsg = new ModifyMsg(gen.newCSN(), personWithUUIDEntry.getDN(), |
| | | invalidMods, user1entryUUID); |
| | | if (assured) |
| | | modMsg.setAssured(true); |
| | |
| | | * Test the Reception of Modify Dn Msg |
| | | */ |
| | | moddnMsg = new ModifyDNMsg(personWithUUIDEntry.getDN().toString(), |
| | | gen.newChangeNumber(), |
| | | gen.newCSN(), |
| | | user1entryUUID, null, |
| | | true, null, "uid= new person"); |
| | | if (assured) |
| | |
| | | * Test the Reception of Delete Msg |
| | | */ |
| | | delMsg = new DeleteMsg("uid= new person,ou=People," + TEST_ROOT_DN_STRING, |
| | | gen.newChangeNumber(), user1entryUUID); |
| | | gen.newCSN(), user1entryUUID); |
| | | if (assured) |
| | | delMsg.setAssured(true); |
| | | broker.publish(delMsg); |
| | |
| | | openReplicationSession(baseDn, 11, 100, replServerPort, 1000, true); |
| | | try |
| | | { |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator( 11, 0); |
| | | CSNGenerator gen = new CSNGenerator( 11, 0); |
| | | |
| | | // Create a test entry. |
| | | String personLdif = "dn: uid=user.2,ou=People," + TEST_ROOT_DN_STRING + "\n" |
| | |
| | | { |
| | | // Publish a delete message for this test entry. |
| | | DeleteMsg delMsg = new DeleteMsg(tmp.getDN().toString(), |
| | | gen.newChangeNumber(), |
| | | gen.newCSN(), |
| | | uuid); |
| | | broker.publish(delMsg); |
| | | |
| | |
| | | } |
| | | /** |
| | | * Test that the ReplicationDomain (plugin inside LDAP server) adjust |
| | | * its internal change number generator to the last change number |
| | | * received. Steps: |
| | | * - create a domain with the current date in the CN generator |
| | | * - make it receive an update with a CN in the future |
| | | * its internal CSN generator to the last CSN received. Steps: |
| | | * - create a domain with the current date in the CSN generator |
| | | * - make it receive an update with a CSN in the future |
| | | * - do a local operation replicated on that domain |
| | | * - check that the update generated for that operation has a CN in the |
| | | * - check that the update generated for that operation has a CSN in the |
| | | * future. |
| | | * @throws Exception |
| | | */ |
| | | @Test(enabled=true) |
| | | public void CNGeneratorAdjust() throws Exception |
| | | public void csnGeneratorAdjust() throws Exception |
| | | { |
| | | int serverId = 88; |
| | | logError(Message.raw(Category.SYNC, Severity.INFORMATION, |
| | | "Starting synchronization test : CNGeneratorAdjust")); |
| | | "Starting synchronization test : CSNGeneratorAdjust")); |
| | | |
| | | final DN baseDn = DN.decode("ou=People," + TEST_ROOT_DN_STRING); |
| | | |
| | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new changenumbers |
| | | * Create a CSN generator to generate new CSNs |
| | | * when we need to send operation messages to the replicationServer. |
| | | */ |
| | | long inTheFutur = System.currentTimeMillis() + (3600 * 1000); |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(serverId, inTheFutur); |
| | | CSNGenerator gen = new CSNGenerator(serverId, inTheFutur); |
| | | |
| | | // Create and publish an update message to add an entry. |
| | | AddMsg addMsg = new AddMsg( |
| | | gen.newChangeNumber(), |
| | | gen.newCSN(), |
| | | user3dn.toString(), |
| | | user3UUID, |
| | | baseUUID, |
| | |
| | | assertTrue(msg instanceof ModifyMsg, |
| | | "The received replication message is not a MODIFY msg"); |
| | | ModifyMsg modMsg = (ModifyMsg) msg; |
| | | assertEquals(addMsg.getChangeNumber().getTimeSec(), |
| | | modMsg.getChangeNumber().getTimeSec(), |
| | | assertEquals(addMsg.getCSN().getTimeSec(), |
| | | modMsg.getCSN().getTimeSec(), |
| | | "The MOD timestamp should have been adjusted to the ADD one"); |
| | | |
| | | // Delete the entries to clean the database. |
| | | DeleteMsg delMsg = |
| | | new DeleteMsg( |
| | | user3Entry.getDN().toString(), |
| | | gen.newChangeNumber(), |
| | | gen.newCSN(), |
| | | user3UUID); |
| | | broker.publish(delMsg); |
| | | |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, |
| | | * add the following below this CDDL HEADER, with the fields enclosed |
| | | * by brackets "[]" replaced with your own identifying information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.common; |
| | | |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.util.TimeThread; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | @SuppressWarnings("javadoc") |
| | | public class CSNGeneratorTest extends ReplicationTestCase |
| | | { |
| | | /** |
| | | * Test the adjust method of CSNGenerator |
| | | */ |
| | | @Test |
| | | public void adjustTest() |
| | | { |
| | | CSNGenerator generator = new CSNGenerator(5, TimeThread.getTime()); |
| | | |
| | | CSN csn = generator.newCSN(); |
| | | |
| | | CSN csn1 = new CSN(csn.getTime() + 5000, csn.getSeqnum(), 6); |
| | | generator.adjust(csn1); |
| | | |
| | | CSN csn2 = generator.newCSN(); |
| | | |
| | | assertTrue(csn2.compareTo(csn1) > 0, |
| | | "CSNGenerator generated an earlier CSN after calling the adjust method."); |
| | | } |
| | | |
| | | @Test |
| | | public void adjustSameMilliTest() |
| | | { |
| | | CSNGenerator generator = new CSNGenerator(5, TimeThread.getTime()); |
| | | |
| | | CSN csn = generator.newCSN(); |
| | | |
| | | CSN csn1 = new CSN(csn.getTime(), csn.getSeqnum() + 10, 6); |
| | | generator.adjust(csn1); |
| | | |
| | | CSN csn2 = generator.newCSN(); |
| | | |
| | | assertTrue(csn2.compareTo(csn1) > 0, |
| | | "CSNGenerator generated an earlier CSN after calling the adjust method."); |
| | | } |
| | | |
| | | /** |
| | | * Test the correct behavior of the CSNGenerator when |
| | | * the seqnum is rolling over its limit |
| | | */ |
| | | @Test |
| | | public void adjustRollingSeqnum() |
| | | { |
| | | ServerState state = new ServerState(); |
| | | CSN csn1 = new CSN(TimeThread.getTime(), Integer.MAX_VALUE, 5); |
| | | state.update(csn1); |
| | | |
| | | CSNGenerator generator = new CSNGenerator(5, state); |
| | | CSN csn2 = generator.newCSN(); |
| | | |
| | | assertTrue(csn2.getSeqnum() == 0); |
| | | assertTrue(csn2.getTime() > csn1.getTime()); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, |
| | | * add the following below this CDDL HEADER, with the fields enclosed |
| | | * by brackets "[]" replaced with your own identifying information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2006-2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.common; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.util.TimeThread; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test {@link CSN} and {@link CSNGenerator} |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class CSNTest extends ReplicationTestCase |
| | | { |
| | | |
| | | /** |
| | | * Create CSN Data |
| | | */ |
| | | @DataProvider(name = "csnData") |
| | | public Object[][] createConstructorData() { |
| | | long time = 0x12ABC; |
| | | return new Object[][] { |
| | | {1, 0, 1, "0000000000000001000100000000"}, |
| | | {time, 123, 45, "0000000000012abc002d0000007b"}, |
| | | {time, 123456789, 32767, "0000000000012abc7fff075bcd15"}, |
| | | {time, 123456789, 32768, "0000000000012abc8000075bcd15"}, |
| | | {time, 123456789, 65000, "0000000000012abcfde8075bcd15"}, |
| | | {time, 123, 45678, "0000000000012abcb26e0000007b"} |
| | | }; |
| | | } |
| | | |
| | | /** Test CSN constructor */ |
| | | @Test(dataProvider = "csnData") |
| | | public void createCSN(long time, int seq, int id, String str) throws Exception |
| | | { |
| | | CSN csn = new CSN(time, seq, id); |
| | | assertEquals(csn.toString(), str); |
| | | |
| | | new CSN(time, seq, id); |
| | | new CSN(time+1, seq, id); |
| | | new CSN(time, seq+1, id); |
| | | new CSN(time, seq, id+1); |
| | | } |
| | | |
| | | /** |
| | | * Test toString and constructor from String |
| | | */ |
| | | @Test(dataProvider = "csnData") |
| | | public void csnEncodeDecode(long time, int seq, int id, String str) throws Exception |
| | | { |
| | | // Create 2 CSN with the same data and check equality |
| | | CSN csn = new CSN(time, seq, id); |
| | | CSN csn2 = new CSN(csn.toString()); |
| | | |
| | | assertEquals(csn, csn2, |
| | | "The encoding/decoding is not reversible"); |
| | | assertEquals(csn2.toString(), str, |
| | | "The encoding/decoding of CSN is not reversible for toString()"); |
| | | } |
| | | |
| | | /** |
| | | * Create CSN |
| | | */ |
| | | @DataProvider(name = "createCSN") |
| | | public Object[][] createCSNData() |
| | | { |
| | | long time[] = {1, TimeThread.getTime()}; |
| | | int seq[] = {0, 123}; |
| | | int id [] = {1, 45}; |
| | | |
| | | Object[][] obj = new Object[time.length][5]; |
| | | for (int i=0; i<time.length; i++) |
| | | { |
| | | obj[i][0] = new CSN(time[i], seq[i], id[i]); |
| | | obj[i][1] = new CSN(time[i], seq[i], id[i]); |
| | | obj[i][2] = new CSN(time[i]+1, seq[i], id[i]); |
| | | obj[i][3] = new CSN(time[i], seq[i]+1, id[i]); |
| | | obj[i][4] = new CSN(time[i], seq[i], id[i]+1); |
| | | } |
| | | return obj; |
| | | } |
| | | |
| | | /** Test {@link CSN#hashCode()} method */ |
| | | @Test(dataProvider = "createCSN") |
| | | public void csnHashCode(CSN csn1, CSN csn2, CSN csn3, CSN csn4, CSN csn5) throws Exception |
| | | { |
| | | assertEquals(csn1.hashCode(), csn2.hashCode()); |
| | | } |
| | | |
| | | /** Test {@link CSN#equals(Object)} method */ |
| | | @Test(dataProvider = "createCSN") |
| | | public void csnEquals(CSN csn1, CSN csn2, CSN csn3, CSN csn4, CSN csn5) throws Exception |
| | | { |
| | | assertFalse(csn1.equals(new Object())); |
| | | assertTrue(csn1.equals(csn1)); |
| | | assertTrue(csn1.equals(csn2)); |
| | | assertFalse(csn1.equals(csn3)); |
| | | assertFalse(csn1.equals(csn4)); |
| | | assertFalse(csn1.equals(csn5)); |
| | | } |
| | | |
| | | /** Test {@link CSN#getTimeSec()} method */ |
| | | @Test(dataProvider = "createCSN") |
| | | public void csnGetTimeSec(CSN csn1, CSN csn2, CSN csn3, CSN csn4, CSN csn5) throws Exception |
| | | { |
| | | // Check time in sec |
| | | assertEquals(csn1.getTime()/1000, csn1.getTimeSec()); |
| | | assertEquals(csn2.getTime()/1000, csn2.getTimeSec()); |
| | | assertEquals(csn3.getTime()/1000, csn3.getTimeSec()); |
| | | assertEquals(csn4.getTime()/1000, csn4.getTimeSec()); |
| | | assertEquals(csn5.getTime()/1000, csn5.getTimeSec()); |
| | | } |
| | | |
| | | /** Test {@link CSN#compare(CSN, CSN)} method */ |
| | | @Test(dataProvider = "createCSN") |
| | | public void csnCompare(CSN csn1, CSN csn2, CSN csn3, CSN csn4, CSN csn5) throws Exception |
| | | { |
| | | assertTrue(CSN.compare(null, null) == 0); |
| | | assertTrue(CSN.compare(null, csn2) < 0); |
| | | assertTrue(CSN.compare(csn1, null) > 0); |
| | | assertTrue(CSN.compare(csn1, csn2) == 0); |
| | | assertTrue(CSN.compare(csn1, csn3) < 0); |
| | | assertTrue(CSN.compare(csn3, csn1) > 0); |
| | | assertTrue(CSN.compare(csn1, csn4) < 0); |
| | | assertTrue(CSN.compare(csn4, csn1) > 0); |
| | | assertTrue(CSN.compare(csn1, csn5) < 0); |
| | | assertTrue(CSN.compare(csn5, csn1) > 0); |
| | | } |
| | | |
| | | /** Test {@link CSN#older(CSN)} method */ |
| | | @Test(dataProvider = "createCSN") |
| | | public void csnOlder(CSN csn1, CSN csn2, CSN csn3, CSN csn4, CSN csn5) throws Exception |
| | | { |
| | | assertFalse(csn1.older(null)); |
| | | assertFalse(csn1.older(csn1)); |
| | | assertTrue(csn1.older(csn3)); |
| | | assertTrue(csn1.older(csn4)); |
| | | assertTrue(csn1.older(csn5)); |
| | | } |
| | | |
| | | /** Test {@link CSN#olderOrEqual(CSN)} method */ |
| | | @Test(dataProvider = "createCSN") |
| | | public void csnOlderOrEqual(CSN csn1, CSN csn2, CSN csn3, CSN csn4, CSN csn5) throws Exception |
| | | { |
| | | assertFalse(csn1.olderOrEqual(null)); |
| | | assertTrue(csn1.olderOrEqual(csn1)); |
| | | assertTrue(csn1.olderOrEqual(csn3)); |
| | | assertTrue(csn1.olderOrEqual(csn4)); |
| | | assertTrue(csn1.olderOrEqual(csn5)); |
| | | } |
| | | |
| | | /** Test {@link CSN#newer(CSN)} method */ |
| | | @Test(dataProvider = "createCSN") |
| | | public void csnNewer(CSN csn1, CSN csn2, CSN csn3, CSN csn4, CSN csn5) throws Exception |
| | | { |
| | | assertTrue(csn1.newer(null)); |
| | | assertFalse(csn1.newer(csn1)); |
| | | assertFalse(csn1.newer(csn3)); |
| | | assertFalse(csn1.newer(csn4)); |
| | | assertFalse(csn1.newer(csn5)); |
| | | } |
| | | |
| | | /** Test {@link CSN#newerOrEquals(CSN)} method */ |
| | | @Test(dataProvider = "createCSN") |
| | | public void csnNewerOrEquals(CSN csn1, CSN csn2, CSN csn3, CSN csn4, CSN csn5) throws Exception |
| | | { |
| | | assertTrue(csn1.newerOrEquals(null)); |
| | | assertTrue(csn1.newerOrEquals(csn1)); |
| | | assertFalse(csn1.newerOrEquals(csn3)); |
| | | assertFalse(csn1.newerOrEquals(csn4)); |
| | | assertFalse(csn1.newerOrEquals(csn5)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a {@link CSNGenerator}, then call {@link CSNGenerator#newCSN()} and |
| | | * {@link CSNGenerator#adjust()} |
| | | * <p> |
| | | * FIXME these tests are calling Thread.sleep() which makes them slow. We |
| | | * should really have a way to control time (make it go slower or faster) for |
| | | * the unit tests to avoid such waits. |
| | | */ |
| | | @Test |
| | | public void csnGenerator() throws Exception |
| | | { |
| | | CSN csn1; |
| | | CSN csn2; |
| | | |
| | | CSNGenerator csng = |
| | | new CSNGenerator( 0, TimeThread.getTime()); |
| | | |
| | | // Generate 2 CSNs and check that they are different |
| | | csn1 = csng.newCSN(); |
| | | csn2 = csng.newCSN(); |
| | | assertTrue(csn1.compareTo(csn2) != 0); |
| | | |
| | | // Generate a CSN separated by 10 milliseconds |
| | | // and check that they are different |
| | | Thread.sleep(10); |
| | | csn2 = csng.newCSN(); |
| | | assertTrue(csn1.compareTo(csn2) != 0); |
| | | |
| | | // Generate a CSN separated by 300 milliseconds |
| | | // and check that they are different |
| | | Thread.sleep(300); |
| | | csn2 = csng.newCSN(); |
| | | assertTrue(csn1.compareTo(csn2) != 0); |
| | | |
| | | // Adjust with the oldest CSN |
| | | csng.adjust(csn1); |
| | | csn2 = csng.newCSN(); |
| | | assertTrue(csn1.compareTo(csn2) != 0 ); |
| | | |
| | | // Adjust with the newest generated |
| | | csng.adjust(csn2); |
| | | csn1 = csn2; |
| | | csn2 = csng.newCSN(); |
| | | assertTrue(csn1.compareTo(csn2) != 0 ); |
| | | |
| | | // Adjust with the newest generated (time + 300) |
| | | csn1 = new CSN(csn2.getTime() +300 ,csn2.getSeqnum(), csn2.getServerId()); |
| | | csng.adjust(csn1); |
| | | csn2 = csng.newCSN(); |
| | | assertTrue(csn1.compareTo(csn2) != 0 ); |
| | | |
| | | // Adjust with the newest generated (seqmun + 10) |
| | | csn1 = new CSN(csn2.getTime() ,csn2.getSeqnum() +10, csn2.getServerId()); |
| | | csng.adjust(csn1); |
| | | csn2 = csng.newCSN(); |
| | | assertTrue(csn1.compareTo(csn2) != 0 ); |
| | | |
| | | // Adjust with the newest generated (seqmun = 0XFFFF) |
| | | csn1 = new CSN(csn2.getTime() ,0XFFFF +10,csn2.getServerId()); |
| | | csng.adjust(csn1); |
| | | csn2 = csng.newCSN(); |
| | | assertTrue(csn1.compareTo(csn2) != 0 ); |
| | | } |
| | | |
| | | /** |
| | | * Test the difference in seq num between 2 CSNs. |
| | | */ |
| | | @Test |
| | | public void csnDiffSeqNum() throws Exception |
| | | { |
| | | CSN csn1; |
| | | CSN csn2; |
| | | |
| | | csn1 = new CSN(0, 3, 0); |
| | | |
| | | // 3-0 = 3 |
| | | csn2 = new CSN(0, 0, 0); |
| | | assertEquals(CSN.diffSeqNum(csn1, csn2), 3); |
| | | |
| | | // 3-1 = 2 |
| | | csn2 = new CSN(0, 1, 0); |
| | | assertEquals(CSN.diffSeqNum(csn1, csn2), 2); |
| | | |
| | | // 3-3 = 0 |
| | | csn2 = new CSN(0, 3, 0); |
| | | assertEquals(CSN.diffSeqNum(csn1, csn2), 0); |
| | | |
| | | // 3-4 = 0 (csn1 must be newer otherwise 0 should be returned) |
| | | csn2 = new CSN(0, 4, 0); |
| | | assertEquals(CSN.diffSeqNum(csn1, csn2), 0); |
| | | |
| | | csn1 = new CSN(0, 0, 0); |
| | | |
| | | // 0-0 = 0 |
| | | csn2 = new CSN(0, 0, 0); |
| | | assertEquals(CSN.diffSeqNum(csn1, csn2), 0); |
| | | |
| | | // 0-1 = 0 (csn1 must be newer otherwise 0 should be returned) |
| | | csn2 = new CSN(0, 1, 0); |
| | | assertEquals(CSN.diffSeqNum(csn1, csn2), 0); |
| | | |
| | | csn1 = new CSN(0, 5, 0); |
| | | csn2 = new CSN(0, 2, 0); |
| | | |
| | | // 5-null = 5 |
| | | assertEquals(CSN.diffSeqNum(csn1, null), 5); |
| | | |
| | | // null-2 = 0 |
| | | assertEquals(CSN.diffSeqNum(null, csn2), 0); |
| | | |
| | | // null-null = 0 |
| | | assertEquals(CSN.diffSeqNum(null, null), 0); |
| | | |
| | | csn1 = new CSN(1111111, 2, 0); |
| | | csn2 = new CSN(3333333, 4, 0); |
| | | |
| | | // csn1 older than csn2 -> 0 |
| | | assertEquals(CSN.diffSeqNum(csn1, csn2), 0); |
| | | |
| | | csn1 = new CSN(3333333, 1, 0); |
| | | csn2 = new CSN(1111111, Integer.MAX_VALUE-1, 0); |
| | | |
| | | // csn1 seqnum looped |
| | | assertEquals(CSN.diffSeqNum(csn1, csn2), 3); |
| | | } |
| | | |
| | | @DataProvider |
| | | public Iterator<Object[]> createCSNPairsToCompare() |
| | | { |
| | | final List<Object> allCSNs = new ArrayList<Object>(); |
| | | for (Object[] csnData : createCSNData()) |
| | | { |
| | | allCSNs.addAll(Arrays.asList(csnData)); |
| | | } |
| | | |
| | | final List<Object[]> results = new ArrayList<Object[]>(); |
| | | for (Object csn1 : allCSNs) |
| | | { |
| | | for (Object csn2 : allCSNs) |
| | | { |
| | | /* |
| | | * it is ok to compare to the exact same CSN to ensure operations are |
| | | * reflexive, and it is also ok to compare csn1 to csn2, and csn2 to |
| | | * csn1 to ensure operations are symmetric |
| | | */ |
| | | results.add(new Object[] { csn1, csn2 }); |
| | | } |
| | | } |
| | | return results.iterator(); |
| | | } |
| | | |
| | | @Test(dataProvider = "createCSNPairsToCompare") |
| | | public void compareToEquivalentToEquals(CSN csn1, CSN csn2) throws Exception |
| | | { |
| | | assertEquals(csn1.compareTo(csn2) == 0, csn1.equals(csn2)); |
| | | } |
| | | |
| | | @Test(dataProvider = "createCSNPairsToCompare") |
| | | public void hashCodesEqualWhenCSNsEqual(CSN csn1, CSN csn2) throws Exception |
| | | { |
| | | if (csn1.equals(csn2)) |
| | | { |
| | | assertEquals(csn1.hashCode(), csn2.hashCode()); |
| | | } |
| | | } |
| | | |
| | | @Test(dataProvider = "createCSNPairsToCompare") |
| | | public void hashCodesEqualWhenCompareToEqual(CSN csn1, CSN csn2) throws Exception |
| | | { |
| | | if (csn1.compareTo(csn2) == 0) |
| | | { |
| | | assertEquals(csn1.hashCode(), csn2.hashCode()); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.common; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | import java.util.Set; |
| | | |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.util.TimeThread; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test the ServerState |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class ServerStateTest extends ReplicationTestCase |
| | | { |
| | | |
| | | /** |
| | | * Create ChangeNumber Data |
| | | * Create CSN Data |
| | | */ |
| | | @DataProvider(name = "changeNumberData") |
| | | public Object[][] createChangeNumberData() { |
| | | @DataProvider(name = "csnData") |
| | | public Object[][] createCSNData() |
| | | { |
| | | return new Object[][] { |
| | | {new ChangeNumber(1, 0, 1)}, |
| | | {new ChangeNumber(TimeThread.getTime(), 123, 45)} |
| | | {new CSN(1, 0, 1)}, |
| | | {new CSN(TimeThread.getTime(), 123, 45)} |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * Create a new ServerState object |
| | | */ |
| | | @Test(dataProvider = "changeNumberData") |
| | | public void serverStateTest(ChangeNumber cn) |
| | | throws Exception |
| | | @Test(dataProvider = "csnData") |
| | | public void serverStateTest(CSN csn) throws Exception |
| | | { |
| | | // Check constructor |
| | | ServerState serverState = new ServerState() ; |
| | |
| | | // TODO Check result; |
| | | |
| | | // Check update |
| | | assertFalse(serverState.update((ChangeNumber)null)); |
| | | assertTrue(serverState.update(cn)); |
| | | assertFalse(serverState.update(cn)); |
| | | ChangeNumber cn1, cn2, cn3; |
| | | cn1 = new ChangeNumber(cn.getTime()+1,cn.getSeqnum(),cn.getServerId()); |
| | | cn2 = new ChangeNumber(cn1.getTime(),cn1.getSeqnum()+1,cn1.getServerId()); |
| | | cn3 = new ChangeNumber(cn2.getTime(),cn2.getSeqnum(),(cn2.getServerId()+1)); |
| | | assertFalse(serverState.update((CSN)null)); |
| | | assertTrue(serverState.update(csn)); |
| | | assertFalse(serverState.update(csn)); |
| | | CSN csn1, csn2, csn3; |
| | | csn1 = new CSN(csn.getTime() + 1, csn.getSeqnum(), csn.getServerId()); |
| | | csn2 = new CSN(csn1.getTime(), csn1.getSeqnum() + 1, csn1.getServerId()); |
| | | csn3 = new CSN(csn2.getTime(), csn2.getSeqnum(), (csn2.getServerId() + 1)); |
| | | |
| | | assertTrue(serverState.update(cn1)) ; |
| | | assertTrue(serverState.update(cn2)) ; |
| | | assertTrue(serverState.update(cn3)) ; |
| | | assertTrue(serverState.update(csn1)); |
| | | assertTrue(serverState.update(csn2)); |
| | | assertTrue(serverState.update(csn3)); |
| | | |
| | | // Check toStringSet |
| | | ChangeNumber[] cns = {cn2,cn3}; |
| | | CSN[] csns = { csn2, csn3 }; |
| | | Set<String> stringSet = serverState.toStringSet(); |
| | | assertEquals(cns.length, stringSet.size()); |
| | | assertEquals(csns.length, stringSet.size()); |
| | | // TODO Check the value |
| | | |
| | | // Check getMaxChangeNumber |
| | | assertEquals(cn2.compareTo(serverState.getChangeNumber(cn2.getServerId())),0); |
| | | assertEquals(cn3.compareTo(serverState.getChangeNumber(cn3.getServerId())),0); |
| | | // Check getMaxCSN |
| | | assertEquals(csn2.compareTo(serverState.getCSN(csn2.getServerId())), 0); |
| | | assertEquals(csn3.compareTo(serverState.getCSN(csn3.getServerId())), 0); |
| | | |
| | | // Check the toString |
| | | String stringRep = serverState.toString(); |
| | | assertTrue(stringRep.contains(cn2.toString())); |
| | | assertTrue(stringRep.contains(cn3.toString())); |
| | | assertTrue(stringRep.contains(csn2.toString())); |
| | | assertTrue(stringRep.contains(csn3.toString())); |
| | | |
| | | // Check getBytes |
| | | byte[] b = serverState.getBytes(); |
| | | ServerState generatedServerState = new ServerState(b,0,b.length -1) ; |
| | | |
| | | |
| | | |
| | | assertEquals(b, generatedServerState.getBytes()) ; |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Create a new ServerState object |
| | | */ |
| | | @Test(dataProvider = "changeNumberData") |
| | | public void serverStateReloadTest(ChangeNumber cn) |
| | | @Test(dataProvider = "csnData") |
| | | public void serverStateReloadTest(CSN csn) |
| | | throws Exception |
| | | { |
| | | ChangeNumber cn1, cn3; |
| | | cn1 = new ChangeNumber(cn.getTime()+1,cn.getSeqnum(),cn.getServerId()); |
| | | cn3 = new ChangeNumber(cn1.getTime(),cn1.getSeqnum(),(cn1.getServerId()+1)); |
| | | CSN csn1, csn3; |
| | | csn1 = new CSN(csn.getTime() + 1, csn.getSeqnum(), csn.getServerId()); |
| | | csn3 = new CSN(csn1.getTime(), csn1.getSeqnum(), (csn1.getServerId() + 1)); |
| | | |
| | | ServerState state1 = new ServerState(); |
| | | state1.update(cn1); |
| | | state1.update(cn3); |
| | | state1.update(csn1); |
| | | state1.update(csn3); |
| | | |
| | | ServerState state2 = new ServerState(); |
| | | state2.reload(state1); |
| | |
| | | */ |
| | | private Entry createNotAssuredDomain() throws Exception |
| | | { |
| | | |
| | | // Create a not assured config entry ldif |
| | | String configEntryLdif = "dn: cn=" + testName + ", cn=domains, " + |
| | | SYNCHRO_PLUGIN_DN + "\n" + "objectClass: top\n" + |
| | |
| | | // Parameters given at constructor time |
| | | private final int port; |
| | | private int serverId = -1; |
| | | boolean isAssured = false; // Default value for config |
| | | AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE; // Default value for config |
| | | byte safeDataLevel = (byte) 1; // Default value for config |
| | | private boolean isAssured = false; |
| | | private AssuredMode assuredMode = AssuredMode.SAFE_DATA_MODE; |
| | | private byte safeDataLevel = 1; |
| | | |
| | | // Predefined config parameters |
| | | private final int degradedStatusThreshold = 5000; |
| | | |
| | | // Parameters set with received server start message |
| | | private String baseDn = null; |
| | | private String baseDn; |
| | | private long generationId = -1L; |
| | | private ServerState serverState = null; |
| | | private ServerState serverState; |
| | | private int windowSize = -1; |
| | | private byte groupId = (byte) -1; |
| | | private byte groupId = -1; |
| | | private boolean sslEncryption = false; |
| | | // The scenario this RS is expecting |
| | | /** The scenario this RS is expecting */ |
| | | private int scenario = -1; |
| | | |
| | | // parameters at handshake are ok |
| | | /** parameters at handshake are ok */ |
| | | private boolean handshakeOk = false; |
| | | // signal that the current scenario the RS must execute reached the point |
| | | // where the main code can perform test assertion |
| | | /** |
| | | * signal that the current scenario the RS must execute reached the point |
| | | * where the main code can perform test assertion. |
| | | */ |
| | | private boolean scenarioExecuted = false; |
| | | |
| | | private ChangeNumberGenerator gen = null; |
| | | private CSNGenerator gen; |
| | | private String testcase; |
| | | |
| | | // Constructor for RS receiving updates in SR assured mode or not assured |
| | | // The assured boolean means: |
| | | // - true: SR mode |
| | | // - false: not assured |
| | | /** |
| | | * Constructor for RS receiving updates in SR assured mode or not assured |
| | | * The assured boolean means: |
| | | * - true: SR mode |
| | | * - false: not assured |
| | | */ |
| | | public FakeReplicationServer(byte groupId, int port, int serverId, boolean assured, |
| | | String testcase) |
| | | { |
| | |
| | | */ |
| | | public void start(int scenario) |
| | | { |
| | | |
| | | gen = new ChangeNumberGenerator(3, 0L); |
| | | gen = new CSNGenerator(3, 0L); |
| | | |
| | | // Store expected test case |
| | | this.scenario = scenario; |
| | |
| | | @Override |
| | | public void run() |
| | | { |
| | | |
| | | // Create server socket |
| | | try |
| | | { |
| | |
| | | */ |
| | | private boolean performHandshake() |
| | | { |
| | | |
| | | try |
| | | { |
| | | // Receive server start |
| | |
| | | private void handleClientConnection() |
| | | { |
| | | debugInfo("handleClientConnection " + testcase + " " + scenario); |
| | | // Handle DS connexion |
| | | // Handle DS connection |
| | | if (!performHandshake()) |
| | | { |
| | | session.close(); |
| | |
| | | { |
| | | try |
| | | { |
| | | // Create add message |
| | | AddMsg addMsg = |
| | | new AddMsg(gen.newChangeNumber(), entry.getDN().toString(), UUID.randomUUID().toString(), |
| | | new AddMsg(gen.newCSN(), entry.getDN().toString(), UUID.randomUUID().toString(), |
| | | parentUid, |
| | | entry.getObjectClassAttribute(), |
| | | entry.getAttributes(), null ); |
| | |
| | | */ |
| | | private void executeNoTimeoutScenario() |
| | | { |
| | | |
| | | try |
| | | { |
| | | UpdateMsg updateMsg = (UpdateMsg) session.receive(); |
| | |
| | | sleep(NO_TIMEOUT_RS_SLEEP_TIME); |
| | | |
| | | // Send the ack without errors |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber()); |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getCSN()); |
| | | session.publish(ackMsg); |
| | | |
| | | scenarioExecuted = true; |
| | |
| | | { |
| | | assertEquals(updateMsg.isAssured(), isAssured, |
| | | "msg=" + ((updateMsg instanceof AddMsg)? |
| | | ((AddMsg)updateMsg).getDn():updateMsg.getChangeNumber())); |
| | | ((AddMsg)updateMsg).getDn():updateMsg.getCSN())); |
| | | if (isAssured) |
| | | { |
| | | assertEquals(updateMsg.getAssuredMode(), assuredMode); |
| | |
| | | */ |
| | | private void executeSafeReadManyErrorsScenario() |
| | | { |
| | | |
| | | try |
| | | { |
| | | // Read first update |
| | |
| | | List<Integer> serversInError = new ArrayList<Integer>(); |
| | | serversInError.add(10); |
| | | serversInError.add(20); |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber(), false, false, true, serversInError); |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getCSN(), false, false, true, serversInError); |
| | | session.publish(ackMsg); |
| | | |
| | | // Read second update |
| | |
| | | serversInError.add(10); |
| | | serversInError.add(20); |
| | | serversInError.add(30); |
| | | ackMsg = new AckMsg(updateMsg.getChangeNumber(), true, true, true, serversInError); |
| | | ackMsg = new AckMsg(updateMsg.getCSN(), true, true, true, serversInError); |
| | | session.publish(ackMsg); |
| | | |
| | | // Read third update |
| | |
| | | */ |
| | | private void executeSafeDataManyErrorsScenario() |
| | | { |
| | | |
| | | try |
| | | { |
| | | // Read first update |
| | |
| | | // - server 10 error |
| | | List<Integer> serversInError = new ArrayList<Integer>(); |
| | | serversInError.add(10); |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber(), true, false, false, serversInError); |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getCSN(), true, false, false, serversInError); |
| | | session.publish(ackMsg); |
| | | |
| | | // Read second update |
| | |
| | | serversInError = new ArrayList<Integer>(); |
| | | serversInError.add(10); |
| | | serversInError.add(20); |
| | | ackMsg = new AckMsg(updateMsg.getChangeNumber(), true, false, false, serversInError); |
| | | ackMsg = new AckMsg(updateMsg.getCSN(), true, false, false, serversInError); |
| | | session.publish(ackMsg); |
| | | |
| | | // Read third update |
| | |
| | | protected Map<Integer,Integer> getErrorsByServers(DN baseDn, |
| | | AssuredMode assuredMode) throws Exception |
| | | { |
| | | /* |
| | | * Find monitoring entry for requested base DN |
| | | */ |
| | | // Find monitoring entry for requested base DN |
| | | String monitorFilter = |
| | | "(&(cn=Directory server*)(domain-name=" + baseDn + "))"; |
| | | |
| | |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.Map; |
| | | |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.AttributeValues; |
| | |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test AttrInfo and AttrInfoWithOptions |
| | | */ |
| | |
| | | AttributeValue att2 = AttributeValues.create(type, "value"); |
| | | AttributeValue att3 = AttributeValues.create(type, "again"); |
| | | |
| | | ChangeNumber del1 = new ChangeNumber(1, 0, 1); |
| | | ChangeNumber del2 = new ChangeNumber(1, 1, 1); |
| | | ChangeNumber del3 = new ChangeNumber(1, 0, 2); |
| | | CSN del1 = new CSN(1, 0, 1); |
| | | CSN del2 = new CSN(1, 1, 1); |
| | | CSN del3 = new CSN(1, 0, 2); |
| | | |
| | | ChangeNumber upd1 = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | ChangeNumber upd2 = new ChangeNumber(TimeThread.getTime() + 1000, 123, 45); |
| | | ChangeNumber upd3 = new ChangeNumber(TimeThread.getTime(), 321, 54); |
| | | CSN upd1 = new CSN(TimeThread.getTime(), 123, 45); |
| | | CSN upd2 = new CSN(TimeThread.getTime() + 1000, 123, 45); |
| | | CSN upd3 = new CSN(TimeThread.getTime(), 321, 54); |
| | | |
| | | return new Object[][] |
| | | { |
| | |
| | | */ |
| | | @Test(dataProvider = "attrInfo") |
| | | public void attrInfo( |
| | | AttributeValue att, ChangeNumber deleteTime, ChangeNumber updateTime) |
| | | AttributeValue att, CSN deleteTime, CSN updateTime) |
| | | throws Exception |
| | | { |
| | | // Create an empty AttrInfo |
| | | AttrHistoricalMultiple attrInfo1 = new AttrHistoricalMultiple(); |
| | | |
| | | // Check add(AttributeValue val, ChangeNumber CN) |
| | | // Check |
| | | attrInfo1.add(att, updateTime); |
| | | Map<AttrValueHistorical,AttrValueHistorical> values1 = attrInfo1.getValuesHistorical(); |
| | | assertEquals(values1.size(), 1); |
| | |
| | | assertEquals(attrInfo4.getDeleteTime().compareTo(attrInfo3.getDeleteTime()), 0); |
| | | assertEquals(values4.size(), values3.size()); |
| | | |
| | | // Check delete(AttributeValue val, ChangeNumber CN) |
| | | // Check |
| | | attrInfo4.delete(att, updateTime); |
| | | assertEquals(attrInfo4.getValuesHistorical().size(), 1); |
| | | |
| | | // Check delete(LinkedHashSet<AttributeValue> values, ChangeNumber CN) |
| | | // Check |
| | | AttributeType type = DirectoryServer.getAttributeType("description"); |
| | | attrInfo3.delete(Attributes.create(type, att), updateTime) ; |
| | | assertEquals(attrInfo3.getValuesHistorical().size(), 1); |
| | | |
| | | // Check delete(ChangeNumber CN) |
| | | // Check |
| | | attrInfo2.delete(updateTime) ; |
| | | assertEquals(attrInfo2.getValuesHistorical().size(), 0); |
| | | } |
| | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import static org.opends.server.replication.service.ReplicationBroker.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | import static org.opends.server.loggers.debug.DebugLogger.getTracer; |
| | | import static org.testng.Assert.*; |
| | | |
| | | import org.opends.messages.Category; |
| | | import org.opends.messages.Message; |
| | | import org.opends.messages.Severity; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.RSInfo; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.protocol.ReplServerStartMsg; |
| | | import org.opends.server.replication.server.ReplicationServer; |
| | | import org.opends.server.replication.service.ReplicationBroker.ReplicationServerInfo; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.replication.service.ReplicationBroker.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test the algorithm for finding the best replication server among the |
| | | * configured ones. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class ComputeBestServerTest extends ReplicationTestCase |
| | | { |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * Test with one replication server, nobody has a change number (simulates) |
| | | * very first connection. |
| | | * |
| | | * @throws Exception If a problem occurred |
| | | * Test with one replication server, nobody has a CSN (simulates) very first |
| | | * connection. |
| | | */ |
| | | @Test |
| | | public void testNullCNBoth() throws Exception |
| | | public void testNullCSNBoth() throws Exception |
| | | { |
| | | String testCase = "testNullCNBoth"; |
| | | String testCase = "testNullCSNBoth"; |
| | | |
| | | debugInfo("Starting " + testCase); |
| | | |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(0L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(0L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(0L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(0L, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, WINNER, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | /** |
| | | * Test with one replication server, only replication server has a non null |
| | | * changenumber for ds server id |
| | | * @throws Exception If a problem occurred |
| | | * CSN for ds server id |
| | | */ |
| | | @Test |
| | | public void testNullCNDS() throws Exception |
| | | public void testNullCSNDS() throws Exception |
| | | { |
| | | String testCase = "testNullCNDS"; |
| | | String testCase = "testNullCSNDS"; |
| | | |
| | | debugInfo("Starting " + testCase); |
| | | |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(0L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(0L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(0L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(0L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(0L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(0L, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, WINNER, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Test with one replication server, only ds server has a non null |
| | | * changenumber for ds server id but rs has a null one. |
| | | * |
| | | * @throws Exception If a problem occurred |
| | | * Test with one replication server, only ds server has a non null CSN for ds |
| | | * server id but rs has a null one. |
| | | */ |
| | | @Test |
| | | public void testNullCNRS() throws Exception |
| | | public void testNullCSNRS() throws Exception |
| | | { |
| | | String testCase = "testNullCNRS"; |
| | | String testCase = "testNullCSNRS"; |
| | | |
| | | debugInfo("Starting " + testCase); |
| | | |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(1L, 0, myId1); |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(1L, 0, myId1); |
| | | mySt.update(csn); |
| | | csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(0L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(0L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(0L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(0L, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, WINNER, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(1L, 0, myId1); |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(1L, 0, myId1); |
| | | mySt.update(csn); |
| | | csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(1L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(1L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, WINNER, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(1L, 0, myId1); |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(1L, 0, myId1); |
| | | mySt.update(csn); |
| | | csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(1L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(1L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, LOOSER1, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // State for server 2 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(2L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(2L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(12, WINNER, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(1L, 0, myId1); |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(1L, 0, myId1); |
| | | mySt.update(csn); |
| | | csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(1L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(1L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | // This server has less changes than the other one but it has the same |
| | | // group id as us so he should be the winner |
| | | ReplServerStartMsg replServerStartMsg = |
| | |
| | | |
| | | // State for server 2 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(2L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(2L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(12, LOOSER1, null, 0, aState, 0L, |
| | | false, (byte)2, 0); |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(1L, 0, myId1); |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(1L, 0, myId1); |
| | | mySt.update(csn); |
| | | csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(1L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(1L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, LOOSER1, null, 0, aState, 0L, |
| | | false, (byte)2, 0); |
| | |
| | | |
| | | // State for server 2 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(2L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(2L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(2L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(2L, 0, myId3); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(12, WINNER, null, 0, aState, 0L, |
| | | false, (byte)2, 0); |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(1L, 0, myId1); |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(1L, 0, myId1); |
| | | mySt.update(csn); |
| | | csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(1L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(1L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, LOOSER1, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // State for server 2 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(2L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(4L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(2L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(4L, 0, myId3); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(12, LOOSER2, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // State for server 3 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(3L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(3L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(2L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(13, WINNER, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(1L, 0, myId1); |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(1L, 0, myId1); |
| | | mySt.update(csn); |
| | | csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(1L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(1L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, LOOSER1, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // State for server 2 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(2L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(2L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(3L, 0, myId3); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(12, LOOSER2, null, 0, aState, 0L, |
| | | false, (byte)2, 0); |
| | |
| | | |
| | | // State for server 3 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(3L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(3L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(2L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | // This server has less changes than looser2 but it has the same |
| | | // group id as us so he should be the winner |
| | | replServerStartMsg = |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(1L, 0, myId1); |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(1L, 0, myId1); |
| | | mySt.update(csn); |
| | | csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(0L, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(1L, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(0L, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(1L, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, WINNER, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(4L, 0, myId1); |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | cn = new ChangeNumber(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(4L, 0, myId1); |
| | | mySt.update(csn); |
| | | csn = new CSN(2L, 0, myId2); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | csn = new CSN(3L, 0, myId3); // Should not be used inside algo |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(looser1T1, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(looser1T2, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(looser1T3, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(looser1T1, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(looser1T2, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(looser1T3, 0, myId3); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, LOOSER1, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // State for server 2 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(winnerT1, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(winnerT2, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(winnerT3, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(winnerT1, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(winnerT2, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(winnerT3, 0, myId3); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(12, WINNER, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // State for server 3 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(looser2T1, 0, myId1); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(looser2T2, 0, myId2); |
| | | aState.update(cn); |
| | | cn = new ChangeNumber(looser2T3, 0, myId3); |
| | | aState.update(cn); |
| | | csn = new CSN(looser2T1, 0, myId1); |
| | | aState.update(csn); |
| | | csn = new CSN(looser2T2, 0, myId2); |
| | | aState.update(csn); |
| | | csn = new CSN(looser2T3, 0, myId3); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(13, LOOSER2, null, 0, aState, 0L, |
| | | false, (byte)1, 0); |
| | |
| | | |
| | | // definitions for server ids |
| | | int myId1 = 1; |
| | | int myId2 = 2; |
| | | int myId3 = 3; |
| | | |
| | | // definitions for server names |
| | | final String WINNER = "localhost:123"; |
| | | final String LOOSER1 = "localhost:456"; |
| | |
| | | |
| | | // Create my state |
| | | ServerState mySt = new ServerState(); |
| | | ChangeNumber cn = new ChangeNumber(4L, 0, myId1); |
| | | mySt.update(cn); |
| | | CSN csn = new CSN(4L, 0, myId1); |
| | | mySt.update(csn); |
| | | |
| | | // Create replication servers info list |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = |
| | | Map<Integer, ReplicationServerInfo> rsInfos = |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | |
| | | // State for server 1 |
| | | ServerState aState = new ServerState(); |
| | | cn = new ChangeNumber(looser1T1, 0, myId1); |
| | | aState.update(cn); |
| | | csn = new CSN(looser1T1, 0, myId1); |
| | | aState.update(csn); |
| | | ReplServerStartMsg replServerStartMsg = |
| | | new ReplServerStartMsg(11, LOOSER1, null, 0, aState, looser1GenId, |
| | | false, looser1GroupId, 0); |
| | |
| | | |
| | | // State for server 2 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(winnerT1, 0, myId1); |
| | | aState.update(cn); |
| | | csn = new CSN(winnerT1, 0, myId1); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(12, WINNER, null, 0, aState, winnerGenId, |
| | | false, winnerGroupId, 0); |
| | |
| | | |
| | | // State for server 3 |
| | | aState = new ServerState(); |
| | | cn = new ChangeNumber(looser2T1, 0, myId1); |
| | | aState.update(cn); |
| | | csn = new CSN(looser2T1, 0, myId1); |
| | | aState.update(csn); |
| | | replServerStartMsg = |
| | | new ReplServerStartMsg(13, LOOSER2, null, 0, aState, looser2GenId, |
| | | false, looser2GroupId, 0); |
| | |
| | | |
| | | Object[][] testData = new Object[24][]; |
| | | |
| | | HashMap<Integer, ReplicationServerInfo> rsInfos = null; |
| | | Map<Integer, ReplicationServerInfo> rsInfos = null; |
| | | new HashMap<Integer, ReplicationServerInfo>(); |
| | | RSInfo rsInfo = null; |
| | | List<Integer> connectedDSs = null; |
| | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.common.ServerStatus; |
| | | import org.opends.server.replication.protocol.AddMsg; |
| | | import org.opends.server.replication.protocol.ModifyDNMsg; |
| | |
| | | private static final int INCLUDE_FRAC_MODE = 1; |
| | | |
| | | int initWindow = 100; |
| | | private ChangeNumberGenerator gen = null; |
| | | private CSNGenerator gen = null; |
| | | |
| | | /** The tracer object for the debug logger */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | |
| | | fractionalDomainCfgEntry = null; |
| | | replicationServer = null; |
| | | |
| | | // Initialize the test backend |
| | | TestCaseUtils.initializeTestBackend(false); |
| | | |
| | | // initialize cn generator |
| | | gen = new ChangeNumberGenerator(DS2_ID, 0L); |
| | | gen = new CSNGenerator(DS2_ID, 0L); |
| | | } |
| | | |
| | | private void endTest() |
| | |
| | | Entry entry = TestCaseUtils.entryFromLdifString(entryLdif); |
| | | |
| | | // Create an update message to add an entry. |
| | | AddMsg addMsg = new AddMsg(gen.newChangeNumber(), |
| | | AddMsg addMsg = new AddMsg(gen.newCSN(), |
| | | entry.getDN().toString(), |
| | | ENTRY_UUID, |
| | | null, |
| | |
| | | mods.add(mod); |
| | | |
| | | DN entryDn = DN.decode((firstBackend ? ENTRY_DN : ENTRY_DN2)); |
| | | ModifyMsg modifyMsg = new ModifyMsg(gen.newChangeNumber(), entryDn, mods, |
| | | ModifyMsg modifyMsg = new ModifyMsg(gen.newCSN(), entryDn, mods, |
| | | ENTRY_UUID); |
| | | |
| | | replicationDomain.publish(modifyMsg); |
| | |
| | | Entry entry = TestCaseUtils.entryFromLdifString(entryLdif); |
| | | |
| | | // Create an update message to add an entry. |
| | | AddMsg addMsg = new AddMsg(gen.newChangeNumber(), |
| | | AddMsg addMsg = new AddMsg(gen.newCSN(), |
| | | entry.getDN().toString(), |
| | | ENTRY_UUID, |
| | | null, |
| | |
| | | entry = TestCaseUtils.entryFromLdifString(entryLdif); |
| | | |
| | | // Create an update message to add an entry. |
| | | addMsg = new AddMsg(gen.newChangeNumber(), |
| | | addMsg = new AddMsg(gen.newCSN(), |
| | | entry.getDN().toString(), |
| | | ENTRY_UUID2, |
| | | null, |
| | |
| | | Entry entry = TestCaseUtils.entryFromLdifString(entryLdif); |
| | | |
| | | // Create an update message to add an entry. |
| | | AddMsg addMsg = new AddMsg(gen.newChangeNumber(), |
| | | AddMsg addMsg = new AddMsg(gen.newCSN(), |
| | | entry.getDN().toString(), |
| | | ENTRY_UUID, |
| | | null, |
| | |
| | | entry = TestCaseUtils.entryFromLdifString(entryLdif); |
| | | |
| | | // Create an update message to add an entry. |
| | | addMsg = new AddMsg(gen.newChangeNumber(), |
| | | addMsg = new AddMsg(gen.newCSN(), |
| | | entry.getDN().toString(), |
| | | ENTRY_UUID2, |
| | | null, |
| | |
| | | Entry entry = TestCaseUtils.entryFromLdifString(entryLdif); |
| | | |
| | | // Create an update message to add an entry. |
| | | AddMsg addMsg = new AddMsg(gen.newChangeNumber(), |
| | | AddMsg addMsg = new AddMsg(gen.newCSN(), |
| | | entry.getDN().toString(), |
| | | ENTRY_UUID, |
| | | null, |
| | |
| | | DN newEntryDn = DN.decode(newEntryName); |
| | | |
| | | // Create modify dn message to modify the entry. |
| | | ModifyDNMsg modDnMsg = new ModifyDNMsg(entryName, gen.newChangeNumber(), |
| | | ModifyDNMsg modDnMsg = new ModifyDNMsg(entryName, gen.newCSN(), |
| | | ENTRY_UUID, ENTRY_UUID3, false, TEST_ROOT_DN_STRING, |
| | | "displayName=ValueToBeKept", null); |
| | | |
| | |
| | | Entry entry = TestCaseUtils.entryFromLdifString(entryLdif); |
| | | |
| | | // Create an update message to add an entry. |
| | | AddMsg addMsg = new AddMsg(gen.newChangeNumber(), |
| | | AddMsg addMsg = new AddMsg(gen.newCSN(), |
| | | entry.getDN().toString(), |
| | | ENTRY_UUID, |
| | | null, |
| | |
| | | DN newEntryDn = DN.decode(newEntryName); |
| | | |
| | | // Create modify dn message to modify the entry. |
| | | ModifyDNMsg modDnMsg = new ModifyDNMsg(entryName, gen.newChangeNumber(), |
| | | ModifyDNMsg modDnMsg = new ModifyDNMsg(entryName, gen.newCSN(), |
| | | ENTRY_UUID, ENTRY_UUID3, false, TEST_ROOT_DN_STRING, |
| | | "displayName=ValueToBeKept", null); |
| | | |
| | |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.opends.server.TestCaseUtils.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | import java.io.File; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.*; |
| | | import org.opends.server.replication.server.ReplServerFakeConfiguration; |
| | | import org.opends.server.replication.server.ReplicationServer; |
| | |
| | | import org.opends.server.util.TimeThread; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.opends.server.TestCaseUtils.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test the usage of the historical data of the replication. |
| | | */ |
| | |
| | | { |
| | | List<ReplicationMsg> list = null; |
| | | |
| | | public TestBroker(LinkedList<ReplicationMsg> list) |
| | | public TestBroker(List<ReplicationMsg> list) |
| | | { |
| | | super(null, null, null, 0, 0, 0, 0, null, (byte) 0, 0); |
| | | this.list = list; |
| | |
| | | HistoricalCsnOrderingMatchingRule r = |
| | | new HistoricalCsnOrderingMatchingRule(); |
| | | |
| | | ChangeNumber del1 = new ChangeNumber(1, 0, 1); |
| | | ChangeNumber del2 = new ChangeNumber(1, 1, 1); |
| | | CSN del1 = new CSN(1, 0, 1); |
| | | CSN del2 = new CSN(1, 1, 1); |
| | | |
| | | ByteString v1 = ByteString.valueOf("a"+":"+del1.toString()); |
| | | ByteString v2 = ByteString.valueOf("a"+":"+del2.toString()); |
| | |
| | | "description: foo"); |
| | | assertEquals(resultCode, 0); |
| | | |
| | | // Read the entry back to get its historical and included changeNumber |
| | | // Read the entry back to get its historical and included CSN |
| | | Entry entry = DirectoryServer.getEntry(dn1); |
| | | List<Attribute> attrs1 = entry.getAttribute(histType); |
| | | |
| | |
| | | |
| | | boolean result = |
| | | rd1.buildAndPublishMissingChanges( |
| | | new ChangeNumber(startTime, 0, serverId), |
| | | new CSN(startTime, 0, serverId), |
| | | session); |
| | | assertTrue(result, "buildAndPublishMissingChanges has failed"); |
| | | assertEquals(opList.size(), 3, "buildAndPublishMissingChanges should return 3 operations"); |
| | | assertTrue(opList.getFirst().getClass().equals(AddMsg.class)); |
| | | |
| | | |
| | | // Build a change number from the first modification |
| | | // Build a CSN from the first modification |
| | | String hv[] = histValue.split(":"); |
| | | logError(Message.raw(Category.SYNC, Severity.INFORMATION, hv[1])); |
| | | ChangeNumber fromChangeNumber = new ChangeNumber(hv[1]); |
| | | CSN fromCSN = new CSN(hv[1]); |
| | | |
| | | opList = new LinkedList<ReplicationMsg>(); |
| | | session = new TestBroker(opList); |
| | | |
| | | result = |
| | | rd1.buildAndPublishMissingChanges( |
| | | fromChangeNumber, |
| | | session); |
| | | result = rd1.buildAndPublishMissingChanges(fromCSN, session); |
| | | assertTrue(result, "buildAndPublishMissingChanges has failed"); |
| | | assertEquals(opList.size(), 1, "buildAndPublishMissingChanges should return 1 operation"); |
| | | assertTrue(opList.getFirst().getClass().equals(ModifyMsg.class)); |
| | |
| | | // correctly generates the 4 operations in the correct order. |
| | | boolean result = |
| | | rd1.buildAndPublishMissingChanges( |
| | | new ChangeNumber(startTime, 0, serverId), |
| | | new CSN(startTime, 0, serverId), |
| | | session); |
| | | assertTrue(result, "buildAndPublishMissingChanges has failed"); |
| | | assertEquals(opList.size(), 5, "buildAndPublishMissingChanges should return 5 operations"); |
| | |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.protocols.ldap.LDAPFilter; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.AddMsg; |
| | | import org.opends.server.replication.protocol.LDAPUpdateMsg; |
| | | import org.opends.server.replication.protocol.ModifyMsg; |
| | |
| | | * Tests the Historical class. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class HistoricalTest |
| | | extends ReplicationTestCase |
| | | public class HistoricalTest extends ReplicationTestCase |
| | | { |
| | | |
| | | private int replServerPort; |
| | | private String testName = "historicalTest"; |
| | | |
| | |
| | | |
| | | long now = System.currentTimeMillis(); |
| | | // A change on a first server. |
| | | ChangeNumber t1 = new ChangeNumber(now, 0, 3); |
| | | CSN t1 = new CSN(now, 0, 3); |
| | | |
| | | // A change on a second server. |
| | | ChangeNumber t2 = new ChangeNumber(now+1, 0, 4); |
| | | CSN t2 = new CSN(now+1, 0, 4); |
| | | |
| | | // Simulate the ordering t1:add:A followed by t2:add:B that would |
| | | // happen on one server. |
| | |
| | | // Simulate the reverse ordering t2:add:B followed by t1:add:A that |
| | | // would happen on the other server. |
| | | |
| | | t1 = new ChangeNumber(now+3, 0, 3); |
| | | t2 = new ChangeNumber(now+4, 0, 4); |
| | | t1 = new CSN(now+3, 0, 3); |
| | | t2 = new CSN(now+4, 0, 4); |
| | | |
| | | // Replay an add of a value B at time t2 on a second server. |
| | | attr = Attributes.create(attrType.getNormalizedPrimaryName(), "B"); |
| | |
| | | } |
| | | |
| | | private static |
| | | void publishModify(ReplicationBroker broker, ChangeNumber changeNum, |
| | | void publishModify(ReplicationBroker broker, CSN changeNum, |
| | | DN dn, String entryuuid, Modification mod) |
| | | { |
| | | List<Modification> mods = new ArrayList<Modification>(1); |
| | |
| | | // - the dn should be dn1, |
| | | // - the entry id and the parent id should match the ids from the entry |
| | | FakeAddOperation addOp = (FakeAddOperation) op; |
| | | assertTrue(addOp.getChangeNumber() != null); |
| | | assertTrue(addOp.getCSN() != null); |
| | | AddMsg addmsg = addOp.generateMessage(); |
| | | assertTrue(dn1.equals(DN.decode(addmsg.getDn()))); |
| | | assertTrue(addmsg.getEntryUUID().equals(EntryHistorical.getEntryUUID(entry))); |
| | |
| | | * |
| | | * TODO: another test should be written that configures the task no NOT have |
| | | * the time to purge everything in 1 run .. and thus to relauch it to finish |
| | | * the purge. And verify that the second run starts on the changeNumber where |
| | | * the purge. And verify that the second run starts on the CSN where |
| | | * the previous task run had stopped. |
| | | */ |
| | | @Test(enabled=true) |
| | |
| | | import org.opends.server.core.ModifyOperationBasis; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.LDAPUpdateMsg; |
| | | import org.opends.server.replication.protocol.ModifyContext; |
| | | import org.opends.server.replication.protocol.ReplicationMsg; |
| | |
| | | { |
| | | InternalClientConnection aConnection = |
| | | InternalClientConnection.getRootConnection(); |
| | | ChangeNumber t = new ChangeNumber(date, 0, 0); |
| | | CSN t = new CSN(date, 0, 0); |
| | | |
| | | ModifyOperationBasis modOpBasis = |
| | | new ModifyOperationBasis(aConnection, 1, 1, null, entry.getDN(), mods); |
| | |
| | | |
| | | InternalClientConnection aConnection = |
| | | InternalClientConnection.getRootConnection(); |
| | | ChangeNumber t = new ChangeNumber(date, 0, 0); |
| | | CSN t = new CSN(date, 0, 0); |
| | | |
| | | List<Modification> mods = new ArrayList<Modification>(); |
| | | mods.add(mod); |
| | |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.opends.server.TestCaseUtils.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.TreeSet; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | |
| | | import org.opends.server.core.ModifyDNOperation; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.protocol.AddMsg; |
| | | import org.opends.server.replication.protocol.DeleteMsg; |
| | | import org.opends.server.replication.protocol.ModifyDNMsg; |
| | | import org.opends.server.types.*; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.opends.server.TestCaseUtils.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test the naming conflict resolution code. |
| | | */ |
| | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new ChangeNumbers |
| | | * Create a CSN generator to generate new CSNs |
| | | * when we need to send operations messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(201, 0); |
| | | CSNGenerator gen = new CSNGenerator(201, 0); |
| | | |
| | | String parentUUID = getEntryUUID(DN.decode(TEST_ROOT_DN_STRING)); |
| | | |
| | |
| | | TestCaseUtils.addEntry(entry); |
| | | String entryUUID = getEntryUUID(entry.getDN()); |
| | | |
| | | // generate two consecutive ChangeNumber that will be used in backward order |
| | | ChangeNumber cn1 = gen.newChangeNumber(); |
| | | ChangeNumber cn2 = gen.newChangeNumber(); |
| | | // generate two consecutive CSN that will be used in backward order |
| | | CSN csn1 = gen.newCSN(); |
| | | CSN csn2 = gen.newCSN(); |
| | | |
| | | ModifyDNMsg modDnMsg = new ModifyDNMsg( |
| | | entry.getDN().toNormalizedString(), cn2, |
| | | entry.getDN().toNormalizedString(), csn2, |
| | | entryUUID, parentUUID, false, |
| | | TEST_ROOT_DN_STRING, |
| | | "uid=simultaneous2"); |
| | |
| | | // This MODIFY DN uses an older DN and should therefore be cancelled |
| | | // at replay time. |
| | | modDnMsg = new ModifyDNMsg( |
| | | entry.getDN().toNormalizedString(), cn1, |
| | | entry.getDN().toNormalizedString(), csn1, |
| | | entryUUID, parentUUID, false, |
| | | TEST_ROOT_DN_STRING, |
| | | "uid=simulatneouswrong"); |
| | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new ChangeNumbers |
| | | * Create a CSN generator to generate new CSNs |
| | | * when we need to send operations messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(201, 0); |
| | | CSNGenerator gen = new CSNGenerator(201, 0); |
| | | |
| | | String entryldif = |
| | | "dn: cn=conflictCleaningDelete, "+ TEST_ROOT_DN_STRING + "\n" |
| | |
| | | TestCaseUtils.addEntry(entry); |
| | | String parentUUID = getEntryUUID(DN.decode(TEST_ROOT_DN_STRING)); |
| | | |
| | | ChangeNumber cn1 = gen.newChangeNumber(); |
| | | CSN csn1 = gen.newCSN(); |
| | | |
| | | // Now try to add the same entry with same DN but a different |
| | | // unique ID though the replication |
| | | AddMsg addMsg = |
| | | new AddMsg(cn1, |
| | | AddMsg addMsg = new AddMsg(csn1, |
| | | entry.getDN().toNormalizedString(), |
| | | "c9cb8c3c-615a-4122-865d-50323aaaed48", parentUUID, |
| | | entry.getObjectClasses(), entry.getUserAttributes(), |
| | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new ChangeNumbers |
| | | * when we need to send operations messages to the replicationServer. |
| | | * Create a CSN generator to generate new CSNs when we need to send |
| | | * operations messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(201, 0); |
| | | CSNGenerator gen = new CSNGenerator(201, 0); |
| | | |
| | | String entryldif = |
| | | "dn: cn=conflictCleaningDelete, "+ TEST_ROOT_DN_STRING + "\n" |
| | |
| | | TestCaseUtils.addEntry(entry); |
| | | String parentUUID = getEntryUUID(DN.decode(TEST_ROOT_DN_STRING)); |
| | | |
| | | ChangeNumber cn1 = gen.newChangeNumber(); |
| | | CSN csn1 = gen.newCSN(); |
| | | |
| | | // Now try to add the same entry with same DN but a different |
| | | // unique ID though the replication |
| | | AddMsg addMsg = |
| | | new AddMsg(cn1, |
| | | AddMsg addMsg = new AddMsg(csn1, |
| | | entry.getDN().toNormalizedString(), |
| | | "c9cb8c3c-615a-4122-865d-50323aaaed48", parentUUID, |
| | | entry.getObjectClasses(), entry.getUserAttributes(), |
| | |
| | | * ADD uid=xx,ou=parent,... [SUBTREE] DEL ou=parent, ... |
| | | * |
| | | * 1/ removeParentConflict1 (on S1) |
| | | * - t1(cn1) ADD uid=xx,ou=parent,... |
| | | * - t2(cn2) replay SUBTREE DEL ou=parent, .... |
| | | * - t1(csn1) ADD uid=xx,ou=parent,... |
| | | * - t2(csn2) replay SUBTREE DEL ou=parent, .... |
| | | * => No conflict : expect the parent entry & subtree to be deleted |
| | | * |
| | | * 2/ removeParentConflict2 (on S1) |
| | | * - t1(cn1) ADD uid=xx,ou=parent,... |
| | | * - replay t2(cn2) DEL ou=parent, .... |
| | | * - t1(csn1) ADD uid=xx,ou=parent,... |
| | | * - replay t2(csn2) DEL ou=parent, .... |
| | | * => Conflict and no automatic resolution: expect |
| | | * - the child entry to be renamed under root entry |
| | | * - the parent entry to be deleted |
| | | * |
| | | * 3/ removeParentConflict3 (on S2) |
| | | * - t2(cn2) DEL or SUBTREE DEL ou=parent, .... |
| | | * - t1(cn1) replay ADD uid=xx,ou=parent,... |
| | | * - t2(csn2) DEL or SUBTREE DEL ou=parent, .... |
| | | * - t1(csn1) replay ADD uid=xx,ou=parent,... |
| | | * => Conflict and no automatic resolution: expect |
| | | * - the child entry to be renamed under root entry |
| | | * |
| | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new ChangeNumbers |
| | | * Create a CSN generator to generate new CSNs |
| | | * when we need to send operations messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(201, 0); |
| | | CSNGenerator gen = new CSNGenerator(201, 0); |
| | | |
| | | Entry parentEntry = TestCaseUtils.entryFromLdifString( |
| | | "dn: ou=rpConflict, "+ TEST_ROOT_DN_STRING + "\n" |
| | |
| | | |
| | | String parentUUID = getEntryUUID(parentEntry.getDN()); |
| | | |
| | | ChangeNumber cn2 = gen.newChangeNumber(); |
| | | CSN csn2 = gen.newCSN(); |
| | | |
| | | DeleteMsg delMsg = new DeleteMsg( |
| | | parentEntry.getDN().toNormalizedString(), |
| | | cn2, |
| | | csn2, |
| | | parentUUID); |
| | | delMsg.setSubtreeDelete(true); |
| | | |
| | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new ChangeNumbers |
| | | * Create a CSN generator to generate new CSNs |
| | | * when we need to send operations messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(201, 0); |
| | | CSNGenerator gen = new CSNGenerator(201, 0); |
| | | |
| | | Entry parentEntry = TestCaseUtils.entryFromLdifString( |
| | | "dn: ou=rpConflict, "+ TEST_ROOT_DN_STRING + "\n" |
| | |
| | | String parentUUID = getEntryUUID(parentEntry.getDN()); |
| | | String childUUID = getEntryUUID(childEntry.getDN()); |
| | | |
| | | ChangeNumber cn2 = gen.newChangeNumber(); |
| | | CSN csn2 = gen.newCSN(); |
| | | |
| | | DeleteMsg delMsg = new DeleteMsg( |
| | | parentEntry.getDN().toNormalizedString(), |
| | | cn2, |
| | | csn2, |
| | | parentUUID); |
| | | // NOT SUBTREE |
| | | |
| | |
| | | try |
| | | { |
| | | /* |
| | | * Create a Change number generator to generate new ChangeNumbers |
| | | * Create a CSN generator to generate new CSNs |
| | | * when we need to send operations messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(201, 0); |
| | | CSNGenerator gen = new CSNGenerator(201, 0); |
| | | |
| | | Entry parentEntry = TestCaseUtils.entryFromLdifString( |
| | | "dn: ou=rpConflict, "+ TEST_ROOT_DN_STRING + "\n" |
| | |
| | | String parentUUID = getEntryUUID(parentEntry.getDN()); |
| | | TestCaseUtils.deleteEntry(parentEntry); |
| | | |
| | | ChangeNumber cn1 = gen.newChangeNumber(); |
| | | CSN csn1 = gen.newCSN(); |
| | | |
| | | // Create and publish an update message to add the child entry. |
| | | String childUUID = "44444444-4444-4444-4444-444444444444"; |
| | | AddMsg addMsg = new AddMsg( |
| | | cn1, |
| | | csn1, |
| | | childEntry.getDN().toString(), |
| | | childUUID, |
| | | parentUUID, |
| | |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.types.DN; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.opends.server.TestCaseUtils.TEST_ROOT_DN_STRING; |
| | | import static org.testng.Assert.assertEquals; |
| | | import static org.opends.server.TestCaseUtils.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test the PersistentServerState class. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class PersistentServerStateTest extends ReplicationTestCase |
| | | { |
| | | /** |
| | |
| | | * retrieve ServerState to persistent storage. |
| | | */ |
| | | @Test(dataProvider = "suffix") |
| | | public void persistentServerStateTest(String dn) |
| | | throws Exception |
| | | public void persistentServerStateTest(String dn) throws Exception |
| | | { |
| | | /* |
| | | * Create a new PersistentServerState, |
| | | * update it with 2 new ChangeNumbers with 2 different server Ids |
| | | * update it with 2 new csns with 2 different server Ids |
| | | * save it |
| | | * |
| | | * Then creates a new PersistentServerState and check that the |
| | | * 2 ChangeNumbers have been saved in this new PersistentServerState. |
| | | * 2 csns have been saved in this new PersistentServerState. |
| | | */ |
| | | DN baseDn = DN.decode(dn); |
| | | ServerState origState = new ServerState(); |
| | | PersistentServerState state = |
| | | new PersistentServerState(baseDn, 1, origState); |
| | | ChangeNumberGenerator gen1 = |
| | | new ChangeNumberGenerator( 1, origState); |
| | | ChangeNumberGenerator gen2 = |
| | | new ChangeNumberGenerator( 2, origState); |
| | | CSNGenerator gen1 = new CSNGenerator(1, origState); |
| | | CSNGenerator gen2 = new CSNGenerator(2, origState); |
| | | |
| | | ChangeNumber cn1 = gen1.newChangeNumber(); |
| | | ChangeNumber cn2 = gen2.newChangeNumber(); |
| | | CSN csn1 = gen1.newCSN(); |
| | | CSN csn2 = gen2.newCSN(); |
| | | |
| | | assertEquals(state.update(cn1), true); |
| | | assertEquals(state.update(cn2), true); |
| | | assertEquals(state.update(csn1), true); |
| | | assertEquals(state.update(csn2), true); |
| | | |
| | | state.save(); |
| | | |
| | | PersistentServerState stateSaved = new PersistentServerState(baseDn, 1); |
| | | ChangeNumber cn1Saved = stateSaved.getMaxChangeNumber( 1); |
| | | ChangeNumber cn2Saved = stateSaved.getMaxChangeNumber( 2); |
| | | PersistentServerState stateSaved = new PersistentServerState(baseDn, 1); |
| | | CSN csn1Saved = stateSaved.getMaxCSN(1); |
| | | CSN csn2Saved = stateSaved.getMaxCSN(2); |
| | | |
| | | assertEquals(cn1Saved, cn1, |
| | | "cn1 has not been saved or loaded correctly for " + dn); |
| | | assertEquals(cn2Saved, cn2, |
| | | "cn2 has not been saved or loaded correctly for " + dn); |
| | | assertEquals(csn1Saved, csn1, |
| | | "csn1 has not been saved or loaded correctly for " + dn); |
| | | assertEquals(csn2Saved, csn2, |
| | | "csn2 has not been saved or loaded correctly for " + dn); |
| | | |
| | | state.clear(); |
| | | stateSaved = new PersistentServerState(baseDn, 1); |
| | | cn1Saved = stateSaved.getMaxChangeNumber( 1); |
| | | assertEquals(cn1Saved, null, |
| | | "cn1 has not been saved after clear for " + dn); |
| | | |
| | | csn1Saved = stateSaved.getMaxCSN(1); |
| | | assertEquals(csn1Saved, null, |
| | | "csn1 has not been saved after clear for " + dn); |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.net.SocketTimeoutException; |
| | |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.common.DSInfo; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.common.ServerStatus; |
| | |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Some tests to go through the DS state machine and validate we get the |
| | | * expected status according to the actions we perform. |
| | |
| | | /** Number of sent changes */ |
| | | private int nChangesSent = 0; |
| | | private int nChangesSentLimit = 0; |
| | | ChangeNumberGenerator gen = null; |
| | | CSNGenerator gen = null; |
| | | private Object sleeper = new Object(); |
| | | /** |
| | | * If the BrokerWriter is to be used for a lot of changes to send (which is |
| | |
| | | super("BrokerWriter for broker " + serverId); |
| | | this.rb = rb; |
| | | this.serverId = serverId; |
| | | // Create a Change number generator to generate new change numbers |
| | | // Create a csn generator to generate new csns |
| | | // when we need to send changes |
| | | gen = new ChangeNumberGenerator(serverId, 0); |
| | | gen = new CSNGenerator(serverId, 0); |
| | | |
| | | // Start thread (is paused by default so will have to call follow anyway) |
| | | start(); |
| | |
| | | } |
| | | |
| | | // Create an update message to add an entry. |
| | | return new AddMsg(gen.newChangeNumber(), |
| | | return new AddMsg(gen.newCSN(), |
| | | personWithUUIDEntry.getDN().toString(), |
| | | userEntryUUID, |
| | | null, |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.plugin.AttrValueHistorical; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.AttributeValues; |
| | | import org.opends.server.util.TimeThread; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | import static org.testng.Assert.*; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test of ValueInfo |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class ValueInfoTest extends ReplicationTestCase |
| | | { |
| | | |
| | | /** |
| | | * Build some data for the ValueInfo test below. |
| | | */ |
| | |
| | | AttributeValue att2 = AttributeValues.create(type, "value"); |
| | | AttributeValue att3 = AttributeValues.create(type, "again"); |
| | | |
| | | ChangeNumber del1 = new ChangeNumber(1, 0, 1); |
| | | ChangeNumber del2 = new ChangeNumber(1, 1, 1); |
| | | ChangeNumber del3 = new ChangeNumber(1, 0, 2); |
| | | CSN del1 = new CSN(1, 0, 1); |
| | | CSN del2 = new CSN(1, 1, 1); |
| | | CSN del3 = new CSN(1, 0, 2); |
| | | |
| | | ChangeNumber upd1 = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | ChangeNumber upd2 = new ChangeNumber(TimeThread.getTime()+ 1000, 123, 45); |
| | | ChangeNumber upd3 = new ChangeNumber(TimeThread.getTime(), 321, 54); |
| | | CSN upd1 = new CSN(TimeThread.getTime(), 123, 45); |
| | | CSN upd2 = new CSN(TimeThread.getTime()+ 1000, 123, 45); |
| | | CSN upd3 = new CSN(TimeThread.getTime(), 321, 54); |
| | | |
| | | return new Object[][] { |
| | | {att1,null,null}, |
| | |
| | | * Create a ValueInfo and check the methods |
| | | */ |
| | | @Test(dataProvider = "valueInfo") |
| | | public void valueInfo(AttributeValue value, |
| | | ChangeNumber CNupdate, |
| | | ChangeNumber CNdelete) |
| | | throws Exception |
| | | public void valueInfo(AttributeValue value, CSN csnUpdate, CSN csnDelete) throws Exception |
| | | { |
| | | AttributeType type = DirectoryServer.getAttributeType("description"); |
| | | AttrValueHistorical valInfo1 = new AttrValueHistorical(value,CNupdate,CNdelete); |
| | | AttrValueHistorical valInfo2 = new AttrValueHistorical(value,CNupdate,CNupdate); |
| | | AttrValueHistorical valInfo1 = new AttrValueHistorical(value, csnUpdate, csnDelete); |
| | | AttrValueHistorical valInfo2 = new AttrValueHistorical(value, csnUpdate, csnUpdate); |
| | | AttrValueHistorical valInfo3 = new AttrValueHistorical(AttributeValues.create(type,"Test"), |
| | | CNupdate,CNupdate); |
| | | csnUpdate, csnUpdate); |
| | | |
| | | // Check equals |
| | | assertFalse(valInfo1.equals(new Object())) ; |
| | |
| | | // Check getValueDeleteTime |
| | | if (valInfo1.getValueDeleteTime() != null) |
| | | { |
| | | assertTrue(valInfo1.getValueDeleteTime().compareTo(CNdelete) == 0); |
| | | assertTrue(valInfo1.getValueDeleteTime().compareTo(csnDelete) == 0); |
| | | } |
| | | |
| | | // Check getValueUpdateTime |
| | | if (valInfo1.getValueUpdateTime() != null) |
| | | { |
| | | assertTrue(valInfo1.getValueUpdateTime().compareTo(CNupdate) == 0); |
| | | assertTrue(valInfo1.getValueUpdateTime().compareTo(csnUpdate) == 0); |
| | | } |
| | | |
| | | // Check getValue |
| | | assertTrue(valInfo1.getAttributeValue().equals(value)) ; |
| | | |
| | | // Chek valueUpdateTime |
| | | if (CNupdate == null) |
| | | if (csnUpdate == null) |
| | | { |
| | | assertFalse(valInfo1.isUpdate()); |
| | | } |
| | |
| | | * Copyright 2009-2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2011-2013 ForgeRock AS |
| | | */ |
| | | |
| | | package org.opends.server.replication.protocol; |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | |
| | | import org.opends.server.core.ModifyDNOperationBasis; |
| | | import org.opends.server.core.ModifyOperationBasis; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.AssuredMode; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.DSInfo; |
| | | import org.opends.server.replication.common.RSInfo; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.common.ServerStatus; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeBuilder; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.Attributes; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.ModificationType; |
| | | import org.opends.server.types.ObjectClass; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.RawAttribute; |
| | | import org.opends.server.replication.common.*; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.util.TimeThread; |
| | | import org.testng.annotations.AfterClass; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | import static org.opends.server.replication.protocol.OperationContext.SYNCHROCONTEXT; |
| | | import static org.opends.server.replication.protocol.ProtocolVersion.getCurrentVersion; |
| | | import static org.opends.server.util.StaticUtils.byteToHex; |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | |
| | | import static org.testng.Assert.assertEquals; |
| | | import static org.testng.Assert.assertTrue; |
| | | import static org.opends.server.replication.protocol.OperationContext.*; |
| | | import static org.opends.server.replication.protocol.ProtocolVersion.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test the conversions between the various protocol versions. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class ProtocolCompatibilityTest extends ReplicationTestCase { |
| | | |
| | | short REPLICATION_PROTOCOL_VLAST = ProtocolVersion.getCurrentVersion(); |
| | |
| | | { |
| | | String baseDN = "o=test"; |
| | | ServerState state = new ServerState(); |
| | | state.update(new ChangeNumber((long)0, 0,0)); |
| | | state.update(new CSN(0, 0,0)); |
| | | Object[] set1 = new Object[] {1, baseDN, 0, "localhost:8989", state, 0L, (byte)0, 0}; |
| | | |
| | | baseDN = "dc=example,dc=com"; |
| | | state = new ServerState(); |
| | | state.update(new ChangeNumber((long)75, 5,263)); |
| | | state.update(new CSN(75, 5,263)); |
| | | Object[] set2 = new Object[] {16, baseDN, 100, "anotherHost:1025", state, 1245L, (byte)25, 3456}; |
| | | |
| | | return new Object [][] { set1, set2 }; |
| | |
| | | assertEquals(msg.getServerURL(), newMsg.getServerURL()); |
| | | assertEquals(msg.getBaseDn(), newMsg.getBaseDn()); |
| | | assertEquals(msg.getWindowSize(), newMsg.getWindowSize()); |
| | | assertEquals(msg.getServerState().getChangeNumber(1), |
| | | newMsg.getServerState().getChangeNumber(1)); |
| | | assertEquals(msg.getServerState().getCSN(1), newMsg.getServerState().getCSN(1)); |
| | | assertEquals(msg.getSSLEncryption(), newMsg.getSSLEncryption()); |
| | | |
| | | // Check default value for only post V1 fields |
| | |
| | | assertEquals(msg.getServerURL(), vlastMsg.getServerURL()); |
| | | assertEquals(msg.getBaseDn(), vlastMsg.getBaseDn()); |
| | | assertEquals(msg.getWindowSize(), vlastMsg.getWindowSize()); |
| | | assertEquals(msg.getServerState().getChangeNumber(1), |
| | | vlastMsg.getServerState().getChangeNumber(1)); |
| | | assertEquals(msg.getServerState().getCSN(1), vlastMsg.getServerState().getCSN(1)); |
| | | assertEquals(msg.getSSLEncryption(), vlastMsg.getSSLEncryption()); |
| | | assertEquals(msg.getGroupId(), vlastMsg.getGroupId()); |
| | | assertEquals(msg.getDegradedStatusThreshold(), vlastMsg.getDegradedStatusThreshold()); |
| | |
| | | new HashMap<AttributeType,List<Attribute>>(); |
| | | opList.put(attr.getAttributeType(), operationalAttributes); |
| | | |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | CSN csn = new CSN(TimeThread.getTime(), 123, 45); |
| | | |
| | | AddMsg msg = new AddMsg(cn, rawDN, "thisIsaUniqueID", "parentUniqueId", |
| | | AddMsg msg = new AddMsg(csn, rawDN, "thisIsaUniqueID", "parentUniqueId", |
| | | objectClass, userAttributes, |
| | | operationalAttributes); |
| | | |
| | |
| | | // Check fields common to both versions |
| | | assertEquals(newMsg.getEntryUUID(), msg.getEntryUUID()); |
| | | assertEquals(newMsg.getDn(), msg.getDn()); |
| | | assertEquals(newMsg.getChangeNumber(), msg.getChangeNumber()); |
| | | assertEquals(newMsg.getCSN(), msg.getCSN()); |
| | | assertEquals(newMsg.isAssured(), msg.isAssured()); |
| | | assertEquals(newMsg.getParentEntryUUID(), msg.getParentEntryUUID()); |
| | | |
| | |
| | | // Check we retrieve original VLAST message (VLAST fields) |
| | | assertEquals(msg.getEntryUUID(), vlastMsg.getEntryUUID()); |
| | | assertEquals(msg.getDn(), vlastMsg.getDn()); |
| | | assertEquals(msg.getChangeNumber(), vlastMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), vlastMsg.getCSN()); |
| | | assertEquals(msg.getParentEntryUUID(), vlastMsg.getParentEntryUUID()); |
| | | assertEquals(msg.isAssured(), vlastMsg.isAssured()); |
| | | assertEquals(msg.getAssuredMode(), vlastMsg.getAssuredMode()); |
| | |
| | | byte safeDataLevel, List<Attribute> entryAttrList) |
| | | throws Exception |
| | | { |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | DeleteMsg msg = new DeleteMsg(rawDN, cn, "thisIsaUniqueID"); |
| | | CSN csn = new CSN(TimeThread.getTime(), 123, 45); |
| | | DeleteMsg msg = new DeleteMsg(rawDN, csn, "thisIsaUniqueID"); |
| | | |
| | | msg.setAssured(isAssured); |
| | | msg.setAssuredMode(assuredMode); |
| | |
| | | // Check fields common to both versions |
| | | assertEquals(newMsg.getEntryUUID(), msg.getEntryUUID()); |
| | | assertEquals(newMsg.getDn(), msg.getDn()); |
| | | assertEquals(newMsg.getChangeNumber(), msg.getChangeNumber()); |
| | | assertEquals(newMsg.getCSN(), msg.getCSN()); |
| | | assertEquals(newMsg.isAssured(), msg.isAssured()); |
| | | |
| | | // Check default value for only VLAST fields |
| | |
| | | // Check we retrieve original VLAST message (VLAST fields) |
| | | assertEquals(msg.getEntryUUID(), vlastMsg.getEntryUUID()); |
| | | assertEquals(msg.getDn(), vlastMsg.getDn()); |
| | | assertEquals(msg.getChangeNumber(), vlastMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), vlastMsg.getCSN()); |
| | | assertEquals(msg.isAssured(), vlastMsg.isAssured()); |
| | | assertEquals(msg.getAssuredMode(), vlastMsg.getAssuredMode()); |
| | | assertEquals(msg.getSafeDataLevel(), vlastMsg.getSafeDataLevel()); |
| | |
| | | */ |
| | | @DataProvider(name = "createModifyData") |
| | | public Object[][] createModifyData() { |
| | | ChangeNumber cn1 = new ChangeNumber(1, 0, 1); |
| | | ChangeNumber cn2 = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | CSN csn1 = new CSN(1, 0, 1); |
| | | CSN csn2 = new CSN(TimeThread.getTime(), 123, 45); |
| | | |
| | | AttributeType type = DirectoryServer.getAttributeType("description"); |
| | | |
| | |
| | | entryAttrList.add(eattr2); |
| | | |
| | | return new Object[][] { |
| | | { cn1, "dc=test", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0, null}, |
| | | { cn2, "dc=cn2", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)1, entryAttrList}, |
| | | { cn2, "dc=test with a much longer dn in case this would " |
| | | { csn1, "dc=test", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte) 0, null }, |
| | | { csn2, "dc=cn2", mods1, true, AssuredMode.SAFE_READ_MODE, (byte) 1, entryAttrList }, |
| | | { csn2, "dc=test with a much longer dn in case this would " |
| | | + "make a difference", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3, null}, |
| | | { cn2, "dc=test, cn=with a, o=more complex, ou=dn", mods1, false, AssuredMode.SAFE_READ_MODE, (byte)5, entryAttrList}, |
| | | { cn2, "cn=use\\, backslash", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3, null}, |
| | | { cn2, "dc=test with several mod", mods2, false, AssuredMode.SAFE_DATA_MODE, (byte)16, entryAttrList}, |
| | | { cn2, "dc=test with several values", mods3, false, AssuredMode.SAFE_READ_MODE, (byte)3, null}, |
| | | { cn2, "dc=test with long mod", mods4, true, AssuredMode.SAFE_READ_MODE, (byte)120, entryAttrList}, |
| | | { cn2, "dc=testDsaOperation", mods5, true, AssuredMode.SAFE_DATA_MODE, (byte)99, null}, |
| | | { csn2, "dc=test, cn=with a, o=more complex, ou=dn", mods1, false, AssuredMode.SAFE_READ_MODE, (byte) 5, entryAttrList }, |
| | | { csn2, "cn=use\\, backslash", mods1, true, AssuredMode.SAFE_READ_MODE, (byte) 3, null }, |
| | | { csn2, "dc=test with several mod", mods2, false, AssuredMode.SAFE_DATA_MODE, (byte) 16, entryAttrList }, |
| | | { csn2, "dc=test with several values", mods3, false, AssuredMode.SAFE_READ_MODE, (byte) 3, null }, |
| | | { csn2, "dc=test with long mod", mods4, true, AssuredMode.SAFE_READ_MODE, (byte) 120, entryAttrList }, |
| | | { csn2, "dc=testDsaOperation", mods5, true, AssuredMode.SAFE_DATA_MODE, (byte) 99, null }, |
| | | }; |
| | | } |
| | | |
| | |
| | | * using protocol V1 and V2 are working. |
| | | */ |
| | | @Test(dataProvider = "createModifyData") |
| | | public void modifyMsgTestVLASTV2(ChangeNumber changeNumber, |
| | | public void modifyMsgTestVLASTV2(CSN csn, |
| | | String rawdn, List<Modification> mods, |
| | | boolean isAssured, AssuredMode assuredMode, |
| | | byte safeDataLevel, |
| | |
| | | * using protocol V1 and VLAST are working. |
| | | */ |
| | | @Test(enabled=false,dataProvider = "createModifyData") |
| | | public void modifyMsgTestVLASTV1(ChangeNumber changeNumber, |
| | | public void modifyMsgTestVLASTV1(CSN csn, |
| | | String rawdn, List<Modification> mods, |
| | | boolean isAssured, AssuredMode assuredMode, |
| | | byte safeDataLevel, |
| | |
| | | { |
| | | // Create VLAST message |
| | | DN dn = DN.decode(rawdn); |
| | | ModifyMsg origVlastMsg = new ModifyMsg(changeNumber, dn, mods, "fakeuniqueid"); |
| | | ModifyMsg origVlastMsg = new ModifyMsg(csn, dn, mods, "fakeuniqueid"); |
| | | |
| | | origVlastMsg.setAssured(isAssured); |
| | | origVlastMsg.setAssuredMode(assuredMode); |
| | |
| | | // Check fields common to both versions |
| | | assertEquals(newv1Msg.getEntryUUID(), origVlastMsg.getEntryUUID()); |
| | | assertEquals(newv1Msg.getDn(), origVlastMsg.getDn()); |
| | | assertEquals(newv1Msg.getChangeNumber(), origVlastMsg.getChangeNumber()); |
| | | assertEquals(newv1Msg.getCSN(), origVlastMsg.getCSN()); |
| | | assertEquals(newv1Msg.isAssured(), origVlastMsg.isAssured()); |
| | | |
| | | // Create a modify operation from each message to compare mods (kept encoded in messages) |
| | |
| | | // Check we retrieve original VLAST message (VLAST fields) |
| | | assertEquals(origVlastMsg.getEntryUUID(), generatedVlastMsg.getEntryUUID()); |
| | | assertEquals(origVlastMsg.getDn(), generatedVlastMsg.getDn()); |
| | | assertEquals(origVlastMsg.getChangeNumber(), generatedVlastMsg.getChangeNumber()); |
| | | assertEquals(origVlastMsg.getCSN(), generatedVlastMsg.getCSN()); |
| | | assertEquals(origVlastMsg.isAssured(), generatedVlastMsg.isAssured()); |
| | | assertEquals(origVlastMsg.getAssuredMode(), generatedVlastMsg.getAssuredMode()); |
| | | assertEquals(origVlastMsg.getSafeDataLevel(), generatedVlastMsg.getSafeDataLevel()); |
| | |
| | | throws Exception |
| | | { |
| | | // Create VLAST message |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), 596, 13); |
| | | ModifyDNMsg msg = new ModifyDNMsg(rawDN, cn, uid, |
| | | CSN csn = new CSN(TimeThread.getTime(), 596, 13); |
| | | ModifyDNMsg msg = new ModifyDNMsg(rawDN, csn, uid, |
| | | newParentUid, deleteOldRdn, |
| | | newSuperior, newRdn, mods); |
| | | |
| | |
| | | // Check fields common to both versions |
| | | assertEquals(newMsg.getEntryUUID(), msg.getEntryUUID()); |
| | | assertEquals(newMsg.getDn(), msg.getDn()); |
| | | assertEquals(newMsg.getChangeNumber(), msg.getChangeNumber()); |
| | | assertEquals(newMsg.getCSN(), msg.getCSN()); |
| | | assertEquals(newMsg.isAssured(), msg.isAssured()); |
| | | assertEquals(newMsg.getNewRDN(), msg.getNewRDN()); |
| | | assertEquals(newMsg.getNewSuperior(), msg.getNewSuperior()); |
| | |
| | | // Check we retrieve original VLAST message (VLAST fields) |
| | | assertEquals(msg.getEntryUUID(), vlastMsg.getEntryUUID()); |
| | | assertEquals(msg.getDn(), vlastMsg.getDn()); |
| | | assertEquals(msg.getChangeNumber(), vlastMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), vlastMsg.getCSN()); |
| | | assertEquals(msg.isAssured(), vlastMsg.isAssured()); |
| | | assertEquals(msg.getAssuredMode(), vlastMsg.getAssuredMode()); |
| | | assertEquals(msg.getSafeDataLevel(), vlastMsg.getSafeDataLevel()); |
| | |
| | | {"1603303030303030303030303030303030313030303130303030303030300064633" + |
| | | "d746573740066616b65756e69717565696400000200301f0a0102301a040b646573" + |
| | | "6372697074696f6e310b04096e65772076616c756500", |
| | | ModifyMsg.class, new ChangeNumber(1, 0, 1), "dc=test" }, |
| | | ModifyMsg.class, new CSN(1, 0, 1), "dc=test" }, |
| | | {"1803303030303031323366313238343132303030326430303030303037620064633" + |
| | | "d636f6d00756e69717565696400000201", |
| | | DeleteMsg.class, new ChangeNumber(0x123f1284120L,123,45), "dc=com"}, |
| | | DeleteMsg.class, new CSN(0x123f1284120L,123,45), "dc=com"}, |
| | | {"1803303030303031323366313238343132303030326430303030303037620064633" + |
| | | "d64656c6574652c64633d616e2c64633d656e7472792c64633d776974682c64633d" + |
| | | "612c64633d6c6f6e6720646e00756e69717565696400000201", |
| | | DeleteMsg.class, new ChangeNumber(0x123f1284120L,123,45), |
| | | DeleteMsg.class, new CSN(0x123f1284120L,123,45), |
| | | "dc=delete,dc=an,dc=entry,dc=with,dc=a,dc=long dn"}, |
| | | {"1903303030303031323366313238613762333030326430303030303037620064633" + |
| | | "d746573742c64633d636f6d00756e6971756569640000020164633d6e6577006463" + |
| | | "3d6368616e6765006e6577706172656e7449640000301f0a0102301a040b6465736" + |
| | | "372697074696f6e310b04096e65772076616c756500", |
| | | ModifyDNMsg.class, new ChangeNumber(0x123f128a7b3L,123,45), "dc=test,dc=com"}, |
| | | ModifyDNMsg.class, new CSN(0x123f128a7b3L,123,45), "dc=test,dc=com"}, |
| | | {"1703303030303031323366313239333431323030326430303030303037620064633" + |
| | | "d6578616d706c652c64633d636f6d0074686973497361556e697175654944000002" + |
| | | "00706172656e74556e69717565496400301d040b6f626a656374436c617373310e0" + |
| | | "40c6f7267616e697a6174696f6e300a04016f31050403636f6d301c040c63726561" + |
| | | "746f72736e616d65310c040a64633d63726561746f72", |
| | | AddMsg.class, new ChangeNumber(0x123f1293412L,123,45), "dc=example,dc=com"} |
| | | AddMsg.class, new CSN(0x123f1293412L,123,45), "dc=example,dc=com"} |
| | | }; |
| | | } |
| | | |
| | |
| | | */ |
| | | @Test(dataProvider = "createOldUpdateData") |
| | | public void createOldUpdate( |
| | | String encodedString, Class<?> msgType, ChangeNumber cn, String dn) |
| | | String encodedString, Class<?> msgType, CSN csn, String dn) |
| | | throws UnsupportedEncodingException, DataFormatException, |
| | | NotSupportedOldVersionPDUException, DirectoryException |
| | | { |
| | | LDAPUpdateMsg msg = (LDAPUpdateMsg) ReplicationMsg.generateMsg( |
| | | hexStringToByteArray(encodedString), ProtocolVersion.REPLICATION_PROTOCOL_V3); |
| | | assertEquals(msg.getDn(), dn); |
| | | assertEquals(msg.getChangeNumber(), cn); |
| | | assertEquals(msg.getCSN(), csn); |
| | | assertEquals(msg.getClass(), msgType); |
| | | BigInteger bi = new BigInteger(msg.getBytes(ProtocolVersion.REPLICATION_PROTOCOL_V3)); |
| | | assertEquals(bi.toString(16), encodedString); |
| | |
| | | return new Object[][] { |
| | | {"05303030303031323366316535383832383030326430303030303037" + |
| | | "6200010101313030003230303000333030303000", |
| | | new ChangeNumber(0x123f1e58828L, 123, 45), true, fservers4 } |
| | | new CSN(0x123f1e58828L, 123, 45), true, fservers4 } |
| | | }; |
| | | } |
| | | @Test(dataProvider = "createoldAckMsgData") |
| | | public void oldAckMsgPDUs(String oldPdu, ChangeNumber cn, |
| | | public void oldAckMsgPDUs(String oldPdu, CSN csn, |
| | | boolean hasTimeout, ArrayList<Integer> failedServers) throws Exception |
| | | { |
| | | AckMsg msg = new AckMsg(hexStringToByteArray(oldPdu)); |
| | | assertEquals(msg.getChangeNumber(), cn); |
| | | assertEquals(msg.getCSN(), csn); |
| | | assertEquals(msg.hasTimeout(), hasTimeout); |
| | | assertEquals(msg.getFailedServers(), failedServers); |
| | | // We use V4 here because these PDU have not changed since 2.0. |
| | |
| | | urls4.add("ldaps://host:port/dc=foobar1??sub?(sn=Another Entry 1)"); |
| | | urls4.add("ldaps://host:port/dc=foobar2??sub?(sn=Another Entry 2)"); |
| | | |
| | | DSInfo dsInfo1 = new DSInfo(13, "dsHost1:111", 26, (long)154631, ServerStatus.FULL_UPDATE_STATUS, |
| | | DSInfo dsInfo1 = new DSInfo(13, "dsHost1:111", 26, 154631, ServerStatus.FULL_UPDATE_STATUS, |
| | | false, AssuredMode.SAFE_DATA_MODE, (byte)12, (byte)132, urls1, new HashSet<String>(), new HashSet<String>(), (short)-1); |
| | | |
| | | DSInfo dsInfo2 = new DSInfo(-436, "dsHost2:222", 493, (long)-227896, ServerStatus.DEGRADED_STATUS, |
| | | DSInfo dsInfo2 = new DSInfo(-436, "dsHost2:222", 493, -227896, ServerStatus.DEGRADED_STATUS, |
| | | true, AssuredMode.SAFE_READ_MODE, (byte)-7, (byte)-265, urls2, new HashSet<String>(), new HashSet<String>(), (short)-1); |
| | | |
| | | DSInfo dsInfo3 = new DSInfo(2436, "dsHost3:333", 591, (long)0, ServerStatus.NORMAL_STATUS, |
| | | DSInfo dsInfo3 = new DSInfo(2436, "dsHost3:333", 591, 0, ServerStatus.NORMAL_STATUS, |
| | | false, AssuredMode.SAFE_READ_MODE, (byte)17, (byte)0, urls3, new HashSet<String>(), new HashSet<String>(), (short)-1); |
| | | |
| | | DSInfo dsInfo4 = new DSInfo(415, "dsHost4:444", 146, (long)0, ServerStatus.BAD_GEN_ID_STATUS, |
| | | DSInfo dsInfo4 = new DSInfo(415, "dsHost4:444", 146, 0, ServerStatus.BAD_GEN_ID_STATUS, |
| | | true, AssuredMode.SAFE_DATA_MODE, (byte)2, (byte)15, urls4, new HashSet<String>(), new HashSet<String>(), (short)-1); |
| | | |
| | | List<DSInfo> dsList1 = new ArrayList<DSInfo>(); |
| | |
| | | dsList4.add(dsInfo2); |
| | | dsList4.add(dsInfo1); |
| | | |
| | | RSInfo rsInfo1 = new RSInfo(4527, null, (long)45316, (byte)103, 1); |
| | | |
| | | RSInfo rsInfo2 = new RSInfo(4527, null, (long)0, (byte)0, 1); |
| | | |
| | | RSInfo rsInfo3 = new RSInfo(0, null, (long)-21113, (byte)98, 1); |
| | | RSInfo rsInfo1 = new RSInfo(4527, null, 45316, (byte)103, 1); |
| | | RSInfo rsInfo2 = new RSInfo(4527, null, 0, (byte)0, 1); |
| | | RSInfo rsInfo3 = new RSInfo(0, null, -21113, (byte)98, 1); |
| | | |
| | | List<RSInfo> rsList1 = new ArrayList<RSInfo>(); |
| | | rsList1.add(rsInfo1); |
| | |
| | | */ |
| | | @DataProvider(name = "createModifyData") |
| | | public Object[][] createModifyData() { |
| | | ChangeNumber cn1 = new ChangeNumber(1, 0, 1); |
| | | ChangeNumber cn2 = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | ChangeNumber cn3 = new ChangeNumber(TimeThread.getTime(), 67894123, 45678); |
| | | CSN csn1 = new CSN(1, 0, 1); |
| | | CSN csn2 = new CSN(TimeThread.getTime(), 123, 45); |
| | | CSN csn3 = new CSN(TimeThread.getTime(), 67894123, 45678); |
| | | |
| | | AttributeType type = DirectoryServer.getAttributeType("description"); |
| | | |
| | |
| | | |
| | | List<Attribute> eclIncludes = getEntryAttributes(); |
| | | return new Object[][] { |
| | | { cn1, "dc=test", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0, null}, |
| | | { cn2, "dc=cn2", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)1, eclIncludes}, |
| | | { cn2, "dc=test with a much longer dn in case this would " |
| | | { csn1, "dc=test", mods1, false, AssuredMode.SAFE_DATA_MODE, (byte)0, null}, |
| | | { csn2, "dc=cn2", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)1, eclIncludes}, |
| | | { csn2, "dc=test with a much longer dn in case this would " |
| | | + "make a difference", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3, null}, |
| | | { cn2, "dc=test, cn=with a, o=more complex, ou=dn", mods1, false, AssuredMode.SAFE_READ_MODE, (byte)5, eclIncludes}, |
| | | { cn2, "cn=use\\, backslash", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3, null}, |
| | | { cn2, "dc=test with several mod", mods2, false, AssuredMode.SAFE_DATA_MODE, (byte)16, eclIncludes}, |
| | | { cn2, "dc=test with several values", mods3, false, AssuredMode.SAFE_READ_MODE, (byte)3, null}, |
| | | { cn2, "dc=test with long mod", mods4, true, AssuredMode.SAFE_READ_MODE, (byte)120, eclIncludes}, |
| | | { cn2, "dc=testDsaOperation", mods5, true, AssuredMode.SAFE_DATA_MODE, (byte)99, null}, |
| | | { cn3, "dc=serverIdLargerThan32767", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)1, null}, |
| | | { csn2, "dc=test, cn=with a, o=more complex, ou=dn", mods1, false, AssuredMode.SAFE_READ_MODE, (byte)5, eclIncludes}, |
| | | { csn2, "cn=use\\, backslash", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)3, null}, |
| | | { csn2, "dc=test with several mod", mods2, false, AssuredMode.SAFE_DATA_MODE, (byte)16, eclIncludes}, |
| | | { csn2, "dc=test with several values", mods3, false, AssuredMode.SAFE_READ_MODE, (byte)3, null}, |
| | | { csn2, "dc=test with long mod", mods4, true, AssuredMode.SAFE_READ_MODE, (byte)120, eclIncludes}, |
| | | { csn2, "dc=testDsaOperation", mods5, true, AssuredMode.SAFE_DATA_MODE, (byte)99, null}, |
| | | { csn3, "dc=serverIdLargerThan32767", mods1, true, AssuredMode.SAFE_READ_MODE, (byte)1, null}, |
| | | }; |
| | | } |
| | | |
| | |
| | | * Finally test that both Msg matches. |
| | | */ |
| | | @Test(enabled=true,dataProvider = "createModifyData") |
| | | public void modifyMsgTest(ChangeNumber changeNumber, |
| | | public void modifyMsgTest(CSN csn, |
| | | String rawdn, List<Modification> mods, |
| | | boolean isAssured, AssuredMode assuredMode, |
| | | byte safeDataLevel, |
| | |
| | | DN dn = DN.decode(rawdn); |
| | | InternalClientConnection connection = |
| | | InternalClientConnection.getRootConnection(); |
| | | ModifyMsg msg = new ModifyMsg(changeNumber, dn, mods, "fakeuniqueid"); |
| | | ModifyMsg msg = new ModifyMsg(csn, dn, mods, "fakeuniqueid"); |
| | | |
| | | msg.setAssured(isAssured); |
| | | msg.setAssuredMode(assuredMode); |
| | |
| | | assertEquals(generatedMsg.getAssuredMode(), assuredMode); |
| | | assertEquals(generatedMsg.getSafeDataLevel(), safeDataLevel); |
| | | |
| | | assertEquals(msg.getChangeNumber(), generatedMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), generatedMsg.getCSN()); |
| | | |
| | | // Get ECL entry attributes |
| | | assertAttributesEqual(entryAttrList, generatedMsg.getEclIncludes()); |
| | |
| | | * Finally test that both Msgs match. |
| | | */ |
| | | @Test(enabled=true,dataProvider = "createModifyData") |
| | | public void updateMsgTest(ChangeNumber changeNumber, |
| | | public void updateMsgTest(CSN csn, |
| | | String rawdn, List<Modification> mods, |
| | | boolean isAssured, AssuredMode assuredMode, |
| | | byte safeDataLevel , |
| | |
| | | throws Exception |
| | | { |
| | | DN dn = DN.decode(rawdn); |
| | | ModifyMsg msg = new ModifyMsg(changeNumber, dn, mods, "fakeuniqueid"); |
| | | ModifyMsg msg = new ModifyMsg(csn, dn, mods, "fakeuniqueid"); |
| | | |
| | | // Check isAssured |
| | | assertFalse(msg.isAssured()); |
| | |
| | | assertFalse(msg.equals(null)); |
| | | assertFalse(msg.equals(new Object())); |
| | | |
| | | // Check change number |
| | | // Check CSN |
| | | assertTrue(msg.equals(generatedMsg)); |
| | | |
| | | // Check hashCode |
| | |
| | | deleteOp.addRequestControl(new SubtreeDeleteControl(false)); |
| | | } |
| | | LocalBackendDeleteOperation op = new LocalBackendDeleteOperation(deleteOp); |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(),123, 45); |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(cn, "uniqueid")); |
| | | CSN csn = new CSN(TimeThread.getTime(), 123, 45); |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(csn, "uniqueid")); |
| | | DeleteMsg msg = new DeleteMsg(op); |
| | | assertEquals(msg.isSubtreeDelete(), subtree); |
| | | // Set ECL entry attributes |
| | |
| | | |
| | | assertEquals(msg.toString(), generatedMsg.toString()); |
| | | assertEquals(msg.getInitiatorsName(), generatedMsg.getInitiatorsName()); |
| | | assertEquals(msg.getChangeNumber(), generatedMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), generatedMsg.getCSN()); |
| | | assertEquals(generatedMsg.isSubtreeDelete(), subtree); |
| | | |
| | | // Get ECL entry attributes |
| | |
| | | |
| | | // Create an update message from this op |
| | | DeleteMsg updateMsg = (DeleteMsg) LDAPUpdateMsg.generateMsg(op); |
| | | assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), updateMsg.getCSN()); |
| | | assertEquals(msg.isSubtreeDelete(), updateMsg.isSubtreeDelete()); |
| | | } |
| | | |
| | |
| | | DN.decode(rawDN), RDN.decode(newRdn), deleteOldRdn, |
| | | (newSuperior.length() != 0 ? DN.decode(newSuperior) : null)); |
| | | |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | CSN csn = new CSN(TimeThread.getTime(), 123, 45); |
| | | op.setAttachment(SYNCHROCONTEXT, |
| | | new ModifyDnContext(cn, "uniqueid", "newparentId")); |
| | | new ModifyDnContext(csn, "uniqueid", "newparentId")); |
| | | LocalBackendModifyDNOperation localOp = |
| | | new LocalBackendModifyDNOperation(op); |
| | | for (Modification mod : mods) |
| | |
| | | ModifyDNOperation moddn1 = (ModifyDNOperation) oriOp; |
| | | ModifyDNOperation moddn2 = (ModifyDNOperation) generatedOperation; |
| | | |
| | | assertEquals(msg.getChangeNumber(), generatedMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), generatedMsg.getCSN()); |
| | | assertEquals(moddn1.getRawEntryDN(), moddn2.getRawEntryDN()); |
| | | assertEquals(moddn1.getRawNewRDN(), moddn2.getRawNewRDN()); |
| | | assertEquals(moddn1.deleteOldRDN(), moddn2.deleteOldRDN()); |
| | |
| | | |
| | | // Create an update message from this op |
| | | ModifyDNMsg updateMsg = (ModifyDNMsg) LDAPUpdateMsg.generateMsg(localOp); |
| | | assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), updateMsg.getCSN()); |
| | | } |
| | | |
| | | @DataProvider(name = "createAddData") |
| | |
| | | new HashMap<AttributeType, List<Attribute>>(); |
| | | opList.put(attr.getAttributeType(), operationalAttributes); |
| | | |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | CSN csn = new CSN(TimeThread.getTime(), 123, 45); |
| | | |
| | | AddMsg msg = new AddMsg(cn, rawDN, "thisIsaUniqueID", "parentUniqueId", |
| | | AddMsg msg = new AddMsg(csn, rawDN, "thisIsaUniqueID", "parentUniqueId", |
| | | objectClass, userAttributes, |
| | | operationalAttributes); |
| | | |
| | |
| | | AddOperation addOpB = new AddOperationBasis(connection, |
| | | 1, 1, null, dn, objectClassList, userAttList, opList); |
| | | LocalBackendAddOperation localAddOp = new LocalBackendAddOperation(addOpB); |
| | | OperationContext opCtx = new AddContext(cn, "thisIsaUniqueID", |
| | | OperationContext opCtx = new AddContext(csn, "thisIsaUniqueID", |
| | | "parentUniqueId"); |
| | | localAddOp.setAttachment(SYNCHROCONTEXT, opCtx); |
| | | |
| | |
| | | |
| | | // Create an update message from this op |
| | | AddMsg updateMsg = (AddMsg) LDAPUpdateMsg.generateMsg(localAddOp); |
| | | assertEquals(msg.getChangeNumber(), updateMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), updateMsg.getCSN()); |
| | | } |
| | | |
| | | private void assertAttributesEqual(List<Attribute> entryAttrList, |
| | |
| | | */ |
| | | @DataProvider(name = "createAckData") |
| | | public Object[][] createAckData() { |
| | | ChangeNumber cn1 = new ChangeNumber(1, 0, 1); |
| | | ChangeNumber cn2 = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | ChangeNumber cn3 = new ChangeNumber(TimeThread.getTime(), 1234567, 45678); |
| | | CSN csn1 = new CSN(1, 0, 1); |
| | | CSN csn2 = new CSN(TimeThread.getTime(), 123, 45); |
| | | CSN csn3 = new CSN(TimeThread.getTime(), 1234567, 45678); |
| | | |
| | | List<Integer> fservers1 = newList(12345, -12345, 31657, -28456, 0); |
| | | List<Integer> fservers2 = newList(); |
| | |
| | | List<Integer> fservers4 = newList(100, 2000, 30000, -100, -2000, -30000); |
| | | |
| | | return new Object[][] { |
| | | {cn1, true, false, false, fservers1}, |
| | | {cn2, false, true, false, fservers2}, |
| | | {cn1, false, false, true, fservers3}, |
| | | {cn2, false, false, false, fservers4}, |
| | | {cn1, true, true, false, fservers1}, |
| | | {cn2, false, true, true, fservers2}, |
| | | {cn1, true, false, true, fservers3}, |
| | | {cn2, true, true, true, fservers4}, |
| | | {cn3, true, true, true, fservers4} |
| | | {csn1, true, false, false, fservers1}, |
| | | {csn2, false, true, false, fservers2}, |
| | | {csn1, false, false, true, fservers3}, |
| | | {csn2, false, false, false, fservers4}, |
| | | {csn1, true, true, false, fservers1}, |
| | | {csn2, false, true, true, fservers2}, |
| | | {csn1, true, false, true, fservers3}, |
| | | {csn2, true, true, true, fservers4}, |
| | | {csn3, true, true, true, fservers4} |
| | | }; |
| | | } |
| | | |
| | | @Test(enabled=true,dataProvider = "createAckData") |
| | | public void ackMsgTest(ChangeNumber cn, boolean hasTimeout, boolean hasWrongStatus, |
| | | public void ackMsgTest(CSN csn, boolean hasTimeout, boolean hasWrongStatus, |
| | | boolean hasReplayError, List<Integer> failedServers) |
| | | throws Exception |
| | | { |
| | |
| | | |
| | | // Constructor test (with ChangeNumber) |
| | | // Check that retrieved CN is OK |
| | | msg1 = new AckMsg(cn); |
| | | assertEquals(msg1.getChangeNumber().compareTo(cn), 0); |
| | | msg1 = new AckMsg(csn); |
| | | assertEquals(msg1.getCSN().compareTo(csn), 0); |
| | | |
| | | // Check default values for error info |
| | | assertFalse(msg1.hasTimeout()); |
| | |
| | | assertEquals(msg1.getFailedServers().size(), 0); |
| | | |
| | | // Check constructor with error info |
| | | msg1 = new AckMsg(cn, hasTimeout, hasWrongStatus, hasReplayError, failedServers); |
| | | assertEquals(msg1.getChangeNumber().compareTo(cn), 0); |
| | | msg1 = new AckMsg(csn, hasTimeout, hasWrongStatus, hasReplayError, failedServers); |
| | | assertEquals(msg1.getCSN().compareTo(csn), 0); |
| | | assertEquals(msg1.hasTimeout(), hasTimeout); |
| | | assertEquals(msg1.hasWrongStatus(), hasWrongStatus); |
| | | assertEquals(msg1.hasReplayError(), hasReplayError); |
| | |
| | | |
| | | // Constructor test (with byte[]) |
| | | msg2 = new AckMsg(msg1.getBytes(getCurrentVersion())); |
| | | assertEquals(msg2.getChangeNumber().compareTo(cn), 0); |
| | | assertEquals(msg2.getCSN().compareTo(csn), 0); |
| | | assertEquals(msg1.hasTimeout(), msg2.hasTimeout()); |
| | | assertEquals(msg1.hasWrongStatus(), msg2.hasWrongStatus()); |
| | | assertEquals(msg1.hasReplayError(), msg2.hasReplayError()); |
| | |
| | | assertTrue(true); |
| | | } |
| | | |
| | | // Check that retrieved CN is OK |
| | | // Check that retrieved CSN is OK |
| | | msg2 = (AckMsg) ReplicationMsg.generateMsg( |
| | | msg1.getBytes(getCurrentVersion()), getCurrentVersion()); |
| | | } |
| | |
| | | DeleteOperation deleteOp = |
| | | new DeleteOperationBasis(connection, 1, 1,null, DN.decode("cn=t1")); |
| | | LocalBackendDeleteOperation op = new LocalBackendDeleteOperation(deleteOp); |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(cn, "uniqueid")); |
| | | CSN csn = new CSN(TimeThread.getTime(), 123, 45); |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(csn, "uniqueid")); |
| | | DeleteMsg delmsg = new DeleteMsg(op); |
| | | int draftcn = 21; |
| | | |
| | |
| | | { |
| | | String baseDN = TEST_ROOT_DN_STRING; |
| | | ServerState state = new ServerState(); |
| | | state.update(new ChangeNumber(0, 0,0)); |
| | | state.update(new CSN(0, 0,0)); |
| | | Object[] set1 = new Object[] {1, baseDN, 0, state, 0L, false, (byte)0}; |
| | | |
| | | state = new ServerState(); |
| | | state.update(new ChangeNumber(75, 5,263)); |
| | | state.update(new CSN(75, 5,263)); |
| | | Object[] set2 = new Object[] {16, baseDN, 100, state, 1248L, true, (byte)31}; |
| | | |
| | | state = new ServerState(); |
| | | state.update(new ChangeNumber(75, 98573895,45263)); |
| | | state.update(new CSN(75, 98573895,45263)); |
| | | Object[] set3 = new Object[] {16, baseDN, 100, state, 1248L, true, (byte)31}; |
| | | |
| | | return new Object [][] { set1, set2, set3 }; |
| | |
| | | assertEquals(msg.getWindowSize(), newMsg.getWindowSize()); |
| | | assertEquals(msg.getHeartbeatInterval(), newMsg.getHeartbeatInterval()); |
| | | assertEquals(msg.getSSLEncryption(), newMsg.getSSLEncryption()); |
| | | assertEquals(msg.getServerState().getChangeNumber(1), |
| | | newMsg.getServerState().getChangeNumber(1)); |
| | | assertEquals(msg.getServerState().getCSN(1), |
| | | newMsg.getServerState().getCSN(1)); |
| | | assertEquals(newMsg.getVersion(), getCurrentVersion()); |
| | | assertEquals(msg.getGenerationId(), newMsg.getGenerationId()); |
| | | assertEquals(msg.getGroupId(), newMsg.getGroupId()); |
| | |
| | | { |
| | | String baseDN = TEST_ROOT_DN_STRING; |
| | | ServerState state = new ServerState(); |
| | | state.update(new ChangeNumber(0, 0,0)); |
| | | state.update(new CSN(0, 0,0)); |
| | | Object[] set1 = new Object[] {1, baseDN, 0, "localhost:8989", state, 0L, (byte)0, 0}; |
| | | |
| | | state = new ServerState(); |
| | | state.update(new ChangeNumber(75, 5,263)); |
| | | state.update(new CSN(75, 5,263)); |
| | | Object[] set2 = new Object[] {16, baseDN, 100, "anotherHost:1025", state, 1245L, (byte)25, 3456}; |
| | | |
| | | state = new ServerState(); |
| | | state.update(new ChangeNumber(75, 5, 45263)); |
| | | state.update(new CSN(75, 5, 45263)); |
| | | Object[] set3 = new Object[] {16, baseDN, 100, "anotherHost:1025", state, 1245L, (byte)25, 3456}; |
| | | |
| | | return new Object [][] { set1, set2, set3 }; |
| | |
| | | assertEquals(msg.getServerURL(), newMsg.getServerURL()); |
| | | assertEquals(msg.getBaseDn(), newMsg.getBaseDn()); |
| | | assertEquals(msg.getWindowSize(), newMsg.getWindowSize()); |
| | | assertEquals(msg.getServerState().getChangeNumber(1), |
| | | newMsg.getServerState().getChangeNumber(1)); |
| | | assertEquals(msg.getServerState().getCSN(1), |
| | | newMsg.getServerState().getCSN(1)); |
| | | assertEquals(newMsg.getVersion(), getCurrentVersion()); |
| | | assertEquals(msg.getGenerationId(), newMsg.getGenerationId()); |
| | | assertEquals(msg.getSSLEncryption(), newMsg.getSSLEncryption()); |
| | |
| | | { |
| | | String baseDN = TEST_ROOT_DN_STRING; |
| | | ServerState state = new ServerState(); |
| | | state.update(new ChangeNumber(0, 0, 0)); |
| | | state.update(new CSN(0, 0, 0)); |
| | | Object[] set1 = new Object[] {1, baseDN, 0, "localhost:8989", state, 0L, (byte)0, 0, 0, 0}; |
| | | |
| | | state = new ServerState(); |
| | | state.update(new ChangeNumber(75, 5, 263)); |
| | | state.update(new CSN(75, 5, 263)); |
| | | Object[] set2 = new Object[] {16, baseDN, 100, "anotherHost:1025", state, 1245L, (byte)25, 3456, 3, 31512}; |
| | | |
| | | state = new ServerState(); |
| | | state.update(new ChangeNumber(123, 5, 98)); |
| | | state.update(new CSN(123, 5, 98)); |
| | | Object[] set3 = new Object[] {36, baseDN, 100, "anotherHostAgain:8017", state, 6841L, (byte)32, 2496, 630, 9524}; |
| | | |
| | | return new Object [][] { set1, set2, set3 }; |
| | |
| | | assertEquals(msg.getServerURL(), newMsg.getServerURL()); |
| | | assertEquals(msg.getBaseDn(), newMsg.getBaseDn()); |
| | | assertEquals(msg.getWindowSize(), newMsg.getWindowSize()); |
| | | assertEquals(msg.getServerState().getChangeNumber(1), |
| | | newMsg.getServerState().getChangeNumber(1)); |
| | | assertEquals(msg.getServerState().getCSN(1), |
| | | newMsg.getServerState().getCSN(1)); |
| | | assertEquals(newMsg.getVersion(), getCurrentVersion()); |
| | | assertEquals(msg.getGenerationId(), newMsg.getGenerationId()); |
| | | assertEquals(msg.getSSLEncryption(), newMsg.getSSLEncryption()); |
| | |
| | | |
| | | // RS State |
| | | ServerState rsState = new ServerState(); |
| | | ChangeNumber rscn1 = new ChangeNumber(1, 1, 1); |
| | | ChangeNumber rscn2 = new ChangeNumber(1, 1, 45678); |
| | | rsState.update(rscn1); |
| | | rsState.update(rscn2); |
| | | CSN rsCSN1 = new CSN(1, 1, 1); |
| | | CSN rsCSN2 = new CSN(1, 1, 45678); |
| | | rsState.update(rsCSN1); |
| | | rsState.update(rsCSN2); |
| | | |
| | | // LS1 state |
| | | ServerState s1 = new ServerState(); |
| | | int sid1 = 111; |
| | | ChangeNumber cn1 = new ChangeNumber(1, 1, sid1); |
| | | s1.update(cn1); |
| | | CSN csn1 = new CSN(1, 1, sid1); |
| | | s1.update(csn1); |
| | | |
| | | // LS2 state |
| | | ServerState s2 = new ServerState(); |
| | | int sid2 = 222; |
| | | Long now = ((Integer)10).longValue(); |
| | | ChangeNumber cn2 = new ChangeNumber(now, 123, sid2); |
| | | s2.update(cn2); |
| | | CSN csn2 = new CSN(now, 123, sid2); |
| | | s2.update(csn2); |
| | | |
| | | // LS3 state |
| | | ServerState s3 = new ServerState(); |
| | | int sid3 = 56789; |
| | | ChangeNumber cn3 = new ChangeNumber(now, 123, sid3); |
| | | s3.update(cn3); |
| | | CSN csn3 = new CSN(now, 123, sid3); |
| | | s3.update(csn3); |
| | | |
| | | MonitorMsg msg = |
| | | new MonitorMsg(sender, dest); |
| | |
| | | public void UpdateMsgTest() throws Exception |
| | | { |
| | | final String test = "string used for test"; |
| | | ChangeNumber cn = new ChangeNumber(1, 2 , 39123); |
| | | UpdateMsg msg = new UpdateMsg(cn, test.getBytes()); |
| | | CSN csn = new CSN(1, 2, 39123); |
| | | UpdateMsg msg = new UpdateMsg(csn, test.getBytes()); |
| | | UpdateMsg newMsg = new UpdateMsg(msg.getBytes()); |
| | | assertEquals(test.getBytes(), newMsg.getPayload()); |
| | | } |
| | |
| | | assertEquals(msg.getWindowSize(), newMsg.getWindowSize()); |
| | | assertEquals(msg.getHeartbeatInterval(), newMsg.getHeartbeatInterval()); |
| | | assertEquals(msg.getSSLEncryption(), newMsg.getSSLEncryption()); |
| | | assertEquals(msg.getServerState().getChangeNumber(1), |
| | | newMsg.getServerState().getChangeNumber(1)); |
| | | assertEquals(msg.getServerState().getCSN(1), |
| | | newMsg.getServerState().getCSN(1)); |
| | | assertEquals(newMsg.getVersion(), getCurrentVersion()); |
| | | assertEquals(msg.getGenerationId(), newMsg.getGenerationId()); |
| | | assertEquals(msg.getGroupId(), newMsg.getGroupId()); |
| | |
| | | throws Exception |
| | | { |
| | | // data |
| | | ChangeNumber changeNumber = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | CSN csn = new CSN(TimeThread.getTime(), 123, 45); |
| | | ServerState state = new ServerState(); |
| | | assertTrue(state.update(new ChangeNumber(75, 5,263))); |
| | | assertTrue(state.update(new CSN(75, 5,263))); |
| | | |
| | | // create original |
| | | StartECLSessionMsg msg = new StartECLSessionMsg(); |
| | | msg.setChangeNumber(changeNumber); |
| | | msg.setCSN(csn); |
| | | msg.setCrossDomainServerState("fakegenstate"); |
| | | msg.setPersistent(StartECLSessionMsg.PERSISTENT); |
| | | msg.setFirstDraftChangeNumber(13); |
| | |
| | | // create copy |
| | | StartECLSessionMsg newMsg = new StartECLSessionMsg(msg.getBytes(getCurrentVersion())); |
| | | // test equality between the two copies |
| | | assertEquals(msg.getChangeNumber(), newMsg.getChangeNumber()); |
| | | assertEquals(msg.getCSN(), newMsg.getCSN()); |
| | | assertEquals(msg.isPersistent(), newMsg.isPersistent()); |
| | | assertEquals(msg.getFirstDraftChangeNumber(), newMsg.getFirstDraftChangeNumber()); |
| | | assertEquals(msg.getECLRequestType(), newMsg.getECLRequestType()); |
| | |
| | | new HashMap<AttributeType,List<Attribute>>(); |
| | | opList.put(attr.getAttributeType(), operationalAttributes); |
| | | |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | CSN csn = new CSN(TimeThread.getTime(), 123, 45); |
| | | DN dn = DN.decode(rawDN); |
| | | |
| | | for (int i=1;i<perfRep;i++) |
| | |
| | | AddOperation addOpB = new AddOperationBasis(connection, |
| | | 1, 1, null, dn, objectClassList, userAttList, opList); |
| | | LocalBackendAddOperation addOp = new LocalBackendAddOperation(addOpB); |
| | | OperationContext opCtx = new AddContext(cn, "thisIsaUniqueID", |
| | | OperationContext opCtx = new AddContext(csn, "thisIsaUniqueID", |
| | | "parentUniqueId"); |
| | | addOp.setAttachment(SYNCHROCONTEXT, opCtx); |
| | | t2 = System.nanoTime(); |
| | |
| | | } |
| | | |
| | | @Test(enabled=false,dataProvider = "createModifyData") |
| | | public void modMsgPerfs(ChangeNumber changeNumber, |
| | | public void modMsgPerfs(CSN csn, |
| | | String rawdn, List<Modification> mods, |
| | | boolean isAssured, AssuredMode assuredMode, |
| | | byte safeDataLevel, List<Attribute> entryAttrList) |
| | |
| | | long buildnew = 0; |
| | | long t1,t2,t3,t31,t4,t5,t6 = 0; |
| | | |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | CSN csn2 = new CSN(TimeThread.getTime(), 123, 45); |
| | | DN dn = DN.decode(rawdn); |
| | | |
| | | for (int i=1;i<perfRep;i++) |
| | |
| | | connection, 1, 1, null, dn, mods); |
| | | LocalBackendModifyOperation modifyOp = |
| | | new LocalBackendModifyOperation(modifyOpB); |
| | | OperationContext opCtx = new ModifyContext(cn, "thisIsaUniqueID"); |
| | | OperationContext opCtx = new ModifyContext(csn2, "thisIsaUniqueID"); |
| | | modifyOp.setAttachment(SYNCHROCONTEXT, opCtx); |
| | | t2 = System.nanoTime(); |
| | | createop += (t2 - t1); |
| | |
| | | new DeleteOperationBasis(connection, 1, 1,null, DN.decode(rawDN)); |
| | | LocalBackendDeleteOperation op = |
| | | new LocalBackendDeleteOperation(deleteOp); |
| | | ChangeNumber cn = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(cn, "uniqueid")); |
| | | CSN csn = new CSN(TimeThread.getTime(), 123, 45); |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(csn, "uniqueid")); |
| | | t2 = System.nanoTime(); |
| | | createop += (t2 - t1); |
| | | |
| | |
| | | private int scenario = -1; |
| | | private long generationId = -1; |
| | | |
| | | private ChangeNumberGenerator gen = null; |
| | | private CSNGenerator gen = null; |
| | | |
| | | /** False if a received update had assured parameters not as expected */ |
| | | private boolean everyUpdatesAreOk = true; |
| | |
| | | setAssuredTimeout(assuredTimeout); |
| | | this.scenario = scenario; |
| | | |
| | | gen = new ChangeNumberGenerator(serverID, 0L); |
| | | gen = new CSNGenerator(serverID, 0L); |
| | | } |
| | | |
| | | public boolean receivedUpdatesOk() |
| | |
| | | // be done when using asynchronous process update mechanism |
| | | // (see processUpdate javadoc) |
| | | processUpdateDone(updateMsg, null); |
| | | getServerState().update(updateMsg.getChangeNumber()); |
| | | getServerState().update(updateMsg.getCSN()); |
| | | break; |
| | | case TIMEOUT_DS_SCENARIO: |
| | | // Let timeout occur |
| | |
| | | // be done when using asynchronous process update mechanism |
| | | // (see processUpdate javadoc) |
| | | processUpdateDone(updateMsg, "This is the replay error message generated from fake DS " + |
| | | getServerId() + " for update with change number " + updateMsg. |
| | | getChangeNumber()); |
| | | getServerState().update(updateMsg.getChangeNumber()); |
| | | getServerId() + " for update with CSN " + updateMsg.getCSN()); |
| | | getServerState().update(updateMsg.getCSN()); |
| | | break; |
| | | default: |
| | | fail("Unknown scenario: " + scenario); |
| | |
| | | { |
| | | // Create a new delete update message (the simplest to create) |
| | | DeleteMsg delMsg = |
| | | new DeleteMsg(getBaseDNString(), gen.newChangeNumber(), |
| | | new DeleteMsg(getBaseDNString(), gen.newCSN(), |
| | | UUID.randomUUID().toString()); |
| | | |
| | | // Send it (this uses the defined assured conf at constructor time) |
| | |
| | | /** The scenario this RS is expecting */ |
| | | private int scenario = -1; |
| | | |
| | | private ChangeNumberGenerator gen = null; |
| | | private CSNGenerator gen = null; |
| | | |
| | | /** False if a received update had assured parameters not as expected */ |
| | | private boolean everyUpdatesAreOk = true; |
| | |
| | | this.assuredMode = assuredMode; |
| | | this.safeDataLevel = (byte) safeDataLevel; |
| | | |
| | | gen = new ChangeNumberGenerator(serverId + 10, 0L); |
| | | gen = new CSNGenerator(serverId + 10, 0L); |
| | | } |
| | | |
| | | /** |
| | |
| | | public AckMsg sendNewFakeUpdate() throws Exception |
| | | { |
| | | // Create a new delete update message (the simplest to create) |
| | | DeleteMsg delMsg = new DeleteMsg(baseDn, gen.newChangeNumber(), |
| | | DeleteMsg delMsg = new DeleteMsg(baseDn, gen.newCSN(), |
| | | UUID.randomUUID().toString()); |
| | | |
| | | // Send del message in assured mode |
| | |
| | | if (updateMsg.isAssured()) |
| | | { |
| | | // Send the ack without errors |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber()); |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getCSN()); |
| | | session.publish(ackMsg); |
| | | ackReplied = true; |
| | | } |
| | |
| | | // Emulate RS waiting for virtual DS ack |
| | | sleep(MAX_SEND_UPDATE_TIME); |
| | | // Send the ack with timeout error from a virtual DS with id (ours + 10) |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber()); |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getCSN()); |
| | | ackMsg.setHasTimeout(true); |
| | | List<Integer> failedServers = new ArrayList<Integer>(); |
| | | failedServers.add(serverId + 10); |
| | |
| | | if (updateMsg.isAssured()) |
| | | { |
| | | // Send the ack with wrong status error from a virtual DS with id (ours + 10) |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber()); |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getCSN()); |
| | | ackMsg.setHasWrongStatus(true); |
| | | List<Integer> failedServers = new ArrayList<Integer>(); |
| | | failedServers.add((serverId + 10)); |
| | |
| | | if (updateMsg.isAssured()) |
| | | { |
| | | // Send the ack with replay error from a virtual DS with id (ours + 10) |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getChangeNumber()); |
| | | AckMsg ackMsg = new AckMsg(updateMsg.getCSN()); |
| | | ackMsg.setHasReplayError(true); |
| | | List<Integer> failedServers = new ArrayList<Integer>(); |
| | | failedServers.add((serverId + 10)); |
| | |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.plugin.LDAPReplicationDomain; |
| | | import org.opends.server.replication.protocol.AddMsg; |
| | | import org.opends.server.replication.protocol.ReplicationMsg; |
| | |
| | | + "userPassword: password\n" + "initials: AA\n"; |
| | | } |
| | | |
| | | static private ReplicationMsg createAddMsg(ChangeNumber cn) |
| | | static private ReplicationMsg createAddMsg(CSN csn) |
| | | { |
| | | Entry personWithUUIDEntry = null; |
| | | String user1entryUUID; |
| | |
| | | } |
| | | |
| | | // Create and publish an update message to add an entry. |
| | | return new AddMsg(cn, |
| | | return new AddMsg(csn, |
| | | personWithUUIDEntry.getDN().toString(), |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | } |
| | | |
| | | /* |
| | | * Create a Change number generator to generate new changenumbers |
| | | * Create a CSN generator to generate new CSNs |
| | | * when we need to send operation messages to the replicationServer. |
| | | */ |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(server3ID, 0); |
| | | CSNGenerator gen = new CSNGenerator(server3ID, 0); |
| | | |
| | | for (int i = 0; i < 10; i++) |
| | | { |
| | | broker3.publish(createAddMsg(gen.newChangeNumber())); |
| | | broker3.publish(createAddMsg(gen.newCSN())); |
| | | } |
| | | |
| | | searchMonitor(); |
| | |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.protocols.ldap.LDAPControl; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.common.ServerState; |
| | | import org.opends.server.replication.common.ServerStatus; |
| | | import org.opends.server.replication.plugin.MultimasterReplication; |
| | |
| | | */ |
| | | private int replicationServerPort; |
| | | |
| | | private ChangeNumber firstChangeNumberServer1 = null; |
| | | private ChangeNumber secondChangeNumberServer1 = null; |
| | | private ChangeNumber firstChangeNumberServer2 = null; |
| | | private ChangeNumber secondChangeNumberServer2 = null; |
| | | private CSN firstCSNServer1 = null; |
| | | private CSN secondCSNServer1 = null; |
| | | private CSN firstCSNServer2 = null; |
| | | private CSN secondCSNServer2 = null; |
| | | |
| | | private ChangeNumber unknownChangeNumberServer1; |
| | | private CSN unknownCSNServer1; |
| | | |
| | | private static final String exportLDIFAllFile = "exportLDIF.ldif"; |
| | | private String exportLDIFDomainFile = null; |
| | |
| | | assertTrue(server2.isConnected()); |
| | | |
| | | /* |
| | | * Create change numbers for the messages sent from server 1 |
| | | * with current time sequence 1 and with current time + 2 sequence 2 |
| | | * Create CSNs for the messages sent from server 1 with current time |
| | | * sequence 1 and with current time + 2 sequence 2 |
| | | */ |
| | | long time = TimeThread.getTime(); |
| | | firstChangeNumberServer1 = new ChangeNumber(time, 1, 1); |
| | | secondChangeNumberServer1 = new ChangeNumber(time + 2, 2, 1); |
| | | firstCSNServer1 = new CSN(time, 1, 1); |
| | | secondCSNServer1 = new CSN(time + 2, 2, 1); |
| | | |
| | | /* |
| | | * Create change numbers for the messages sent from server 2 |
| | | * with current time sequence 1 and with current time + 3 sequence 2 |
| | | * Create CSNs for the messages sent from server 2 with current time |
| | | * sequence 1 and with current time + 3 sequence 2 |
| | | */ |
| | | firstChangeNumberServer2 = new ChangeNumber(time+ 1, 1, 2); |
| | | secondChangeNumberServer2 = new ChangeNumber(time + 3, 2, 2); |
| | | firstCSNServer2 = new CSN(time + 1, 1, 2); |
| | | secondCSNServer2 = new CSN(time + 3, 2, 2); |
| | | |
| | | /* |
| | | * Create a ChangeNumber between firstChangeNumberServer1 and |
| | | * secondChangeNumberServer1 that will not be used to create a |
| | | * change sent to the replicationServer but that will be used |
| | | * in the Server State when opening a connection to the |
| | | * ReplicationServer to make sure that the ReplicationServer is |
| | | * able to accept such clients. |
| | | * Create a CSN between firstCSNServer1 and secondCSNServer1 that will not |
| | | * be used to create a change sent to the replicationServer but that will |
| | | * be used in the Server State when opening a connection to the |
| | | * ReplicationServer to make sure that the ReplicationServer is able to |
| | | * accept such clients. |
| | | */ |
| | | unknownChangeNumberServer1 = new ChangeNumber(time+1, 1, 1); |
| | | unknownCSNServer1 = new CSN(time + 1, 1, 1); |
| | | |
| | | /* |
| | | * Send and receive a Delete Msg from server 1 to server 2 |
| | | */ |
| | | DeleteMsg msg = |
| | | new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, firstChangeNumberServer1, |
| | | new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, firstCSNServer1, |
| | | "uid"); |
| | | server1.publish(msg); |
| | | ReplicationMsg msg2 = server2.receive(); |
| | |
| | | /* |
| | | * Send and receive a second Delete Msg |
| | | */ |
| | | msg = new DeleteMsg(TEST_ROOT_DN_STRING, secondChangeNumberServer1, "uid"); |
| | | msg = new DeleteMsg(TEST_ROOT_DN_STRING, secondCSNServer1, "uid"); |
| | | server1.publish(msg); |
| | | msg2 = server2.receive(); |
| | | server2.updateWindowAfterReplay(); |
| | |
| | | * Send and receive a Delete Msg from server 2 to server 1 |
| | | */ |
| | | msg = |
| | | new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, firstChangeNumberServer2, |
| | | new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, firstCSNServer2, |
| | | "other-uid"); |
| | | server2.publish(msg); |
| | | msg2 = server1.receive(); |
| | |
| | | /* |
| | | * Send and receive a second Delete Msg |
| | | */ |
| | | msg = new DeleteMsg(TEST_ROOT_DN_STRING, secondChangeNumberServer2, "uid"); |
| | | msg = new DeleteMsg(TEST_ROOT_DN_STRING, secondCSNServer2, "uid"); |
| | | server2.publish(msg); |
| | | msg2 = server1.receive(); |
| | | server1.updateWindowAfterReplay(); |
| | |
| | | |
| | | ReplicationMsg msg2 = broker.receive(); |
| | | broker.updateWindowAfterReplay(); |
| | | assertDeleteMsgChangeNumberEquals(msg2, firstChangeNumberServer1, |
| | | "first"); |
| | | assertDeleteMsgCSNEquals(msg2, firstCSNServer1, "first"); |
| | | debugInfo("Ending newClient"); |
| | | } |
| | | finally |
| | |
| | | * Test that a client that has already seen some changes now receive |
| | | * the correct next change. |
| | | */ |
| | | private void newClientWithChanges( |
| | | ServerState state, ChangeNumber nextChangeNumber) throws Exception |
| | | private void newClientWithChanges(ServerState state, CSN nextCSN) throws Exception |
| | | { |
| | | ReplicationBroker broker = null; |
| | | |
| | | /* |
| | | * Connect to the replicationServer using the state created above. |
| | | */ |
| | | // Connect to the replicationServer using the state created above. |
| | | try { |
| | | broker = |
| | | openReplicationSession(DN.decode(TEST_ROOT_DN_STRING), 3, |
| | |
| | | |
| | | ReplicationMsg msg2 = broker.receive(); |
| | | broker.updateWindowAfterReplay(); |
| | | assertDeleteMsgChangeNumberEquals(msg2, nextChangeNumber, "second"); |
| | | assertDeleteMsgCSNEquals(msg2, nextCSN, "second"); |
| | | } |
| | | finally |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Asserts that the change number for the passed in message matches the |
| | | * supplied change number. |
| | | * |
| | | * @param msg |
| | | * @param nextChangeNumber |
| | | * @param msgNumber |
| | | * Asserts that the CSN for the passed in message matches the supplied CSN. |
| | | */ |
| | | private void assertDeleteMsgChangeNumberEquals(ReplicationMsg msg, |
| | | ChangeNumber nextChangeNumber, String msgNumber) |
| | | private void assertDeleteMsgCSNEquals(ReplicationMsg msg, CSN nextCSN, String msgNumber) |
| | | { |
| | | if (msg instanceof DeleteMsg) |
| | | { |
| | | DeleteMsg del = (DeleteMsg) msg; |
| | | assertEquals(del.getChangeNumber(), nextChangeNumber, "The " + msgNumber |
| | | assertEquals(del.getCSN(), nextCSN, "The " + msgNumber |
| | | + " message received by a new client was the wrong one."); |
| | | } |
| | | else |
| | |
| | | * done in test changelogBasic. |
| | | */ |
| | | ServerState state = new ServerState(); |
| | | state.update(firstChangeNumberServer1); |
| | | state.update(firstChangeNumberServer2); |
| | | state.update(firstCSNServer1); |
| | | state.update(firstCSNServer2); |
| | | |
| | | newClientWithChanges(state, secondChangeNumberServer1); |
| | | newClientWithChanges(state, secondCSNServer1); |
| | | debugInfo("Ending newClientWithFirstChanges"); |
| | | } |
| | | |
| | |
| | | { |
| | | debugInfo("Starting newClientWithUnknownChanges"); |
| | | /* |
| | | * Create a ServerState with wrongChangeNumberServer1 |
| | | * Create a ServerState with wrongCSNServer1 |
| | | */ |
| | | ServerState state = new ServerState(); |
| | | state.update(unknownChangeNumberServer1); |
| | | state.update(secondChangeNumberServer2); |
| | | state.update(unknownCSNServer1); |
| | | state.update(secondCSNServer2); |
| | | |
| | | newClientWithChanges(state, secondChangeNumberServer1); |
| | | newClientWithChanges(state, secondCSNServer1); |
| | | debugInfo("Ending newClientWithUnknownChanges"); |
| | | } |
| | | |
| | |
| | | * Create a ServerState updated with the first change from server 1 |
| | | */ |
| | | ServerState state = new ServerState(); |
| | | state.update(firstChangeNumberServer1); |
| | | state.update(firstCSNServer1); |
| | | |
| | | newClientWithChanges(state, firstChangeNumberServer2); |
| | | newClientWithChanges(state, firstCSNServer2); |
| | | debugInfo("Ending newClientWithChangefromServer1"); |
| | | } |
| | | |
| | |
| | | * Create a ServerState updated with the first change from server 1 |
| | | */ |
| | | ServerState state = new ServerState(); |
| | | state.update(firstChangeNumberServer2); |
| | | state.update(firstCSNServer2); |
| | | |
| | | newClientWithChanges(state, firstChangeNumberServer1); |
| | | newClientWithChanges(state, firstCSNServer1); |
| | | debugInfo("Ending newClientWithChangefromServer2"); |
| | | } |
| | | |
| | |
| | | * Create a ServerState updated with the first change from server 1 |
| | | */ |
| | | ServerState state = new ServerState(); |
| | | state.update(secondChangeNumberServer2); |
| | | state.update(firstChangeNumberServer1); |
| | | state.update(secondCSNServer2); |
| | | state.update(firstCSNServer1); |
| | | |
| | | newClientWithChanges(state, secondChangeNumberServer1); |
| | | newClientWithChanges(state, secondCSNServer1); |
| | | debugInfo("Ending newClientLateServer1"); |
| | | } |
| | | |
| | |
| | | int TOTAL_MSG = 1000; // number of messages to send during the test |
| | | int CLIENT_THREADS = 2; // number of threads that will try to read |
| | | // the messages |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(5 , 0); |
| | | CSNGenerator gen = new CSNGenerator(5 , 0); |
| | | |
| | | BrokerReader client[] = new BrokerReader[CLIENT_THREADS]; |
| | | ReplicationBroker clientBroker[] = new ReplicationBroker[CLIENT_THREADS]; |
| | |
| | | for (int i = 0; i< TOTAL_MSG; i++) |
| | | { |
| | | DeleteMsg msg = |
| | | new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, gen.newChangeNumber(), |
| | | new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, gen.newCSN(), |
| | | "uid"); |
| | | server.publish(msg); |
| | | } |
| | |
| | | for (int i = 0; i< THREADS; i++) |
| | | { |
| | | int serverId = 10 + i; |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator(serverId , 0); |
| | | CSNGenerator gen = new CSNGenerator(serverId , 0); |
| | | broker[i] = |
| | | openReplicationSession( DN.decode(TEST_ROOT_DN_STRING), serverId, |
| | | 100, replicationServerPort, 3000, 1000, 0, true); |
| | |
| | | // - Delete |
| | | long time = TimeThread.getTime(); |
| | | int ts = 1; |
| | | ChangeNumber cn = new ChangeNumber(time, ts++, brokerIds[0]); |
| | | CSN csn = new CSN(time, ts++, brokerIds[0]); |
| | | |
| | | DeleteMsg delMsg = new DeleteMsg("o=example" + itest + "," + TEST_ROOT_DN_STRING, cn, "uid"); |
| | | DeleteMsg delMsg = new DeleteMsg("o=example" + itest + "," + TEST_ROOT_DN_STRING, csn, "uid"); |
| | | broker1.publish(delMsg); |
| | | |
| | | String user1entryUUID = "33333333-3333-3333-3333-333333333333"; |
| | |
| | | + "objectClass: top\n" + "objectClass: domain\n" |
| | | + "entryUUID: 11111111-1111-1111-1111-111111111111\n"; |
| | | Entry entry = TestCaseUtils.entryFromLdifString(lentry); |
| | | cn = new ChangeNumber(time, ts++, brokerIds[0]); |
| | | AddMsg addMsg = new AddMsg(cn, "o=example," + TEST_ROOT_DN_STRING, |
| | | csn = new CSN(time, ts++, brokerIds[0]); |
| | | AddMsg addMsg = new AddMsg(csn, "o=example," + TEST_ROOT_DN_STRING, |
| | | user1entryUUID, baseUUID, entry.getObjectClassAttribute(), entry |
| | | .getAttributes(), new ArrayList<Attribute>()); |
| | | broker1.publish(addMsg); |
| | |
| | | Modification mod1 = new Modification(ModificationType.REPLACE, attr1); |
| | | List<Modification> mods = new ArrayList<Modification>(); |
| | | mods.add(mod1); |
| | | cn = new ChangeNumber(time, ts++, brokerIds[0]); |
| | | ModifyMsg modMsg = new ModifyMsg(cn, DN |
| | | csn = new CSN(time, ts++, brokerIds[0]); |
| | | ModifyMsg modMsg = new ModifyMsg(csn, DN |
| | | .decode("o=example," + TEST_ROOT_DN_STRING), mods, "fakeuniqueid"); |
| | | broker1.publish(modMsg); |
| | | |
| | | // - ModifyDN |
| | | cn = new ChangeNumber(time, ts++, brokerIds[0]); |
| | | csn = new CSN(time, ts++, brokerIds[0]); |
| | | ModifyDNOperationBasis op = new ModifyDNOperationBasis(connection, 1, 1, null, DN |
| | | .decode("o=example," + TEST_ROOT_DN_STRING), RDN.decode("o=example2"), true, |
| | | null); |
| | | op.setAttachment(SYNCHROCONTEXT, new ModifyDnContext(cn, "uniqueid", |
| | | op.setAttachment(SYNCHROCONTEXT, new ModifyDnContext(csn, "uniqueid", |
| | | "newparentId")); |
| | | LocalBackendModifyDNOperation localOp = |
| | | new LocalBackendModifyDNOperation(op); |
| | |
| | | { |
| | | int count; |
| | | private ReplicationBroker broker; |
| | | ChangeNumberGenerator gen; |
| | | CSNGenerator gen; |
| | | |
| | | public BrokerWriter(ReplicationBroker broker, ChangeNumberGenerator gen, |
| | | public BrokerWriter(ReplicationBroker broker, CSNGenerator gen, |
| | | int count) |
| | | { |
| | | this.broker = broker; |
| | |
| | | count--; |
| | | |
| | | DeleteMsg msg = |
| | | new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, gen.newChangeNumber(), |
| | | new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, gen.newCSN(), |
| | | "uid"); |
| | | broker.publish(msg); |
| | | |
| | |
| | | List<UpdateMsg> l = new ArrayList<UpdateMsg>(); |
| | | long time = TimeThread.getTime(); |
| | | int ts = 1; |
| | | ChangeNumber cn; |
| | | CSN csn; |
| | | |
| | | try |
| | | { |
| | |
| | | + "objectClass: domain\n" |
| | | + "entryUUID: 11111111-1111-1111-1111-111111111111\n"; |
| | | Entry entry = TestCaseUtils.entryFromLdifString(lentry); |
| | | cn = new ChangeNumber(time, ts++, serverId); |
| | | AddMsg addMsg = new AddMsg(cn, "o=example,"+suffix, |
| | | csn = new CSN(time, ts++, serverId); |
| | | AddMsg addMsg = new AddMsg(csn, "o=example,"+suffix, |
| | | user1entryUUID, baseUUID, entry.getObjectClassAttribute(), entry |
| | | .getAttributes(), new ArrayList<Attribute>()); |
| | | l.add(addMsg); |
| | |
| | | + "entryUUID: " + user1entryUUID +"\n" |
| | | + "userpassword: fjen$$en" + "\n"; |
| | | Entry uentry = TestCaseUtils.entryFromLdifString(luentry); |
| | | cn = new ChangeNumber(time, ts++, serverId); |
| | | csn = new CSN(time, ts++, serverId); |
| | | AddMsg addMsg2 = new AddMsg( |
| | | cn, |
| | | csn, |
| | | "uid=new person,ou=People,"+suffix, |
| | | user1entryUUID, |
| | | baseUUID, |
| | |
| | | mods.add(mod2); |
| | | mods.add(mod3); |
| | | |
| | | cn = new ChangeNumber(time, ts++, serverId); |
| | | csn = new CSN(time, ts++, serverId); |
| | | DN dn = DN.decode("o=example,"+suffix); |
| | | ModifyMsg modMsg = new ModifyMsg(cn, dn, |
| | | ModifyMsg modMsg = new ModifyMsg(csn, dn, |
| | | mods, "fakeuniqueid"); |
| | | l.add(modMsg); |
| | | |
| | | // Modify DN |
| | | cn = new ChangeNumber(time, ts++, serverId); |
| | | csn = new CSN(time, ts++, serverId); |
| | | ModifyDNMsg modDnMsg = new ModifyDNMsg( |
| | | "uid=new person,ou=People,"+suffix, cn, |
| | | "uid=new person,ou=People,"+suffix, csn, |
| | | user1entryUUID, baseUUID, false, |
| | | "uid=wrong, ou=people,"+suffix, |
| | | "uid=newrdn"); |
| | | l.add(modDnMsg); |
| | | |
| | | // Del |
| | | cn = new ChangeNumber(time, ts++, serverId); |
| | | DeleteMsg delMsg = new DeleteMsg("o=example,"+suffix, cn, "uid"); |
| | | csn = new CSN(time, ts++, serverId); |
| | | DeleteMsg delMsg = new DeleteMsg("o=example,"+suffix, csn, "uid"); |
| | | l.add(delMsg); |
| | | } |
| | | catch(Exception ignored) {} |
| | |
| | | // - Test messages between clients by publishing now |
| | | long time = TimeThread.getTime(); |
| | | int ts = 1; |
| | | ChangeNumber cn; |
| | | CSN csn; |
| | | String user1entryUUID = "33333333-3333-3333-3333-333333333333"; |
| | | String baseUUID = "22222222-2222-2222-2222-222222222222"; |
| | | |
| | |
| | | + "objectClass: top\n" + "objectClass: domain\n" |
| | | + "entryUUID: " + user1entryUUID + "\n"; |
| | | Entry entry = TestCaseUtils.entryFromLdifString(lentry); |
| | | cn = new ChangeNumber(time, ts++, brokerIds[0]); |
| | | AddMsg addMsg = new AddMsg(cn, "o=example," + TEST_ROOT_DN_STRING, |
| | | csn = new CSN(time, ts++, brokerIds[0]); |
| | | AddMsg addMsg = new AddMsg(csn, "o=example," + TEST_ROOT_DN_STRING, |
| | | user1entryUUID, baseUUID, entry.getObjectClassAttribute(), entry |
| | | .getAttributes(), new ArrayList<Attribute>()); |
| | | broker1.publish(addMsg); |
| | |
| | | Modification mod1 = new Modification(ModificationType.REPLACE, attr1); |
| | | List<Modification> mods = new ArrayList<Modification>(); |
| | | mods.add(mod1); |
| | | cn = new ChangeNumber(time, ts++, brokerIds[0]); |
| | | ModifyMsg modMsg = new ModifyMsg(cn, DN |
| | | csn = new CSN(time, ts++, brokerIds[0]); |
| | | ModifyMsg modMsg = new ModifyMsg(csn, DN |
| | | .decode("o=example," + TEST_ROOT_DN_STRING), mods, "fakeuniqueid"); |
| | | broker1.publish(modMsg); |
| | | |
| | |
| | | try |
| | | { |
| | | // - Del |
| | | cn = new ChangeNumber(time, ts++, brokerIds[0]); |
| | | DeleteMsg delMsg = new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, cn, user1entryUUID); |
| | | csn = new CSN(time, ts++, brokerIds[0]); |
| | | DeleteMsg delMsg = new DeleteMsg("o=example," + TEST_ROOT_DN_STRING, csn, user1entryUUID); |
| | | broker1.publish(delMsg); |
| | | // Should receive some TopologyMsg messages for disconnection |
| | | // between the 2 RSs |
| | |
| | | * |
| | | * |
| | | * Copyright 2006-2009 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013 ForgeRock AS |
| | | */ |
| | | package org.opends.server.replication.server; |
| | | |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.opends.server.replication.protocol.OperationContext.SYNCHROCONTEXT; |
| | | import static org.testng.Assert.*; |
| | | |
| | | |
| | | import org.opends.server.core.DeleteOperation; |
| | | import org.opends.server.core.DeleteOperationBasis; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.DeleteContext; |
| | | import org.opends.server.replication.protocol.DeleteMsg; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | import org.opends.server.replication.server.UpdateComparator; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.util.TimeThread; |
| | | import org.opends.server.workflowelement.localbackend.LocalBackendDeleteOperation; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.opends.server.TestCaseUtils.*; |
| | | |
| | | |
| | | import static org.opends.server.replication.protocol.OperationContext.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test ChangeNumber and ChangeNumberGenerator |
| | | * Test CSN and CSNGenerator |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class UpdateComparatorTest extends ReplicationTestCase |
| | | { |
| | | |
| | |
| | | */ |
| | | @DataProvider(name = "updateMessageData") |
| | | public Object[][] createUpdateMessageData() { |
| | | CSN csn1 = new CSN(1, 0, 1); |
| | | CSN csn2 = new CSN(TimeThread.getTime(), 123, 45); |
| | | |
| | | ChangeNumber cn1 = new ChangeNumber(1, 0, 1); |
| | | ChangeNumber cn2 = new ChangeNumber(TimeThread.getTime(), 123, 45); |
| | | |
| | | // |
| | | // Create the update message |
| | | InternalClientConnection connection = |
| | | InternalClientConnection.getRootConnection(); |
| | |
| | | // TODO Auto-generated catch block |
| | | e.printStackTrace(); |
| | | } |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(cn1, "uniqueid 1")); |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(csn1, "uniqueid 1")); |
| | | DeleteMsg msg1 = new DeleteMsg(op); |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(cn2, "uniqueid 2")); |
| | | op.setAttachment(SYNCHROCONTEXT, new DeleteContext(csn2, "uniqueid 2")); |
| | | DeleteMsg msg2 = new DeleteMsg(op); |
| | | |
| | | |
| | | return new Object[][] { |
| | | {msg1, msg1, 0}, |
| | | {msg1, msg2, -1}, |
| | |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.protocol.DeleteMsg; |
| | | import org.opends.server.replication.server.ReplServerFakeConfiguration; |
| | | import org.opends.server.replication.server.ReplicationServer; |
| | |
| | | dbEnv = new ReplicationDbEnv(testRoot.getPath(), replicationServer); |
| | | handler = new DbHandler(1, TEST_ROOT_DN_STRING, replicationServer, dbEnv, 5000); |
| | | |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator( 1, 0); |
| | | ChangeNumber changeNumber1 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber2 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber3 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber4 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber5 = gen.newChangeNumber(); |
| | | CSNGenerator gen = new CSNGenerator( 1, 0); |
| | | CSN csn1 = gen.newCSN(); |
| | | CSN csn2 = gen.newCSN(); |
| | | CSN csn3 = gen.newCSN(); |
| | | CSN csn4 = gen.newCSN(); |
| | | CSN csn5 = gen.newCSN(); |
| | | |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, changeNumber1, "uid")); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, changeNumber2, "uid")); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, changeNumber3, "uid")); |
| | | DeleteMsg update4 = new DeleteMsg(TEST_ROOT_DN_STRING, changeNumber4, "uid"); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, csn1, "uid")); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, csn2, "uid")); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, csn3, "uid")); |
| | | DeleteMsg update4 = new DeleteMsg(TEST_ROOT_DN_STRING, csn4, "uid"); |
| | | |
| | | //-- |
| | | // Iterator tests with memory queue only populated |
| | |
| | | // verify that memory queue is populated |
| | | assertEquals(handler.getQueueSize(),3); |
| | | |
| | | assertFoundInOrder(handler, changeNumber1, changeNumber2, changeNumber3); |
| | | assertNotFound(handler, changeNumber5); |
| | | assertFoundInOrder(handler, csn1, csn2, csn3); |
| | | assertNotFound(handler, csn5); |
| | | |
| | | //-- |
| | | // Iterator tests with db only populated |
| | |
| | | // verify that memory queue is empty (all changes flushed in the db) |
| | | assertEquals(handler.getQueueSize(),0); |
| | | |
| | | assertFoundInOrder(handler, changeNumber1, changeNumber2, changeNumber3); |
| | | assertNotFound(handler, changeNumber5); |
| | | assertFoundInOrder(handler, csn1, csn2, csn3); |
| | | assertNotFound(handler, csn5); |
| | | |
| | | // Test first and last |
| | | assertEquals(changeNumber1, handler.getFirstChange()); |
| | | assertEquals(changeNumber3, handler.getLastChange()); |
| | | assertEquals(csn1, handler.getFirstChange()); |
| | | assertEquals(csn3, handler.getLastChange()); |
| | | |
| | | //-- |
| | | // Cursor tests with db and memory queue populated |
| | |
| | | // verify memory queue contains this one |
| | | assertEquals(handler.getQueueSize(),1); |
| | | |
| | | assertFoundInOrder(handler, changeNumber1, changeNumber2, changeNumber3, changeNumber4); |
| | | // Test cursor from existing CN at the limit between queue and db |
| | | assertFoundInOrder(handler, changeNumber3, changeNumber4); |
| | | assertFoundInOrder(handler, changeNumber4); |
| | | assertNotFound(handler, changeNumber5); |
| | | assertFoundInOrder(handler, csn1, csn2, csn3, csn4); |
| | | // Test cursor from existing CSN at the limit between queue and db |
| | | assertFoundInOrder(handler, csn3, csn4); |
| | | assertFoundInOrder(handler, csn4); |
| | | assertNotFound(handler, csn5); |
| | | |
| | | handler.setPurgeDelay(1); |
| | | |
| | |
| | | int count = 300; // wait at most 60 seconds |
| | | while (!purged && (count > 0)) |
| | | { |
| | | ChangeNumber firstChange = handler.getFirstChange(); |
| | | ChangeNumber lastChange = handler.getLastChange(); |
| | | if ((!firstChange.equals(changeNumber4) || |
| | | (!lastChange.equals(changeNumber4)))) |
| | | CSN firstChange = handler.getFirstChange(); |
| | | CSN lastChange = handler.getLastChange(); |
| | | if (!firstChange.equals(csn4) || !lastChange.equals(csn4)) |
| | | { |
| | | TestCaseUtils.sleep(100); |
| | | } else |
| | |
| | | return testRoot; |
| | | } |
| | | |
| | | private void assertFoundInOrder(DbHandler handler, |
| | | ChangeNumber... changeNumbers) throws Exception |
| | | private void assertFoundInOrder(DbHandler handler, CSN... csns) throws Exception |
| | | { |
| | | if (changeNumbers.length == 0) |
| | | if (csns.length == 0) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | ReplicaDBCursor cursor = handler.generateCursorFrom(changeNumbers[0]); |
| | | ReplicaDBCursor cursor = handler.generateCursorFrom(csns[0]); |
| | | try |
| | | { |
| | | for (int i = 1; i < changeNumbers.length; i++) |
| | | for (int i = 1; i < csns.length; i++) |
| | | { |
| | | assertTrue(cursor.next()); |
| | | final ChangeNumber cn = cursor.getChange().getChangeNumber(); |
| | | assertEquals(cn, changeNumbers[i]); |
| | | final CSN csn = cursor.getChange().getCSN(); |
| | | assertEquals(csn, csns[i]); |
| | | } |
| | | assertFalse(cursor.next()); |
| | | assertNull(cursor.getChange(), "Actual change number=" |
| | |
| | | } |
| | | } |
| | | |
| | | private void assertNotFound(DbHandler handler, ChangeNumber changeNumber) |
| | | private void assertNotFound(DbHandler handler, CSN csn) |
| | | { |
| | | ReplicaDBCursor cursor = null; |
| | | try |
| | | { |
| | | cursor = handler.generateCursorFrom(changeNumber); |
| | | cursor = handler.generateCursorFrom(csn); |
| | | fail("Expected exception"); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | assertEquals(e.getLocalizedMessage(), "ChangeNumber not available"); |
| | | assertEquals(e.getLocalizedMessage(), "CSN not available"); |
| | | } |
| | | finally |
| | | { |
| | |
| | | handler = new DbHandler(1, TEST_ROOT_DN_STRING, replicationServer, dbEnv, 5000); |
| | | |
| | | // Creates changes added to the dbHandler |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator( 1, 0); |
| | | ChangeNumber changeNumber1 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber2 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber3 = gen.newChangeNumber(); |
| | | CSNGenerator gen = new CSNGenerator( 1, 0); |
| | | CSN csn1 = gen.newCSN(); |
| | | CSN csn2 = gen.newCSN(); |
| | | CSN csn3 = gen.newCSN(); |
| | | |
| | | // Add the changes |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, changeNumber1, "uid")); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, changeNumber2, "uid")); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, changeNumber3, "uid")); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, csn1, "uid")); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, csn2, "uid")); |
| | | handler.add(new DeleteMsg(TEST_ROOT_DN_STRING, csn3, "uid")); |
| | | |
| | | // Check they are here |
| | | assertEquals(changeNumber1, handler.getFirstChange()); |
| | | assertEquals(changeNumber3, handler.getLastChange()); |
| | | assertEquals(csn1, handler.getFirstChange()); |
| | | assertEquals(csn3, handler.getLastChange()); |
| | | |
| | | // Clear ... |
| | | handler.clear(); |
| | |
| | | |
| | | // Populate the db with 'max' msg |
| | | int mySeqnum = 1; |
| | | ChangeNumber cnarray[] = new ChangeNumber[2*(max+1)]; |
| | | CSN csnArray[] = new CSN[2 * (max + 1)]; |
| | | long now = System.currentTimeMillis(); |
| | | for (int i=1; i<=max; i++) |
| | | { |
| | | cnarray[i] = new ChangeNumber(now+i, mySeqnum, 1); |
| | | csnArray[i] = new CSN(now + i, mySeqnum, 1); |
| | | mySeqnum+=2; |
| | | DeleteMsg update1 = new DeleteMsg(TEST_ROOT_DN_STRING, cnarray[i], "uid"); |
| | | DeleteMsg update1 = new DeleteMsg(TEST_ROOT_DN_STRING, csnArray[i], "uid"); |
| | | handler.add(update1); |
| | | } |
| | | handler.flush(); |
| | | |
| | | // Test first and last |
| | | ChangeNumber cn1 = handler.getFirstChange(); |
| | | assertEquals(cn1, cnarray[1], "First change"); |
| | | ChangeNumber cnlast = handler.getLastChange(); |
| | | assertEquals(cnlast, cnarray[max], "Last change"); |
| | | CSN csn1 = handler.getFirstChange(); |
| | | assertEquals(csn1, csnArray[1], "First change"); |
| | | CSN csnLast = handler.getLastChange(); |
| | | assertEquals(csnLast, csnArray[max], "Last change"); |
| | | |
| | | // Test count in different subcases trying to handle all special cases |
| | | // regarding the 'counter' record and 'count' algorithm |
| | | testcase="FROM change1 TO change1 "; |
| | | actualCnt = handler.getCount(cnarray[1], cnarray[1]); |
| | | actualCnt = handler.getCount(csnArray[1], csnArray[1]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, 1, testcase); |
| | | |
| | | testcase="FROM change1 TO change2 "; |
| | | actualCnt = handler.getCount(cnarray[1], cnarray[2]); |
| | | actualCnt = handler.getCount(csnArray[1], csnArray[2]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, 2, testcase); |
| | | |
| | | testcase="FROM change1 TO counterWindow="+(counterWindow); |
| | | actualCnt = handler.getCount(cnarray[1], cnarray[counterWindow]); |
| | | actualCnt = handler.getCount(csnArray[1], csnArray[counterWindow]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, counterWindow, testcase); |
| | | |
| | | testcase="FROM change1 TO counterWindow+1="+(counterWindow+1); |
| | | actualCnt = handler.getCount(cnarray[1], cnarray[counterWindow+1]); |
| | | actualCnt = handler.getCount(csnArray[1], csnArray[counterWindow+1]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, counterWindow+1, testcase); |
| | | |
| | | testcase="FROM change1 TO 2*counterWindow="+(2*counterWindow); |
| | | actualCnt = handler.getCount(cnarray[1], cnarray[2*counterWindow]); |
| | | actualCnt = handler.getCount(csnArray[1], csnArray[2*counterWindow]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, 2*counterWindow, testcase); |
| | | |
| | | testcase="FROM change1 TO 2*counterWindow+1="+((2*counterWindow)+1); |
| | | actualCnt = handler.getCount(cnarray[1], cnarray[(2*counterWindow)+1]); |
| | | actualCnt = handler.getCount(csnArray[1], csnArray[(2*counterWindow)+1]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, (2*counterWindow)+1, testcase); |
| | | |
| | | testcase="FROM change2 TO change5 "; |
| | | actualCnt = handler.getCount(cnarray[2], cnarray[5]); |
| | | actualCnt = handler.getCount(csnArray[2], csnArray[5]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, 4, testcase); |
| | | |
| | | testcase="FROM counterWindow+2 TO counterWindow+5 "; |
| | | actualCnt = handler.getCount(cnarray[(counterWindow+2)], cnarray[(counterWindow+5)]); |
| | | actualCnt = handler.getCount(csnArray[(counterWindow+2)], csnArray[(counterWindow+5)]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, 4, testcase); |
| | | |
| | | testcase="FROM change2 TO counterWindow+5 "; |
| | | actualCnt = handler.getCount(cnarray[2], cnarray[(counterWindow+5)]); |
| | | actualCnt = handler.getCount(csnArray[2], csnArray[(counterWindow+5)]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, counterWindow+4, testcase); |
| | | |
| | | testcase="FROM counterWindow+4 TO counterWindow+4 "; |
| | | actualCnt = handler.getCount(cnarray[(counterWindow+4)], cnarray[(counterWindow+4)]); |
| | | actualCnt = handler.getCount(csnArray[(counterWindow+4)], csnArray[(counterWindow+4)]); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, 1, testcase); |
| | | |
| | | // Now test with changes older than first or newer than last |
| | | ChangeNumber olderThanFirst = null; |
| | | ChangeNumber newerThanLast = |
| | | new ChangeNumber(System.currentTimeMillis() + (2*(max+1)), 100, 1); |
| | | CSN olderThanFirst = null; |
| | | CSN newerThanLast = new CSN(System.currentTimeMillis() + (2*(max+1)), 100, 1); |
| | | |
| | | // Now we want to test with start and stop outside of the db |
| | | |
| | | testcase="FROM our first generated change TO now (> newest change in the db)"; |
| | | actualCnt = handler.getCount(cnarray[1], newerThanLast); |
| | | actualCnt = handler.getCount(csnArray[1], newerThanLast); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, max, testcase); |
| | | |
| | |
| | | handler.setCounterWindowSize(counterWindow); |
| | | |
| | | // Test first and last |
| | | cn1 = handler.getFirstChange(); |
| | | assertEquals(cn1, cnarray[1], "First change"); |
| | | cnlast = handler.getLastChange(); |
| | | assertEquals(cnlast, cnarray[max], "Last change"); |
| | | csn1 = handler.getFirstChange(); |
| | | assertEquals(csn1, csnArray[1], "First change"); |
| | | csnLast = handler.getLastChange(); |
| | | assertEquals(csnLast, csnArray[max], "Last change"); |
| | | |
| | | testcase="FROM our first generated change TO now (> newest change in the db)"; |
| | | actualCnt = handler.getCount(cnarray[1], newerThanLast); |
| | | actualCnt = handler.getCount(csnArray[1], newerThanLast); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, max, testcase); |
| | | |
| | | // Populate the db with 'max' msg |
| | | for (int i=max+1; i<=(2*max); i++) |
| | | { |
| | | cnarray[i] = new ChangeNumber(now+i, mySeqnum, 1); |
| | | csnArray[i] = new CSN(now+i, mySeqnum, 1); |
| | | mySeqnum+=2; |
| | | DeleteMsg update1 = new DeleteMsg(TEST_ROOT_DN_STRING, cnarray[i], "uid"); |
| | | DeleteMsg update1 = new DeleteMsg(TEST_ROOT_DN_STRING, csnArray[i], "uid"); |
| | | handler.add(update1); |
| | | } |
| | | handler.flush(); |
| | | |
| | | // Test first and last |
| | | cn1 = handler.getFirstChange(); |
| | | assertEquals(cn1, cnarray[1], "First change"); |
| | | cnlast = handler.getLastChange(); |
| | | assertEquals(cnlast, cnarray[2*max], "Last change"); |
| | | csn1 = handler.getFirstChange(); |
| | | assertEquals(csn1, csnArray[1], "First change"); |
| | | csnLast = handler.getLastChange(); |
| | | assertEquals(csnLast, csnArray[2 * max], "Last change"); |
| | | |
| | | testcase="FROM our first generated change TO now (> newest change in the db)"; |
| | | actualCnt = handler.getCount(cnarray[1], newerThanLast); |
| | | actualCnt = handler.getCount(csnArray[1], newerThanLast); |
| | | debugInfo(tn,testcase + " actualCnt=" + actualCnt); |
| | | assertEquals(actualCnt, (2*max), testcase); |
| | | |
| | |
| | | |
| | | testcase="AFTER PURGE (first, last)="; |
| | | debugInfo(tn,testcase + handler.getFirstChange() + handler.getLastChange()); |
| | | assertEquals(handler.getLastChange(), cnarray[2*max], "Last="); |
| | | assertEquals(handler.getLastChange(), csnArray[2*max], "Last="); |
| | | |
| | | testcase="AFTER PURGE "; |
| | | actualCnt = handler.getCount(cnarray[1], newerThanLast); |
| | | actualCnt = handler.getCount(csnArray[1], newerThanLast); |
| | | int expectedCnt; |
| | | if (totalCount>1) |
| | | { |
| | |
| | | |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.ChangeNumber; |
| | | import org.opends.server.replication.common.ChangeNumberGenerator; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.server.ReplServerFakeConfiguration; |
| | | import org.opends.server.replication.server.ReplicationServer; |
| | | import org.opends.server.replication.server.changelog.api.ChangelogException; |
| | | import org.opends.server.replication.server.changelog.api.ChangelogDBIterator; |
| | | import org.opends.server.replication.server.changelog.api.ChangelogException; |
| | | import org.opends.server.replication.server.changelog.je.DraftCNDB.DraftCNDBCursor; |
| | | import org.opends.server.util.StaticUtils; |
| | | import org.testng.annotations.Test; |
| | |
| | | * - periodic trim |
| | | * - call to clear method() |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class DraftCNDbHandlerTest extends ReplicationTestCase |
| | | { |
| | | /** |
| | |
| | | * - set a very short trim period |
| | | * - wait for the db to be trimmed / here since the changes are not stored in |
| | | * the replication changelog, the draftCNDb will be cleared. |
| | | * |
| | | * @throws Exception |
| | | */ |
| | | @Test() |
| | | void testDraftCNDbHandlerTrim() throws Exception |
| | |
| | | String baseDN2 = "baseDN2"; |
| | | String baseDN3 = "baseDN3"; |
| | | |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator( 1, 0); |
| | | ChangeNumber changeNumber1 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber2 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber3 = gen.newChangeNumber(); |
| | | CSNGenerator gen = new CSNGenerator( 1, 0); |
| | | CSN csn1 = gen.newCSN(); |
| | | CSN csn2 = gen.newCSN(); |
| | | CSN csn3 = gen.newCSN(); |
| | | |
| | | // Add records |
| | | handler.add(sn1, value1, baseDN1, changeNumber1); |
| | | handler.add(sn2, value2, baseDN2, changeNumber2); |
| | | handler.add(sn3, value3, baseDN3, changeNumber3); |
| | | handler.add(sn1, value1, baseDN1, csn1); |
| | | handler.add(sn2, value2, baseDN2, csn2); |
| | | handler.add(sn3, value3, baseDN3, csn3); |
| | | |
| | | // The ChangeNumber should not get purged |
| | | final int firstDraftCN = handler.getFirstDraftCN(); |
| | |
| | | DraftCNDBCursor dbc = handler.getReadCursor(firstDraftCN); |
| | | try |
| | | { |
| | | assertEquals(dbc.currentChangeNumber(), changeNumber1); |
| | | assertEquals(dbc.currentCSN(), csn1); |
| | | assertEquals(dbc.currentBaseDN(), baseDN1); |
| | | assertEquals(dbc.currentValue(), value1); |
| | | assertTrue(dbc.toString().length() != 0); |
| | | |
| | | assertTrue(dbc.next()); |
| | | |
| | | assertEquals(dbc.currentChangeNumber(), changeNumber2); |
| | | assertEquals(dbc.currentCSN(), csn2); |
| | | assertEquals(dbc.currentBaseDN(), baseDN2); |
| | | assertEquals(dbc.currentValue(), value2); |
| | | |
| | | assertTrue(dbc.next()); |
| | | |
| | | assertEquals(dbc.currentChangeNumber(), changeNumber3); |
| | | assertEquals(dbc.currentCSN(), csn3); |
| | | assertEquals(dbc.currentBaseDN(), baseDN3); |
| | | assertEquals(dbc.currentValue(), value3); |
| | | |
| | |
| | | String baseDN2 = "baseDN2"; |
| | | String baseDN3 = "baseDN3"; |
| | | |
| | | ChangeNumberGenerator gen = new ChangeNumberGenerator( 1, 0); |
| | | ChangeNumber changeNumber1 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber2 = gen.newChangeNumber(); |
| | | ChangeNumber changeNumber3 = gen.newChangeNumber(); |
| | | CSNGenerator gen = new CSNGenerator( 1, 0); |
| | | CSN csn1 = gen.newCSN(); |
| | | CSN csn2 = gen.newCSN(); |
| | | CSN csn3 = gen.newCSN(); |
| | | |
| | | // Add records |
| | | handler.add(sn1, value1, baseDN1, changeNumber1); |
| | | handler.add(sn2, value2, baseDN2, changeNumber2); |
| | | handler.add(sn3, value3, baseDN3, changeNumber3); |
| | | handler.add(sn1, value1, baseDN1, csn1); |
| | | handler.add(sn2, value2, baseDN2, csn2); |
| | | handler.add(sn3, value3, baseDN3, csn3); |
| | | Thread.sleep(500); |
| | | |
| | | // Checks |