From baaa5723228c4d6f0ff30a505d1f9ecd7a5db2be Mon Sep 17 00:00:00 2001 From: Zhong Date: Wed, 2 Aug 2017 15:37:44 +0800 Subject: [PATCH 1/2] APACHE-KYLIN-2716: remove non-thread-safe WeakHashMap for ClassUtil --- .../main/java/org/apache/kylin/common/util/ClassUtil.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/core-common/src/main/java/org/apache/kylin/common/util/ClassUtil.java b/core-common/src/main/java/org/apache/kylin/common/util/ClassUtil.java index 0eb1af5..a78cb48 100644 --- a/core-common/src/main/java/org/apache/kylin/common/util/ClassUtil.java +++ b/core-common/src/main/java/org/apache/kylin/common/util/ClassUtil.java @@ -27,7 +27,6 @@ import java.net.URLDecoder; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; -import java.util.WeakHashMap; import org.slf4j.LoggerFactory; @@ -54,7 +53,6 @@ public class ClassUtil { } } - private static final WeakHashMap> forNameCache = new WeakHashMap<>(); private static final Map classRenameMap; static { classRenameMap = new HashMap<>(); @@ -71,15 +69,8 @@ public class ClassUtil { @SuppressWarnings("unchecked") public static Class forName(String name, Class clz) throws ClassNotFoundException { - String origName = name; - - Class result = (Class) forNameCache.get(origName); - if (result == null) { - name = forRenamedClass(name); - result = (Class) Class.forName(name); - forNameCache.put(origName, result); - } - return result; + name = forRenamedClass(name); + return (Class) Class.forName(name); } private static String forRenamedClass(String name) { -- 2.5.4 (Apple Git-61) From f7266b46b852d7f89b23cdca089719f0dff78a4f Mon Sep 17 00:00:00 2001 From: Zhong Date: Wed, 2 Aug 2017 16:27:39 +0800 Subject: [PATCH 2/2] APACHE-KYLIN-2716: replace non-thread-safe WeakHashMap with Guava Cache for htableInfoCache in CubeService --- .../kylin/rest/controller/CubeController.java | 3 +- .../kylin/rest/controller2/CubeControllerV2.java | 3 +- .../org/apache/kylin/rest/service/CubeService.java | 99 +++++++++++++++++----- 3 files changed, 81 insertions(+), 24 deletions(-) diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java index a370292..a2cf0fb 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java +++ b/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ExecutionException; import org.apache.commons.lang.StringUtils; import org.apache.kylin.common.util.JsonUtil; @@ -553,7 +554,7 @@ public class CubeController extends BasicController { // Get info of given table. try { hr = cubeService.getHTableInfo(tableName); - } catch (IOException e) { + } catch (IOException | ExecutionException e) { logger.error("Failed to calcuate size of HTable \"" + tableName + "\".", e); } diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller2/CubeControllerV2.java b/server-base/src/main/java/org/apache/kylin/rest/controller2/CubeControllerV2.java index 9ffc062..aba2cf9 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/controller2/CubeControllerV2.java +++ b/server-base/src/main/java/org/apache/kylin/rest/controller2/CubeControllerV2.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; import org.apache.kylin.cube.CubeInstance; import org.apache.kylin.cube.CubeManager; @@ -445,7 +446,7 @@ public class CubeControllerV2 extends BasicController { // Get info of given table. try { hr = cubeService.getHTableInfo(tableName); - } catch (IOException e) { + } catch (IOException | ExecutionException e) { logger.error("Failed to calculate size of HTable \"" + tableName + "\".", e); } diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/CubeService.java b/server-base/src/main/java/org/apache/kylin/rest/service/CubeService.java index aa42cb0..c32133d 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/service/CubeService.java +++ b/server-base/src/main/java/org/apache/kylin/rest/service/CubeService.java @@ -24,7 +24,8 @@ import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.List; -import java.util.WeakHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import org.apache.commons.lang.StringUtils; import org.apache.kylin.common.KylinConfig; @@ -41,6 +42,7 @@ import org.apache.kylin.engine.mr.CubingJob; import org.apache.kylin.job.exception.JobException; import org.apache.kylin.job.execution.DefaultChainedExecutable; import org.apache.kylin.job.execution.ExecutableState; +import org.apache.kylin.metadata.cachesync.Broadcaster; import org.apache.kylin.metadata.draft.Draft; import org.apache.kylin.metadata.model.DataModelDesc; import org.apache.kylin.metadata.model.SegmentStatusEnum; @@ -52,6 +54,7 @@ import org.apache.kylin.metadata.realization.RealizationType; import org.apache.kylin.rest.constant.Constant; import org.apache.kylin.rest.exception.BadRequestException; import org.apache.kylin.rest.exception.ForbiddenException; +import org.apache.kylin.rest.exception.InternalErrorException; import org.apache.kylin.rest.msg.Message; import org.apache.kylin.rest.msg.MsgPicker; import org.apache.kylin.rest.request.MetricsRequest; @@ -61,6 +64,7 @@ import org.apache.kylin.rest.security.AclPermission; import org.apache.kylin.rest.util.AclUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.access.AccessDeniedException; @@ -69,6 +73,11 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; import com.google.common.collect.Lists; /** @@ -77,13 +86,13 @@ import com.google.common.collect.Lists; * @author yangli9 */ @Component("cubeMgmtService") -public class CubeService extends BasicService { +public class CubeService extends BasicService implements InitializingBean { private static final Logger logger = LoggerFactory.getLogger(CubeService.class); public static final char[] VALID_CUBENAME = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_".toCharArray(); - private WeakHashMap htableInfoCache = new WeakHashMap<>(); + private static final int CACHE_MAX_SIZE = 5000; @Autowired @Qualifier("accessService") @@ -100,6 +109,69 @@ public class CubeService extends BasicService { @Autowired private AclUtil aclUtil; + /* + * (non-Javadoc) + * + * @see + * org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + @SuppressWarnings("unchecked") + @Override + public void afterPropertiesSet() throws Exception { + Broadcaster.getInstance(getConfig()).registerListener(new HTableInfoSyncListener(), "cube"); + } + + private final LoadingCache htableInfoCache = CacheBuilder.newBuilder() + .maximumSize(CACHE_MAX_SIZE).expireAfterAccess(7, TimeUnit.DAYS) + .removalListener(new RemovalListener() { + @Override + public void onRemoval(RemovalNotification notification) { + logger.info("Hbase table: " + notification.getKey() + " cache is removed due to " + + notification.getCause()); + } + }).build(new CacheLoader() { + @Override + public HBaseResponse load(String tableName) throws Exception { + HBaseResponse hr = new HBaseResponse(); + if ("hbase".equals(getConfig().getMetadataUrl().getScheme())) { + try { + // use reflection to isolate NoClassDef errors when HBase is not available + hr = (HBaseResponse) Class.forName("org.apache.kylin.rest.service.HBaseInfoUtil")// + .getMethod("getHBaseInfo", new Class[] { String.class, String.class })// + .invoke(null, new Object[] { tableName, getConfig().getStorageUrl() }); + } catch (Throwable e) { + throw new IOException(e); + } + } + return hr; + } + }); + + private class HTableInfoSyncListener extends Broadcaster.Listener { + @Override + public void onClearAll(Broadcaster broadcaster) throws IOException { + htableInfoCache.invalidateAll(); + } + + @Override + public void onEntityChange(Broadcaster broadcaster, String entity, Broadcaster.Event event, String cacheKey) + throws IOException { + String cubeName = cacheKey; + + CubeInstance cube = getCubeManager().getCube(cubeName); + if (null == cube) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + + List htableNameList = Lists.newArrayListWithExpectedSize(cube.getSegments().size()); + for (CubeSegment segment : cube.getSegments()) { + htableNameList.add(segment.getStorageLocationIdentifier()); + } + + htableInfoCache.invalidateAll(htableNameList); + } + } + @PostFilter(Constant.ACCESS_POST_FILTER_READ) public List listAllCubes(final String cubeName, final String projectName, final String modelName, boolean exactMatch) { List cubeInstances = null; @@ -418,25 +490,8 @@ public class CubeService extends BasicService { * if error happens * @throws IOException Exception when HTable resource is not closed correctly. */ - public HBaseResponse getHTableInfo(String tableName) throws IOException { - if (htableInfoCache.containsKey(tableName)) { - return htableInfoCache.get(tableName); - } - - HBaseResponse hr = new HBaseResponse(); - if ("hbase".equals(getConfig().getMetadataUrl().getScheme())) { - try { - // use reflection to isolate NoClassDef errors when HBase is not available - hr = (HBaseResponse) Class.forName("org.apache.kylin.rest.service.HBaseInfoUtil")// - .getMethod("getHBaseInfo", new Class[] { String.class, String.class })// - .invoke(null, new Object[] { tableName, this.getConfig().getStorageUrl() }); - } catch (Throwable e) { - throw new IOException(e); - } - } - - htableInfoCache.put(tableName, hr); - return hr; + public HBaseResponse getHTableInfo(String tableName) throws IOException, ExecutionException { + return htableInfoCache.get(tableName); } @PreAuthorize(Constant.ACCESS_HAS_ROLE_ADMIN -- 2.5.4 (Apple Git-61)