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}