001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS
026 */
027package org.opends.server.replication.server;
028
029import java.util.ArrayList;
030import java.util.List;
031import java.util.Set;
032
033import org.forgerock.i18n.slf4j.LocalizedLogger;
034import org.opends.server.replication.common.AssuredMode;
035import org.opends.server.replication.common.CSN;
036import org.opends.server.replication.protocol.AckMsg;
037
038/**
039 * This class holds every info needed about the expected acks for a received
040 * update message requesting assured replication with Safe Read sub-mode.
041 * It also includes info/routines for constructing the final ack to be sent to
042 * the sender of the update message.
043 */
044public class SafeReadExpectedAcksInfo extends ExpectedAcksInfo
045{
046  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
047
048  /** Did some servers go in timeout when the matching update was sent ?. */
049  private boolean hasTimeout;
050
051  /** Were some servers in wrong status when the matching update was sent ?. */
052  private boolean hasWrongStatus;
053
054  /** Did some servers make an error replaying the sent matching update ?. */
055  private boolean hasReplayError;
056
057  /**
058   * The list of server ids that had errors for the sent matching update Each
059   * server id of the list had one of the 3 possible errors (timeout, wrong
060   * status or replay error).
061   */
062  private List<Integer> failedServers = new ArrayList<>();
063
064  /**
065   * Number of servers we want an ack from and from which we received the ack.
066   * Said differently: the number of servers in expectedServersAckStatus whose
067   * value is true. When this value reaches the size of expectedServersAckStatus
068   * we can compute an ack message (based on info in this object), to be
069   * returned to the (requester) server that sent us an assured update message.
070   */
071  private int numKnownAckStatus;
072
073  /**
074   * Creates a new SafeReadExpectedAcksInfo.
075   * @param csn The CSN of the assured update message
076   * @param requesterServerHandler The server that sent the assured update
077   * message
078   * @param expectedServers The list of servers we want an ack from (they are
079   * in normal status and have the same group id as us)
080   * @param wrongStatusServers The list of all servers already detected in
081   * wrongStatus (degraded status) to keep trace of the error for the future
082   * returning ack we gonna compute
083   */
084  public SafeReadExpectedAcksInfo(CSN csn,
085    ServerHandler requesterServerHandler, List<Integer> expectedServers,
086    List<Integer> wrongStatusServers)
087  {
088    super(csn, requesterServerHandler, AssuredMode.SAFE_READ_MODE,
089      expectedServers);
090
091    // Keep track of potential servers detected in wrong status
092    if (!wrongStatusServers.isEmpty())
093    {
094      hasWrongStatus = true;
095      failedServers = wrongStatusServers;
096    }
097  }
098
099  /**
100   * Sets the timeout marker for the future update ack.
101   * @param hasTimeout True if some timeout occurred
102   */
103  public void setHasTimeout(boolean hasTimeout)
104  {
105    this.hasTimeout = hasTimeout;
106  }
107
108  /**
109   * Sets the wrong status marker for the future update ack.
110   * @param hasWrongStatus True if some servers were in wrong status
111   */
112  public void setHasWrongStatus(boolean hasWrongStatus)
113  {
114    this.hasWrongStatus = hasWrongStatus;
115  }
116
117  /**
118   * Sets the replay error marker for the future update ack.
119   * @param hasReplayError True if some servers had errors replaying the change
120   */
121  public void setHasReplayError(boolean hasReplayError)
122  {
123    this.hasReplayError = hasReplayError;
124  }
125
126  /**
127   * Gets the timeout marker for the future update ack.
128   * @return The timeout marker for the future update ack.
129   */
130  public boolean hasTimeout()
131  {
132    return hasTimeout;
133  }
134
135  /**
136   * Gets the wrong status marker for the future update ack.
137   * @return hasWrongStatus The wrong status marker for the future update ack.
138   */
139  public boolean hasWrongStatus()
140  {
141    return hasWrongStatus;
142  }
143
144  /**
145   * Gets the replay error marker for the future update ack.
146   * @return hasReplayError The replay error marker for the future update ack.
147   */
148  public boolean hasReplayError()
149  {
150    return hasReplayError;
151  }
152
153  /** {@inheritDoc} */
154  @Override
155  public boolean processReceivedAck(ServerHandler ackingServer, AckMsg ackMsg)
156  {
157    // Get the ack status for the matching server
158    int ackingServerId = ackingServer.getServerId();
159    boolean ackReceived = expectedServersAckStatus.get(ackingServerId);
160    if (ackReceived)
161    {
162      // Sanity check: this should never happen
163      if (logger.isTraceEnabled())
164      {
165        logger.trace("Received unexpected ack from server id: "
166          + ackingServerId + " ack message: " + ackMsg);
167      }
168      return false;
169    } else
170    {
171      // Analyze received ack and update info for the ack to be later computed
172      // accordingly
173      boolean someErrors = false;
174      if (ackMsg.hasTimeout())
175      {
176        hasTimeout = true;
177        someErrors = true;
178      }
179      if (ackMsg.hasWrongStatus())
180      {
181        hasWrongStatus = true;
182        someErrors = true;
183      }
184      if (ackMsg.hasReplayError())
185      {
186        hasReplayError = true;
187        someErrors = true;
188      }
189      if (someErrors)
190      {
191        failedServers.addAll(ackMsg.getFailedServers());
192      }
193
194      // Mark this ack received for the server
195      expectedServersAckStatus.put(ackingServerId, true);
196      numKnownAckStatus++;
197    }
198
199    return numKnownAckStatus == expectedServersAckStatus.size();
200  }
201
202  /** {@inheritDoc} */
203  @Override
204  public AckMsg createAck(boolean timeout)
205  {
206    AckMsg ack = new AckMsg(csn);
207
208    // Fill collected errors info
209    ack.setHasTimeout(hasTimeout);
210    ack.setHasWrongStatus(hasWrongStatus);
211    ack.setHasReplayError(hasReplayError);
212
213    if (timeout)
214    {
215      // Force anyway timeout flag if requested
216      ack.setHasTimeout(true);
217
218      // Add servers that did not respond in time
219      Set<Integer> serverIds = expectedServersAckStatus.keySet();
220      serversInTimeout = new ArrayList<>(); // Use next loop to fill it
221      for (int serverId : serverIds)
222      {
223        boolean ackReceived = expectedServersAckStatus.get(serverId);
224        if (!ackReceived && !failedServers.contains(serverId))
225        {
226          failedServers.add(serverId);
227          serversInTimeout.add(serverId);
228        }
229      }
230    }
231
232    ack.setFailedServers(failedServers);
233
234    return ack;
235  }
236}