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

org.apache.accumulo.server.replication.ReplicationUtil Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
/*
 * 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 org.apache.accumulo.server.replication;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;

import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.BatchScanner;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.client.impl.Tables;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.master.thrift.MasterMonitorInfo;
import org.apache.accumulo.core.metadata.MetadataTable;
import org.apache.accumulo.core.metadata.RootTable;
import org.apache.accumulo.core.replication.ReplicationSchema.WorkSection;
import org.apache.accumulo.core.replication.ReplicationTable;
import org.apache.accumulo.core.replication.ReplicationTableOfflineException;
import org.apache.accumulo.core.replication.ReplicationTarget;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.server.AccumuloServerContext;
import org.apache.accumulo.server.conf.TableConfiguration;
import org.apache.accumulo.server.replication.proto.Replication.Status;
import org.apache.accumulo.server.zookeeper.ZooCache;
import org.apache.hadoop.io.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Iterables;
import com.google.protobuf.InvalidProtocolBufferException;

public class ReplicationUtil {
  private static final Logger log = LoggerFactory.getLogger(ReplicationUtil.class);
  public static final String STATUS_FORMATTER_CLASS_NAME = StatusFormatter.class.getName();

  private final AccumuloServerContext context;
  private final ZooCache zooCache;
  private final ReplicaSystemFactory factory;

  public ReplicationUtil(AccumuloServerContext context) {
    this(context, new ZooCache(), new ReplicaSystemFactory());
  }

  public ReplicationUtil(AccumuloServerContext context, ZooCache cache, ReplicaSystemFactory factory) {
    this.zooCache = cache;
    this.context = context;
    this.factory = factory;
  }

  public int getMaxReplicationThreads(MasterMonitorInfo mmi) {
    int activeTservers = mmi.getTServerInfoSize();

    // The number of threads each tserver will use at most to replicate data
    int replicationThreadsPerServer = Integer.parseInt(context.getConfiguration().get(Property.REPLICATION_WORKER_THREADS));

    // The total number of "slots" we have to replicate data
    return activeTservers * replicationThreadsPerServer;
  }

  /**
   * Extract replication peers from system configuration
   *
   * @return Configured replication peers
   */
  public Map getPeers() {
    Map peers = new HashMap<>();

    // Get the defined peers and what ReplicaSystem impl they're using
    for (Entry property : context.getConfiguration().getAllPropertiesWithPrefix(Property.REPLICATION_PEERS).entrySet()) {
      String key = property.getKey();
      // Filter out cruft that we don't want
      if (!key.startsWith(Property.REPLICATION_PEER_USER.getKey()) && !key.startsWith(Property.REPLICATION_PEER_PASSWORD.getKey())
          && !key.startsWith(Property.REPLICATION_PEER_KEYTAB.getKey())) {
        String peerName = property.getKey().substring(Property.REPLICATION_PEERS.getKey().length());
        Entry entry;
        try {
          entry = factory.parseReplicaSystemConfiguration(property.getValue());
        } catch (Exception e) {
          log.warn("Could not instantiate ReplicaSystem for {} with configuration {}", property.getKey(), property.getValue(), e);
          continue;
        }

        peers.put(peerName, entry.getKey());
      }
    }

    return peers;
  }

  public Set getReplicationTargets() {
    // The total set of configured targets
    final Set allConfiguredTargets = new HashSet<>();
    final Map tableNameToId = Tables.getNameToIdMap(context.getInstance());

    for (String table : tableNameToId.keySet()) {
      if (MetadataTable.NAME.equals(table) || RootTable.NAME.equals(table)) {
        continue;
      }

      String localId = tableNameToId.get(table);
      if (null == localId) {
        log.trace("Could not determine ID for {}", table);
        continue;
      }

      TableConfiguration tableConf = context.getServerConfigurationFactory().getTableConfiguration(localId);
      if (null == tableConf) {
        log.trace("Could not get configuration for table {} (it no longer exists)", table);
        continue;
      }

      for (Entry prop : tableConf.getAllPropertiesWithPrefix(Property.TABLE_REPLICATION_TARGET).entrySet()) {
        String peerName = prop.getKey().substring(Property.TABLE_REPLICATION_TARGET.getKey().length());
        String remoteIdentifier = prop.getValue();
        ReplicationTarget target = new ReplicationTarget(peerName, remoteIdentifier, localId);

        allConfiguredTargets.add(target);
      }
    }

    return allConfiguredTargets;
  }

  public Map getPendingReplications() {
    final Map counts = new HashMap<>();

    // Read over the queued work
    BatchScanner bs;
    try {
      bs = context.getConnector().createBatchScanner(ReplicationTable.NAME, Authorizations.EMPTY, 4);
    } catch (TableNotFoundException | AccumuloException | AccumuloSecurityException e) {
      log.debug("No replication table exists", e);
      return counts;
    }

    bs.setRanges(Collections.singleton(new Range()));
    WorkSection.limit(bs);
    try {
      Text buffer = new Text();
      for (Entry entry : bs) {
        Key k = entry.getKey();
        k.getColumnQualifier(buffer);
        ReplicationTarget target = ReplicationTarget.from(buffer);

        // TODO ACCUMULO-2835 once explicit lengths are tracked, we can give size-based estimates instead of just file-based
        Long count = counts.get(target);
        if (null == count) {
          counts.put(target, Long.valueOf(1l));
        } else {
          counts.put(target, count + 1);
        }
      }
    } finally {
      bs.close();
    }

    return counts;
  }

  /**
   * Fetches the absolute path of the file to be replicated.
   *
   * @param conn
   *          Accumulo Connector
   * @param workQueuePath
   *          Root path for the Replication WorkQueue
   * @param queueKey
   *          The Replication work queue key
   * @return The absolute path for the file, or null if the key is no longer in ZooKeeper
   */
  public String getAbsolutePath(Connector conn, String workQueuePath, String queueKey) {
    byte[] data = zooCache.get(workQueuePath + "/" + queueKey);
    if (null != data) {
      return new String(data, UTF_8);
    }

    return null;
  }

  /**
   * Compute a progress string for the replication of the given WAL
   *
   * @param conn
   *          Accumulo Connector
   * @param path
   *          Absolute path to a WAL, or null
   * @param target
   *          ReplicationTarget the WAL is being replicated to
   * @return A status message for a file being replicated
   */
  public String getProgress(Connector conn, String path, ReplicationTarget target) {
    // We could try to grep over the table, but without knowing the full file path, we
    // can't find the status quickly
    String status = "Unknown";
    if (null != path) {
      Scanner s;
      try {
        s = ReplicationTable.getScanner(conn);
      } catch (ReplicationTableOfflineException e) {
        log.debug("Replication table no longer online", e);
        return status;
      }

      s.setRange(Range.exact(path));
      s.fetchColumn(WorkSection.NAME, target.toText());

      // Fetch the work entry for this item
      Entry kv = null;
      try {
        kv = Iterables.getOnlyElement(s);
      } catch (NoSuchElementException e) {
        log.trace("Could not find status of {} replicating to {}", path, target);
        status = "Unknown";
      } finally {
        s.close();
      }

      // If we found the work entry for it, try to compute some progress
      if (null != kv) {
        try {
          Status stat = Status.parseFrom(kv.getValue().get());
          if (StatusUtil.isFullyReplicated(stat)) {
            status = "Finished";
          } else {
            if (stat.getInfiniteEnd()) {
              status = stat.getBegin() + "/∞ records";
            } else {
              status = stat.getBegin() + "/" + stat.getEnd() + " records";
            }
          }
        } catch (InvalidProtocolBufferException e) {
          log.warn("Could not deserialize protobuf for {}", kv.getKey(), e);
          status = "Unknown";
        }
      }
    }

    return status;
  }

  public Map invert(Map map) {
    Map newMap = new HashMap<>(map.size());
    for (Entry entry : map.entrySet()) {
      newMap.put(entry.getValue(), entry.getKey());
    }
    return newMap;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy