Skip to content

Commit

Permalink
[AMBARI-26076] Upgrade net.sf.ehcache to 3.10.0 (apache#3825)
Browse files Browse the repository at this point in the history
  • Loading branch information
sandeep318kumar authored Sep 25, 2024
1 parent 7182923 commit b6a3185
Show file tree
Hide file tree
Showing 14 changed files with 466 additions and 497 deletions.
29 changes: 27 additions & 2 deletions ambari-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,10 @@
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</exclusion>
<exclusion>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
Expand Down Expand Up @@ -1661,9 +1665,30 @@
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.istack</groupId>
<artifactId>istack-commons-runtime</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.fastinfoset</groupId>
<artifactId>FastInfoset</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.0</version>
<version>3.10.0</version>
<exclusions>
<exclusion>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2061,6 +2061,15 @@ public class Configuration {
public static final ConfigurationProperty<Integer> TIMELINE_METRICS_CACHE_IDLE_TIME = new ConfigurationProperty<>(
"server.timeline.metrics.cache.entry.idle.seconds", 1800);

/**
* Cache size in entry units that ambari metrics cache will hold.
*/
@Markdown(
relatedTo = "server.timeline.metrics.cache.disabled",
description = "cache size, in entries, that ambari metrics cache will hold.")
public static final ConfigurationProperty<Integer> TIMELINE_METRICS_CACHE_ENTRY_UNIT_SIZE = new ConfigurationProperty<>(
"server.timeline.metrics.cache.entry.entry.unit.size", 100);

/**
* The time, in {@link TimeUnit#MILLISECONDS}, that initial requests made to
* Ambari Metrics will wait while reading from the socket before timing out.
Expand Down Expand Up @@ -5269,6 +5278,13 @@ public int getMetricCacheIdleSeconds() {
return Integer.parseInt(getProperty(TIMELINE_METRICS_CACHE_IDLE_TIME));
}

/**
* Ambari metrics cache size.
*/
public int getMetricCacheEntryUnitSize() {
return Integer.parseInt(getProperty(TIMELINE_METRICS_CACHE_ENTRY_UNIT_SIZE));
}

/**
* Separate timeout settings for metrics cache.
* @return milliseconds
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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.ambari.server.controller.metrics.timeline.cache;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;

import org.ehcache.spi.serialization.Serializer;
import org.ehcache.spi.serialization.SerializerException;

public class TimelineAppMetricCacheKeySerializer implements Serializer<TimelineAppMetricCacheKey> {
private final ClassLoader classLoader;
public TimelineAppMetricCacheKeySerializer(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public ByteBuffer serialize(TimelineAppMetricCacheKey key) throws SerializerException {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(key);
objectOutputStream.close();
return ByteBuffer.wrap(byteArrayOutputStream.toByteArray());
} catch (Exception e) {
throw new SerializerException(e);
}
}

@Override
public TimelineAppMetricCacheKey read(ByteBuffer binary) throws ClassNotFoundException, SerializerException {
try {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(binary.array());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (TimelineAppMetricCacheKey) objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new SerializerException("Error during deserialization", e);
}
}

@Override
public boolean equals(TimelineAppMetricCacheKey key, ByteBuffer binary) throws ClassNotFoundException, SerializerException {
try {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(binary.array());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
TimelineAppMetricCacheKey deserializedKey = (TimelineAppMetricCacheKey) objectInputStream.readObject();

// Now compare key and deserializedKey
if (key == deserializedKey) return true;
if (deserializedKey == null || (key.getClass() != deserializedKey.getClass())) return false;

if (!key.getMetricNames().equals(deserializedKey.getMetricNames())) return false;
if (!key.getAppId().equals(deserializedKey.getAppId())) return false;
return !(key.getHostNames() != null ? !key.getHostNames().equals(deserializedKey.getHostNames()) : deserializedKey.getHostNames() != null);

} catch (IOException e) {
throw new SerializerException("Error during deserialization", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,36 @@
package org.apache.ambari.server.controller.metrics.timeline.cache;

import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.hadoop.metrics2.sink.timeline.TimelineMetrics;
import org.ehcache.Cache;
import org.ehcache.core.internal.statistics.DefaultStatisticsService;
import org.ehcache.core.statistics.CacheStatistics;
import org.ehcache.spi.loaderwriter.CacheLoadingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.constructs.blocking.LockTimeoutException;
import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory;
import net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache;
import net.sf.ehcache.statistics.StatisticsGateway;

public class TimelineMetricCache extends UpdatingSelfPopulatingCache {

public class TimelineMetricCache {
private final Cache<TimelineAppMetricCacheKey, TimelineMetricsCacheValue> cache;
private final DefaultStatisticsService statisticsService;
private final TimelineMetricCacheEntryFactory cacheEntryFactory;
public static final String TIMELINE_METRIC_CACHE_INSTANCE_NAME = "timelineMetricCache";
private final static Logger LOG = LoggerFactory.getLogger(TimelineMetricCache.class);
private static AtomicInteger printCacheStatsCounter = new AtomicInteger(0);

/**
* Creates a SelfPopulatingCache.
* Creates a TimelineMetricCache.
*
* @param cache @Cache
* @param factory @CacheEntryFactory
* @param cacheEntryFactory @CacheEntryFactory
* @param statisticsService @DefaultStatisticsService
*/
public TimelineMetricCache(Ehcache cache, UpdatingCacheEntryFactory factory) throws CacheException {
super(cache, factory);
public TimelineMetricCache(Cache<TimelineAppMetricCacheKey, TimelineMetricsCacheValue> cache, TimelineMetricCacheEntryFactory cacheEntryFactory, DefaultStatisticsService statisticsService) {
this.cache = cache;
this.cacheEntryFactory = cacheEntryFactory;
this.statisticsService = statisticsService;
}

/**
Expand All @@ -63,26 +64,22 @@ public TimelineMetrics getAppTimelineMetricsFromCache(TimelineAppMetricCacheKey
// Make sure key is valid
validateKey(key);

Element element = null;
TimelineMetricsCacheValue value = null;
try {
element = get(key);
} catch (LockTimeoutException le) {
// Ehcache masks the Socket Timeout to look as a LockTimeout
Throwable t = le.getCause();
if (t instanceof CacheException) {
t = t.getCause();
if (t instanceof SocketTimeoutException) {
throw new SocketTimeoutException(t.getMessage());
}
if (t instanceof ConnectException) {
throw new ConnectException(t.getMessage());
}
value = cache.get(key);
} catch (CacheLoadingException cle) {
Throwable t = cle.getCause();
if(t instanceof SocketTimeoutException) {
throw new SocketTimeoutException(t.getMessage());
}
if(t instanceof IOException) {
throw new IOException(t.getMessage());
}
throw cle;
}

TimelineMetrics timelineMetrics = new TimelineMetrics();
if (element != null && element.getObjectValue() != null) {
TimelineMetricsCacheValue value = (TimelineMetricsCacheValue) element.getObjectValue();
if (value != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Returning value from cache: {}", value);
}
Expand All @@ -92,51 +89,21 @@ public TimelineMetrics getAppTimelineMetricsFromCache(TimelineAppMetricCacheKey
if (LOG.isDebugEnabled()) {
// Print stats every 100 calls - Note: Supported in debug mode only
if (printCacheStatsCounter.getAndIncrement() == 0) {
StatisticsGateway statistics = this.getStatistics();
LOG.debug("Metrics cache stats => \n, Evictions = {}, Expired = {}, Hits = {}, Misses = {}, Hit ratio = {}, Puts = {}, Size in MB = {}",
statistics.cacheEvictedCount(), statistics.cacheExpiredCount(), statistics.cacheHitCount(), statistics.cacheMissCount(), statistics.cacheHitRatio(),
statistics.cachePutCount(), statistics.getLocalHeapSizeInBytes() / 1048576);
CacheStatistics cacheStatistics = statisticsService.getCacheStatistics(TIMELINE_METRIC_CACHE_INSTANCE_NAME);
if(cacheStatistics == null) {
LOG.warn("Cache statistics not available.");
return timelineMetrics;
}
LOG.debug("Metrics cache stats => \n, Evictions = {}, Expired = {}, Hits = {}, Misses = {}, Hit ratio = {}, Puts = {}",
cacheStatistics.getCacheEvictions(), cacheStatistics.getCacheExpirations(), cacheStatistics.getCacheHits(), cacheStatistics.getCacheMisses(), cacheStatistics.getCacheHitPercentage(), cacheStatistics.getCachePuts()
);
} else {
printCacheStatsCounter.compareAndSet(100, 0);
}
}

return timelineMetrics;
}

/**
* Set new time bounds on the cache key so that update can use the new
* query window. We do this quietly which means regular get/update logic is
* not invoked.
*/
@Override
public Element get(Object key) throws LockTimeoutException {
Element element = this.getQuiet(key);
if (element != null) {
if (LOG.isTraceEnabled()) {
LOG.trace("key : {}", element.getObjectKey());
LOG.trace("value : {}", element.getObjectValue());
}

// Set new time boundaries on the key
TimelineAppMetricCacheKey existingKey = (TimelineAppMetricCacheKey) element.getObjectKey();

LOG.debug("Existing temporal info: {} for : {}", existingKey.getTemporalInfo(), existingKey.getMetricNames());

TimelineAppMetricCacheKey newKey = (TimelineAppMetricCacheKey) key;
existingKey.setTemporalInfo(newKey.getTemporalInfo());

LOG.debug("New temporal info: {} for : {}", newKey.getTemporalInfo(), existingKey.getMetricNames());

if (existingKey.getSpec() == null || !existingKey.getSpec().equals(newKey.getSpec())) {
existingKey.setSpec(newKey.getSpec());
LOG.debug("New spec: {} for : {}", newKey.getSpec(), existingKey.getMetricNames());
}
}

return super.get(key);
}

private void validateKey(TimelineAppMetricCacheKey key) throws IllegalArgumentException {
StringBuilder msg = new StringBuilder("Invalid metric key requested.");
boolean throwException = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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.ambari.server.controller.metrics.timeline.cache;

import java.util.concurrent.TimeUnit;

import org.ehcache.ValueSupplier;
import org.ehcache.expiry.Duration;
import org.ehcache.expiry.Expiry;


public class TimelineMetricCacheCustomExpiry implements Expiry<TimelineAppMetricCacheKey, TimelineMetricsCacheValue> {

private final Duration timeToLive;
private final Duration timeToIdle;

public TimelineMetricCacheCustomExpiry(java.time.Duration timeToLive, java.time.Duration timeToIdle) {
this.timeToLive = convertJavaDurationToEhcacheDuration(timeToLive);
this.timeToIdle = convertJavaDurationToEhcacheDuration(timeToIdle);
}

@Override
public Duration getExpiryForCreation(TimelineAppMetricCacheKey key, TimelineMetricsCacheValue value) {
return timeToLive;
}

@Override
public Duration getExpiryForAccess(TimelineAppMetricCacheKey key, ValueSupplier<? extends TimelineMetricsCacheValue> value) {
return timeToIdle;
}

@Override
public Duration getExpiryForUpdate(TimelineAppMetricCacheKey key, ValueSupplier<? extends TimelineMetricsCacheValue> oldValue, TimelineMetricsCacheValue newValue) {
return timeToLive;
}

private Duration convertJavaDurationToEhcacheDuration(java.time.Duration javaDuration) {
return Duration.of(javaDuration.toNanos(), TimeUnit.NANOSECONDS);
}
}
Loading

0 comments on commit b6a3185

Please sign in to comment.