public Object down(Message msg) { Address dest = msg.getDest(); boolean multicast = dest == null; if (msg.getSrc() == null) msg.setSrc(localAddress()); if (discard_all) { if (dest == null || dest.equals(localAddress())) loopback(msg); return null; } if (!multicast && drop_down_unicasts > 0) { drop_down_unicasts = Math.max(0, drop_down_unicasts - 1); return null; } if (multicast && drop_down_multicasts > 0) { drop_down_multicasts = Math.max(0, drop_down_multicasts - 1); return null; } if (down > 0) { double r = Math.random(); if (r < down) { if (excludeItself && dest != null && dest.equals(localAddress())) { if (log.isTraceEnabled()) log.trace("excluding itself"); } else { log.trace("dropping message"); num_down++; return null; } } } return down_prot.down(msg); }
/** Checks if a message should be passed up, or not */ protected boolean shouldDropUpMessage( @SuppressWarnings("UnusedParameters") Message msg, Address sender) { if (discard_all && !sender.equals(localAddress())) return true; if (ignoredMembers.contains(sender)) { if (log.isTraceEnabled()) log.trace(localAddress + ": dropping message from " + sender); num_up++; return true; } if (up > 0) { double r = Math.random(); if (r < up) { if (excludeItself && sender.equals(localAddress())) { if (log.isTraceEnabled()) log.trace("excluding myself"); } else { if (log.isTraceEnabled()) log.trace(localAddress + ": dropping message from " + sender); num_up++; return true; } } } return false; }
// If we're becoming coordinator, we need to handle TMP_VIEW as // an immediate change of view. See JGRP-1452. private void handleTmpView(View v) { List<Address> mbrs = v.getMembers(); if (mbrs.isEmpty()) return; Address new_coord = mbrs.get(0); if (!new_coord.equals(coord) && local_addr != null && local_addr.equals(new_coord)) handleViewChange(v); }
protected void deliver(Message msg, Event evt, SequencerHeader hdr) { Address sender = msg.getSrc(); if (sender == null) { if (log.isErrorEnabled()) log.error(local_addr + ": sender is null, cannot deliver " + "::" + hdr.getSeqno()); return; } long msg_seqno = hdr.getSeqno(); if (sender.equals(local_addr)) { forward_table.remove(msg_seqno); if (hdr.flush_ack) { ack_promise.setResult(msg_seqno); if (ack_mode && !flushing && threshold > 0 && ++num_acks >= threshold) { ack_mode = false; num_acks = 0; } } } if (!canDeliver(sender, msg_seqno)) { if (log.isWarnEnabled()) log.warn(local_addr + ": dropped duplicate message " + sender + "::" + msg_seqno); return; } if (log.isTraceEnabled()) log.trace(local_addr + ": delivering " + sender + "::" + msg_seqno); up_prot.up(evt); delivered_bcasts++; }
/** * An event is to be sent down the stack. The layer may want to examine its type and perform some * action on it, depending on the event's type. If the event is a message MSG, then the layer may * need to add a header to it (or do nothing at all) before sending it down the stack using <code> * PassDown</code>. In case of a GET_ADDRESS event (which tries to retrieve the stack's address * from one of the bottom layers), the layer may need to send a new response event back up the * stack using <code>up_prot.up()</code>. The PING protocol is interested in several different * down events, Event.FIND_INITIAL_MBRS - sent by the GMS layer and expecting a GET_MBRS_OK * Event.TMP_VIEW and Event.VIEW_CHANGE - a view change event Event.BECOME_SERVER - called after * client has joined and is fully working group member Event.CONNECT, Event.DISCONNECT. */ @SuppressWarnings("unchecked") public Object down(Event evt) { switch (evt.getType()) { case Event.FIND_INITIAL_MBRS: // sent by GMS layer case Event.FIND_ALL_VIEWS: // sends the GET_MBRS_REQ to all members, waits 'timeout' ms or until 'num_initial_members' // have been retrieved long start = System.currentTimeMillis(); boolean find_all_views = evt.getType() == Event.FIND_ALL_VIEWS; Promise<JoinRsp> promise = (Promise<JoinRsp>) evt.getArg(); List<PingData> rsps = find_all_views ? findAllViews(promise) : findInitialMembers(promise); long diff = System.currentTimeMillis() - start; if (log.isTraceEnabled()) log.trace("discovery took " + diff + " ms: responses: " + Util.printPingData(rsps)); return rsps; case Event.TMP_VIEW: case Event.VIEW_CHANGE: List<Address> tmp; view = (View) evt.getArg(); if ((tmp = view.getMembers()) != null) { synchronized (members) { members.clear(); members.addAll(tmp); } } current_coord = !members.isEmpty() ? members.get(0) : null; is_coord = current_coord != null && local_addr != null && current_coord.equals(local_addr); return down_prot.down(evt); case Event.BECOME_SERVER: // called after client has joined and is fully working group member down_prot.down(evt); is_server = true; return null; case Event.SET_LOCAL_ADDRESS: local_addr = (Address) evt.getArg(); return down_prot.down(evt); case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: is_leaving = false; group_addr = (String) evt.getArg(); Object ret = down_prot.down(evt); handleConnect(); return ret; case Event.DISCONNECT: is_leaving = true; handleDisconnect(); return down_prot.down(evt); default: return down_prot.down(evt); // Pass on to the layer below us } }
/** Execute when new member join or leave Group */ public void viewAccepted(View v) { memberSize = v.size(); if (mainFrame != null) setTitle(); members.clear(); members.addAll(v.getMembers()); if (v instanceof MergeView) { System.out.println("** " + v); // This is a simple merge function, which fetches the state from the coordinator // on a merge and overwrites all of its own state if (useState && !members.isEmpty()) { Address coord = members.get(0); Address local_addr = channel.getAddress(); if (local_addr != null && !local_addr.equals(coord)) { try { // make a copy of our state first Map<Point, Color> copy = null; if (send_own_state_on_merge) { synchronized (drawPanel.state) { copy = new LinkedHashMap<Point, Color>(drawPanel.state); } } System.out.println("fetching state from " + coord); channel.getState(coord, 5000); if (copy != null) sendOwnState(copy); // multicast my own state so everybody else has it too } catch (Exception e) { e.printStackTrace(); } } } } else System.out.println("** View=" + v); }
/** * Invoked upon receiving a MERGE event from the MERGE layer. Starts the merge protocol. See * description of protocol in DESIGN. * * @param views A List of <em>different</em> views detected by the merge protocol */ public void merge(Map<Address, View> views) { if (isMergeInProgress()) { if (log.isTraceEnabled()) log.trace(gms.local_addr + ": merge is already running (merge_id=" + merge_id + ")"); return; } // we need the merge *coordinators* not merge participants because not everyone can lead a merge // ! Collection<Address> coords = Util.determineMergeCoords(views); Collection<Address> merge_participants = Util.determineMergeParticipants(views); Membership tmp = new Membership(coords); // establish a deterministic order, so that coords can elect leader tmp.sort(); Address merge_leader = tmp.elementAt(0); if (log.isDebugEnabled()) log.debug("determining merge leader from " + merge_participants); if (merge_leader.equals(gms.local_addr)) { if (log.isDebugEnabled()) log.debug( "I (" + gms.local_addr + ") will be the leader. Starting the merge task for " + merge_participants); merge_task.start(views); } else { if (log.isDebugEnabled()) log.debug( "I (" + gms.local_addr + ") am not the merge leader, " + "waiting for merge leader (" + merge_leader + ") to initiate merge"); } }
public void sendGetMembersRequest(String cluster_name, Promise promise, boolean return_views_only) throws Exception { PhysicalAddress physical_addr = (PhysicalAddress) down_prot.down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); PingData data = new PingData(local_addr, null, false, UUID.get(local_addr), Arrays.asList(physical_addr)); PingHeader hdr = new PingHeader(PingHeader.GET_MBRS_REQ, data, cluster_name); hdr.return_view_only = return_views_only; Set<PhysicalAddress> combined_target_members = new HashSet<PhysicalAddress>(initial_hosts); combined_target_members.addAll(dynamic_hosts); for (final Address addr : combined_target_members) { if (addr.equals(physical_addr)) continue; final Message msg = new Message(addr, null, null); msg.setFlag(Message.OOB); msg.putHeader(this.id, hdr); if (log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS] sending PING request to " + msg.getDest()); timer.execute( new Runnable() { public void run() { try { down_prot.down(new Event(Event.MSG, msg)); } catch (Exception ex) { if (log.isErrorEnabled()) log.error("failed sending discovery request to " + addr + ": " + ex); } } }); } }
/** Checks whether the potential_new_coord would be the new coordinator (2nd in line) */ protected boolean wouldBeNewCoordinator(Address potential_new_coord) { if (potential_new_coord == null) return false; synchronized (members) { if (members.size() < 2) return false; Address new_coord = members.elementAt(1); // member at 2nd place return new_coord != null && new_coord.equals(potential_new_coord); } }
protected void handleView(View view) { view_size = view.size(); Address tmp = Util.pickNext(view.getMembers(), local_addr); if (tmp != null && !tmp.equals(local_addr)) { next = tmp; if (log.isDebugEnabled()) log.debug("next=" + next); } }
/** Returns true if local_addr is member of mbrs, else false */ protected boolean checkSelfInclusion(Vector mbrs) { Object mbr; if (mbrs == null) return false; for (int i = 0; i < mbrs.size(); i++) { mbr = mbrs.elementAt(i); if (mbr != null && local_addr.equals(mbr)) return true; } return false; }
public void suspect(Address mbr) { if (mbr.equals(gms.local_addr)) { if (log.isWarnEnabled()) log.warn("I am the coord and I'm suspected -- will probably leave shortly"); return; } Collection<Request> suspected = new LinkedHashSet<>(1); suspected.add(new Request(Request.SUSPECT, mbr, true)); handleMembershipChange(suspected); }
public Object down(Event evt) { switch (evt.getType()) { case Event.TMP_VIEW: case Event.VIEW_CHANGE: handleViewChange((View) evt.getArg()); break; case Event.GET_STATE: Address target; StateTransferInfo info = (StateTransferInfo) evt.getArg(); if (info.target == null) { target = determineCoordinator(); } else { target = info.target; if (target.equals(local_addr)) { log.error("%s: cannot fetch state from myself", local_addr); target = null; } } if (target == null) { log.debug("%s: first member (no state)", local_addr); up_prot.up(new Event(Event.GET_STATE_OK, new StateTransferInfo())); } else { Message state_req = new Message(target) .putHeader(this.id, new StateHeader(StateHeader.STATE_REQ)) .setFlag(Message.Flag.DONT_BUNDLE, Message.Flag.OOB, Message.Flag.SKIP_BARRIER); log.debug("%s: asking %s for state", local_addr, target); // suspend sending and handling of message garbage collection gossip messages, // fixes bugs #943480 and #938584). Wake up when state has been received /*if(log.isDebugEnabled()) log.debug("passing down a SUSPEND_STABLE event"); down_prot.down(new Event(Event.SUSPEND_STABLE, new Long(info.timeout)));*/ waiting_for_state_response = true; start = System.currentTimeMillis(); down_prot.down(new Event(Event.MSG, state_req)); } return null; // don't pass down any further ! case Event.CONFIG: Map<String, Object> config = (Map<String, Object>) evt.getArg(); if (config != null && config.containsKey("flush_supported")) { flushProtocolInStack = true; } break; case Event.SET_LOCAL_ADDRESS: local_addr = (Address) evt.getArg(); break; } return down_prot.down(evt); // pass on to the layer below us }
// @see java.lang.Object#equals(java.lang.Object) @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Owner other = (Owner) obj; if (address == null) { if (other.address != null) return false; } else if (!address.equals(other.address)) return false; return requestId == other.requestId; }
public void sendDiscoveryRequest(String cluster_name, Promise promise, ViewId view_id) throws Exception { PingData data = null; PhysicalAddress physical_addr = (PhysicalAddress) down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr)); if (view_id == null) { List<PhysicalAddress> physical_addrs = Arrays.asList(physical_addr); data = new PingData(local_addr, null, false, UUID.get(local_addr), physical_addrs); } PingHeader hdr = new PingHeader(PingHeader.GET_MBRS_REQ, data, cluster_name); hdr.view_id = view_id; Collection<PhysicalAddress> cluster_members = fetchClusterMembers(cluster_name); if (cluster_members == null) { Message msg = new Message(null); // multicast msg msg.setFlag(Message.OOB); msg.putHeader(getId(), hdr); sendMcastDiscoveryRequest(msg); } else { if (cluster_members.isEmpty()) { // if we don't find any members, return immediately if (promise != null) promise.setResult(null); } else { for (final Address addr : cluster_members) { if (addr.equals(physical_addr)) // no need to send the request to myself continue; final Message msg = new Message(addr, null, null); msg.setFlag(Message.OOB); msg.putHeader(this.id, hdr); if (log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS] sending discovery request to " + msg.getDest()); if (!sendDiscoveryRequestsInParallel()) { down_prot.down(new Event(Event.MSG, msg)); } else { timer.execute( new Runnable() { public void run() { try { down_prot.down(new Event(Event.MSG, msg)); } catch (Exception ex) { if (log.isErrorEnabled()) log.error("failed sending discovery request to " + addr + ": " + ex); } } }); } } } } }
@Override public void channelConnected(Channel channel) { // Validate view String localName = channel.getName(); Address localAddress = channel.getAddress(); for (Address address : channel.getView()) { String name = channel.getName(address); if ((name != null) && name.equals(localName) && !address.equals(localAddress)) { channel.close(); throw JGroupsLogger.ROOT_LOGGER.duplicateNodeName( this.factory.getValue().getProtocolStackConfiguration().getEnvironment().getNodeName()); } } }
/** The coordinator itself wants to leave the group */ public void leave(Address mbr) { if (mbr == null) { if (log.isErrorEnabled()) log.error(Util.getMessage("MemberSAddressIsNull")); return; } if (mbr.equals(gms.local_addr)) leaving = true; gms.getViewHandler().add(new Request(Request.LEAVE, mbr, false)); gms.getViewHandler() .stop(true); // wait until all requests have been processed, then close the queue and leave // If we're the coord leaving, ignore gms.leave_timeout: // https://issues.jboss.org/browse/JGRP-1509 long timeout = (long) (Math.max(gms.leave_timeout, gms.view_ack_collection_timeout) * 1.10); gms.getViewHandler().waitUntilCompleted(timeout); }
@ManagedOperation(description = "Reads data from local caches and dumps them to a file") public void dumpCache(String output_filename) throws Exception { Map<Address, PhysicalAddress> cache_contents = (Map<Address, PhysicalAddress>) down_prot.down(new Event(Event.GET_LOGICAL_PHYSICAL_MAPPINGS, false)); List<PingData> list = new ArrayList<>(cache_contents.size()); for (Map.Entry<Address, PhysicalAddress> entry : cache_contents.entrySet()) { Address addr = entry.getKey(); PhysicalAddress phys_addr = entry.getValue(); PingData data = new PingData(addr, true, UUID.get(addr), phys_addr).coord(addr.equals(local_addr)); list.add(data); } OutputStream out = new FileOutputStream(output_filename); write(list, out); }
private synchronized void handleViewChange(View view, boolean makeServer) { if (makeServer) initializeNewSymmetricKey(view instanceof MergeView); // if view is a bit broken set me as keyserver List<Address> members = view.getMembers(); if (members == null || members.isEmpty() || members.get(0) == null) { becomeKeyServer(local_addr, false); return; } // otherwise get keyserver from view controller Address tmpKeyServer = view.getMembers().get(0); // I am keyserver - either first member of group or old key server is no more and // I have been voted new controller if (makeServer || (tmpKeyServer.equals(local_addr))) becomeKeyServer(tmpKeyServer, makeServer); else handleNewKeyServer(tmpKeyServer, view instanceof MergeView); }
protected void handleConsumerFoundResponse(long threadId, Address address) { final Runnable runnable = _awaitingConsumer.poll(); // This is a representation of the server side owner running our task. Owner owner; if (runnable == null) { owner = new Owner(address, threadId); // For some reason we don't have a runnable anymore // so we have to send back to the coordinator that // the consumer is still available. The runnable // would be removed on a cancel sendToCoordinator(Type.CONSUMER_READY, owner.getRequestId(), owner.getAddress()); } else { final Long requestId = _requestId.get(runnable); if (requestId == null) { // requestId is not available - this means the result has been // returned already or it has been interrupted return; } owner = new Owner(address, requestId); _awaitingReturn.put(owner, runnable); // If local we pass along without serializing if (local_addr.equals(owner.getAddress())) { handleTaskSubmittedRequest(runnable, local_addr, requestId, threadId); } else { try { if (runnable instanceof DistributedFuture) { Callable<?> callable = ((DistributedFuture<?>) runnable).getCallable(); sendThreadRequest( owner.getAddress(), threadId, Type.RUN_SUBMITTED, requestId, callable); } else { sendThreadRequest( owner.getAddress(), threadId, Type.RUN_SUBMITTED, requestId, runnable); } } // This relies on the Mesasge class to throw this when a // serialization issue occurs catch (IllegalArgumentException e) { ExecutorNotification notificiation = notifiers.remove(runnable); if (notificiation != null) { notificiation.throwableEncountered(e); } throw e; } } } }
protected Address getReceiver() { try { List<Address> mbrs = channel.getView().getMembers(); System.out.println("pick receiver from the following members:"); int i = 0; for (Address mbr : mbrs) { if (mbr.equals(channel.getAddress())) System.out.println("[" + i + "]: " + mbr + " (self)"); else System.out.println("[" + i + "]: " + mbr); i++; } System.out.flush(); System.in.skip(System.in.available()); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String tmp = reader.readLine().trim(); int index = Integer.parseInt(tmp); return mbrs.get(index); // index out of bounds caught below } catch (Exception e) { System.err.println("UnicastTest.getReceiver(): " + e); return null; } }
/** * Decrements credits bytes from all elements and add new_credits to member (if non null). The * lowest credit needs to be greater than min_credits. Needs to be called with lock held * * @param member The member to which new_credits are added. NOP if null * @param new_credits Number of bytes to add to member. NOP if 0. */ protected void decrementAndAdd(Address member, long new_credits) { boolean replenish = member != null && new_credits > 0; if (accumulated_credits > 0) { for (Map.Entry<Address, Long> entry : this.credits.entrySet()) { entry.setValue(Math.max(0, entry.getValue().longValue() - accumulated_credits)); if (replenish) { Address tmp = entry.getKey(); if (tmp.equals(member)) entry.setValue(Math.min(max_credits, entry.getValue().longValue() + new_credits)); } } accumulated_credits = 0; } else { if (replenish) { Long val = this.credits.get(member); if (val != null) this.credits.put(member, Math.min(max_credits, val.longValue() + new_credits)); } } }
/** * Removes all members from a given view which don't have us in their view * (https://jira.jboss.org/browse/JGRP-1061). Example: * * <pre> * A: AB * B: AB * C: ABC * </pre> * * becomes * * <pre> * A: AB * B: AB * C: C // A and B don't have C in their views * </pre> * * @param map A map of members and their associated views */ public static void sanitizeViews(Map<Address, View> map) { if (map == null) return; for (Map.Entry<Address, View> entry : map.entrySet()) { Address key = entry.getKey(); Collection<Address> members = new ArrayList<Address>(entry.getValue().getMembers()); boolean modified = false; for (Iterator<Address> it = members.iterator(); it.hasNext(); ) { Address val = it.next(); if (val.equals(key)) // we can always talk to ourself ! continue; View view = map.get(val); final Collection<Address> tmp_mbrs = view != null ? view.getMembers() : null; if (tmp_mbrs != null && !tmp_mbrs.contains(key)) { it.remove(); modified = true; } } if (modified) { View old_view = entry.getValue(); entry.setValue(new View(old_view.getVid(), members)); } } }
private void handleXmitRequest(Address sender, long low, long high) { if (log.isTraceEnabled()) log.trace( new StringBuilder() .append(local_addr) .append(" <-- XMIT(") .append(sender) .append(": #") .append(low) .append("-") .append(high) .append(')')); SenderEntry entry = send_table.get(sender); AckSenderWindow win = entry != null ? entry.sent_msgs : null; if (win != null) { for (long i = low; i <= high; i++) { Message msg = win.get(i); if (msg == null) { if (log.isWarnEnabled() && !local_addr.equals(sender)) { StringBuilder sb = new StringBuilder(); sb.append("(requester=").append(sender).append(", local_addr=").append(this.local_addr); sb.append(") message ").append(sender).append("::").append(i); sb.append(" not found in retransmission table of ") .append(sender) .append(":\n") .append(win); log.warn(sb.toString()); } continue; } down_prot.down(new Event(Event.MSG, msg)); num_xmits++; } } }
public void send(Address dest, byte[] data, int offset, int length) throws Exception { if (dest == null) { if (log.isErrorEnabled()) log.error("destination is null"); return; } if (data == null) { log.warn("data is null; discarding packet"); return; } if (!running.get()) { if (log.isDebugEnabled()) log.debug("connection table is not running, discarding message to " + dest); return; } if (dest.equals(local_addr)) { receive(local_addr, data, offset, length); return; } // 1. Try to obtain correct Connection (or create one if not yet existent) TCPConnection conn; conn = mapper.getConnection(dest); // 2. Send the message using that connection if (conn != null) { try { conn.send(data, offset, length); } catch (Exception ex) { mapper.removeConnection(dest); throw ex; } } }
protected void handleXmitRequest(Address sender, SeqnoList missing) { if (log.isTraceEnabled()) log.trace( new StringBuilder() .append(local_addr) .append(" <-- XMIT(") .append(sender) .append(": #") .append(missing) .append(')')); SenderEntry entry = send_table.get(sender); xmit_reqs_received.addAndGet(missing.size()); Table<Message> win = entry != null ? entry.sent_msgs : null; if (win != null) { for (long seqno : missing) { Message msg = win.get(seqno); if (msg == null) { if (log.isWarnEnabled() && !local_addr.equals(sender)) { StringBuilder sb = new StringBuilder(); sb.append("(requester=").append(sender).append(", local_addr=").append(this.local_addr); sb.append(") message ").append(sender).append("::").append(seqno); sb.append(" not found in retransmission table of ") .append(sender) .append(":\n") .append(win); log.warn(sb.toString()); } continue; } down_prot.down(new Event(Event.MSG, msg)); xmit_rsps_sent.incrementAndGet(); } } }
/** * An event was received from the layer below. Usually the current layer will want to examine the * event type and - depending on its type - perform some computation (e.g. removing headers from a * MSG event type, or updating the internal membership list when receiving a VIEW_CHANGE event). * Finally the event is either a) discarded, or b) an event is sent down the stack using <code> * PassDown</code> or c) the event (or another event) is sent up the stack using <code>PassUp * </code>. * * <p>For the PING protocol, the Up operation does the following things. 1. If the event is a * Event.MSG then PING will inspect the message header. If the header is null, PING simply passes * up the event If the header is PingHeader.GET_MBRS_REQ then the PING protocol will PassDown a * PingRequest message If the header is PingHeader.GET_MBRS_RSP we will add the message to the * initial members vector and wake up any waiting threads. 2. If the event is Event.SET_LOCAL_ADDR * we will simple set the local address of this protocol 3. For all other messages we simple pass * it up to the protocol above * * @param evt - the event that has been sent from the layer below */ @SuppressWarnings("unchecked") public Object up(Event evt) { switch (evt.getType()) { case Event.MSG: Message msg = (Message) evt.getArg(); PingHeader hdr = (PingHeader) msg.getHeader(this.id); if (hdr == null) return up_prot.up(evt); PingData data = hdr.data; Address logical_addr = data != null ? data.getAddress() : null; if (is_leaving) return null; // prevents merging back a leaving member // (https://issues.jboss.org/browse/JGRP-1336) switch (hdr.type) { case PingHeader.GET_MBRS_REQ: // return Rsp(local_addr, coord) if (group_addr == null || hdr.cluster_name == null) { if (log.isWarnEnabled()) log.warn( "group_addr (" + group_addr + ") or cluster_name of header (" + hdr.cluster_name + ") is null; passing up discovery request from " + msg.getSrc() + ", but this should not" + " be the case"); } else { if (!group_addr.equals(hdr.cluster_name)) { if (log.isWarnEnabled()) log.warn( "discarding discovery request for cluster '" + hdr.cluster_name + "' from " + msg.getSrc() + "; our cluster name is '" + group_addr + "'. " + "Please separate your clusters cleanly."); return null; } } // add physical address and logical name of the discovery sender (if available) to the // cache if (data != null) { if (logical_addr == null) logical_addr = msg.getSrc(); Collection<PhysicalAddress> physical_addrs = data.getPhysicalAddrs(); PhysicalAddress physical_addr = physical_addrs != null && !physical_addrs.isEmpty() ? physical_addrs.iterator().next() : null; if (logical_addr != null && physical_addr != null) down( new Event( Event.SET_PHYSICAL_ADDRESS, new Tuple<Address, PhysicalAddress>(logical_addr, physical_addr))); if (logical_addr != null && data.getLogicalName() != null) UUID.add(logical_addr, data.getLogicalName()); discoveryRequestReceived(msg.getSrc(), data.getLogicalName(), physical_addrs); synchronized (ping_responses) { for (Responses response : ping_responses) { response.addResponse(data, false); } } } if (hdr.view_id != null) { // If the discovery request is merge-triggered, and the ViewId shipped with it // is the same as ours, we don't respond (JGRP-1315). ViewId my_view_id = view != null ? view.getViewId() : null; if (my_view_id != null && my_view_id.equals(hdr.view_id)) return null; boolean send_discovery_rsp = force_sending_discovery_rsps || is_coord || current_coord == null || current_coord.equals(msg.getSrc()); if (!send_discovery_rsp) { if (log.isTraceEnabled()) log.trace( local_addr + ": suppressing merge response as I'm not a coordinator and the " + "discovery request was not sent by a coordinator"); return null; } } if (isMergeRunning()) { if (log.isTraceEnabled()) log.trace( local_addr + ": suppressing merge response as a merge is already in progress"); return null; } List<PhysicalAddress> physical_addrs = hdr.view_id != null ? null : Arrays.asList( (PhysicalAddress) down(new Event(Event.GET_PHYSICAL_ADDRESS, local_addr))); sendDiscoveryResponse( local_addr, physical_addrs, is_server, hdr.view_id != null, UUID.get(local_addr), msg.getSrc()); return null; case PingHeader.GET_MBRS_RSP: // add response to vector and notify waiting thread // add physical address (if available) to transport's cache if (data != null) { Address response_sender = msg.getSrc(); if (logical_addr == null) logical_addr = msg.getSrc(); Collection<PhysicalAddress> addrs = data.getPhysicalAddrs(); PhysicalAddress physical_addr = addrs != null && !addrs.isEmpty() ? addrs.iterator().next() : null; if (logical_addr != null && physical_addr != null) down( new Event( Event.SET_PHYSICAL_ADDRESS, new Tuple<Address, PhysicalAddress>(logical_addr, physical_addr))); if (logical_addr != null && data.getLogicalName() != null) UUID.add(logical_addr, data.getLogicalName()); if (log.isTraceEnabled()) log.trace( local_addr + ": received GET_MBRS_RSP from " + response_sender + ": " + data); boolean overwrite = logical_addr != null && logical_addr.equals(response_sender); synchronized (ping_responses) { for (Responses response : ping_responses) { response.addResponse(data, overwrite); } } } return null; default: if (log.isWarnEnabled()) log.warn("got PING header with unknown type (" + hdr.type + ')'); return null; } case Event.GET_PHYSICAL_ADDRESS: try { sendDiscoveryRequest(group_addr, null, null); } catch (InterruptedIOException ie) { if (log.isWarnEnabled()) { log.warn("Discovery request for cluster " + group_addr + " interrupted"); } Thread.currentThread().interrupt(); } catch (Exception ex) { if (log.isErrorEnabled()) log.error("failed sending discovery request", ex); } return null; case Event.FIND_INITIAL_MBRS: // sent by transport return findInitialMembers(null); } return up_prot.up(evt); }
protected boolean isCoord(Address member) { return member.equals(current_coord); }
public Object down(Event evt) { switch (evt.getType()) { case ExecutorEvent.TASK_SUBMIT: Runnable runnable = evt.getArg(); // We are limited to a number of concurrent request id's // equal to 2^63-1. This is quite large and if it // overflows it will still be positive long requestId = Math.abs(counter.getAndIncrement()); if (requestId == Long.MIN_VALUE) { // TODO: need to fix this it isn't safe for concurrent modifications counter.set(0); requestId = Math.abs(counter.getAndIncrement()); } // Need to make sure to put the requestId in our map before // adding the runnable to awaiting consumer in case if // coordinator sent a consumer found and their original task // is no longer around // see https://issues.jboss.org/browse/JGRP-1744 _requestId.put(runnable, requestId); _awaitingConsumer.add(runnable); sendToCoordinator(RUN_REQUEST, requestId, local_addr); break; case ExecutorEvent.CONSUMER_READY: Thread currentThread = Thread.currentThread(); long threadId = currentThread.getId(); _consumerId.put(threadId, PRESENT); try { for (; ; ) { CyclicBarrier barrier = new CyclicBarrier(2); _taskBarriers.put(threadId, barrier); // We only send to the coordinator that we are ready after // making the barrier, wait for request to come and let // us free sendToCoordinator(Type.CONSUMER_READY, threadId, local_addr); try { barrier.await(); break; } catch (BrokenBarrierException e) { if (log.isDebugEnabled()) log.debug( "Producer timed out before we picked up" + " the task, have to tell coordinator" + " we are still good."); } } // This should always be non nullable since the latch // was freed runnable = _tasks.remove(threadId); _runnableThreads.put(runnable, currentThread); return runnable; } catch (InterruptedException e) { if (log.isDebugEnabled()) log.debug("Consumer " + threadId + " stopped via interrupt"); sendToCoordinator(Type.CONSUMER_UNREADY, threadId, local_addr); Thread.currentThread().interrupt(); } finally { // Make sure the barriers are cleaned up as well _taskBarriers.remove(threadId); _consumerId.remove(threadId); } break; case ExecutorEvent.TASK_COMPLETE: Object arg = evt.getArg(); Throwable throwable = null; if (arg instanceof Object[]) { Object[] array = (Object[]) arg; runnable = (Runnable) array[0]; throwable = (Throwable) array[1]; } else { runnable = (Runnable) arg; } Owner owner = _running.remove(runnable); // This won't remove anything if owner doesn't come back _runnableThreads.remove(runnable); Object value = null; boolean exception = false; if (throwable != null) { // InterruptedException is special telling us that // we interrupted the thread while waiting but still got // a task therefore we have to reject it. if (throwable instanceof InterruptedException) { if (log.isDebugEnabled()) log.debug("Run rejected due to interrupted exception returned"); sendRequest(owner.address, Type.RUN_REJECTED, owner.requestId, null); break; } value = throwable; exception = true; } else if (runnable instanceof RunnableFuture<?>) { RunnableFuture<?> future = (RunnableFuture<?>) runnable; boolean interrupted = false; boolean gotValue = false; // We have the value, before we interrupt at least get it! while (!gotValue) { try { value = future.get(); gotValue = true; } catch (InterruptedException e) { interrupted = true; } catch (ExecutionException e) { value = e.getCause(); exception = true; gotValue = true; } } if (interrupted) { Thread.currentThread().interrupt(); } } if (owner != null) { final Type type; final Object valueToSend; if (value == null) { type = Type.RESULT_SUCCESS; valueToSend = value; } // Both serializable values and exceptions would go in here else if (value instanceof Serializable || value instanceof Externalizable || value instanceof Streamable) { type = exception ? Type.RESULT_EXCEPTION : Type.RESULT_SUCCESS; valueToSend = value; } // This would happen if the value wasn't serializable, // so we have to send back to the client that the class // wasn't serializable else { type = Type.RESULT_EXCEPTION; valueToSend = new NotSerializableException(value.getClass().getName()); } if (local_addr.equals(owner.getAddress())) { if (log.isTraceEnabled()) log.trace( "[redirect] <--> [" + local_addr + "] " + type.name() + " [" + value + (owner.requestId != -1 ? " request id: " + owner.requestId : "") + "]"); if (type == Type.RESULT_SUCCESS) { handleValueResponse(local_addr, owner.requestId, valueToSend); } else if (type == Type.RESULT_EXCEPTION) { handleExceptionResponse(local_addr, owner.requestId, (Throwable) valueToSend); } } else { sendRequest(owner.getAddress(), type, owner.requestId, valueToSend); } } else { if (log.isTraceEnabled()) { log.trace("Could not return result - most likely because it was interrupted"); } } break; case ExecutorEvent.TASK_CANCEL: Object[] array = evt.getArg(); runnable = (Runnable) array[0]; if (_awaitingConsumer.remove(runnable)) { _requestId.remove(runnable); ExecutorNotification notification = notifiers.remove(runnable); if (notification != null) { notification.interrupted(runnable); } if (log.isTraceEnabled()) log.trace("Cancelled task " + runnable + " before it was picked up"); return Boolean.TRUE; } // This is guaranteed to not be null so don't take cost of auto unboxing else if (array[1] == Boolean.TRUE) { owner = removeKeyForValue(_awaitingReturn, runnable); if (owner != null) { Long requestIdValue = _requestId.remove(runnable); // We only cancel if the requestId is still available // this means the result hasn't been returned yet and // we still have a chance to interrupt if (requestIdValue != null) { if (requestIdValue != owner.getRequestId()) { log.warn("Cancelling requestId didn't match waiting"); } sendRequest(owner.getAddress(), Type.INTERRUPT_RUN, owner.getRequestId(), null); } } else { if (log.isTraceEnabled()) log.warn("Couldn't interrupt server task: " + runnable); } ExecutorNotification notification = notifiers.remove(runnable); if (notification != null) { notification.interrupted(runnable); } return Boolean.TRUE; } else { return Boolean.FALSE; } case ExecutorEvent.ALL_TASK_CANCEL: array = evt.getArg(); // This is a RunnableFuture<?> so this cast is okay @SuppressWarnings("unchecked") Set<Runnable> runnables = (Set<Runnable>) array[0]; Boolean booleanValue = (Boolean) array[1]; List<Runnable> notRan = new ArrayList<>(); for (Runnable cancelRunnable : runnables) { // Removed from the consumer if (!_awaitingConsumer.remove(cancelRunnable) && booleanValue == Boolean.TRUE) { synchronized (_awaitingReturn) { owner = removeKeyForValue(_awaitingReturn, cancelRunnable); if (owner != null) { Long requestIdValue = _requestId.remove(cancelRunnable); if (requestIdValue != owner.getRequestId()) { log.warn("Cancelling requestId didn't match waiting"); } sendRequest(owner.getAddress(), Type.INTERRUPT_RUN, owner.getRequestId(), null); } ExecutorNotification notification = notifiers.remove(cancelRunnable); if (notification != null) { log.trace("Notifying listener"); notification.interrupted(cancelRunnable); } } } else { _requestId.remove(cancelRunnable); notRan.add(cancelRunnable); } } return notRan; case Event.SET_LOCAL_ADDRESS: local_addr = evt.getArg(); break; case Event.VIEW_CHANGE: handleView(evt.getArg()); break; } return down_prot.down(evt); }
public void handleView(View view) { Address oldCoord = coord; if (view.size() > 0) { coord = view.getMembers().iterator().next(); is_coord = coord.equals(local_addr); if (log.isDebugEnabled()) log.debug("local_addr=" + local_addr + ", coord=" + coord + ", is_coord=" + is_coord); } // If we got a new coordinator we have to send all the requests for // tasks and consumers again just incase they were missed when // coordinator went down // We are okay with duplicates since we don't add multiple times. // We also have a problem that if a task/consumer was picked up as the // consumer is changing we may have duplicates. But this is technically // okay in that an extra consumer will reject and an extra task will just // be ran and return nowhere, but at least we won't lose data. if (oldCoord != coord) { for (Long requests : _requestId.values()) { sendToCoordinator(Type.RUN_REQUEST, requests, local_addr); } for (Long requests : _consumerId.keySet()) { sendToCoordinator(Type.CONSUMER_READY, requests, local_addr); } } if (num_backups > 0) { if (is_coord) { List<Address> new_backups = Util.pickNext(view.getMembers(), local_addr, num_backups); List<Address> new_members = null; synchronized (backups) { if (!backups.equals(new_backups)) { new_members = new ArrayList<Address>(new_backups); new_members.removeAll(backups); backups.clear(); backups.addAll(new_backups); } } if (new_members != null && !new_members.isEmpty()) copyQueueTo(new_members); } // We keep what backups we have ourselves, so that when we become // the coordinator we don't update them again. Technically we can // send multiple requests but don't if to prevent more message being // sent. else { List<Address> possiblebackups = Util.pickNext(view.getMembers(), coord, num_backups); boolean foundMyself = false; List<Address> myBackups = new ArrayList<Address>(); for (Address backup : possiblebackups) { if (foundMyself) { myBackups.add(backup); } else if (backup.equals(local_addr)) { foundMyself = true; } } synchronized (backups) { backups.clear(); backups.addAll(myBackups); } } } // Need to run this last so the backups are updated super.handleView(view); }