You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by vg...@apache.org on 2015/10/08 22:07:26 UTC
hive git commit: HIVE-11408: HiveServer2 is leaking ClassLoaders when
add jar / temporary functions are used due to constructor caching in Hadoop
ReflectionUtils (Vaibhav Gumashta reviewed by Thejas Nair)
Repository: hive
Updated Branches:
refs/heads/branch-1.1.1 3e8d832a1 -> b1ca4e3cd
HIVE-11408: HiveServer2 is leaking ClassLoaders when add jar / temporary functions are used due to constructor caching in Hadoop ReflectionUtils (Vaibhav Gumashta reviewed by Thejas Nair)
Project: http://git-wip-us.apache.org/repos/asf/hive/repo
Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/b1ca4e3c
Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/b1ca4e3c
Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/b1ca4e3c
Branch: refs/heads/branch-1.1.1
Commit: b1ca4e3cdb90932189d35a1e20e02a7bd0334920
Parents: 3e8d832
Author: Vaibhav Gumashta <vg...@apache.org>
Authored: Thu Oct 8 13:07:20 2015 -0700
Committer: Vaibhav Gumashta <vg...@apache.org>
Committed: Thu Oct 8 13:07:20 2015 -0700
----------------------------------------------------------------------
.../apache/hive/jdbc/TestJdbcWithMiniHS2.java | 71 ++++++++++++++++++--
.../hadoop/hive/ql/session/SessionState.java | 21 +++++-
2 files changed, 86 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hive/blob/b1ca4e3c/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcWithMiniHS2.java
----------------------------------------------------------------------
diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcWithMiniHS2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcWithMiniHS2.java
index 5087f87..c470c80 100644
--- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcWithMiniHS2.java
+++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcWithMiniHS2.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
@@ -39,16 +40,20 @@ import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hadoop.hive.ql.exec.FunctionRegistry;
+import org.apache.hadoop.hive.ql.session.SessionState;
+import org.apache.hadoop.util.ReflectionUtils;
import org.apache.hive.jdbc.miniHS2.MiniHS2;
import org.junit.After;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestJdbcWithMiniHS2 {
private static MiniHS2 miniHS2 = null;
- private static Path dataFilePath;
+ private static String dataFileDir;
+ private static Path kvDataFilePath;
private static final String tmpDir = System.getProperty("test.tmp.dir");
private Connection hs2Conn = null;
@@ -59,9 +64,8 @@ public class TestJdbcWithMiniHS2 {
HiveConf conf = new HiveConf();
conf.setBoolVar(ConfVars.HIVE_SUPPORT_CONCURRENCY, false);
miniHS2 = new MiniHS2(conf);
- String dataFileDir = conf.get("test.data.files").replace('\\', '/')
- .replace("c:", "");
- dataFilePath = new Path(dataFileDir, "kv1.txt");
+ dataFileDir = conf.get("test.data.files").replace('\\', '/').replace("c:", "");
+ kvDataFilePath = new Path(dataFileDir, "kv1.txt");
Map<String, String> confOverlay = new HashMap<String, String>();
miniHS2.start(confOverlay);
}
@@ -101,7 +105,7 @@ public class TestJdbcWithMiniHS2 {
// load data
stmt.execute("load data local inpath '"
- + dataFilePath.toString() + "' into table " + tableName);
+ + kvDataFilePath.toString() + "' into table " + tableName);
ResultSet res = stmt.executeQuery("SELECT * FROM " + tableName);
assertTrue(res.next());
@@ -545,4 +549,61 @@ public class TestJdbcWithMiniHS2 {
fs.getFileStatus(scratchDirPath).getPermission());
}
}
+
+ /**
+ * Tests that Hadoop's ReflectionUtils.CONSTRUCTOR_CACHE clears cached class objects (& hence
+ * doesn't leak classloaders) on closing any session
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testAddJarConstructorUnCaching() throws Exception {
+ Path jarFilePath = new Path(dataFileDir, "identity_udf.jar");
+ Connection conn = getConnection(miniHS2.getJdbcURL(), "foo", "bar");
+ String tableName = "testAddJar";
+ Statement stmt = conn.createStatement();
+ stmt.execute("SET hive.support.concurrency = false");
+ // Create table
+ stmt.execute("DROP TABLE IF EXISTS " + tableName);
+ stmt.execute("CREATE TABLE " + tableName + " (key INT, value STRING)");
+ // Load data
+ stmt.execute("LOAD DATA LOCAL INPATH '" + kvDataFilePath.toString() + "' INTO TABLE "
+ + tableName);
+ ResultSet res = stmt.executeQuery("SELECT * FROM " + tableName);
+ // Ensure table is populated
+ assertTrue(res.next());
+
+ int cacheBeforeClose;
+ int cacheAfterClose;
+ // Add the jar file
+ stmt.execute("ADD JAR " + jarFilePath.toString());
+ // Create a temporary function using the jar
+ stmt.execute("CREATE TEMPORARY FUNCTION func AS 'IdentityStringUDF'");
+ // Execute the UDF
+ stmt.execute("SELECT func(value) from " + tableName);
+ cacheBeforeClose = getReflectionUtilsCacheSize();
+ System.out.println("Cache before connection close: " + cacheBeforeClose);
+ // Cache size should be > 0 now
+ Assert.assertTrue(cacheBeforeClose > 0);
+ conn.close();
+ cacheAfterClose = getReflectionUtilsCacheSize();
+ System.out.println("Cache after connection close: " + cacheAfterClose);
+ // Cache size should be 0 now
+ Assert.assertTrue("Failed: " + cacheAfterClose, cacheAfterClose == 0);
+ }
+
+ // Call ReflectionUtils#getCacheSize (which is private)
+ private int getReflectionUtilsCacheSize() {
+ Method getCacheSizeMethod;
+ try {
+ getCacheSizeMethod = ReflectionUtils.class.getDeclaredMethod("getCacheSize");
+ if (getCacheSizeMethod != null) {
+ getCacheSizeMethod.setAccessible(true);
+ return (Integer) getCacheSizeMethod.invoke(null);
+ }
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ return -1;
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hive/blob/b1ca4e3c/ql/src/java/org/apache/hadoop/hive/ql/session/SessionState.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/session/SessionState.java b/ql/src/java/org/apache/hadoop/hive/ql/session/SessionState.java
index c315985..70d35e6 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/session/SessionState.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/session/SessionState.java
@@ -23,6 +23,8 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URI;
import java.net.URLClassLoader;
import java.util.*;
@@ -1299,8 +1301,25 @@ public class SessionState {
sparkSession = null;
}
}
-
dropSessionPaths(conf);
+ // Hadoop's ReflectionUtils caches constructors for the classes it instantiated.
+ // In UDFs, this can result in classloaders not getting GCed for a temporary function,
+ // resulting in a PermGen leak when used extensively from HiveServer2
+ clearReflectionUtilsCache();
+ }
+
+ private void clearReflectionUtilsCache() {
+ Method clearCacheMethod;
+ try {
+ clearCacheMethod = ReflectionUtils.class.getDeclaredMethod("clearCache");
+ if (clearCacheMethod != null) {
+ clearCacheMethod.setAccessible(true);
+ clearCacheMethod.invoke(null);
+ LOG.debug("Cleared Hadoop ReflectionUtils CONSTRUCTOR_CACHE");
+ }
+ } catch (Exception e) {
+ LOG.info(e);
+ }
}
public AuthorizationMode getAuthorizationMode(){