diff --git common/src/java/org/apache/hadoop/hive/conf/HiveConf.java common/src/java/org/apache/hadoop/hive/conf/HiveConf.java index 8264b16..6398418 100644 --- common/src/java/org/apache/hadoop/hive/conf/HiveConf.java +++ common/src/java/org/apache/hadoop/hive/conf/HiveConf.java @@ -110,6 +110,7 @@ HiveConf.ConfVars.METASTORETHRIFTFAILURERETRIES, HiveConf.ConfVars.METASTORE_CLIENT_CONNECT_RETRY_DELAY, HiveConf.ConfVars.METASTORE_CLIENT_SOCKET_TIMEOUT, + HiveConf.ConfVars.METASTORE_SERVER_RUNNING_METHOD_TIMEOUT, HiveConf.ConfVars.METASTOREPWD, HiveConf.ConfVars.METASTORECONNECTURLHOOK, HiveConf.ConfVars.METASTORECONNECTURLKEY, @@ -163,7 +164,8 @@ */ public static final HiveConf.ConfVars[] metaConfVars = { HiveConf.ConfVars.METASTORE_TRY_DIRECT_SQL, - HiveConf.ConfVars.METASTORE_TRY_DIRECT_SQL_DDL + HiveConf.ConfVars.METASTORE_TRY_DIRECT_SQL_DDL, + HiveConf.ConfVars.METASTORE_SERVER_RUNNING_METHOD_TIMEOUT }; static { @@ -367,6 +369,9 @@ METASTORE_CLIENT_SOCKET_TIMEOUT("hive.metastore.client.socket.timeout", "600s", new TimeValidator(TimeUnit.SECONDS), "MetaStore Client socket timeout in seconds"), + METASTORE_SERVER_RUNNING_METHOD_TIMEOUT("hive.metastore.server.running.method.timeout", "600s", + new TimeValidator(TimeUnit.SECONDS), + "MetaStore Server long running method timeout in seconds"), METASTOREPWD("javax.jdo.option.ConnectionPassword", "mine", "password to use against metastore database"), METASTORECONNECTURLHOOK("hive.metastore.ds.connection.url.hook", "", diff --git metastore/src/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java metastore/src/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java index 2db90cc..29da6ad 100644 --- metastore/src/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java +++ metastore/src/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java @@ -234,6 +234,10 @@ // embedded metastore or a remote one private static boolean isMetaStoreRemote = false; + // Used for testing to simulate method timeout. + static boolean TESTING_TIMEOUT = false; + static long TESTING_TIMEOUT_VALUE = 5 * 1000; + /** A fixed date format to be used for hive partition column values. */ public static final ThreadLocal PARTITION_DATE_FORMAT = new ThreadLocal() { @@ -465,6 +469,7 @@ public void init() throws MetaException { hiveConf.getVar(HiveConf.ConfVars.METASTORE_PRE_EVENT_LISTENERS)); listeners = MetaStoreUtils.getMetaStoreListeners(MetaStoreEventListener.class, hiveConf, hiveConf.getVar(HiveConf.ConfVars.METASTORE_EVENT_LISTENERS)); + addSessionPropertiesListener(listeners, hiveConf); endFunctionListeners = MetaStoreUtils.getMetaStoreListeners( MetaStoreEndFunctionListener.class, hiveConf, hiveConf.getVar(HiveConf.ConfVars.METASTORE_END_FUNCTION_LISTENERS)); @@ -485,6 +490,10 @@ public void init() throws MetaException { } } + private void addSessionPropertiesListener(List listeners, HiveConf hiveConf) { + listeners.add(new SessionPropertiesListener(hiveConf)); + } + private String addPrefix(String s) { return threadLocalId.get() + ": " + s; } @@ -731,7 +740,7 @@ private void logInfo(String m) { logAuditEvent(m); } - private String startFunction(String function, String extraLogInfo) { + private String startFunction(String function, String extraLogInfo) throws MetaException { incrementCounter(function); logInfo((getIpAddress() == null ? "" : "source:" + getIpAddress() + " ") + function + extraLogInfo); @@ -741,47 +750,53 @@ private String startFunction(String function, String extraLogInfo) { LOG.debug("Exception when starting metrics scope" + e.getClass().getName() + " " + e.getMessage(), e); } + RuntimeTimeout.startCounting(function); + return function; } - private String startFunction(String function) { + private String startFunction(String function) throws MetaException { return startFunction(function, ""); } - private String startTableFunction(String function, String db, String tbl) { + private String startTableFunction(String function, String db, String tbl) throws MetaException { return startFunction(function, " : db=" + db + " tbl=" + tbl); } - private String startMultiTableFunction(String function, String db, List tbls) { + private String startMultiTableFunction(String function, String db, + List tbls) throws MetaException { String tableNames = join(tbls, ","); return startFunction(function, " : db=" + db + " tbls=" + tableNames); } private String startPartitionFunction(String function, String db, String tbl, - List partVals) { + List partVals) throws MetaException { return startFunction(function, " : db=" + db + " tbl=" + tbl + "[" + join(partVals, ",") + "]"); } private String startPartitionFunction(String function, String db, String tbl, - Map partName) { + Map partName) throws MetaException { return startFunction(function, " : db=" + db + " tbl=" + tbl + "partition=" + partName); } - private void endFunction(String function, boolean successful, Exception e) { + private void endFunction(String function, boolean successful, + Exception e) throws MetaException { endFunction(function, successful, e, null); } private void endFunction(String function, boolean successful, Exception e, - String inputTableName) { + String inputTableName) throws MetaException { endFunction(function, new MetaStoreEndFunctionContext(successful, e, inputTableName)); } - private void endFunction(String function, MetaStoreEndFunctionContext context) { + private void endFunction(String function, MetaStoreEndFunctionContext context) throws + MetaException { try { Metrics.endScope(function); } catch (IOException e) { LOG.debug("Exception when closing metrics scope" + e); } + RuntimeTimeout.endCounting(); for (MetaStoreEndFunctionListener listener : endFunctionListeners) { listener.onEndFunction(function, context); @@ -878,6 +893,15 @@ public void create_database(final Database db) // expected } + if (TESTING_TIMEOUT) { + try { + Thread.sleep(TESTING_TIMEOUT_VALUE); + } catch (InterruptedException e) { + // do nothing + } + RuntimeTimeout.checkTimeout(); + } + create_database_core(getMS(), db); success = true; } catch (Exception e) { diff --git metastore/src/java/org/apache/hadoop/hive/metastore/MetaStoreDirectSql.java metastore/src/java/org/apache/hadoop/hive/metastore/MetaStoreDirectSql.java index 564ac8b..4488447 100644 --- metastore/src/java/org/apache/hadoop/hive/metastore/MetaStoreDirectSql.java +++ metastore/src/java/org/apache/hadoop/hive/metastore/MetaStoreDirectSql.java @@ -493,6 +493,7 @@ private boolean isViewTable(String dbName, String tblName) throws MetaException @SuppressWarnings("unchecked") List sqlResult = executeWithArray(query, null, queryText); long queryTime = doTrace ? System.nanoTime() : 0; + RuntimeTimeout.checkTimeout(); // Read all the fields and create partitions, SDs and serdes. TreeMap partitions = new TreeMap(); @@ -581,6 +582,7 @@ private boolean isViewTable(String dbName, String tblName) throws MetaException serde.setSerializationLib((String)fields[13]); serdeSb.append(serdeId).append(","); sd.setSerdeInfo(serde); + RuntimeTimeout.checkTimeout(); } query.closeAll(); timingTrace(doTrace, queryText, start, queryTime); diff --git metastore/src/java/org/apache/hadoop/hive/metastore/RetryingHMSHandler.java metastore/src/java/org/apache/hadoop/hive/metastore/RetryingHMSHandler.java index 01ad36a..404aa07 100644 --- metastore/src/java/org/apache/hadoop/hive/metastore/RetryingHMSHandler.java +++ metastore/src/java/org/apache/hadoop/hive/metastore/RetryingHMSHandler.java @@ -86,6 +86,10 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg HiveConf.ConfVars.HMSHANDLERINTERVAL, TimeUnit.MILLISECONDS); int retryLimit = HiveConf.getIntVar(origConf, HiveConf.ConfVars.HMSHANDLERATTEMPTS); + long timeout = HiveConf.getTimeVar(origConf, + HiveConf.ConfVars.METASTORE_SERVER_RUNNING_METHOD_TIMEOUT, TimeUnit.MILLISECONDS); + + RuntimeTimeout.registerIfNot(timeout); if (reloadConf) { MetaStoreInit.updateConnectionURL(origConf, getActiveConf(), @@ -132,11 +136,20 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg LOG.error(ExceptionUtils.getStackTrace(e.getCause())); } throw e.getCause(); - } else if (e.getCause() instanceof MetaException && e.getCause().getCause() != null - && (e.getCause().getCause() instanceof javax.jdo.JDOException || - e.getCause().getCause() instanceof NucleusException)) { - // The JDOException or the Nucleus Exception may be wrapped further in a MetaException - caughtException = e.getCause().getCause(); + } else if (e.getCause() instanceof MetaException && e.getCause().getCause() != null) { + if (e.getCause().getCause() instanceof javax.jdo.JDOException || + e.getCause().getCause() instanceof NucleusException) { + // The JDOException or the Nucleus Exception may be wrapped further in a MetaException + caughtException = e.getCause().getCause(); + } else if (e.getCause().getCause() instanceof RuntimeTimeoutException) { + // The RuntimeTimeout Exception needs no retry and be thrown immediately. + RuntimeTimeout.clear(); + LOG.error(ExceptionUtils.getStackTrace(e.getCause())); + throw e.getCause(); + } else { + LOG.error(ExceptionUtils.getStackTrace(e.getCause())); + throw e.getCause(); + } } else { LOG.error(ExceptionUtils.getStackTrace(e.getCause())); throw e.getCause(); diff --git metastore/src/java/org/apache/hadoop/hive/metastore/RuntimeTimeout.java metastore/src/java/org/apache/hadoop/hive/metastore/RuntimeTimeout.java new file mode 100644 index 0000000..0e8ae24 --- /dev/null +++ metastore/src/java/org/apache/hadoop/hive/metastore/RuntimeTimeout.java @@ -0,0 +1,176 @@ +/** + * 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.hadoop.hive.metastore; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hive.metastore.api.MetaException; + +/** + * This class is used to monitor long running methods in a thread. + * It is recommended to use it as a ThreadLocal variable. + */ +public class RuntimeTimeout { + private static final Log LOG = LogFactory.getLog(RuntimeTimeout.class.getName()); + + /** + * its value is init from conf, and could be reset from client. + */ + private long timeout; + + /** + * it is reset before executing a method + */ + private long startTime = -1; + + /** + * The name of public methods in HMSHandler + */ + private String method; + + private RuntimeTimeout(long timeout) { + this.timeout = timeout; + } + + /** + * RuntimeTimeout object per thread. + */ + private static final ThreadLocal RUNTIME_TIMEOUT_THREAD_LOCAL = new + ThreadLocal() { + @Override + protected synchronized RuntimeTimeout initialValue() { + return null; + } + }; + + static void setCurrentRuntimeTimeout(RuntimeTimeout runtimeTimeout) { + RUNTIME_TIMEOUT_THREAD_LOCAL.set(runtimeTimeout); + } + + static RuntimeTimeout getCurrentRuntimeTimeout() { + return RUNTIME_TIMEOUT_THREAD_LOCAL.get(); + } + + static void removeCurrentRuntimeTimeout() { + RUNTIME_TIMEOUT_THREAD_LOCAL.remove(); + } + + /** + * register a RuntimeTimeout threadlocal object to current thread. + * @param timeout + */ + public static void registerIfNot(long timeout) { + if (getCurrentRuntimeTimeout() == null) { + setCurrentRuntimeTimeout(new RuntimeTimeout(timeout)); + } + } + + /** + * reset the timeout value of this timer. + * @param timeout + */ + public static void resetTimeout(long timeout) throws MetaException { + if (timeout <= 0) { + throw newMetaException(new RuntimeTimeoutException("The reset timeout value should be " + + "larger than 0: " + timeout)); + } + RuntimeTimeout runtimeTimeout = getCurrentRuntimeTimeout(); + if (runtimeTimeout != null) { + runtimeTimeout.timeout = timeout; + } else { + throw newMetaException(new RuntimeTimeoutException("The threadlocal RuntimeTimeout is null," + + " please register it firstly.")); + } + } + + /** + * start the timer before a method is invoked. + * @param method + */ + public static void startCounting(String method) throws MetaException { + RuntimeTimeout runtimeTimeout = getCurrentRuntimeTimeout(); + if (runtimeTimeout != null) { + runtimeTimeout.startTime = System.currentTimeMillis(); + runtimeTimeout.method = method; + } else { + throw newMetaException(new RuntimeTimeoutException("The threadlocal RuntimeTimeout is null," + + " please register it firstly.")); + } + } + + /** + * end the time after a method is done. + */ + public static void endCounting() throws MetaException { + RuntimeTimeout runtimeTimeout = getCurrentRuntimeTimeout(); + if (runtimeTimeout != null) { + runtimeTimeout.startTime = -1; + runtimeTimeout.method = ""; + } else { + throw newMetaException(new RuntimeTimeoutException("The threadlocal RuntimeTimeout is null," + + " please register it firstly.")); + } + } + + /** + * remove the registered RuntimeTimeout threadlocal object from current thread. + */ + public static void clear() { + removeCurrentRuntimeTimeout(); + } + + /** + * Check whether the long running method timeout. + * @throws RuntimeTimeoutException when the method timeout + */ + public static void checkTimeout() throws MetaException { + RuntimeTimeout runtimeTimeout = getCurrentRuntimeTimeout(); + if (runtimeTimeout != null) { + runtimeTimeout.check(); + } else { + throw newMetaException(new RuntimeTimeoutException("The threadlocal RuntimeTimeout is null," + + " please register it firstly.")); + } + } + + private void check() throws MetaException{ + try { + if (startTime < 0) { + throw new RuntimeTimeoutException("Should execute startCounting() method before " + + "checkTimeout. Error happens in method: " + method); + } + if (startTime + timeout < System.currentTimeMillis()) { + throw new RuntimeTimeoutException("Timeout when execute method: " + method); + } + } catch (RuntimeTimeoutException e) { + throw newMetaException(e); + } + } + + /** + * convert RuntimeTimeoutException to MetaException + * @param e + * @return + */ + private static MetaException newMetaException(RuntimeTimeoutException e) { + MetaException metaException = new MetaException(e.getMessage()); + metaException.initCause(e); + return metaException; + } +} diff --git metastore/src/java/org/apache/hadoop/hive/metastore/RuntimeTimeoutException.java metastore/src/java/org/apache/hadoop/hive/metastore/RuntimeTimeoutException.java new file mode 100644 index 0000000..1cffd01 --- /dev/null +++ metastore/src/java/org/apache/hadoop/hive/metastore/RuntimeTimeoutException.java @@ -0,0 +1,29 @@ +/** + * 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.hadoop.hive.metastore; + +/** + * Thrown when a long running method timeout is checked. + */ +public class RuntimeTimeoutException extends Exception { + + public RuntimeTimeoutException(String message) { + super(message); + } +} diff --git metastore/src/java/org/apache/hadoop/hive/metastore/SessionPropertiesListener.java metastore/src/java/org/apache/hadoop/hive/metastore/SessionPropertiesListener.java new file mode 100644 index 0000000..e0faafb --- /dev/null +++ metastore/src/java/org/apache/hadoop/hive/metastore/SessionPropertiesListener.java @@ -0,0 +1,46 @@ +/** + * 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.hadoop.hive.metastore; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hive.conf.HiveConf; +import org.apache.hadoop.hive.metastore.api.MetaException; +import org.apache.hadoop.hive.metastore.events.ConfigChangeEvent; + +import java.util.concurrent.TimeUnit; + +/** + * It handles the changed properties in the change event. + */ +public class SessionPropertiesListener extends MetaStoreEventListener { + + public SessionPropertiesListener(Configuration configuration) { + super(configuration); + } + + @Override + public void onConfigChange(ConfigChangeEvent changeEvent) throws MetaException { + // case when key is hive.metastore.server.running.method.timeout + if (changeEvent.getKey().equals(HiveConf.ConfVars.METASTORE_SERVER_RUNNING_METHOD_TIMEOUT + .varname)) { + RuntimeTimeout.resetTimeout(HiveConf.toTime(changeEvent.getNewValue(), TimeUnit.SECONDS, + TimeUnit.MILLISECONDS)); + } + } +} diff --git metastore/src/test/org/apache/hadoop/hive/metastore/TestHiveMetaStoreTimeout.java metastore/src/test/org/apache/hadoop/hive/metastore/TestHiveMetaStoreTimeout.java new file mode 100644 index 0000000..017656e --- /dev/null +++ metastore/src/test/org/apache/hadoop/hive/metastore/TestHiveMetaStoreTimeout.java @@ -0,0 +1,141 @@ +/** + * 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.hadoop.hive.metastore; + +import junit.framework.TestCase; +import org.apache.hadoop.hive.conf.HiveConf; +import org.apache.hadoop.hive.metastore.api.Database; +import org.apache.hadoop.hive.metastore.api.MetaException; +import org.apache.hadoop.util.StringUtils; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +/** + * Test long running request timeout functionality in MetaStore Server + * HiveMetaStore.HMSHandler.create_database() is used to simulate a long running method. + */ +public class TestHiveMetaStoreTimeout extends TestCase{ + protected static HiveMetaStoreClient client; + protected static HiveConf hiveConf; + protected static Warehouse warehouse; + + @Override + protected void setUp() throws Exception { + HiveMetaStore.TESTING_TIMEOUT = true; + hiveConf = new HiveConf(this.getClass()); + hiveConf.setBoolean(HiveConf.ConfVars.HIVE_WAREHOUSE_SUBDIR_INHERIT_PERMS.varname, true); + hiveConf.set(HiveConf.ConfVars.METASTORE_EXPRESSION_PROXY_CLASS.varname, + MockPartitionExpressionForMetastore.class.getCanonicalName()); + hiveConf.setTimeVar(HiveConf.ConfVars.METASTORE_SERVER_RUNNING_METHOD_TIMEOUT, 10 * 1000, + TimeUnit.MILLISECONDS); + warehouse = new Warehouse(hiveConf); + try { + client = new HiveMetaStoreClient(hiveConf, null); + } catch (Throwable e) { + System.err.println("Unable to open the metastore"); + System.err.println(StringUtils.stringifyException(e)); + throw new Exception(e); + } + } + + @Override + protected void tearDown() throws Exception { + HiveMetaStore.TESTING_TIMEOUT = false; + try { + client.close(); + } catch (Throwable e) { + System.err.println("Unable to close metastore"); + System.err.println(StringUtils.stringifyException(e)); + throw new Exception(e); + } + } + + @Test + public void testNoTimeout() throws Exception { + HiveMetaStore.TESTING_TIMEOUT_VALUE = 5 * 1000; + + String dbName = "db"; + client.dropDatabase(dbName, true, true); + + Database db = new Database(); + db.setName(dbName); + try { + client.createDatabase(db); + } catch (MetaException e) { + fail("should not throw timeout exception: " + e.getMessage()); + } + + client.dropDatabase(dbName, true, true); + } + + @Test + public void testTimeout() throws Exception { + HiveMetaStore.TESTING_TIMEOUT_VALUE = 15 * 1000; + + String dbName = "db"; + client.dropDatabase(dbName, true, true); + + Database db = new Database(); + db.setName(dbName); + try { + client.createDatabase(db); + fail("should throw timeout exception."); + } catch (MetaException e) { + assertTrue("unexpected MetaException", e.getMessage().contains("Timeout when execute " + + "method: create_database")); + } + + // restore + HiveMetaStore.TESTING_TIMEOUT_VALUE = 5 * 1000; + } + + @Test + public void testResetTimeout() throws Exception { + HiveMetaStore.TESTING_TIMEOUT_VALUE = 5 * 1000; + String dbName = "db"; + + // no timeout before reset + client.dropDatabase(dbName, true, true); + Database db = new Database(); + db.setName(dbName); + try { + client.createDatabase(db); + } catch (MetaException e) { + fail("should not throw timeout exception: " + e.getMessage()); + } + client.dropDatabase(dbName, true, true); + + // reset + client.setMetaConf("hive.metastore.server.running.method.timeout", "3s"); + + // timeout after reset + try { + client.createDatabase(db); + fail("should throw timeout exception."); + } catch (MetaException e) { + assertTrue("unexpected MetaException", e.getMessage().contains("Timeout when execute " + + "method: create_database")); + } + + // restore + client.dropDatabase(dbName, true, true); + client.setMetaConf("hive.metastore.server.running.method.timeout", "10s"); + } +} \ No newline at end of file diff --git metastore/src/test/org/apache/hadoop/hive/metastore/TestRuntimeTimeout.java metastore/src/test/org/apache/hadoop/hive/metastore/TestRuntimeTimeout.java new file mode 100644 index 0000000..adf7f41 --- /dev/null +++ metastore/src/test/org/apache/hadoop/hive/metastore/TestRuntimeTimeout.java @@ -0,0 +1,117 @@ +/** + * 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.hadoop.hive.metastore; + +import junit.framework.TestCase; +import org.apache.hadoop.hive.metastore.api.MetaException; +import org.junit.Test; + +/** + * Test the RuntimeTimeout class used for long running requests. + */ +public class TestRuntimeTimeout extends TestCase{ + + private long timeout = 1000; + private long resetTimeout = 200; + private long duration = 500; + private boolean isFailed; + private String errorMsg; + + @Test + public void testRuntimeTimeout() throws Exception { + isFailed = false; + errorMsg = ""; + + Thread threadTimeout = new Thread(createRunnable()); + threadTimeout.setDaemon(true); + threadTimeout.start(); + threadTimeout.join(60000); + + if (isFailed) { + fail(errorMsg); + } + } + + private Runnable createRunnable() { + return new Runnable() { + @Override + public void run() { + RuntimeTimeout.registerIfNot(timeout); + try { + // normal + start(); + try { + RuntimeTimeout.checkTimeout(); + } catch (MetaException e) { + failInThread("should not timeout"); + return; + } + RuntimeTimeout.endCounting(); + + // normal. Check endCounting() works. + start(); + try { + RuntimeTimeout.checkTimeout(); + } catch (MetaException e) { + failInThread("should not timeout"); + return; + } + RuntimeTimeout.endCounting(); + + // reset + RuntimeTimeout.resetTimeout(resetTimeout); + + // timeout + start(); + try { + RuntimeTimeout.checkTimeout(); + failInThread("should timeout."); + return; + } catch (MetaException e) { + if (e.getCause() instanceof RuntimeTimeoutException) { + RuntimeTimeout.clear(); + } else { + failInThread("new MetaException failed."); + return; + } + } + if (RuntimeTimeout.getCurrentRuntimeTimeout() != null) { + failInThread("the threadlocal object should be removed after timeout."); + } + } catch (MetaException e) { + failInThread("Odd."); + } + } + }; + } + + private void start() throws MetaException { + RuntimeTimeout.startCounting("test"); + try { + Thread.sleep(duration); + } catch (InterruptedException e) { + failInThread("Odd. Sleeping thread is interrupted."); + } + } + + private void failInThread(String msg) { + isFailed = true; + errorMsg = msg; + } +}