Package net.sf.katta.client

Source Code of net.sf.katta.client.NodeInteraction

/**
* Copyright 2009 the original author or authors.
*
* Licensed 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 net.sf.katta.client;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import net.sf.katta.util.KattaException;

import org.apache.hadoop.ipc.VersionedProtocol;
import org.apache.log4j.Logger;

/**
* This class is responsible for calling the sever node via an RPC proxy, and
* possibly scheduling retires if errors occur. Only M-1 retries are attempted,
* whereby M is the given maxRetryCount (M calls total). With replication level
* N, there can be at most N-1 retries (N calls total).
*/
class NodeInteraction<T> implements Runnable {

  private static final Logger LOG = Logger.getLogger(NodeInteraction.class);

  // Used to make logs easier to read.
  private static int interactionInstanceCounter;

  private final Method _method;
  private final Object[] _args;
  private final int _shardArrayIndex;
  private final String _node;
  private final Map<String, List<String>> _node2ShardsMap;
  private final List<String> _shards;
  private final int _tryCount;
  private final int _maxTryCount;
  private final INodeExecutor _workQueue;
  private final INodeProxyManager _shardManager;
  private final IResultReceiver<T> _result;
  private final int instanceId = interactionInstanceCounter++;

  /**
   * Create a node interaction. This will make one call to one node, listing
   * multiple shards.
   *
   * @param method
   *          Which method to call on the server. This method must have come
   *          from the same interface used to creat the RPC proxy in the first
   *          place (see Client constructor).
   * @param args
   *          The arguments to pass to the method. When calling the server, the
   *          shard list argument will be modified if shardArrayIndex >= 0.
   * @param shardArrayIndex
   *          Which parameter, if any, to overwrite with a String[] of shard
   *          names (this one arg then changes an a per-node basis, otherwise
   *          all arguments are the same for all calls to nodes). This is
   *          optional, depending on the needs of the server. When < 0, no
   *          overwriting is done.
   * @param node
   *          The name of the node to contact. This is used to get the node's
   *          proxy object (see IShardProxyManager).
   * @param node2ShardsMap
   *          The mapping from nodes to shards for all nodes. Used initially
   *          with the node "node", but other nodes if errors occur and we
   *          retry. For every retry the failed node is removed from the map and
   *          the map is then used to submit a retry job.
   * @param tryCount
   *          This interaction is the Nth retry (starts at 1). We use this to
   *          decide if we should retry shards.
   * @param maxTryCount
   *          The maximum number a interaction is tried to be executed.
   * @param shardManager
   *          Our source of node proxies, and access to a node selection policy
   *          to pick the nodes to use for retires. Also we notify this object
   *          on node failures.
   * @param workQueue
   *          Use this if we need to resubmit a retry job. Will result in a new
   *          NodeInteraction.
   * @result The destination to write to. If we get a result from the node we
   *         add it. If we get an error and submit retries we do not use it (the
   *         retry jobs will write to it for us). If we get an error and do not
   *         retry we write the error to it.
   */
  public NodeInteraction(Method method, Object[] args, int shardArrayIndex, String node,
          Map<String, List<String>> node2ShardsMap, int tryCount, int maxTryCount, INodeProxyManager shardManager,
          INodeExecutor workQueue, IResultReceiver<T> result) {
    _method = method;
    // Make a copy in case we will be modifying the shard list.
    _args = Arrays.copyOf(args, args.length);
    _shardArrayIndex = shardArrayIndex;
    _node = node;
    _node2ShardsMap = node2ShardsMap;
    _shards = node2ShardsMap.get(node);
    _tryCount = tryCount;
    _maxTryCount = maxTryCount;
    _workQueue = workQueue;
    _shardManager = shardManager;
    _result = result;
  }

  @SuppressWarnings("unchecked")
  public void run() {
    String methodDesc = null;
    try {
      VersionedProtocol proxy = _shardManager.getProxy(_node, false);
      if (proxy == null) {
        throw new KattaException("No proxy for node: " + _node);
      }
      if (_shardArrayIndex >= 0) {
        // We need to pass the list of shards to the server's method.
        _args[_shardArrayIndex] = _shards.toArray(new String[_shards.size()]);
      }
      long startTime = 0;
      if (LOG.isTraceEnabled()) {
        methodDesc = describeMethodCall(_method, _args, _node);
        LOG.trace(String.format("About to invoke %s using proxy %s (id=%d)", methodDesc, Proxy
                .getInvocationHandler(proxy), instanceId));
        startTime = System.currentTimeMillis();
      }
      T result = (T) _method.invoke(proxy, _args);
      _shardManager.reportNodeCommunicationSuccess(_node);
      if (LOG.isTraceEnabled()) {
        LOG.trace(String.format("Calling %s returned %s, took %d msec (id=%d)", methodDesc, resultToString(result),
                (System.currentTimeMillis() - startTime), instanceId));
        String methodDesc2 = describeMethodCall(_method, _args, _node);
        if (!methodDesc.equals(methodDesc2)) {
          LOG.error(String.format("Method call changed from %s to %s (id=%d)", methodDesc, methodDesc2, instanceId));
        }
      }
      _result.addResult(result, _shards);
    } catch (Throwable t) {
      // Notify the work queue, so it can mark the node as down.
      _shardManager.reportNodeCommunicationFailure(_node, t);
      if (_tryCount >= _maxTryCount) {
        LOG.error(String.format("Error calling %s (try # %d of %d) (id=%d)", (methodDesc != null ? methodDesc : _method
                + " on " + _node), _tryCount, _maxTryCount, instanceId), t);
        _result.addError(new KattaException(String.format("%s for shards %s failed (id=%d)",
                getClass().getSimpleName(), _shards, instanceId), t), _shards);
        return;
      }
      if (!_result.isClosed()) {
        try {
          // Find new node(s) for our shards and add to global node2ShardMap
          Map<String, List<String>> retryMap = _shardManager.createNode2ShardsMap(_node2ShardsMap.get(_node));
          LOG.warn(String.format("Failed to interact with node %s. Trying with other node(s) %s (id=%d)", _node,
                  retryMap.keySet(), instanceId), t);
          // Execute the action again for every node
          for (String newNode : retryMap.keySet()) {
            _workQueue.execute(newNode, retryMap, _tryCount + 1, _maxTryCount);
          }
        } catch (ShardAccessException e) {
          LOG.error(String.format("Error calling %s (try # %d of %d) (id=%d)", (methodDesc != null ? methodDesc
                  : _method + " on " + _node), _tryCount, _maxTryCount, instanceId), t);
          _result.addError(e, _shards);
        }
      }
      // We have no results to report. Submitted jobs will hopefully get results
      // instead.
    }
  }

  private String describeMethodCall(Method method, Object[] args, String nodeName) {
    StringBuilder builder = new StringBuilder(method.getDeclaringClass().getSimpleName());
    builder.append(".");
    builder.append(method.getName());
    builder.append("(");
    String sep = "";
    for (int i = 0; i < args.length; i++) {
      builder.append(sep);
      if (args[i] == null) {
        builder.append("null");
      } else if (args[i] instanceof String[]) {
        // TODO: all array types, lists, maps.
        String[] strs = (String[]) args[i];
        String sep2 = "";
        builder.append("[");
        for (String str : strs) {
          builder.append(sep2 + "\"" + str + "\"");
          sep2 = ", ";
        }
        builder.append("]");
      } else {
        builder.append(args[i].toString());
      }
      sep = ", ";
    }
    builder.append(") on ");
    builder.append(nodeName);
    return builder.toString();
  }

  private String resultToString(T result) {
    String s = "null";
    if (result != null) {
      try {
        s = result.toString();
      } catch (Throwable t) {
        LOG.trace("Error calling toString() on result", t);
        s = "(toString() err)";
      }
    }
    if (s == null) {
      s = "(null toString())";
    }
    return s;
  }

  @Override
  public String toString() {
    return "NodeInteraction: call " + _method.getName() + " on " + _node;
  }
}
TOP

Related Classes of net.sf.katta.client.NodeInteraction

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.