Package org.apache.blur.thrift

Source Code of org.apache.blur.thrift.BlurControllerServer

package org.apache.blur.thrift;

/**
* 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.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.blur.concurrent.Executors;
import org.apache.blur.log.Log;
import org.apache.blur.log.LogFactory;
import org.apache.blur.manager.BlurPartitioner;
import org.apache.blur.manager.BlurQueryChecker;
import org.apache.blur.manager.IndexManager;
import org.apache.blur.manager.clusterstatus.ZookeeperPathConstants;
import org.apache.blur.manager.indexserver.DistributedLayoutManager;
import org.apache.blur.manager.results.BlurResultIterable;
import org.apache.blur.manager.results.BlurResultIterableClient;
import org.apache.blur.manager.results.LazyBlurResult;
import org.apache.blur.manager.results.MergerBlurResultIterable;
import org.apache.blur.manager.stats.MergerTableStats;
import org.apache.blur.manager.status.MergerQueryStatusSingle;
import org.apache.blur.thirdparty.thrift_0_9_0.TException;
import org.apache.blur.thrift.commands.BlurCommand;
import org.apache.blur.thrift.generated.Blur.Client;
import org.apache.blur.thrift.generated.Blur.Iface;
import org.apache.blur.thrift.generated.BlurException;
import org.apache.blur.thrift.generated.BlurQuery;
import org.apache.blur.thrift.generated.BlurQueryStatus;
import org.apache.blur.thrift.generated.BlurResult;
import org.apache.blur.thrift.generated.BlurResults;
import org.apache.blur.thrift.generated.ColumnDefinition;
import org.apache.blur.thrift.generated.FetchResult;
import org.apache.blur.thrift.generated.HighlightOptions;
import org.apache.blur.thrift.generated.Query;
import org.apache.blur.thrift.generated.RowMutation;
import org.apache.blur.thrift.generated.Schema;
import org.apache.blur.thrift.generated.Selector;
import org.apache.blur.thrift.generated.ShardState;
import org.apache.blur.thrift.generated.TableDescriptor;
import org.apache.blur.thrift.generated.TableStats;
import org.apache.blur.utils.BlurConstants;
import org.apache.blur.utils.BlurExecutorCompletionService;
import org.apache.blur.utils.BlurIterator;
import org.apache.blur.utils.BlurUtil;
import org.apache.blur.utils.ForkJoin;
import org.apache.blur.utils.ForkJoin.Merger;
import org.apache.blur.utils.ForkJoin.ParallelCall;
import org.apache.blur.zookeeper.WatchChildren;
import org.apache.blur.zookeeper.WatchChildren.OnChange;
import org.apache.blur.zookeeper.WatchNodeExistance;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;

public class BlurControllerServer extends TableAdmin implements Iface {

  public static abstract class BlurClient {
    public abstract <T> T execute(String node, BlurCommand<T> command, int maxRetries, long backOffTime,
        long maxBackOffTime) throws BlurException, TException, IOException;
  }

  public static class BlurClientRemote extends BlurClient {
    private int _timeout;

    public BlurClientRemote(int timeout) {
      _timeout = timeout;
    }

    @Override
    public <T> T execute(String node, BlurCommand<T> command, int maxRetries, long backOffTime, long maxBackOffTime)
        throws BlurException, TException, IOException {
      return BlurClientManager.execute(node + "#" + _timeout, command, maxRetries, backOffTime, maxBackOffTime);
    }
  }

  private static final String CONTROLLER_THREAD_POOL = "controller-thread-pool";
  private static final Log LOG = LogFactory.getLog(BlurControllerServer.class);
  private static final Map<String, Set<String>> EMPTY_MAP = new HashMap<String, Set<String>>();
  private static final List<String> EMPTY_LIST = new ArrayList<String>();

  private ExecutorService _executor;
  private AtomicReference<Map<String, Map<String, String>>> _shardServerLayout = new AtomicReference<Map<String, Map<String, String>>>(
      new HashMap<String, Map<String, String>>());
  private BlurClient _client;
  private int _threadCount = 64;
  private AtomicBoolean _closed = new AtomicBoolean();
  private Map<String, Integer> _tableShardCountMap = new ConcurrentHashMap<String, Integer>();
  private BlurPartitioner _blurPartitioner = new BlurPartitioner();
  private String _nodeName;
  private int _remoteFetchCount = 100;
  private BlurQueryChecker _queryChecker;
  private AtomicBoolean _running = new AtomicBoolean();

  private int _maxFetchRetries = 3;
  private int _maxMutateRetries = 3;
  private int _maxDefaultRetries = 3;
  private long _fetchDelay = 500;
  private long _mutateDelay = 500;
  private long _defaultDelay = 500;
  private long _maxFetchDelay = 2000;
  private long _maxMutateDelay = 2000;
  private long _maxDefaultDelay = 2000;

  private long _defaultParallelCallTimeout = TimeUnit.MINUTES.toMillis(1);
  private WatchChildren _watchForClusters;
  private ConcurrentMap<String, WatchNodeExistance> _watchForTablesPerClusterExistance = new ConcurrentHashMap<String, WatchNodeExistance>();
  private ConcurrentMap<String, WatchNodeExistance> _watchForOnlineShardsPerClusterExistance = new ConcurrentHashMap<String, WatchNodeExistance>();
  private ConcurrentMap<String, WatchChildren> _watchForTablesPerCluster = new ConcurrentHashMap<String, WatchChildren>();
  private ConcurrentMap<String, WatchChildren> _watchForOnlineShardsPerCluster = new ConcurrentHashMap<String, WatchChildren>();

  public void init() throws KeeperException, InterruptedException {
    setupZookeeper();
    registerMyself();
    _executor = Executors.newThreadPool(CONTROLLER_THREAD_POOL, _threadCount);
    _running.set(true);
    watchForClusterChanges();
    List<String> clusterList = _clusterStatus.getClusterList(false);
    for (String cluster : clusterList) {
      watchForLayoutChanges(cluster);
    }
    updateLayout();
  }

  private void setupZookeeper() throws KeeperException, InterruptedException {
    BlurUtil.createIfMissing(_zookeeper, "/blur");
    BlurUtil.createIfMissing(_zookeeper, ZookeeperPathConstants.getOnlineControllersPath());
    BlurUtil.createIfMissing(_zookeeper, ZookeeperPathConstants.getClustersPath());
  }

  private void watchForClusterChanges() throws KeeperException, InterruptedException {
    _watchForClusters = new WatchChildren(_zookeeper, ZookeeperPathConstants.getClustersPath());
    _watchForClusters.watch(new OnChange() {
      @Override
      public void action(List<String> children) {
        for (String cluster : children) {
          try {
            watchForLayoutChanges(cluster);
          } catch (KeeperException e) {
            LOG.error("Unknown error", e);
            throw new RuntimeException(e);
          } catch (InterruptedException e) {
            LOG.error("Unknown error", e);
            throw new RuntimeException(e);
          }
        }
      }
    });
  }

  private void watchForLayoutChanges(final String cluster) throws KeeperException, InterruptedException {
    WatchNodeExistance we1 = new WatchNodeExistance(_zookeeper, ZookeeperPathConstants.getTablesPath(cluster));
    we1.watch(new WatchNodeExistance.OnChange() {
      @Override
      public void action(Stat stat) {
        if (stat != null) {
          watch(cluster, ZookeeperPathConstants.getTablesPath(cluster), _watchForTablesPerCluster);
        }
      }
    });
    if (_watchForTablesPerClusterExistance.putIfAbsent(cluster, we1) != null) {
      we1.close();
    }

    WatchNodeExistance we2 = new WatchNodeExistance(_zookeeper, ZookeeperPathConstants.getTablesPath(cluster));
    we2.watch(new WatchNodeExistance.OnChange() {
      @Override
      public void action(Stat stat) {
        if (stat != null) {
          watch(cluster, ZookeeperPathConstants.getOnlineShardsPath(cluster), _watchForOnlineShardsPerCluster);
        }
      }
    });
    if (_watchForOnlineShardsPerClusterExistance.putIfAbsent(cluster, we2) != null) {
      we2.close();
    }
  }

  private void watch(String cluster, String path, ConcurrentMap<String, WatchChildren> map) {
    WatchChildren watchForTables = new WatchChildren(_zookeeper, path);
    watchForTables.watch(new OnChange() {
      @Override
      public void action(List<String> children) {
        LOG.info("Layout change.");
        updateLayout();
      }
    });

    if (map.putIfAbsent(cluster, watchForTables) != null) {
      watchForTables.close();
    }
  }

  private synchronized void updateLayout() {
    if (!_clusterStatus.isOpen()) {
      LOG.warn("The cluster status object has been closed.");
      return;
    }
    List<String> tableList = _clusterStatus.getTableList(false);
    HashMap<String, Map<String, String>> newLayout = new HashMap<String, Map<String, String>>();
    for (String table : tableList) {
      DistributedLayoutManager layoutManager = new DistributedLayoutManager();
      String cluster = _clusterStatus.getCluster(false, table);
      if (cluster == null) {
        continue;
      }
      List<String> shardServerList = _clusterStatus.getShardServerList(cluster);
      List<String> offlineShardServers = _clusterStatus.getOfflineShardServers(false, cluster);
      List<String> shardList = getShardList(cluster, table);
      layoutManager.setNodes(shardServerList);
      layoutManager.setNodesOffline(offlineShardServers);
      layoutManager.setShards(shardList);
      layoutManager.init();
      Map<String, String> layout = layoutManager.getLayout();
      newLayout.put(table, layout);
    }
    _shardServerLayout.set(newLayout);
  }

  private List<String> getShardList(String cluster, String table) {
    List<String> shards = new ArrayList<String>();
    TableDescriptor tableDescriptor = _clusterStatus.getTableDescriptor(true, cluster, table);
    for (int i = 0; i < tableDescriptor.shardCount; i++) {
      shards.add(BlurUtil.getShardName(BlurConstants.SHARD_PREFIX, i));
    }
    return shards;
  }

  private void registerMyself() {
    try {
      String onlineControllerPath = ZookeeperPathConstants.getOnlineControllersPath() + "/" + _nodeName;
      while (_zookeeper.exists(onlineControllerPath, false) != null) {
        LOG.info("Node [{0}] already registered, waiting for path [{1}] to be released", _nodeName,
            onlineControllerPath);
        Thread.sleep(3000);
      }
      String version = BlurUtil.getVersion();
      _zookeeper.create(onlineControllerPath, version.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
    } catch (KeeperException e) {
      throw new RuntimeException(e);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
  }

  public synchronized void close() {
    if (!_closed.get()) {
      _closed.set(true);
      _running.set(false);
      _executor.shutdownNow();
      close(_watchForClusters);
      close(_watchForOnlineShardsPerCluster.values());
      close(_watchForOnlineShardsPerClusterExistance.values());
      close(_watchForTablesPerCluster.values());
      close(_watchForTablesPerClusterExistance.values());
    }
  }

  private void close(Collection<? extends Closeable> closableLst) {
    for (Closeable closeable : closableLst) {
      close(closeable);
    }
  }

  private void close(Closeable closeable) {
    try {
      closeable.close();
    } catch (IOException e) {
      LOG.error("Unknown", e);
    }
  }

  @Override
  public BlurResults query(final String table, final BlurQuery blurQuery) throws BlurException, TException {
    checkTable(table);
    String cluster = _clusterStatus.getCluster(true, table);
    _queryChecker.checkQuery(blurQuery);
    checkSelectorFetchSize(blurQuery.getSelector());
    int shardCount = _clusterStatus.getShardCount(true, cluster, table);
    if (blurQuery.getUuid() == null) {
      blurQuery.setUuid(UUID.randomUUID().toString());
    }

    OUTER: for (int retries = 0; retries < _maxDefaultRetries; retries++) {
      try {
        final AtomicLongArray facetCounts = BlurUtil.getAtomicLongArraySameLengthAsList(blurQuery.facets);

        BlurQuery original = new BlurQuery(blurQuery);

        BlurUtil.setStartTime(original);

        Selector selector = blurQuery.getSelector();
        if (selector == null) {
          selector = new Selector();
          selector.setColumnFamiliesToFetch(EMPTY_LIST);
          selector.setColumnsToFetch(EMPTY_MAP);
          if (!blurQuery.query.rowQuery) {
            selector.setRecordOnly(true);
          }
        } else {
          HighlightOptions highlightOptions = selector.getHighlightOptions();
          if (highlightOptions != null && highlightOptions.getQuery() == null) {
            highlightOptions.setQuery(blurQuery.getQuery());
          }
        }
        blurQuery.setSelector(null);

        BlurCommand<BlurResultIterable> command = new BlurCommand<BlurResultIterable>() {
          @Override
          public BlurResultIterable call(Client client, Connection connection) throws BlurException, TException {
            return new BlurResultIterableClient(connection, client, table, blurQuery, facetCounts, _remoteFetchCount);
          }

          @Override
          public BlurResultIterable call(Client client) throws BlurException, TException {
            throw new RuntimeException("Won't be called.");
          }
        };

        command.setDetachClient(true);

        MergerBlurResultIterable merger = new MergerBlurResultIterable(blurQuery);
        BlurResultIterable hitsIterable = null;
        try {
          hitsIterable = scatterGather(getCluster(table), command, merger);
          BlurResults results = convertToBlurResults(hitsIterable, blurQuery, facetCounts, _executor, selector, table);
          if (!validResults(results, shardCount, blurQuery)) {
            BlurClientManager.sleep(_defaultDelay, _maxDefaultDelay, retries, _maxDefaultRetries);
            continue OUTER;
          }
          return results;
        } finally {
          if (hitsIterable != null) {
            hitsIterable.close();
          }
        }
      } catch (Exception e) {
        if (e instanceof BlurException) {
          throw (BlurException) e;
        }
        LOG.error("Unknown error during search of [table={0},blurQuery={1}]", e, table, blurQuery);
        throw new BException("Unknown error during search of [table={0},blurQuery={1}]", e, table, blurQuery);
      }
    }
    throw new BException("Query could not be completed.");
  }

  public BlurResults convertToBlurResults(BlurResultIterable hitsIterable, BlurQuery query,
      AtomicLongArray facetCounts, ExecutorService executor, Selector selector, final String table)
      throws InterruptedException, ExecutionException, BlurException {
    BlurResults results = new BlurResults();
    results.setTotalResults(hitsIterable.getTotalResults());
    results.setShardInfo(hitsIterable.getShardInfo());
    if (query.minimumNumberOfResults > 0) {
      hitsIterable.skipTo(query.start);
      int count = 0;
      BlurIterator<BlurResult, BlurException> iterator = hitsIterable.iterator();
      while (iterator.hasNext() && count < query.fetch) {
        results.addToResults(iterator.next());
        count++;
      }
    }
    if (results.results == null) {
      results.results = new ArrayList<BlurResult>();
    }
    if (facetCounts != null) {
      results.facetCounts = BlurUtil.toList(facetCounts);
    }
    if (selector != null) {
      List<Future<FetchResult>> futures = new ArrayList<Future<FetchResult>>();
      for (int i = 0; i < results.results.size(); i++) {
        final LazyBlurResult result = (LazyBlurResult) results.results.get(i);
        final Selector s = new Selector(selector);
        s.setLocationId(result.locationId);
        futures.add(executor.submit(new Callable<FetchResult>() {
          @Override
          public FetchResult call() throws Exception {
            return result.fetchRow(table, s);
          }
        }));
      }
      for (int i = 0; i < results.results.size(); i++) {
        Future<FetchResult> future = futures.get(i);
        BlurResult result = results.results.get(i);
        result.setFetchResult(future.get());
        result.setLocationId(null);
      }
    }
    results.query = query;
    results.query.selector = selector;
    return results;
  }

  private boolean validResults(BlurResults results, int shardCount, BlurQuery query) {
    if (results.totalResults >= query.minimumNumberOfResults) {
      return true;
    }
    int shardInfoSize = results.getShardInfoSize();
    if (shardInfoSize == shardCount) {
      return true;
    }
    return false;
  }

  @Override
  public FetchResult fetchRow(final String table, final Selector selector) throws BlurException, TException {
    checkTable(table);
    checkSelectorFetchSize(selector);
    IndexManager.validSelector(selector);
    String clientHostnamePort = null;
    try {
      clientHostnamePort = getNode(table, selector);
      return _client.execute(clientHostnamePort, new BlurCommand<FetchResult>() {
        @Override
        public FetchResult call(Client client) throws BlurException, TException {
          return client.fetchRow(table, selector);
        }
      }, _maxFetchRetries, _fetchDelay, _maxFetchDelay);
    } catch (Exception e) {
      LOG.error("Unknown error during fetch of row from table [{0}] selector [{1}] node [{2}]", e, table, selector,
          clientHostnamePort);
      throw new BException("Unknown error during fetch of row from table [{0}] selector [{1}] node [{2}]", e, table,
          selector, clientHostnamePort);
    }
  }

  @Override
  public void cancelQuery(final String table, final String uuid) throws BlurException, TException {
    checkTable(table);
    try {
      scatter(getCluster(table), new BlurCommand<Void>() {
        @Override
        public Void call(Client client) throws BlurException, TException {
          client.cancelQuery(table, uuid);
          return null;
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to cancel search table [{0}] uuid [{1}]", e, table, uuid);
      throw new BException("Unknown error while trying to cancel search table [{0}] uuid [{1}]", e, table, uuid);
    }
  }

  @Override
  public List<String> queryStatusIdList(final String table) throws BlurException, TException {
    checkTable(table);
    try {
      return scatterGather(getCluster(table), new BlurCommand<List<String>>() {
        @Override
        public List<String> call(Client client) throws BlurException, TException {
          return client.queryStatusIdList(table);
        }
      }, new Merger<List<String>>() {
        @Override
        public List<String> merge(BlurExecutorCompletionService<List<String>> service) throws BlurException {
          Set<String> result = new HashSet<String>();
          while (service.getRemainingCount() > 0) {
            Future<List<String>> future = service.poll(_defaultParallelCallTimeout, TimeUnit.MILLISECONDS, true);
            List<String> ids = service.getResultThrowException(future);
            result.addAll(ids);
          }
          return new ArrayList<String>(result);
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to get query status ids for table [{0}]", e, table);
      throw new BException("Unknown error while trying to get query status ids for table [{0}]", e, table);
    }
  }

  @Override
  public BlurQueryStatus queryStatusById(final String table, final String uuid) throws BlurException, TException {
    checkTable(table);
    try {
      return scatterGather(getCluster(table), new BlurCommand<BlurQueryStatus>() {
        @Override
        public BlurQueryStatus call(Client client) throws BlurException, TException {
          return client.queryStatusById(table, uuid);
        }
      }, new MergerQueryStatusSingle(_defaultParallelCallTimeout));
    } catch (Exception e) {
      LOG.error("Unknown error while trying to get query status [{0}]", e, table, uuid);
      throw new BException("Unknown error while trying to get query status [{0}]", e, table, uuid);
    }
  }

  @Override
  public TableStats tableStats(final String table) throws BlurException, TException {
    checkTable(table);
    try {
      return scatterGather(getCluster(table), new BlurCommand<TableStats>() {
        @Override
        public TableStats call(Client client) throws BlurException, TException {
          return client.tableStats(table);
        }
      }, new MergerTableStats(_defaultParallelCallTimeout));
    } catch (Exception e) {
      LOG.error("Unknown error while trying to get table stats [{0}]", e, table);
      throw new BException("Unknown error while trying to get table stats [{0}]", e, table);
    }
  }

  @Override
  public Map<String, String> shardServerLayout(String table) throws BlurException, TException {
    checkTable(table);
    Map<String, Map<String, String>> layout = _shardServerLayout.get();
    Map<String, String> tableLayout = layout.get(table);
    if (tableLayout == null) {
      return new HashMap<String, String>();
    }
    return tableLayout;
  }

  @Override
  public Map<String, Map<String, ShardState>> shardServerLayoutState(final String table) throws BlurException,
      TException {
    try {
      return scatterGather(getCluster(table), new BlurCommand<Map<String, Map<String, ShardState>>>() {
        @Override
        public Map<String, Map<String, ShardState>> call(Client client) throws BlurException, TException {
          try {
            return client.shardServerLayoutState(table);
          } catch (BlurException e) {
            LOG.error("UNKOWN error from shard server", e);
            throw e;
          }
        }
      }, new Merger<Map<String, Map<String, ShardState>>>() {
        @Override
        public Map<String, Map<String, ShardState>> merge(
            BlurExecutorCompletionService<Map<String, Map<String, ShardState>>> service) throws BlurException {
          Map<String, Map<String, ShardState>> result = new HashMap<String, Map<String, ShardState>>();
          while (service.getRemainingCount() > 0) {
            Future<Map<String, Map<String, ShardState>>> future = service.poll(_defaultParallelCallTimeout,
                TimeUnit.MILLISECONDS, true, table);
            Map<String, Map<String, ShardState>> shardResult = service.getResultThrowException(future, table);
            for (Entry<String, Map<String, ShardState>> entry : shardResult.entrySet()) {
              Map<String, ShardState> map = result.get(entry.getKey());
              if (map == null) {
                map = new HashMap<String, ShardState>();
                result.put(entry.getKey(), map);
              }
              map.putAll(entry.getValue());
            }
          }
          return result;
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to get shard server layout [{0}]", e, table);
      throw new BException("Unknown error while trying to get shard server layout [{0}]", e, table);
    }
  }

  @Override
  public long recordFrequency(final String table, final String columnFamily, final String columnName, final String value)
      throws BlurException, TException {
    checkTable(table);
    try {
      return scatterGather(getCluster(table), new BlurCommand<Long>() {
        @Override
        public Long call(Client client) throws BlurException, TException {
          return client.recordFrequency(table, columnFamily, columnName, value);
        }
      }, new Merger<Long>() {

        @Override
        public Long merge(BlurExecutorCompletionService<Long> service) throws BlurException {
          Long total = 0L;
          while (service.getRemainingCount() > 0) {
            Future<Long> future = service.poll(_defaultParallelCallTimeout, TimeUnit.MILLISECONDS, true, table,
                columnFamily, columnName, value);
            total += service.getResultThrowException(future, table, columnFamily, columnName, value);
          }
          return total;
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to get record frequency [{0}/{1}/{2}/{3}]", e, table, columnFamily,
          columnName, value);
      throw new BException("Unknown error while trying to get record frequency [{0}/{1}/{2}/{3}]", e, table,
          columnFamily, columnName, value);
    }
  }

  @Override
  public Schema schema(final String table) throws BlurException, TException {
    checkTable(table);
    try {
      return scatterGather(getCluster(table), new BlurCommand<Schema>() {
        @Override
        public Schema call(Client client) throws BlurException, TException {
          return client.schema(table);
        }
      }, new Merger<Schema>() {
        @Override
        public Schema merge(BlurExecutorCompletionService<Schema> service) throws BlurException {
          Schema result = null;
          while (service.getRemainingCount() > 0) {
            Future<Schema> future = service.poll(_defaultParallelCallTimeout, TimeUnit.MILLISECONDS, true, table);
            Schema schema = service.getResultThrowException(future, table);
            if (result == null) {
              result = schema;
            } else {
              result = BlurControllerServer.merge(result, schema);
            }
          }
          return result;
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to schema table [{0}]", e, table);
      throw new BException("Unknown error while trying to schema table [{0}]", e, table);
    }
  }

  @Override
  public List<String> terms(final String table, final String columnFamily, final String columnName,
      final String startWith, final short size) throws BlurException, TException {
    checkTable(table);
    try {
      return scatterGather(getCluster(table), new BlurCommand<List<String>>() {
        @Override
        public List<String> call(Client client) throws BlurException, TException {
          return client.terms(table, columnFamily, columnName, startWith, size);
        }
      }, new Merger<List<String>>() {
        @Override
        public List<String> merge(BlurExecutorCompletionService<List<String>> service) throws BlurException {
          TreeSet<String> terms = new TreeSet<String>();
          while (service.getRemainingCount() > 0) {
            Future<List<String>> future = service.poll(_defaultParallelCallTimeout, TimeUnit.MILLISECONDS, true, table,
                columnFamily, columnName, startWith, size);
            terms.addAll(service.getResultThrowException(future, table, columnFamily, columnName, startWith, size));
          }
          return new ArrayList<String>(terms).subList(0, Math.min(terms.size(), size));
        }
      });
    } catch (Exception e) {
      LOG.error(
          "Unknown error while trying to terms table [{0}] columnFamily [{1}] columnName [{2}] startWith [{3}] size [{4}]",
          e, table, columnFamily, columnName, startWith, size);
      throw new BException(
          "Unknown error while trying to terms table [{0}] columnFamily [{1}] columnName [{2}] startWith [{3}] size [{4}]",
          e, table, columnFamily, columnName, startWith, size);
    }
  }

  private String getNode(String table, Selector selector) throws BlurException, TException {
    Map<String, String> layout = shardServerLayout(table);
    String locationId = selector.locationId;
    if (locationId != null) {
      String shard = locationId.substring(0, locationId.indexOf('/'));
      return layout.get(shard);
    }
    int numberOfShards = getShardCount(table);
    if (selector.rowId != null) {
      String shardName = MutationHelper.getShardName(table, selector.rowId, numberOfShards, _blurPartitioner);
      return layout.get(shardName);
    }
    throw new BException("Selector is missing both a locationid and a rowid, one is needed.");
  }

  private <R> R scatterGather(String cluster, final BlurCommand<R> command, Merger<R> merger) throws Exception {
    return ForkJoin.execute(_executor, _clusterStatus.getOnlineShardServers(true, cluster),
        new ParallelCall<String, R>() {
          @Override
          public R call(String hostnamePort) throws BlurException, TException, IOException {
            return _client.execute(hostnamePort, command.clone(), _maxDefaultRetries, _defaultDelay, _maxDefaultDelay);
          }
        }).merge(merger);
  }

  private <R> void scatter(String cluster, BlurCommand<R> command) throws Exception {
    scatterGather(cluster, command, new Merger<R>() {
      @Override
      public R merge(BlurExecutorCompletionService<R> service) throws BlurException {
        while (service.getRemainingCount() > 0) {
          Future<R> future = service.poll(_defaultParallelCallTimeout, TimeUnit.MILLISECONDS, true);
          service.getResultThrowException(future);
        }
        return null;
      }
    });
  }

  private String getCluster(String table) throws BlurException, TException {
    TableDescriptor describe = describe(table);
    if (describe == null) {
      throw new BException("Table [" + table + "] not found.");
    }
    return describe.cluster;
  }

  public static Schema merge(Schema result, Schema schema) {
    Map<String, Map<String, ColumnDefinition>> destColumnFamilies = result.getFamilies();
    Map<String, Map<String, ColumnDefinition>> srcColumnFamilies = schema.getFamilies();
    for (String srcColumnFamily : srcColumnFamilies.keySet()) {
      Map<String, ColumnDefinition> destColumnNames = destColumnFamilies.get(srcColumnFamily);
      Map<String, ColumnDefinition> srcColumnNames = srcColumnFamilies.get(srcColumnFamily);
      if (destColumnNames == null) {
        destColumnFamilies.put(srcColumnFamily, srcColumnNames);
      } else {
        destColumnNames.putAll(srcColumnNames);
      }
    }
    return result;
  }

  @Override
  public void mutate(final RowMutation mutation) throws BlurException, TException {
    checkTable(mutation.table);
    checkForUpdates(mutation.table);
    try {
      MutationHelper.validateMutation(mutation);
      String table = mutation.getTable();

      int numberOfShards = getShardCount(table);
      Map<String, String> tableLayout = _shardServerLayout.get().get(table);
      if (tableLayout.size() != numberOfShards) {
        throw new BException("Cannot update data while shard is missing");
      }

      String shardName = MutationHelper.getShardName(table, mutation.rowId, numberOfShards, _blurPartitioner);
      String node = tableLayout.get(shardName);
      _client.execute(node, new BlurCommand<Void>() {
        @Override
        public Void call(Client client) throws BlurException, TException {
          client.mutate(mutation);
          return null;
        }
      }, _maxMutateRetries, _mutateDelay, _maxMutateDelay);
    } catch (Exception e) {
      LOG.error("Unknown error during mutation of [{0}]", e, mutation);
      throw new BException("Unknown error during mutation of [{0}]", e, mutation);
    }
  }

  private int getShardCount(String table) throws BlurException, TException {
    Integer numberOfShards = _tableShardCountMap.get(table);
    if (numberOfShards == null) {
      TableDescriptor descriptor = describe(table);
      numberOfShards = descriptor.shardCount;
      _tableShardCountMap.put(table, numberOfShards);
    }
    return numberOfShards;
  }

  @Override
  public void mutateBatch(List<RowMutation> mutations) throws BlurException, TException {
    for (RowMutation mutation : mutations) {
      MutationHelper.validateMutation(mutation);
    }
    Map<String, List<RowMutation>> batches = new HashMap<String, List<RowMutation>>();
    for (RowMutation mutation : mutations) {
      checkTable(mutation.table);
      checkForUpdates(mutation.table);

      MutationHelper.validateMutation(mutation);
      String table = mutation.getTable();

      int numberOfShards = getShardCount(table);
      Map<String, String> tableLayout = _shardServerLayout.get().get(table);
      if (tableLayout.size() != numberOfShards) {
        throw new BException("Cannot update data while shard is missing");
      }

      String shardName = MutationHelper.getShardName(table, mutation.rowId, numberOfShards, _blurPartitioner);
      String node = tableLayout.get(shardName);
      List<RowMutation> list = batches.get(node);
      if (list == null) {
        list = new ArrayList<RowMutation>();
        batches.put(node, list);
      }
      list.add(mutation);
    }

    List<Future<Void>> futures = new ArrayList<Future<Void>>();

    for (Entry<String, List<RowMutation>> entry : batches.entrySet()) {
      final String node = entry.getKey();
      final List<RowMutation> mutationsLst = entry.getValue();
      futures.add(_executor.submit(new Callable<Void>() {
        @Override
        public Void call() throws Exception {
          return _client.execute(node, new BlurCommand<Void>() {
            @Override
            public Void call(Client client) throws BlurException, TException {
              client.mutateBatch(mutationsLst);
              return null;
            }
          }, _maxMutateRetries, _mutateDelay, _maxMutateDelay);
        }
      }));
    }

    for (Future<Void> future : futures) {
      try {
        future.get();
      } catch (InterruptedException e) {
        LOG.error("Unknown error during batch mutations", e);
        throw new BException("Unknown error during batch mutations", e);
      } catch (ExecutionException e) {
        LOG.error("Unknown error during batch mutations", e.getCause());
        throw new BException("Unknown error during batch mutations", e.getCause());
      }
    }
  }

  @Override
  public void createSnapshot(final String table, final String name) throws BlurException, TException {
    checkTable(table);
    try {
      scatter(getCluster(table), new BlurCommand<Void>() {
        @Override
        public Void call(Client client) throws BlurException, TException {
          client.createSnapshot(table, name);
          return null;
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to create a snapshot of table [{0}] snapshot name", e, table, name);
      throw new BException("Unknown error while trying to create a snapshot of table [{0}] snapshot name", e, table,
          name);
    }
  }

  @Override
  public void removeSnapshot(final String table, final String name) throws BlurException, TException {
    checkTable(table);
    try {
      scatter(getCluster(table), new BlurCommand<Void>() {
        @Override
        public Void call(Client client) throws BlurException, TException {
          client.removeSnapshot(table, name);
          return null;
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to remove a snapshot of table [{0}] snapshot name", e, table, name);
      throw new BException("Unknown error while trying to remove a snapshot of table [{0}] snapshot name", e, table,
          name);
    }
  }

  @Override
  public Map<String, List<String>> listSnapshots(final String table) throws BlurException, TException {
    checkTable(table);
    try {
      return scatterGather(getCluster(table), new BlurCommand<Map<String, List<String>>>() {
        @Override
        public Map<String, List<String>> call(Client client) throws BlurException, TException {
          return client.listSnapshots(table);
        }
      }, new Merger<Map<String, List<String>>>() {
        @Override
        public Map<String, List<String>> merge(BlurExecutorCompletionService<Map<String, List<String>>> service)
            throws BlurException {
          Map<String, List<String>> result = new HashMap<String, List<String>>();
          while (service.getRemainingCount() > 0) {
            Future<Map<String, List<String>>> future = service.poll(_defaultParallelCallTimeout, TimeUnit.MILLISECONDS,
                true);
            Map<String, List<String>> snapshotsOnAShardServer = service.getResultThrowException(future);
            for (Entry<String, List<String>> entry : snapshotsOnAShardServer.entrySet()) {
              List<String> snapshots = result.get(entry.getKey());
              if (snapshots == null) {
                snapshots = new ArrayList<String>();
                result.put(entry.getKey(), snapshots);
              }
              snapshots.addAll(entry.getValue());
            }
          }
          return result;
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to get the list of snapshots for table [{0}]", e, table);
      throw new BException("Unknown error while trying to get the list of snapshots for table [{0}]", e, table);
    }
  }

  public void setNodeName(String nodeName) {
    _nodeName = nodeName;
  }

  public int getRemoteFetchCount() {
    return _remoteFetchCount;
  }

  public void setRemoteFetchCount(int remoteFetchCount) {
    _remoteFetchCount = remoteFetchCount;
  }

  public void setQueryChecker(BlurQueryChecker queryChecker) {
    _queryChecker = queryChecker;
  }

  public void setThreadCount(int threadCount) {
    _threadCount = threadCount;
  }

  public void setMaxFetchRetries(int maxFetchRetries) {
    _maxFetchRetries = maxFetchRetries;
  }

  public void setMaxMutateRetries(int maxMutateRetries) {
    _maxMutateRetries = maxMutateRetries;
  }

  public void setMaxDefaultRetries(int maxDefaultRetries) {
    _maxDefaultRetries = maxDefaultRetries;
  }

  public void setFetchDelay(long fetchDelay) {
    _fetchDelay = fetchDelay;
  }

  public void setMutateDelay(long mutateDelay) {
    _mutateDelay = mutateDelay;
  }

  public void setDefaultDelay(long defaultDelay) {
    _defaultDelay = defaultDelay;
  }

  public void setMaxFetchDelay(long maxFetchDelay) {
    _maxFetchDelay = maxFetchDelay;
  }

  public void setMaxMutateDelay(long maxMutateDelay) {
    _maxMutateDelay = maxMutateDelay;
  }

  public void setMaxDefaultDelay(long maxDefaultDelay) {
    _maxDefaultDelay = maxDefaultDelay;
  }

  public BlurClient getClient() {
    return _client;
  }

  public void setClient(BlurClient client) {
    _client = client;
  }

  @Override
  public void optimize(final String table, final int numberOfSegmentsPerShard) throws BlurException, TException {
    checkTable(table);
    try {
      scatter(getCluster(table), new BlurCommand<Void>() {
        @Override
        public Void call(Client client) throws BlurException, TException {
          client.optimize(table, numberOfSegmentsPerShard);
          return null;
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to optimize [table={0},numberOfSegmentsPerShard={1}]", e, table,
          numberOfSegmentsPerShard);
      throw new BException("Unknown error while trying to optimize [table={0},numberOfSegmentsPerShard={1}]", e, table,
          numberOfSegmentsPerShard);
    }
  }

  @Override
  public String parseQuery(final String table, final Query simpleQuery) throws BlurException, TException {
    checkTable(table);
    String cluster = getCluster(table);
    List<String> onlineShardServers = _clusterStatus.getOnlineShardServers(true, cluster);
    try {
      return BlurClientManager.execute(getConnections(onlineShardServers), new BlurCommand<String>() {
        @Override
        public String call(Client client) throws BlurException, TException {
          return client.parseQuery(table, simpleQuery);
        }
      });
    } catch (Exception e) {
      LOG.error("Unknown error while trying to parse query [table={0},simpleQuery={1}]", e, table, simpleQuery);
      throw new BException("Unknown error while trying to parse query [table={0},simpleQuery={1}]", e, table,
          simpleQuery);
    }
  }

  private List<Connection> getConnections(List<String> onlineShardServers) {
    List<Connection> connections = new ArrayList<Connection>();
    for (String c : onlineShardServers) {
      connections.add(new Connection(c));
    }
    return connections;
  }

}
TOP

Related Classes of org.apache.blur.thrift.BlurControllerServer

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.