Package com.alibaba.wasp.master

Source Code of com.alibaba.wasp.master.EntityGroupStates

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.wasp.master;

import com.alibaba.wasp.EntityGroupInfo;
import com.alibaba.wasp.EntityGroupTransaction;
import com.alibaba.wasp.Server;
import com.alibaba.wasp.ServerLoad;
import com.alibaba.wasp.ServerName;
import com.alibaba.wasp.meta.FMetaReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
* EntityGroup state accountant. It holds the states of all entityGroups in the
* memory. In normal scenario, it should match the meta table and the true
* entityGroup states.
*
* This map is used by AssignmentManager to track entityGroup states.
*/
@InterfaceAudience.Private
public class EntityGroupStates {
  private static final Log LOG = LogFactory.getLog(EntityGroupStates.class);

  /**
   * EntityGroups currently in transition.
   */
  final HashMap<String, EntityGroupState> entityGroupsInTransition;

  /**
   * EntityGroup encoded name to state map. All the EntityGroups should be in
   * this map.
   */
  private final Map<String, EntityGroupState> entityGroupStates;

  /**
   * Server to EntityGroups assignment map. Contains the set of EntityGroups
   * currently assigned to a given fserver.
   */
  private final Map<ServerName, Set<EntityGroupInfo>> serverHoldings;

  /**
   * EntityGroup to server assignment map. Contains the server a given
   * EntityGroup is currently assigned to.
   */
  private final TreeMap<EntityGroupInfo, ServerName> entityGroupAssignments;

  private final FServerManager fserverManager;
  private final Server server;

  EntityGroupStates(final Server master, final FServerManager fserverManager) {
    entityGroupStates = new HashMap<String, EntityGroupState>();
    entityGroupsInTransition = new HashMap<String, EntityGroupState>();
    serverHoldings = new HashMap<ServerName, Set<EntityGroupInfo>>();
    entityGroupAssignments = new TreeMap<EntityGroupInfo, ServerName>();
    this.fserverManager = fserverManager;
    this.server = master;
  }

  /**
   * @return an unmodifiable the entityGroup assignment map
   */
  @SuppressWarnings("unchecked")
  public synchronized Map<EntityGroupInfo, ServerName> getEntityGroupAssignments() {
    return (Map<EntityGroupInfo, ServerName>) entityGroupAssignments.clone();
  }

  public synchronized ServerName getFServerOfEntityGroup(EntityGroupInfo egi) {
    return entityGroupAssignments.get(egi);
  }

  /**
   * Get entityGroups in transition and their states
   */
  @SuppressWarnings("unchecked")
  public synchronized Map<String, EntityGroupState> getEntityGroupsInTransition() {
    return (Map<String, EntityGroupState>) entityGroupsInTransition.clone();
  }

  /**
   * @return True if specified entityGroup in transition.
   */
  public synchronized boolean isEntityGroupInTransition(
      final EntityGroupInfo egi) {
    return entityGroupsInTransition.containsKey(egi.getEncodedName());
  }

  /**
   * @return True if specified entityGroup in transition.
   */
  public synchronized boolean isEntityGroupInTransition(
      final String entityGroupName) {
    return entityGroupsInTransition.containsKey(entityGroupName);
  }

  /**
   * @return True if any entityGroup in transition.
   */
  public synchronized boolean isEntityGroupsInTransition() {
    return !entityGroupsInTransition.isEmpty();
  }

  /**
   * @return True if specified entityGroup assigned.
   */
  public synchronized boolean isEntityGroupAssigned(final EntityGroupInfo egi) {
    return entityGroupAssignments.containsKey(egi);
  }

  /**
   * @return the server the specified entityGroup assigned to; null if not
   *         assigned.
   */
  public synchronized ServerName getAssignedServer(final EntityGroupInfo egi) {
    return entityGroupAssignments.get(egi);
  }

  /**
   * Wait for the state map to be updated by assignment manager.
   */
  public synchronized void waitForUpdate(
      final long timeout) throws InterruptedException {
    this.wait(timeout);
  }

  /**
   * Get entityGroup transition state
   */
  public synchronized EntityGroupState getEntityGroupTransitionState(
      final EntityGroupInfo egi) {
    return entityGroupsInTransition.get(egi.getEncodedName());
  }

  /**
   * Get entityGroup transition state
   */
  public synchronized EntityGroupState getEntityGroupTransitionState(
      final String entityGroupName) {
    return entityGroupsInTransition.get(entityGroupName);
  }

  /**
   * Add a list of entityGroups to EntityGroupStates. The initial state is
   * OFFLINE. If any entityGroup is already in EntityGroupStates, that
   * entityGroup will be skipped.
   */
  public synchronized void createEntityGroupStates(
      final List<EntityGroupInfo> egis) {
    for (EntityGroupInfo egi : egis) {
      createEntityGroupState(egi);
    }
  }

  /**
   * Add a entityGroup to EntityGroupStates. The initial state is OFFLINE. If it
   * is already in EntityGroupStates, this call has no effect, and the original
   * state is returned.
   */
  public synchronized EntityGroupState createEntityGroupState(
      final EntityGroupInfo egi) {
    String entityGroupName = egi.getEncodedName();
    EntityGroupState entityGroupState = entityGroupStates.get(entityGroupName);
    if (entityGroupState != null) {
      LOG.warn("Tried to create a state of a entityGroup already in EntityGroupStates "
          + egi + ", used existing state: " + entityGroupState
        + ", ignored new state: state=OFFLINE, server=null");
    } else {
      entityGroupState = new EntityGroupState(egi, EntityGroupState.State.OFFLINE);
      entityGroupStates.put(entityGroupName, entityGroupState);
    }
    return entityGroupState;
  }

  /**
   * Update a entityGroup state. If it is not splitting, it will be put in
   * transition if not already there.
   */
  public synchronized EntityGroupState updateEntityGroupState(
      final EntityGroupInfo egi, final EntityGroupState.State state) {
    EntityGroupState entityGroupState = entityGroupStates.get(egi
        .getEncodedName());
    ServerName serverName = (entityGroupState == null || state == EntityGroupState.State.CLOSED || state == EntityGroupState.State.OFFLINE) ? null
        : entityGroupState.getServerName();
    return updateEntityGroupState(egi, state, serverName);
  }

  /**
   * Update a entityGroup state. If it is not splitting, it will be put in
   * transition if not already there.
   *
   * If we can't find the entityGroup info based on the entityGroup name in the
   * transition, log a warning and return null.
   */
  public synchronized EntityGroupState updateEntityGroupState(
      final EntityGroupTransaction transition, final EntityGroupState.State state) {
    byte[] entityGroupName = transition.getEntityGroupName();
    EntityGroupInfo entityGroupInfo = getEntityGroupInfo(entityGroupName);
    if (entityGroupInfo == null) {
      String prettyEntityGroupName = EntityGroupInfo
          .encodeEntityGroupName(entityGroupName);
      LOG.warn("Failed to find entityGroup " + prettyEntityGroupName
        + " in updating its state to " + state
          + " based on entityGroup transition " + transition);
      return null;
    }
    return updateEntityGroupState(entityGroupInfo, state,
      transition.getServerName());
  }

  /**
   * Update a entityGroup state. If it is not splitting, it will be put in
   * transition if not already there.
   */
  public synchronized EntityGroupState updateEntityGroupState(
      final EntityGroupInfo egi, final EntityGroupState.State state, final ServerName serverName) {
    ServerName newServerName = serverName;
    if (serverName != null &&
        (state == EntityGroupState.State.CLOSED || state == EntityGroupState.State.OFFLINE)) {
      LOG.warn("Closed entityGroup " + egi + " still on "
        + serverName + "? Ignored, reset it to null");
      newServerName = null;
    }

    String entityGroupName = egi.getEncodedName();
    EntityGroupState entityGroupState = new EntityGroupState(
      egi, state, System.currentTimeMillis(), newServerName);
    EntityGroupState oldState = entityGroupStates.put(entityGroupName,
        entityGroupState);
    LOG.info("EntityGroup " + egi + " transitioned from " + oldState + " to "
        + entityGroupState);
    if (state != EntityGroupState.State.SPLITTING && (newServerName != null
        || (state != EntityGroupState.State.PENDING_CLOSE && state != EntityGroupState.State.CLOSING))) {
      entityGroupsInTransition.put(entityGroupName, entityGroupState);
    }

    // notify the change
    this.notifyAll();
    return entityGroupState;
  }

  /**
   * A entityGroup is online, won't be in transition any more. We can't confirm
   * it is really online on specified entityGroup server because it hasn't been
   * put in entityGroup server's online entityGroup list yet.
   */
  public synchronized void entityGroupOnline(final EntityGroupInfo egi,
      final ServerName serverName) {
    String entityGroupName = egi.getEncodedName();
    EntityGroupState oldState = entityGroupStates.get(entityGroupName);
    if (oldState == null) {
      LOG.warn("Online a entityGroup not in EntityGroupStates: " + egi);
    } else {
      EntityGroupState.State state = oldState.getState();
      ServerName sn = oldState.getServerName();
      if (state != EntityGroupState.State.OPEN || sn == null || !sn.equals(serverName)) {
        LOG.debug("Online a entityGroup with current state=" + state
            + ", expected state=OPEN"
          + ", assigned to server: " + sn + " expected " + serverName);
      }
    }
    updateEntityGroupState(egi, EntityGroupState.State.OPEN, serverName);
    entityGroupsInTransition.remove(entityGroupName);

    ServerName oldServerName = entityGroupAssignments.put(egi, serverName);
    if (!serverName.equals(oldServerName)) {
      LOG.info("Onlined entityGroup " + egi + " on " + serverName);
      Set<EntityGroupInfo> entityGroups = serverHoldings.get(serverName);
      if (entityGroups == null) {
        entityGroups = new HashSet<EntityGroupInfo>();
        serverHoldings.put(serverName, entityGroups);
      }
      entityGroups.add(egi);
      if (oldServerName != null) {
        LOG.info("Offlined entityGroup " + egi + " from " + oldServerName);
        serverHoldings.get(oldServerName).remove(egi);
      }
    }
  }

  /**
   * A entityGroup is offline, won't be in transition any more.
   */
  public synchronized void entityGroupOffline(final EntityGroupInfo egi) {
    String entityGroupName = egi.getEncodedName();
    EntityGroupState oldState = entityGroupStates.get(entityGroupName);
    if (oldState == null) {
      LOG.warn("Offline a entityGroup not in EntityGroupStates: " + egi);
    } else {
      EntityGroupState.State state = oldState.getState();
      ServerName sn = oldState.getServerName();
      if (state != EntityGroupState.State.OFFLINE || sn != null) {
        LOG.debug("Offline a entityGroup with current state=" + state
            + ", expected state=OFFLINE"
          + ", assigned to server: " + sn + ", expected null");
      }
    }
    updateEntityGroupState(egi, EntityGroupState.State.OFFLINE);
    entityGroupsInTransition.remove(entityGroupName);

    ServerName oldServerName = entityGroupAssignments.remove(egi);
    if (oldServerName != null) {
      LOG.info("Offlined entityGroup " + egi + " from " + oldServerName);
      serverHoldings.get(oldServerName).remove(egi);
    }
  }

  /**
   * A server is offline, all entityGroups on it are dead.
   */
  public synchronized List<EntityGroupState> serverOffline(final ServerName sn) {
    // Clean up this server from map of servers to entityGroups, and remove all
    // entityGroups
    // of this server from online map of entityGroups.
    List<EntityGroupState> rits = new ArrayList<EntityGroupState>();
    Set<EntityGroupInfo> assignedEntityGroups = serverHoldings.get(sn);
    if (assignedEntityGroups == null || assignedEntityGroups.isEmpty()) {
      // No entityGroups on this server, we are done, return empty list of RITs
      return rits;
    }

    for (EntityGroupInfo entityGroup : assignedEntityGroups) {
      entityGroupAssignments.remove(entityGroup);
    }

    // See if any of the entityGroups that were online on this server were in
    // RIT
    // If they are, normal timeouts will deal with them appropriately so
    // let's skip a manual re-assignment.
    for (EntityGroupState state : entityGroupsInTransition.values()) {
      if (assignedEntityGroups.contains(state.getEntityGroup())) {
        rits.add(state);
      }
    }
    assignedEntityGroups.clear();
    this.notifyAll();
    return rits;
  }

  /**
   * Gets the online entityGroups of the specified table. This method looks at
   * the in-memory state. It does not go to <code>.META.</code>. Only returns
   * <em>online</em> entityGroups. If a entityGroup on this table has been
   * closed during a disable, etc., it will be included in the returned list.
   * So, the returned list may not necessarily be ALL entityGroups in this
   * table, its all the ONLINE entityGroups in the table.
   *
   * @param tableName
   * @return Online entityGroups from <code>tableName</code>
   */
  public synchronized List<EntityGroupInfo> getEntityGroupsOfTable(
      byte[] tableName) {
    List<EntityGroupInfo> tableEntityGroups = new ArrayList<EntityGroupInfo>();
    // boundary needs to have table's name but entityGroupID 0 so that it is
    // sorted
    // before all table's entityGroups.
    EntityGroupInfo boundary = new EntityGroupInfo(tableName,  null, null,
        false, 0L);
    for (EntityGroupInfo egi : entityGroupAssignments.tailMap(boundary)
        .keySet()) {
      if(Bytes.equals(egi.getTableName(), tableName)) {
        tableEntityGroups.add(egi);
      } else {
        break;
      }
    }
    return tableEntityGroups;
  }


  /**
   * Wait on entityGroup to clear entityGroups-in-transition.
   * <p>
   * If the entityGroup isn't in transition, returns immediately. Otherwise,
   * method blocks until the entityGroup is out of transition.
   */
  public synchronized void waitOnEntityGroupToClearEntityGroupsInTransition(
      final EntityGroupInfo egi) throws InterruptedException {
    if (!isEntityGroupInTransition(egi))
      return;

    while (!server.isStopped() && isEntityGroupInTransition(egi)) {
      EntityGroupState rs = getEntityGroupState(egi);
      LOG.info("Waiting on " + rs + " to clear entityGroups-in-transition");
      waitForUpdate(100);
    }

    if (server.isStopped()) {
      LOG.info("Giving up wait on entityGroup in "
          +
        "transition because stoppable.isStopped is set");
    }
  }

  /**
   * Waits until the specified entityGroup has completed assignment.
   * <p>
   * If the entityGroup is already assigned, returns immediately. Otherwise,
   * method blocks until the entityGroup is assigned.
   */
  public synchronized void waitForAssignment(final EntityGroupInfo egi)
      throws InterruptedException {
    if (!isEntityGroupAssigned(egi))
      return;

    while (!server.isStopped() && !isEntityGroupAssigned(egi)) {
      EntityGroupState rs = getEntityGroupState(egi);
      LOG.info("Waiting on " + rs + " to be assigned");
      waitForUpdate(100);
    }

    if (server.isStopped()) {
      LOG.info("Giving up wait on entityGroup "
          +
        "assignment because stoppable.isStopped is set");
    }
  }

  /**
   * Compute the average load across all entityGroup servers. Currently, this
   * uses a very naive computation - just uses the number of entityGroups being
   * served, ignoring stats about number of requests.
   *
   * @return the average load
   */
  protected synchronized double getAverageLoad() {
    int numServers = 0, totalLoad = 0;
    for (Map.Entry<ServerName, Set<EntityGroupInfo>> e : serverHoldings
        .entrySet()) {
      Set<EntityGroupInfo> entityGroups = e.getValue();
      ServerName serverName = e.getKey();
      int entityGroupCount = entityGroups.size();
      if (entityGroupCount > 0 || fserverManager.isServerOnline(serverName)) {
        totalLoad += entityGroupCount;
        numServers++;
      }
    }
    return numServers == 0 ? 0.0 :
      (double)totalLoad / (double)numServers;
  }

  /**
   * This is an EXPENSIVE clone. Cloning though is the safest thing to do. Can't
   * let out original since it can change and at least the load balancer wants
   * to iterate this exported list. We need to synchronize on entityGroups since
   * all access to this.servers is under a lock on this.entityGroups.
   *
   * @return A clone of current assignments by table.
   */
  protected Map<String, Map<ServerName, List<EntityGroupInfo>>> getAssignmentsByTable() {
    Map<String, Map<ServerName, List<EntityGroupInfo>>> result = new HashMap<String, Map<ServerName, List<EntityGroupInfo>>>();
    synchronized (this) {
      if (!server.getConfiguration().getBoolean("wasp.master.loadbalance.bytable", false)) {
        Map<ServerName, List<EntityGroupInfo>> svrToEntityGroups = new HashMap<ServerName, List<EntityGroupInfo>>(
            serverHoldings.size());
        for (Map.Entry<ServerName, Set<EntityGroupInfo>> e : serverHoldings
            .entrySet()) {
          svrToEntityGroups.put(e.getKey(),
              new ArrayList<EntityGroupInfo>(e.getValue()));
        }
        result.put("ensemble", svrToEntityGroups);
      } else {
        for (Map.Entry<ServerName, Set<EntityGroupInfo>> e : serverHoldings
            .entrySet()) {
          for (EntityGroupInfo egi : e.getValue()) {
            String tablename = egi.getTableNameAsString();
            Map<ServerName, List<EntityGroupInfo>> svrToEntityGroups = result
                .get(tablename);
            if (svrToEntityGroups == null) {
              svrToEntityGroups = new HashMap<ServerName, List<EntityGroupInfo>>(
                  serverHoldings.size());
              result.put(tablename, svrToEntityGroups);
            }
            List<EntityGroupInfo> entityGroups = svrToEntityGroups.get(e
                .getKey());
            if (entityGroups == null) {
              entityGroups = new ArrayList<EntityGroupInfo>();
              svrToEntityGroups.put(e.getKey(), entityGroups);
            }
            entityGroups.add(egi);
          }
        }
      }
    }

    Map<ServerName, ServerLoad> onlineSvrs = fserverManager.getOnlineServers();
    // Take care of servers w/o assignments.
    for (Map<ServerName, List<EntityGroupInfo>> map : result.values()) {
      for (ServerName svr: onlineSvrs.keySet()) {
        if (!map.containsKey(svr)) {
          map.put(svr, new ArrayList<EntityGroupInfo>());
        }
      }
    }
    return result;
  }

  protected synchronized EntityGroupState getEntityGroupState(
      final EntityGroupInfo egi) {
    return entityGroupStates.get(egi.getEncodedName());
  }

  protected synchronized EntityGroupState getEntityGroupState(
      final String entityGroupName) {
    return entityGroupStates.get(entityGroupName);
  }

  /**
   * Get the EntityGroupInfo from cache, if not there, from the META table
   *
   * @param entityGroupName
   * @return EntityGroupInfo for the entityGroup
   */
  protected EntityGroupInfo getEntityGroupInfo(final byte[] entityGroupName) {
    String encodedName = EntityGroupInfo.encodeEntityGroupName(entityGroupName);
    EntityGroupState entityGroupState = entityGroupStates.get(encodedName);
    if (entityGroupState != null) {
      return entityGroupState.getEntityGroup();
    }

    try {
      Pair<EntityGroupInfo, ServerName> p = FMetaReader
          .getEntityGroupAndLocation(server.getConfiguration(), entityGroupName);
      EntityGroupInfo egi = p == null ? null : p.getFirst();
      if (egi != null) {
        createEntityGroupState(egi);
      }
      return egi;
    } catch (IOException e) {
      server.abort(
          "Aborting because error occoured while reading "
              + Bytes.toStringBinary(entityGroupName) + " from .FMETA.", e);
      return null;
    }
  }
}
TOP

Related Classes of com.alibaba.wasp.master.EntityGroupStates

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.