Example #1
0
  /**
   * 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");
    }
  }
Example #2
0
 /** 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);
   }
 }
Example #3
0
    /**
     * Merge all MergeData. All MergeData elements should be disjunct (both views and digests).
     * However, this method is prepared to resolve duplicate entries (for the same member).
     * Resolution strategy for views is to merge only 1 of the duplicate members. Resolution
     * strategy for digests is to take the higher seqnos for duplicate digests.
     *
     * <p>After merging all members into a Membership and subsequent sorting, the first member of
     * the sorted membership will be the new coordinator. This method has a lock on merge_rsps.
     *
     * @param merge_rsps A list of MergeData items. Elements with merge_rejected=true were removed
     *     before. Is guaranteed not to be null and to contain at least 1 member.
     */
    private MergeData consolidateMergeData(Vector<MergeData> merge_rsps) {
      long logical_time = 0; // for new_vid
      Membership new_mbrs = new Membership();
      Vector<View> subgroups =
          new Vector<View>(11); // contains a list of Views, each View is a subgroup

      for (MergeData tmp_data : merge_rsps) {
        View tmp_view = tmp_data.getView();
        if (tmp_view != null) {
          ViewId tmp_vid = tmp_view.getVid();
          if (tmp_vid != null) {
            // compute the new view id (max of all vids +1)
            logical_time = Math.max(logical_time, tmp_vid.getId());
          }
          // merge all membership lists into one (prevent duplicates)
          new_mbrs.add(tmp_view.getMembers());
          subgroups.addElement((View) tmp_view.clone());
        }
      }

      // the new coordinator is the first member of the consolidated & sorted membership list
      new_mbrs.sort();
      Address new_coord = new_mbrs.size() > 0 ? new_mbrs.elementAt(0) : null;
      if (new_coord == null) {
        if (log.isErrorEnabled()) log.error("new_coord == null");
        return null;
      }
      // should be the highest view ID seen up to now plus 1
      ViewId new_vid = new ViewId(new_coord, logical_time + 1);

      // determine the new view
      MergeView new_view = new MergeView(new_vid, new_mbrs.getMembers(), subgroups);

      // determine the new digest
      Digest new_digest = consolidateDigests(merge_rsps, new_mbrs.size());
      if (new_digest == null) {
        if (log.isErrorEnabled())
          log.error("Merge leader " + gms.local_addr + ": could not consolidate digest for merge");
        return null;
      }
      if (log.isDebugEnabled())
        log.debug(
            "Merge leader "
                + gms.local_addr
                + ": consolidated view="
                + new_view
                + "\nconsolidated digest="
                + new_digest);
      return new MergeData(gms.local_addr, new_view, new_digest);
    }
Example #4
0
  /**
   * Computes the next view. Returns a copy that has <code>old_mbrs</code> and <code>suspected_mbrs
   * </code> removed and <code>new_mbrs</code> added.
   */
  public View getNextView(
      Collection<Address> new_mbrs,
      Collection<Address> old_mbrs,
      Collection<Address> suspected_mbrs) {
    synchronized (members) {
      ViewId view_id = view != null ? view.getViewId() : null;
      if (view_id == null) {
        log.error("view_id is null");
        return null; // this should *never* happen !
      }
      long vid = Math.max(view_id.getId(), ltime) + 1;
      ltime = vid;
      Membership tmp_mbrs = tmp_members.copy(); // always operate on the temporary membership
      tmp_mbrs.remove(suspected_mbrs);
      tmp_mbrs.remove(old_mbrs);
      tmp_mbrs.add(new_mbrs);
      List<Address> mbrs = tmp_mbrs.getMembers();
      Address new_coord = local_addr;
      if (!mbrs.isEmpty()) new_coord = mbrs.get(0);
      View v = new View(new_coord, vid, mbrs);

      // Update membership (see DESIGN for explanation):
      tmp_members.set(mbrs);

      // Update joining list (see DESIGN for explanation)
      if (new_mbrs != null) {
        for (Address tmp_mbr : new_mbrs) {
          if (!joining.contains(tmp_mbr)) joining.add(tmp_mbr);
        }
      }

      // Update leaving list (see DESIGN for explanations)
      if (old_mbrs != null) {
        for (Address addr : old_mbrs) {
          if (!leaving.contains(addr)) leaving.add(addr);
        }
      }
      if (suspected_mbrs != null) {
        for (Address addr : suspected_mbrs) {
          if (!leaving.contains(addr)) leaving.add(addr);
        }
      }
      return v;
    }
  }
Example #5
0
  @SuppressWarnings("unchecked")
  public Object up(Event evt) {
    switch (evt.getType()) {
      case Event.MSG:
        Message msg = (Message) evt.getArg();
        GmsHeader hdr = (GmsHeader) msg.getHeader(this.id);
        if (hdr == null) break;
        switch (hdr.type) {
          case GmsHeader.JOIN_REQ:
            view_handler.add(
                new Request(Request.JOIN, hdr.mbr, false, null, hdr.useFlushIfPresent));
            break;
          case GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER:
            view_handler.add(
                new Request(
                    Request.JOIN_WITH_STATE_TRANSFER, hdr.mbr, false, null, hdr.useFlushIfPresent));
            break;
          case GmsHeader.JOIN_RSP:
            impl.handleJoinResponse(hdr.join_rsp);
            break;
          case GmsHeader.LEAVE_REQ:
            if (hdr.mbr == null) return null;
            view_handler.add(new Request(Request.LEAVE, hdr.mbr, false));
            break;
          case GmsHeader.LEAVE_RSP:
            impl.handleLeaveResponse();
            break;
          case GmsHeader.VIEW:
            View new_view = hdr.view;
            if (new_view == null) return null;

            Address coord = msg.getSrc();
            if (!new_view.containsMember(coord)) {
              sendViewAck(
                  coord); // we need to send the ack first, otherwise the connection is removed
              impl.handleViewChange(new_view, hdr.my_digest);
            } else {
              impl.handleViewChange(new_view, hdr.my_digest);
              sendViewAck(coord); // send VIEW_ACK to sender of view
            }
            break;

          case GmsHeader.VIEW_ACK:
            Address sender = msg.getSrc();
            ack_collector.ack(sender);
            return null; // don't pass further up

          case GmsHeader.MERGE_REQ:
            impl.handleMergeRequest(msg.getSrc(), hdr.merge_id, hdr.mbrs);
            break;

          case GmsHeader.MERGE_RSP:
            MergeData merge_data =
                new MergeData(msg.getSrc(), hdr.view, hdr.my_digest, hdr.merge_rejected);
            if (log.isTraceEnabled()) {
              log.trace(
                  local_addr
                      + ": got merge response from "
                      + msg.getSrc()
                      + ", merge_id="
                      + hdr.merge_id
                      + ", merge data is "
                      + merge_data);
            }
            impl.handleMergeResponse(merge_data, hdr.merge_id);
            break;

          case GmsHeader.INSTALL_MERGE_VIEW:
            impl.handleMergeView(
                new MergeData(msg.getSrc(), hdr.view, hdr.my_digest), hdr.merge_id);
            break;

          case GmsHeader.INSTALL_DIGEST:
            Digest tmp = hdr.my_digest;
            down_prot.down(new Event(Event.MERGE_DIGEST, tmp));
            break;

          case GmsHeader.INSTALL_MERGE_VIEW_OK:
            // [JGRP-700] - FLUSH: flushing should span merge
            merge_ack_collector.ack(msg.getSrc());
            break;

          case GmsHeader.CANCEL_MERGE:
            // [JGRP-524] - FLUSH and merge: flush doesn't wrap entire merge process
            impl.handleMergeCancelled(hdr.merge_id);
            break;

          case GmsHeader.GET_DIGEST_REQ:
            // only handle this request if it was sent by the coordinator (or at least a member) of
            // the current cluster
            synchronized (members) {
              if (!members.contains(msg.getSrc())) break;
            }

            // discard my own request:
            if (msg.getSrc().equals(local_addr)) return null;

            if (hdr.merge_id != null
                && !(merger.matchMergeId(hdr.merge_id) || merger.setMergeId(null, hdr.merge_id)))
              return null;

            // fetch only my own digest
            Digest digest = (Digest) down_prot.down(new Event(Event.GET_DIGEST, local_addr));
            if (digest != null) {
              GmsHeader rsp_hdr = new GmsHeader(GmsHeader.GET_DIGEST_RSP);
              rsp_hdr.my_digest = digest;
              Message get_digest_rsp = new Message(msg.getSrc(), null, null);
              get_digest_rsp.setFlag(Message.OOB);
              get_digest_rsp.putHeader(this.id, rsp_hdr);
              down_prot.down(new Event(Event.MSG, get_digest_rsp));
            }
            break;

          case GmsHeader.GET_DIGEST_RSP:
            Digest digest_rsp = hdr.my_digest;
            impl.handleDigestResponse(msg.getSrc(), digest_rsp);
            break;

          default:
            if (log.isErrorEnabled()) log.error("GmsHeader with type=" + hdr.type + " not known");
        }
        return null; // don't pass up

      case Event.SUSPECT:
        Object retval = up_prot.up(evt);
        Address suspected = (Address) evt.getArg();
        view_handler.add(new Request(Request.SUSPECT, suspected, true));
        ack_collector.suspect(suspected);
        merge_ack_collector.suspect(suspected);
        return retval;

      case Event.UNSUSPECT:
        impl.unsuspect((Address) evt.getArg());
        return null; // discard

      case Event.MERGE:
        view_handler.add(
            new Request(Request.MERGE, null, false, (Map<Address, View>) evt.getArg()));
        return null; // don't pass up

      case Event.IS_MERGE_IN_PROGRESS:
        return merger.isMergeInProgress();
    }
    return up_prot.up(evt);
  }
Example #6
0
 protected Address determineCoordinator() {
   synchronized (members) {
     return members.size() > 0 ? members.elementAt(0) : null;
   }
 }
Example #7
0
  /**
   * Sets the new view and sends a VIEW_CHANGE event up and down the stack. If the view is a
   * MergeView (subclass of View), then digest will be non-null and has to be set before installing
   * the view.
   */
  public void installView(View new_view, Digest digest) {
    ViewId vid = new_view.getVid();
    List<Address> mbrs = new_view.getMembers();
    ltime =
        Math.max(
            vid.getId(),
            ltime); // compute the logical time, regardless of whether the view is accepted

    // Discards view with id lower than or equal to our own. Will be installed without check if it
    // is the first view
    if (view != null) {
      ViewId view_id = view.getViewId();
      int rc = vid.compareToIDs(view_id);
      if (rc <= 0) {
        if (log.isWarnEnabled()
            && rc < 0
            && log_view_warnings) { // only scream if view is smaller, silently discard same views
          log.warn(
              local_addr
                  + ": received view < current view;"
                  + " discarding it (current vid: "
                  + view_id
                  + ", new vid: "
                  + vid
                  + ')');
        }
        return;
      }
    }

    /* Check for self-inclusion: if I'm not part of the new membership, I just discard it.
    This ensures that messages sent in view V1 are only received by members of V1 */
    if (!mbrs.contains(local_addr)) {
      if (log.isWarnEnabled() && log_view_warnings)
        log.warn(local_addr + ": not member of view " + new_view.getViewId() + "; discarding it");
      return;
    }

    if (digest != null) {
      if (new_view instanceof MergeView) mergeDigest(digest);
      else setDigest(digest);
    }

    if (log.isDebugEnabled()) log.debug(local_addr + ": installing view " + new_view);

    Event view_event;
    synchronized (members) {
      view = new View(new_view.getVid(), new_view.getMembers());
      view_event = new Event(Event.VIEW_CHANGE, new_view);

      // Set the membership. Take into account joining members
      if (!mbrs.isEmpty()) {
        members.set(mbrs);
        tmp_members.set(members);
        joining.removeAll(mbrs); // remove all members in mbrs from joining
        // remove all elements from 'leaving' that are not in 'mbrs'
        leaving.retainAll(mbrs);

        tmp_members.add(joining); // add members that haven't yet shown up in the membership
        tmp_members.remove(
            leaving); // remove members that haven't yet been removed from the membership

        // add to prev_members
        for (Address addr : mbrs) {
          if (!prev_members.contains(addr)) prev_members.add(addr);
        }
      }

      Address coord = determineCoordinator();
      if (coord != null && coord.equals(local_addr) && !haveCoordinatorRole()) {
        becomeCoordinator();
      } else {
        if (haveCoordinatorRole() && !local_addr.equals(coord)) {
          becomeParticipant();
          merge_ack_collector.reset(null); // we don't need this one anymore
        }
      }
    }

    // - Changed order of passing view up and down (http://jira.jboss.com/jira/browse/JGRP-347)
    // - Changed it back (bela Sept 4 2007): http://jira.jboss.com/jira/browse/JGRP-564
    // - Moved sending up view_event out of the synchronized block (bela Nov 2011)
    down_prot.down(view_event); // needed e.g. by failure detector or UDP
    up_prot.up(view_event);

    List<Address> tmp_mbrs = new_view.getMembers();
    ack_collector.retainAll(tmp_mbrs);
    merge_ack_collector.retainAll(tmp_mbrs);

    if (new_view instanceof MergeView) merger.forceCancelMerge();

    if (stats) {
      num_views++;
      prev_views.add(new Tuple<View, Long>(new_view, System.currentTimeMillis()));
    }
  }
Example #8
0
 @ManagedAttribute
 public int getNumMembers() {
   return members.size();
 }
Example #9
0
 @ManagedAttribute
 public String getMembers() {
   return members.toString();
 }