package com.navercorp.pinpoint.web.dao.hbase;
import com.google.common.annotations.Beta;
import com.google.common.collect.Lists;
import com.navercorp.pinpoint.common.hbase.HBaseTables;
import com.navercorp.pinpoint.common.hbase.HbaseOperations2;
import com.navercorp.pinpoint.common.hbase.RowMapper;
import com.navercorp.pinpoint.common.server.bo.SpanBo;
import com.navercorp.pinpoint.common.server.bo.serializer.RowKeyEncoder;
import com.navercorp.pinpoint.common.server.bo.serializer.trace.v2.SpanEncoder;
import com.navercorp.pinpoint.common.util.TransactionId;
import com.navercorp.pinpoint.web.dao.TraceDao;
import com.navercorp.pinpoint.web.mapper.CellTraceMapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.filter.BinaryPrefixComparator;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.QualifierFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Woonduk Kang(emeroad)
*/
@Beta
@Repository
public class HbaseTraceDaoV2 implements TraceDao {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private HbaseOperations2 template2;
@Autowired
@Qualifier("traceRowKeyEncoderV2")
private RowKeyEncoder<TransactionId> rowKeyEncoder;
private RowMapper<List<SpanBo>> spanMapperV2;
@Value("#{pinpointWebProps['web.hbase.selectSpans.limit'] ?: 500}")
private int selectSpansLimit;
@Value("#{pinpointWebProps['web.hbase.selectAllSpans.limit'] ?: 500}")
private int selectAllSpansLimit;
private final Filter spanFilter = createSpanQualifierFilter();
@Autowired
@Qualifier("spanMapperV2")
public void setSpanMapperV2(RowMapper<List<SpanBo>> spanMapperV2) {
final Logger logger = LoggerFactory.getLogger(spanMapperV2.getClass());
if (logger.isDebugEnabled()) {
spanMapperV2 = CellTraceMapper.wrap(spanMapperV2);
}
this.spanMapperV2 = spanMapperV2;
}
@Override
public List<SpanBo> selectSpan(TransactionId transactionId) {
if (transactionId == null) {
throw new NullPointerException("transactionId must not be null");
}
byte[] transactionIdRowKey = rowKeyEncoder.encodeRowKey(transactionId);
return template2.get(HBaseTables.TRACE_V2, transactionIdRowKey, HBaseTables.TRACE_V2_CF_SPAN, spanMapperV2);
}
@Override
public List<List<SpanBo>> selectSpans(List<TransactionId> transactionIdList) {
return selectSpans(transactionIdList, selectSpansLimit);
}
List<List<SpanBo>> selectSpans(List<TransactionId> transactionIdList, int eachPartitionSize) {
if (CollectionUtils.isEmpty(transactionIdList)) {
return Collections.emptyList();
}
List<List<TransactionId>> splitTransactionIdList = partition(transactionIdList, eachPartitionSize);
return partitionSelect(splitTransactionIdList, HBaseTables.TRACE_V2_CF_SPAN, spanFilter);
}
@Override
public List<List<SpanBo>> selectAllSpans(List<TransactionId> transactionIdList) {
return selectAllSpans(transactionIdList, selectAllSpansLimit);
}
List<List<SpanBo>> selectAllSpans(List<TransactionId> transactionIdList, int eachPartitionSize) {
if (CollectionUtils.isEmpty(transactionIdList)) {
return Collections.emptyList();
}
List<List<TransactionId>> partitionTransactionIdList = partition(transactionIdList, eachPartitionSize);
return partitionSelect(partitionTransactionIdList, HBaseTables.TRACE_V2_CF_SPAN, null);
}
private List<List<TransactionId>> partition(List<TransactionId> transactionIdList, int maxTransactionIdListSize) {
return Lists.partition(transactionIdList, maxTransactionIdListSize);
}
private List<List<SpanBo>> partitionSelect(List<List<TransactionId>> partitionTransactionIdList, byte[] columnFamily, Filter filter) {
if (CollectionUtils.isEmpty(partitionTransactionIdList)) {
return Collections.emptyList();
}
if (columnFamily == null) {
throw new NullPointerException("columnFamily may not be null.");
}
List<List<SpanBo>> spanBoList = new ArrayList<>();
for (List<TransactionId> transactionIdList : partitionTransactionIdList) {
List<List<SpanBo>> partitionSpanList = select0(transactionIdList, columnFamily, filter);
spanBoList.addAll(partitionSpanList);
}
return spanBoList;
}
private List<List<SpanBo>> select0(List<TransactionId> transactionIdList, byte[] columnFamily, Filter filter) {
if (CollectionUtils.isEmpty(transactionIdList)) {
return Collections.emptyList();
}
final List<Get> multiGet = new ArrayList<>(transactionIdList.size());
for (TransactionId transactionId : transactionIdList) {
final Get get = createGet(transactionId, columnFamily, filter);
multiGet.add(get);
}
return template2.get(HBaseTables.TRACE_V2, multiGet, spanMapperV2);
}
private Get createGet(TransactionId transactionId, byte[] columnFamily, Filter filter) {
byte[] transactionIdRowKey = rowKeyEncoder.encodeRowKey(transactionId);
final Get get = new Get(transactionIdRowKey);
get.addFamily(columnFamily);
if (filter != null) {
get.setFilter(filter);
}
return get;
}
public QualifierFilter createSpanQualifierFilter() {
byte indexPrefix = SpanEncoder.TYPE_SPAN;
BinaryPrefixComparator prefixComparator = new BinaryPrefixComparator(new byte[] {indexPrefix});
QualifierFilter qualifierPrefixFilter = new QualifierFilter(CompareFilter.CompareOp.EQUAL, prefixComparator);
return qualifierPrefixFilter;
}
}