All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.avaje.ebeaninternal.server.cluster.mcast.IncomingPacketsProcessed Maven / Gradle / Ivy

There is a newer version: 2.8.1
Show newest version
/**
 * Copyright (C) 2009 Authors
 * 
 * This file is part of Ebean.
 * 
 * Ebean is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *  
 * Ebean is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Ebean; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA  
 */
package com.avaje.ebeaninternal.server.cluster.mcast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

/**
 * For Incoming Packets remembers the packets we have received and processed.
 * 

* This determines the gotAllPoint per cluster member and identifies missing * packets (gap between gotAllPoint and gotMaxPoint). *

*

* This information is used by the managerThread so send ACK's for messages we * have received and RESEND messages to fill the missing packets we have * detected. *

* * @author rbygrave * */ public class IncomingPacketsProcessed { private final ConcurrentHashMap mapByMember = new ConcurrentHashMap(); private final int maxResendIncoming; public IncomingPacketsProcessed(int maxResendIncoming) { this.maxResendIncoming = maxResendIncoming; } public void removeMember(String memberKey) { mapByMember.remove(memberKey); } /** * Return true if we should process this packet. Return false if we have * already processed the packet. */ public boolean isProcessPacket(String memberKey, long packetId) { GotAllPoint memberPackets = getMemberPackets(memberKey); return memberPackets.processPacket(packetId); } /** * Build the list of ACK and RESEND messages that we should send out * to the other members of the cluster. */ public AckResendMessages getAckResendMessages(IncomingPacketsLastAck lastAck) { // Called by the McastClusterBroadcast manager thread AckResendMessages response = new AckResendMessages(); for (GotAllPoint member : mapByMember.values()) { MessageAck lastAckMessage = lastAck.getLastAck(member.getMemberKey()); member.addAckResendMessages(response, lastAckMessage); } return response; } private GotAllPoint getMemberPackets(String memberKey) { // This method is only called single threaded // by the listener thread so I'm happy that this // put into mapByMember is ok. GotAllPoint memberGotAllPoint = mapByMember.get(memberKey); if (memberGotAllPoint == null) { memberGotAllPoint = new GotAllPoint(memberKey, maxResendIncoming); mapByMember.put(memberKey, memberGotAllPoint); } return memberGotAllPoint; } /** * Keeps track of packets received from a particular member of the cluster. *

* It notes the packetIds of the packets received and uses those to maintain * the 'gotAllPoint'. The 'gotAllPoint' is the packetId which we know we * received all the previous packets. *

*/ public static class GotAllPoint { private static final Logger logger = Logger.getLogger(GotAllPoint.class.getName()); private final String memberKey; private final int maxResendIncoming; private long gotAllPoint; private long gotMaxPoint; /** * Packets received out of order. */ private ArrayList outOfOrderList = new ArrayList(); private HashMap resendCountMap = new HashMap(); public GotAllPoint(String memberKey, int maxResendIncoming) { this.memberKey = memberKey; this.maxResendIncoming = maxResendIncoming; } /** * Add ACK and RESEND messages if required. */ public void addAckResendMessages(AckResendMessages response, MessageAck lastAckMessage) { synchronized (this) { if (lastAckMessage != null && lastAckMessage.getGotAllPacketId() >= gotAllPoint) { // nothing has changed } else { // ACK that we have got every packet up to gotAllPoint response.add(new MessageAck(memberKey, gotAllPoint)); } if (getMissingPacketCount() > 0) { // Ask for these Packets to be RESENT List missingPackets = getMissingPackets(); response.add(new MessageResend(memberKey, missingPackets)); } } } public String getMemberKey() { return memberKey; } public long getGotAllPoint() { synchronized (this) { return gotAllPoint; } } public long getGotMaxPoint() { synchronized (this) { return gotMaxPoint; } } private int getMissingPacketCount() { if (gotMaxPoint <= gotAllPoint) { if (!resendCountMap.isEmpty()) { resendCountMap.clear(); } return 0; } return (int) (gotMaxPoint - gotAllPoint) - outOfOrderList.size(); } public List getMissingPackets() { synchronized (this) { ArrayList missingList = new ArrayList(); // this is not particularly efficient but expecting // the outOfOrderList to be relatively small boolean lostPacket = false; for (long i = gotAllPoint + 1; i < gotMaxPoint; i++) { Long packetId = Long.valueOf(i); if (!outOfOrderList.contains(packetId)) { if (incrementResendCount(packetId)) { // request this packet be resent missingList.add(packetId); } else { lostPacket = true; } } } if (lostPacket){ checkOutOfOrderList(); } return missingList; } } /** * Return true if this packet has not yet exceeded the maxResendCount. */ private boolean incrementResendCount(Long packetId){ Integer resendCount = resendCountMap.get(packetId); if (resendCount != null){ int i = resendCount.intValue() + 1; if (i > maxResendIncoming){ // we are going to give up trying to get this packet now logger.warning("Exceeded maxResendIncoming["+maxResendIncoming+"] for packet["+packetId+"]. Giving up on requesting it."); resendCountMap.remove(packetId); outOfOrderList.add(packetId); return false; } resendCount = Integer.valueOf(i); resendCountMap.put(packetId, resendCount); } else { resendCountMap.put(packetId, ONE); } return true; } private static final Integer ONE = Integer.valueOf(1); public boolean processPacket(long packetId) { synchronized (this) { if (gotAllPoint == 0) { gotAllPoint = packetId; return true; } if (packetId <= gotAllPoint) { // already processed this packet return false; } if (!resendCountMap.isEmpty()){ resendCountMap.remove(Long.valueOf(packetId)); } if (packetId == gotAllPoint + 1) { gotAllPoint = packetId; } else { if (packetId > gotMaxPoint) { gotMaxPoint = packetId; } outOfOrderList.add(Long.valueOf(packetId)); } checkOutOfOrderList(); return true; } } private void checkOutOfOrderList() { if (outOfOrderList.size() == 0) { return; } boolean continueCheck; do { continueCheck = false; long nextPoint = gotAllPoint + 1; Iterator it = outOfOrderList.iterator(); while (it.hasNext()) { Long id = it.next(); if (id.longValue() == nextPoint) { // we found the next one in the outOfOrderList it.remove(); gotAllPoint = nextPoint; continueCheck = true; break; } } } while (continueCheck); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy