diff --git a/commons-hbase/src/main/java/com/navercorp/pinpoint/common/hbase/HBaseTables.java b/commons-hbase/src/main/java/com/navercorp/pinpoint/common/hbase/HBaseTables.java index 467623062..fb6840556 100644 --- a/commons-hbase/src/main/java/com/navercorp/pinpoint/common/hbase/HBaseTables.java +++ b/commons-hbase/src/main/java/com/navercorp/pinpoint/common/hbase/HBaseTables.java @@ -35,6 +35,7 @@ public final class HBaseTables { public static final int APPLICATION_TRACE_INDEX_ROW_DISTRIBUTE_SIZE = 1; // applicationIndex hash size public static final TableName AGENT_STAT = TableName.valueOf("AgentStat"); + public static final TableName AGENT_STAT_AGGR = TableName.valueOf("AgentStatAggr"); public static final byte[] AGENT_STAT_CF_STATISTICS = Bytes.toBytes("S"); // agent statistics column family // FIXME (2014.08) Legacy column for storing serialzied TAgentStat Thrift DTO. @Deprecated public static final byte[] AGENT_STAT_CF_STATISTICS_V1 = Bytes.toBytes("V1"); // qualifier diff --git a/hbase/scripts/README.md b/hbase/scripts/README.md index bfa0545ec..3347b2eb0 100644 --- a/hbase/scripts/README.md +++ b/hbase/scripts/README.md @@ -21,6 +21,7 @@ For example, you can create Pinpoint tables by running the following line from s * ApplicationIndex, HostApplicationMap : Tables for applicationIds and agentIds registered under them * AgentInfo : Table for basic agent information ex) ip, hostname agentversion, start time, etc * AgentStat : Table for agent’s statistical data ex) cpuload, gc, heap etc +* AgentStatAggr : Table for agent’s statistical data aggregated for long term view * AgentLifeCycle : Table for agent’s life cycle data. * AgentEvent : Table for various agent events ex) request for thread dump, etc * ApiMetaData : Meta-table for method information diff --git a/hbase/scripts/hbase-create-snappy.hbase b/hbase/scripts/hbase-create-snappy.hbase index 2b19fc521..c45e81177 100644 --- a/hbase/scripts/hbase-create-snappy.hbase +++ b/hbase/scripts/hbase-create-snappy.hbase @@ -1,5 +1,6 @@ create 'AgentInfo', { NAME => 'Info', TTL => 31536000, COMPRESSION => 'SNAPPY', DATA_BLOCK_ENCODING => 'PREFIX' } create 'AgentStat', { NAME => 'S', TTL => 5184000, COMPRESSION => 'SNAPPY', DATA_BLOCK_ENCODING => 'PREFIX' }, {SPLITS=>["\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"]} +create 'AgentStatAggr', { NAME => 'S', TTL => 5184000, COMPRESSION => 'SNAPPY', DATA_BLOCK_ENCODING => 'PREFIX' }, {SPLITS=>["\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"]} create 'ApplicationIndex', { NAME => 'Agents', TTL => 31536000, COMPRESSION => 'SNAPPY', DATA_BLOCK_ENCODING => 'PREFIX' } create 'AgentLifeCycle', { NAME => 'S', TTL => 5184000, COMPRESSION => 'SNAPPY', DATA_BLOCK_ENCODING => 'PREFIX' } create 'AgentEvent', { NAME => 'E', TTL => 5184000, COMPRESSION => 'SNAPPY', DATA_BLOCK_ENCODING => 'PREFIX' } diff --git a/hbase/scripts/hbase-create.hbase b/hbase/scripts/hbase-create.hbase index 4b2efa0c5..767bdbc40 100644 --- a/hbase/scripts/hbase-create.hbase +++ b/hbase/scripts/hbase-create.hbase @@ -1,5 +1,6 @@ create 'AgentInfo', { NAME => 'Info', TTL => 31536000, DATA_BLOCK_ENCODING => 'PREFIX' } create 'AgentStat', { NAME => 'S', TTL => 5184000, DATA_BLOCK_ENCODING => 'PREFIX' }, {SPLITS=>["\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"]} +create 'AgentStatAggr', { NAME => 'S', TTL => 5184000, DATA_BLOCK_ENCODING => 'PREFIX' }, {SPLITS=>["\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00","\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"]} create 'ApplicationIndex', { NAME => 'Agents', TTL => 31536000, DATA_BLOCK_ENCODING => 'PREFIX' } create 'AgentLifeCycle', { NAME => 'S', TTL => 5184000, DATA_BLOCK_ENCODING => 'PREFIX' } create 'AgentEvent', { NAME => 'E', TTL => 5184000, DATA_BLOCK_ENCODING => 'PREFIX' } diff --git a/hbase/scripts/hbase-drop.hbase b/hbase/scripts/hbase-drop.hbase index 41207d01f..638f28612 100644 --- a/hbase/scripts/hbase-drop.hbase +++ b/hbase/scripts/hbase-drop.hbase @@ -1,5 +1,6 @@ disable 'AgentInfo' disable 'AgentStat' +disable 'AgentStatAggr' disable 'AgentLifeCycle' disable 'AgentEvent' disable 'ApplicationIndex' diff --git a/hbase/scripts/hbase-flush-table.hbase b/hbase/scripts/hbase-flush-table.hbase index a91d5490d..b785294c2 100644 --- a/hbase/scripts/hbase-flush-table.hbase +++ b/hbase/scripts/hbase-flush-table.hbase @@ -1,5 +1,6 @@ flush 'AgentInfo' flush 'AgentStat' +flush 'AgentStatAggr' flush 'AgentLifeCycle' flush 'AgentEvent' flush 'ApplicationIndex' diff --git a/hbase/scripts/hbase-major-compact-htable.hbase b/hbase/scripts/hbase-major-compact-htable.hbase index 1c90bfa6e..2ba03e210 100644 --- a/hbase/scripts/hbase-major-compact-htable.hbase +++ b/hbase/scripts/hbase-major-compact-htable.hbase @@ -1,5 +1,6 @@ major_compact 'AgentInfo' major_compact 'AgentStat' +major_compact 'AgentStatAggr' major_compact 'AgentLifeCycle' major_compact 'AgentEvent' major_compact 'ApplicationIndex' diff --git a/web/src/main/java/com/navercorp/pinpoint/web/dao/hbase/HbaseAgentStatDao.java b/web/src/main/java/com/navercorp/pinpoint/web/dao/hbase/HbaseAgentStatDao.java index 4c3289719..29685e49c 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/dao/hbase/HbaseAgentStatDao.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/dao/hbase/HbaseAgentStatDao.java @@ -19,19 +19,9 @@ package com.navercorp.pinpoint.web.dao.hbase; import static com.navercorp.pinpoint.common.hbase.HBaseTables.*; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import com.navercorp.pinpoint.common.hbase.HBaseTables; -import com.navercorp.pinpoint.common.hbase.HbaseOperations2; -import com.navercorp.pinpoint.common.hbase.ResultsExtractor; -import com.navercorp.pinpoint.common.hbase.RowMapper; -import com.navercorp.pinpoint.common.util.BytesUtils; -import com.navercorp.pinpoint.common.util.RowKeyUtils; -import com.navercorp.pinpoint.common.util.TimeUtils; -import com.navercorp.pinpoint.web.dao.AgentStatDao; -import com.navercorp.pinpoint.web.vo.AgentStat; -import com.navercorp.pinpoint.web.vo.Range; - import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; @@ -41,6 +31,17 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Repository; +import com.navercorp.pinpoint.common.hbase.HBaseTables; +import com.navercorp.pinpoint.common.hbase.HbaseOperations2; +import com.navercorp.pinpoint.common.hbase.ResultsExtractor; +import com.navercorp.pinpoint.common.hbase.RowMapper; +import com.navercorp.pinpoint.common.util.BytesUtils; +import com.navercorp.pinpoint.common.util.RowKeyUtils; +import com.navercorp.pinpoint.common.util.TimeUtils; +import com.navercorp.pinpoint.web.dao.AgentStatDao; +import com.navercorp.pinpoint.web.util.AgentStats; +import com.navercorp.pinpoint.web.vo.AgentStat; +import com.navercorp.pinpoint.web.vo.Range; import com.sematext.hbase.wd.AbstractRowKeyDistributor; /** @@ -49,6 +50,9 @@ import com.sematext.hbase.wd.AbstractRowKeyDistributor; */ @Repository public class HbaseAgentStatDao implements AgentStatDao { + private static final long USE_AGGR_THRESHOLD = 4L * 60 * 60 * 1000; + private static final int AGGR_INTERVAL = 10 * 60 * 1000; + private static final int AGGR_MAX_INTERVAL = AGGR_INTERVAL * 2; private final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -81,8 +85,15 @@ public class HbaseAgentStatDao implements AgentStatDao { if (logger.isDebugEnabled()) { logger.debug("scanAgentStat : agentId={}, {}", agentId, range); } - - + + if (range.getRange() < USE_AGGR_THRESHOLD) { + return getAgentStatListFromRaw(agentId, range); + } else { + return getAgentStatListFromAggr(agentId, range); + } + } + + private List getAgentStatListFromRaw(String agentId, Range range) { Scan scan = createScan(agentId, range); scan.addFamily(HBaseTables.AGENT_STAT_CF_STATISTICS); @@ -97,7 +108,56 @@ public class HbaseAgentStatDao implements AgentStatDao { return merged; } + + public List getAgentStatListFromAggr(String agentId, Range range) { + Scan scan = createScan(agentId, range); + scan.addFamily(HBaseTables.AGENT_STAT_CF_STATISTICS); + + List> intermediate = hbaseOperations2.find(HBaseTables.AGENT_STAT_AGGR, scan, rowKeyDistributor, agentStatMapper); + + List merged = new ArrayList<>(); + for (List each : intermediate) { + merged.addAll(each); + } + + Collections.sort(merged, AgentStats.TIMESTAMP_COMPARATOR); + + + List missingRanges = new ArrayList<>(); + long last = range.getFrom(); + + for (AgentStat stat : merged) { + if (last + AGGR_MAX_INTERVAL < stat.getTimestamp()) { + Range r = new Range(last, stat.getTimestamp() - stat.getCollectInterval()); + missingRanges.add(r); + } + + last = stat.getTimestamp(); + } + + if (last + AGGR_MAX_INTERVAL < range.getTo()) { + Range r = new Range(last, range.getTo()); + missingRanges.add(r); + } + + for (Range r : missingRanges) { + logger.debug("AgentStatAggr doesn't have range: " + r.prettyToString() + " of " + agentId); + + List list = getAgentStatListFromRaw(agentId, r); + + if (list.isEmpty()) { + logger.debug("AgentStat also doesn't have range: " + r.prettyToString() + " of " + agentId); + continue; + } + + List aggregated = AgentStats.aggregate(list, AGGR_INTERVAL); + merged.addAll(aggregated); + } + + return merged; + } + @Override public boolean agentStatExists(String agentId, Range range) { if (agentId == null) { diff --git a/web/src/main/java/com/navercorp/pinpoint/web/util/AgentStats.java b/web/src/main/java/com/navercorp/pinpoint/web/util/AgentStats.java new file mode 100644 index 000000000..bb739f115 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/util/AgentStats.java @@ -0,0 +1,173 @@ +/** + * Copyright 2014 NAVER Corp. + * 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 com.navercorp.pinpoint.web.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import com.navercorp.pinpoint.web.vo.AgentStat; + +/** + * @author Jongho Moon + * + */ +public class AgentStats { + public static final Comparator TIMESTAMP_COMPARATOR = new Comparator() { + + @Override + public int compare(AgentStat o1, AgentStat o2) { + return Long.compare(o1.getTimestamp(), o2.getTimestamp()); + } + }; + + public static List aggregate(List stats, int newInterval) { + return new Aggregator(stats, newInterval).aggregate(); + } + + + private static class Aggregator { + private final List stats; + private final int interval; + + public Aggregator(List stats, int interval) { + this.interval = interval; + this.stats = new ArrayList<>(stats); + Collections.sort(this.stats, TIMESTAMP_COMPARATOR); + } + + public List aggregate() { + if (stats.isEmpty()) { + return stats; + } + + List result = new ArrayList<>(); + AgentStat current = toAggregatedAgentStat(stats.get(0)); + + for (AgentStat stat : stats.subList(1, stats.size())) { + long timestamp = targetTimestamp(stat); + + if (current.getTimestamp() == timestamp) { + current = add(current, stat); + } else { + result.add(current); + current = toAggregatedAgentStat(stat); + } + } + + result.add(current); + + return result; + } + + private AgentStat toAggregatedAgentStat(AgentStat stat) { + long timestamp = targetTimestamp(stat); + AgentStat result = new AgentStat(stat.getAgentId(), timestamp); + + result.setCollectInterval(interval); + + result.setGcType(stat.getGcType()); + result.setGcOldCount(stat.getGcOldCount()); + result.setGcOldTime(stat.getGcOldTime()); + + result.setHeapUsed(stat.getHeapUsed()); + result.setHeapMax(stat.getHeapMax()); + + result.setNonHeapUsed(stat.getNonHeapUsed()); + result.setNonHeapMax(stat.getNonHeapMax()); + + result.setJvmCpuUsage(stat.getJvmCpuUsage()); + result.setSystemCpuUsage(stat.getSystemCpuUsage()); + + result.setSampledNewCount(stat.getSampledNewCount()); + result.setSampledContinuationCount(stat.getSampledContinuationCount()); + result.setUnsampledNewCount(stat.getUnsampledNewCount()); + result.setUnsampledContinuationCount(stat.getUnsampledContinuationCount()); + + result.setHistogramSchema(stat.getHistogramSchema()); + result.setActiveTraceCounts(stat.getActiveTraceCounts()); + + return result; + } + + private long targetTimestamp(AgentStat stat) { + long timestamp = (stat.getTimestamp() / interval) * interval; + + if (stat.getTimestamp() != timestamp) { + timestamp += interval; + } + + return timestamp; + } + + public AgentStat add(AgentStat s1, AgentStat s2) { + AgentStat latest = s1.getTimestamp() > s2.getTimestamp() ? s1 : s2; + + AgentStat stat = new AgentStat(s1.getAgentId(), s1.getTimestamp()); + + stat.setGcType(latest.getGcType()); + stat.setGcOldCount(latest.getGcOldCount()); + stat.setGcOldTime(latest.getGcOldTime()); + + stat.setHeapUsed(latest.getHeapUsed()); + stat.setHeapMax(maxValue(s1.getHeapMax(), s2.getHeapMax())); + + stat.setNonHeapUsed(latest.getNonHeapUsed()); + stat.setNonHeapMax(maxValue(s1.getNonHeapMax(), s2.getNonHeapMax())); + + stat.setJvmCpuUsage(latest.getJvmCpuUsage()); + stat.setSystemCpuUsage(latest.getSystemCpuUsage()); + + stat.setSampledNewCount(addValue(s1.getSampledNewCount(), s2.getSampledNewCount())); + stat.setSampledContinuationCount(addValue(s1.getSampledContinuationCount(), s2.getSampledContinuationCount())); + stat.setUnsampledNewCount(addValue(s1.getUnsampledNewCount(), s2.getUnsampledNewCount())); + stat.setUnsampledContinuationCount(addValue(s1.getUnsampledContinuationCount(), s2.getUnsampledContinuationCount())); + + stat.setHistogramSchema(latest.getHistogramSchema()); + stat.setActiveTraceCounts(latest.getActiveTraceCounts()); + + return stat; + } + + private long addValue(long v1, long v2) { + if (v1 == AgentStat.NOT_COLLECTED) { + if (v2 == AgentStat.NOT_COLLECTED) { + return AgentStat.NOT_COLLECTED; + } else { + return v2; + } + } else { + if (v1 == AgentStat.NOT_COLLECTED) { + return v1; + } else { + return v1 + v2; + } + } + } + + private long maxValue(long v1, long v2) { + if (v1 == AgentStat.NOT_COLLECTED) { + return v2; + } else if (v2 == AgentStat.NOT_COLLECTED) { + return v1; + } + + return v1 < v2 ? v2 : v1; + } + } + + +}