| /** |
| * 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 |
| * <p> |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * <p> |
| * 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.ambari.metrics.core.timeline.source.cache; |
| |
| import net.sf.ehcache.Element; |
| import net.sf.ehcache.pool.Size; |
| import net.sf.ehcache.pool.SizeOfEngine; |
| import net.sf.ehcache.pool.impl.DefaultSizeOfEngine; |
| import net.sf.ehcache.pool.sizeof.ReflectionSizeOf; |
| import net.sf.ehcache.pool.sizeof.SizeOf; |
| import org.apache.hadoop.metrics2.sink.timeline.TimelineMetric; |
| import org.apache.hadoop.metrics2.sink.timeline.TimelineMetrics; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.util.Map; |
| import java.util.TreeMap; |
| |
| public class InternalMetricsCacheSizeOfEngine implements SizeOfEngine { |
| private final static Logger LOG = LoggerFactory.getLogger(InternalMetricsCacheSizeOfEngine.class); |
| |
| private static int DEFAULT_MAX_DEPTH = 1000; |
| private static boolean DEFAULT_ABORT_WHEN_MAX_DEPTH_EXCEEDED = false; |
| |
| // Base Engine |
| protected SizeOfEngine underlying = null; |
| |
| // Counter |
| protected SizeOf reflectionSizeOf = new ReflectionSizeOf(); |
| |
| // Optimizations |
| private volatile long timelineMetricPrimitivesApproximation = 0; |
| |
| // Map entry sizing |
| private long sizeOfMapEntry; |
| private long sizeOfMapEntryOverhead; |
| private long sizeOfElement; |
| |
| protected InternalMetricsCacheSizeOfEngine(SizeOfEngine underlying) { |
| this.underlying = underlying; |
| } |
| |
| public InternalMetricsCacheSizeOfEngine() { |
| this(new DefaultSizeOfEngine(DEFAULT_MAX_DEPTH, DEFAULT_ABORT_WHEN_MAX_DEPTH_EXCEEDED)); |
| |
| this.sizeOfMapEntry = reflectionSizeOf.sizeOf(new Long(1)) + |
| reflectionSizeOf.sizeOf(new Double(2.0)); |
| |
| this.sizeOfElement = reflectionSizeOf.sizeOf(new Element(new Object(), new Object())); |
| |
| //SizeOfMapEntryOverhead = SizeOfMapWithOneEntry - (SizeOfEmptyMap + SizeOfOneEntry) |
| TreeMap<Long, Double> map = new TreeMap<>(); |
| long emptyMapSize = reflectionSizeOf.sizeOf(map); |
| map.put(new Long(1), new Double(2.0)); |
| long sizeOfMapOneEntry = reflectionSizeOf.deepSizeOf(DEFAULT_MAX_DEPTH, DEFAULT_ABORT_WHEN_MAX_DEPTH_EXCEEDED, map).getCalculated(); |
| this.sizeOfMapEntryOverhead = sizeOfMapOneEntry - (emptyMapSize + this.sizeOfMapEntry); |
| |
| LOG.info("Creating custom sizeof engine for TimelineMetrics."); |
| } |
| |
| /** |
| * Return size of the metrics TreeMap in an optimized way. |
| * |
| */ |
| protected long getTimelineMetricsSize(TimelineMetrics metrics) { |
| long size = 8; // Object reference |
| |
| if (metrics != null) { |
| for (TimelineMetric metric : metrics.getMetrics()) { |
| |
| if (timelineMetricPrimitivesApproximation == 0) { |
| timelineMetricPrimitivesApproximation += reflectionSizeOf.sizeOf(metric.getMetricName()); |
| timelineMetricPrimitivesApproximation += reflectionSizeOf.sizeOf(metric.getAppId()); |
| timelineMetricPrimitivesApproximation += reflectionSizeOf.sizeOf(metric.getHostName()); |
| timelineMetricPrimitivesApproximation += reflectionSizeOf.sizeOf(metric.getInstanceId()); |
| timelineMetricPrimitivesApproximation += reflectionSizeOf.sizeOf(metric.getStartTime()); |
| timelineMetricPrimitivesApproximation += reflectionSizeOf.sizeOf(metric.getType()); |
| timelineMetricPrimitivesApproximation += 8; // Object overhead |
| |
| LOG.debug("timelineMetricPrimitivesApproximation bytes = " + timelineMetricPrimitivesApproximation); |
| } |
| size += timelineMetricPrimitivesApproximation; |
| size += getValueMapSize(metric.getMetricValues()); |
| } |
| LOG.debug("Total Size of metric values in cache: " + size); |
| } |
| return size; |
| } |
| |
| protected long getValueMapSize(Map<Long, Double> metricValues) { |
| long size = 0; |
| if (metricValues != null && !metricValues.isEmpty()) { |
| // Numeric wrapper: 12 bytes + 8 bytes Data type + 4 bytes alignment = 48 (Long, Double) |
| // Tree Map: 12 bytes for header + 20 bytes for 5 object fields : pointers + 1 byte for flag = 40 |
| LOG.debug("Size of metric value: " + (sizeOfMapEntry + sizeOfMapEntryOverhead) * metricValues.size()); |
| size += (sizeOfMapEntry + sizeOfMapEntryOverhead) * metricValues.size(); // Treemap size is O(1) |
| } |
| return size; |
| } |
| |
| @Override |
| public Size sizeOf(Object key, Object value, Object container) { |
| return new Size(sizeOfElement + getSizeOfEntry(key, value), false); |
| } |
| |
| @Override |
| public SizeOfEngine copyWith(int maxDepth, boolean abortWhenMaxDepthExceeded) { |
| LOG.debug("Copying tracing sizeof engine, maxdepth: {}, abort: {}", maxDepth, abortWhenMaxDepthExceeded); |
| |
| return underlying.copyWith(maxDepth, abortWhenMaxDepthExceeded); |
| } |
| |
| protected long getSizeOfEntry(Object key, Object value) { |
| try { |
| LOG.debug("BEGIN - Sizeof, key: {}, value: {}", key, value); |
| long size = 0; |
| if (key instanceof InternalMetricCacheKey) { |
| InternalMetricCacheKey metricCacheKey = (InternalMetricCacheKey) key; |
| size += reflectionSizeOf.sizeOf(metricCacheKey.getMetricName()); |
| size += reflectionSizeOf.sizeOf(metricCacheKey.getAppId()); |
| size += reflectionSizeOf.sizeOf(metricCacheKey.getInstanceId()); // null safe |
| size += reflectionSizeOf.sizeOf(metricCacheKey.getHostname()); |
| } |
| if (value instanceof InternalMetricCacheValue) { |
| size += getValueMapSize(((InternalMetricCacheValue) value).getMetricValues()); |
| } |
| // Mark size as not being exact |
| return size; |
| } finally { |
| LOG.debug("END - Sizeof, key: {}", key); |
| } |
| } |
| } |