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}