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

org.apache.helix.resolver.AbstractHelixResolver Maven / Gradle / Ivy

The newest version!
package org.apache.helix.resolver;

/*
 * 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.
 */

import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.apache.helix.HelixDataAccessor;
import org.apache.helix.HelixManager;
import org.apache.helix.NotificationContext;
import org.apache.helix.model.ExternalView;
import org.apache.helix.model.InstanceConfig;
import org.apache.log4j.Logger;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * A basic implementation of a resolver in terms of expiring routing tables
 */
public abstract class AbstractHelixResolver implements HelixResolver {
  private static final Logger LOG = Logger.getLogger(AbstractHelixResolver.class);
  private static final int DEFAULT_THREAD_POOL_SIZE = 10;
  private static final long DEFAULT_LEASE_LENGTH_MS = 60 * 60 * 1000; // TODO: are these good
                                                                      // values?
  private static final String IPC_PORT = "IPC_PORT";
  private final Map _connections;
  private boolean _isConnected;
  private ScheduledExecutorService _executor;

  protected AbstractHelixResolver() {
    _connections = Maps.newHashMap();
    _isConnected = false;
  }

  @Override
  public void connect() {
    _executor = Executors.newScheduledThreadPool(DEFAULT_THREAD_POOL_SIZE);
    _isConnected = true;
  }

  @Override
  public void disconnect() {
    synchronized (_connections) {
      for (Spectator connection : _connections.values()) {
        connection.shutdown();
      }
      _connections.clear();
    }
    _executor.shutdown();
    _isConnected = false;
  }

  @Override
  public Set getDestinations(HelixMessageScope scope) {
    if (!scope.isValid()) {
      LOG.error("Scope " + scope + " is not valid!");
      return new HashSet();
    } else if (!_isConnected) {
      LOG.error("Cannot resolve " + scope + " without first connecting!");
      return new HashSet();
    }

    // Connect or refresh connection
    String cluster = scope.getCluster();
    ResolverRoutingTable routingTable;
    Spectator connection = _connections.get(cluster);
    if (connection == null || !connection.getManager().isConnected()) {
      synchronized (_connections) {
        connection = _connections.get(cluster);
        if (connection == null || !connection.getManager().isConnected()) {
          connection = new Spectator(cluster, DEFAULT_LEASE_LENGTH_MS);
          connection.init();
          _connections.put(cluster, connection);
        }
      }
    }
    routingTable = connection.getRoutingTable();

    // Resolve all resources, either explicitly or match all
    Set resources;
    if (scope.getResource() != null) {
      resources = Sets.newHashSet(scope.getResource());
    } else {
      resources = routingTable.getResources();
    }

    // Resolve all partitions
    Map> partitionMap = Maps.newHashMap();
    if (scope.getPartition() != null) {
      for (String resource : resources) {
        partitionMap.put(resource, Sets.newHashSet(scope.getPartition()));
      }
    } else {
      for (String resource : resources) {
        partitionMap.put(resource, routingTable.getPartitions(resource));
      }
    }

    // Resolve all states
    Set states;
    if (scope.getState() != null) {
      states = Sets.newHashSet(scope.getState());
    } else {
      states = routingTable.getStates();
    }

    // Get all the participants that match
    Set participants = Sets.newHashSet();
    for (String resource : resources) {
      for (String partition : partitionMap.get(resource)) {
        for (String state : states) {
          participants.addAll(routingTable.getInstances(resource, partition, state));
        }
      }
    }

    // Resolve those participants
    Set result = new HashSet();
    for (InstanceConfig participant : participants) {
      String ipcPort = participant.getRecord().getSimpleField(IPC_PORT);
      if (ipcPort == null) {
        LOG.error("No ipc address registered for target instance " + participant.getInstanceName()
            + ", skipping");
      } else {
        result.add(new HelixAddress(scope, participant.getInstanceName(), new InetSocketAddress(
            participant.getHostName(), Integer.valueOf(ipcPort))));
      }
    }

    return result;
  }

  @Override
  public HelixAddress getSource(HelixMessageScope scope) {
    // Connect or refresh connection
    String cluster = scope.getCluster();
    ResolverRoutingTable routingTable;
    Spectator connection = _connections.get(cluster);
    if (connection == null || !connection.getManager().isConnected()) {
      synchronized (_connections) {
        connection = _connections.get(cluster);
        if (connection == null || !connection.getManager().isConnected()) {
          connection = new Spectator(cluster, DEFAULT_LEASE_LENGTH_MS);
          connection.init();
          _connections.put(cluster, connection);
        }
      }
    }
    routingTable = connection.getRoutingTable();

    if (scope.getSourceInstance() != null) {
      InstanceConfig config = routingTable.getInstanceConfig(scope.getSourceInstance());
      String ipcPort = config.getRecord().getSimpleField(IPC_PORT);
      if (ipcPort == null) {
        throw new IllegalStateException("No IPC address registered for source instance "
            + scope.getSourceInstance());
      }
      return new HelixAddress(scope, scope.getSourceInstance(), new InetSocketAddress(
          config.getHostName(), Integer.valueOf(ipcPort)));
    }

    return null;
  }

  @Override
  public boolean isConnected() {
    return _isConnected;
  }

  /**
   * Create a Helix manager connection based on the appropriate backing store
   * @param cluster the name of the cluster to connect to
   * @return HelixManager instance
   */
  protected abstract HelixManager createManager(String cluster);

  private class Spectator {
    private final String _cluster;
    private final HelixManager _manager;
    private final ResolverRoutingTable _routingTable;
    private final long _leaseLengthMs;
    private ScheduledFuture _future;

    /**
     * Initialize a spectator. This does not automatically connect.
     * @param cluster the cluster to spectate
     * @param leaseLengthMs the expiry of this spectator after the last request
     */
    public Spectator(String cluster, long leaseLengthMs) {
      _cluster = cluster;
      _manager = createManager(cluster);
      _leaseLengthMs = leaseLengthMs;
      _routingTable = new ResolverRoutingTable();
    }

    /**
     * Connect and initialize the routing table
     */
    public void init() {
      try {
        _manager.connect();
        _manager.addExternalViewChangeListener(_routingTable);
        _manager.addInstanceConfigChangeListener(_routingTable);

        // Force an initial refresh
        HelixDataAccessor accessor = _manager.getHelixDataAccessor();
        List externalViews =
            accessor.getChildValues(accessor.keyBuilder().externalViews());
        NotificationContext context = new NotificationContext(_manager);
        context.setType(NotificationContext.Type.INIT);
        _routingTable.onExternalViewChange(externalViews, context);
        List instanceConfigs =
            accessor.getChildValues(accessor.keyBuilder().instanceConfigs());
        _routingTable.onInstanceConfigChange(instanceConfigs, context);
      } catch (Exception e) {
        LOG.error("Error setting up routing table", e);
      }
    }

    /**
     * Clean up the connection to the spectated cluster
     */
    public void shutdown() {
      resetFuture();
      expire();
    }

    /**
     * Get the dynamically-updating routing table for this cluster
     * @return ResolverRoutingTable, a RoutingTableProvider that can answer questions about its
     *         contents
     */
    public ResolverRoutingTable getRoutingTable() {
      renew();
      return _routingTable;
    }

    public HelixManager getManager() {
      return _manager;
    }

    private synchronized void renew() {
      resetFuture();

      // Schedule this connection to expire if not renewed quickly enough
      _future = _executor.schedule(new Runnable() {
        @Override
        public void run() {
          expire();
        }
      }, _leaseLengthMs, TimeUnit.MILLISECONDS);

    }

    private synchronized void resetFuture() {
      if (_future != null && !_future.isDone()) {
        _future.cancel(true);
      }
    }

    private void expire() {
      synchronized (_connections) {
        _connections.remove(_cluster);
        if (_manager != null && _manager.isConnected()) {
          _manager.disconnect();
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy