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