/* * Copyright © 2014 Cask Data, Inc. * * Licensed 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 co.cask.cdap.explore.service; import co.cask.cdap.api.dataset.DatasetDefinition; import co.cask.cdap.api.dataset.DatasetProperties; import co.cask.cdap.common.conf.CConfiguration; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.explore.service.datasets.KeyStructValueTableDefinition; import co.cask.cdap.proto.ColumnDesc; import co.cask.cdap.proto.QueryHandle; import co.cask.cdap.proto.QueryStatus; import co.cask.cdap.test.XSlowTests; import co.cask.tephra.Transaction; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TemporaryFolder; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; /** * Tests timeout of operations in HiveExploreService. */ @Category(XSlowTests.class) public class HiveExploreServiceTimeoutTest extends BaseHiveExploreServiceTest { private static final long ACTIVE_OPERATION_TIMEOUT_SECS = 8; private static final long INACTIVE_OPERATION_TIMEOUT_SECS = 5; private static final long CLEANUP_JOB_SCHEDULE_SECS = 1; @ClassRule public static TemporaryFolder tmpFolder = new TemporaryFolder(); private static ExploreService exploreService; @BeforeClass public static void start() throws Exception { // Set smaller values for timeouts for testing CConfiguration cConfiguration = CConfiguration.create(); cConfiguration.setLong(Constants.Explore.ACTIVE_OPERATION_TIMEOUT_SECS, ACTIVE_OPERATION_TIMEOUT_SECS); cConfiguration.setLong(Constants.Explore.INACTIVE_OPERATION_TIMEOUT_SECS, INACTIVE_OPERATION_TIMEOUT_SECS); cConfiguration.setLong(Constants.Explore.CLEANUP_JOB_SCHEDULE_SECS, CLEANUP_JOB_SCHEDULE_SECS); initialize(cConfiguration, tmpFolder); exploreService = injector.getInstance(ExploreService.class); datasetFramework.addModule(KEY_STRUCT_VALUE, new KeyStructValueTableDefinition.KeyStructValueTableModule()); // Performing admin operations to create dataset instance datasetFramework.addInstance("keyStructValueTable", MY_TABLE, DatasetProperties.EMPTY); // Accessing dataset instance to perform data operations KeyStructValueTableDefinition.KeyStructValueTable table = datasetFramework.getDataset(MY_TABLE, DatasetDefinition.NO_ARGUMENTS, null); Assert.assertNotNull(table); Transaction tx1 = transactionManager.startShort(100); table.startTx(tx1); KeyStructValueTableDefinition.KeyValue.Value value1 = new KeyStructValueTableDefinition.KeyValue.Value("first", Lists.newArrayList(1, 2, 3, 4, 5)); KeyStructValueTableDefinition.KeyValue.Value value2 = new KeyStructValueTableDefinition.KeyValue.Value("two", Lists.newArrayList(10, 11, 12, 13, 14)); table.put("1", value1); table.put("2", value2); Assert.assertEquals(value1, table.get("1")); Assert.assertTrue(table.commitTx()); transactionManager.canCommit(tx1, table.getTxChanges()); transactionManager.commit(tx1); table.postTxCommit(); Transaction tx2 = transactionManager.startShort(100); table.startTx(tx2); Assert.assertEquals(value1, table.get("1")); } @AfterClass public static void stop() throws Exception { datasetFramework.deleteInstance(MY_TABLE); datasetFramework.deleteModule(KEY_STRUCT_VALUE); } @Test public void testTimeoutRunning() throws Exception { Set<Long> beforeTxns = transactionManager.getCurrentState().getInProgress().keySet(); QueryHandle handle = exploreService.execute(NAMESPACE_ID, "select key, value from " + MY_TABLE_NAME); Set<Long> queryTxns = Sets.difference(transactionManager.getCurrentState().getInProgress().keySet(), beforeTxns); Assert.assertFalse(queryTxns.isEmpty()); // Sleep for timeout to happen TimeUnit.SECONDS.sleep(ACTIVE_OPERATION_TIMEOUT_SECS + 3); try { exploreService.getStatus(handle); Assert.fail("Should throw HandleNotFoundException due to operation timeout"); } catch (HandleNotFoundException e) { // Expected exception due to timeout } // Make sure that the transaction got closed Assert.assertEquals(ImmutableSet.<Long>of(), Sets.intersection( queryTxns, transactionManager.getCurrentState().getInProgress().keySet()).immutableCopy() ); } @Test public void testTimeoutFetchAllResults() throws Exception { Set<Long> beforeTxns = transactionManager.getCurrentState().getInProgress().keySet(); QueryHandle handle = exploreService.execute(NAMESPACE_ID, "select key, value from " + MY_TABLE_NAME); Set<Long> queryTxns = Sets.difference(transactionManager.getCurrentState().getInProgress().keySet(), beforeTxns); Assert.assertFalse(queryTxns.isEmpty()); QueryStatus status = waitForCompletionStatus(handle, 200, TimeUnit.MILLISECONDS, 20); Assert.assertEquals(QueryStatus.OpStatus.FINISHED, status.getStatus()); Assert.assertTrue(status.hasResults()); List<ColumnDesc> schema = exploreService.getResultSchema(handle); // Fetch all results while (!exploreService.nextResults(handle, 100).isEmpty()) { // nothing to do } // Sleep for some time for txn to get closed TimeUnit.SECONDS.sleep(1); // Make sure that the transaction got closed Assert.assertEquals(ImmutableSet.<Long>of(), Sets.intersection( queryTxns, transactionManager.getCurrentState().getInProgress().keySet()).immutableCopy() ); // Check if calls using inactive handle still work Assert.assertEquals(status, exploreService.getStatus(handle)); Assert.assertEquals(schema, exploreService.getResultSchema(handle)); exploreService.close(handle); // Sleep for timeout to happen TimeUnit.SECONDS.sleep(INACTIVE_OPERATION_TIMEOUT_SECS + 3); try { exploreService.getStatus(handle); Assert.fail("Should throw HandleNotFoundException due to operation cleanup"); } catch (HandleNotFoundException e) { // Expected exception due to timeout } } @Test public void testTimeoutNoResults() throws Exception { Set<Long> beforeTxns = transactionManager.getCurrentState().getInProgress().keySet(); QueryHandle handle = exploreService.execute(NAMESPACE_ID, "drop table if exists not_existing_table_name"); Set<Long> queryTxns = Sets.difference(transactionManager.getCurrentState().getInProgress().keySet(), beforeTxns); Assert.assertFalse(queryTxns.isEmpty()); QueryStatus status = waitForCompletionStatus(handle, 200, TimeUnit.MILLISECONDS, 20); Assert.assertEquals(QueryStatus.OpStatus.FINISHED, status.getStatus()); Assert.assertFalse(status.hasResults()); List<ColumnDesc> schema = exploreService.getResultSchema(handle); // Sleep for some time for txn to get closed TimeUnit.SECONDS.sleep(1); // Make sure that the transaction got closed Assert.assertEquals(ImmutableSet.<Long>of(), Sets.intersection( queryTxns, transactionManager.getCurrentState().getInProgress().keySet()).immutableCopy() ); // Check if calls using inactive handle still work Assert.assertEquals(status, exploreService.getStatus(handle)); Assert.assertEquals(schema, exploreService.getResultSchema(handle)); exploreService.close(handle); // Sleep for timeout to happen TimeUnit.SECONDS.sleep(INACTIVE_OPERATION_TIMEOUT_SECS + 3); try { exploreService.getStatus(handle); Assert.fail("Should throw HandleNotFoundException due to operation cleanup"); } catch (HandleNotFoundException e) { // Expected exception due to timeout } } @Test public void testCloseQuery() throws Exception { QueryHandle handle = exploreService.execute(NAMESPACE_ID, "drop table if exists not_existing_table_name"); exploreService.close(handle); try { exploreService.getStatus(handle); Assert.fail("Should throw HandleNotFoundException due to operation cleanup"); } catch (HandleNotFoundException e) { // Expected exception due to timeout } } }