/** * 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.hive.hcatalog.common; import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.metastore.HiveMetaStore; import org.apache.hadoop.hive.metastore.IMetaStoreClient; import org.apache.hadoop.hive.metastore.api.AlreadyExistsException; import org.apache.hadoop.hive.metastore.api.Database; import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.hadoop.hive.metastore.api.InvalidObjectException; import org.apache.hadoop.hive.metastore.api.MetaException; import org.apache.hadoop.hive.metastore.api.NoSuchObjectException; import org.apache.hadoop.hive.metastore.api.SerDeInfo; import org.apache.hadoop.hive.metastore.api.StorageDescriptor; import org.apache.hadoop.hive.metastore.api.Table; import org.apache.hadoop.hive.serde.serdeConstants; import org.apache.hive.hcatalog.NoExitSecurityManager; import org.apache.hive.hcatalog.cli.SemanticAnalysis.HCatSemanticAnalyzer; import org.apache.thrift.TException; import org.junit.AfterClass; import org.junit.BeforeClass; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.security.auth.login.LoginException; import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class TestHiveClientCache { private static final Logger LOG = LoggerFactory.getLogger(TestHiveClientCache.class); final HiveConf hiveConf = new HiveConf(); @BeforeClass public static void setUp() throws Exception { } @AfterClass public static void tearDown() throws Exception { } @Test public void testCacheHit() throws IOException, MetaException, LoginException { HiveClientCache cache = new HiveClientCache(1000); HiveClientCache.ICacheableMetaStoreClient client = (HiveClientCache.ICacheableMetaStoreClient) cache.get(hiveConf); assertNotNull(client); client.close(); // close shouldn't matter // Setting a non important configuration should return the same client only hiveConf.setIntVar(HiveConf.ConfVars.DYNAMICPARTITIONMAXPARTS, 10); HiveClientCache.ICacheableMetaStoreClient client2 = (HiveClientCache.ICacheableMetaStoreClient) cache.get(hiveConf); assertNotNull(client2); assertSame(client, client2); assertEquals(client.getUsers(), client2.getUsers()); client2.close(); } @Test public void testCacheMiss() throws IOException, MetaException, LoginException { HiveClientCache cache = new HiveClientCache(1000); IMetaStoreClient client = cache.get(hiveConf); assertNotNull(client); // Set different uri as it is one of the criteria deciding whether to return the same client or not hiveConf.setVar(HiveConf.ConfVars.METASTOREURIS, " "); // URIs are checked for string equivalence, even spaces make them different IMetaStoreClient client2 = cache.get(hiveConf); assertNotNull(client2); assertNotSame(client, client2); } /** * Check that a new client is returned for the same configuration after the expiry time. * Also verify that the expiry time configuration is honoured */ @Test public void testCacheExpiry() throws IOException, MetaException, LoginException, InterruptedException { HiveClientCache cache = new HiveClientCache(1); HiveClientCache.ICacheableMetaStoreClient client = (HiveClientCache.ICacheableMetaStoreClient) cache.get(hiveConf); assertNotNull(client); Thread.sleep(2500); HiveClientCache.ICacheableMetaStoreClient client2 = (HiveClientCache.ICacheableMetaStoreClient) cache.get(hiveConf); client.close(); assertTrue(client.isClosed()); // close() after *expiry time* and *a cache access* should have tore down the client assertNotNull(client2); assertNotSame(client, client2); } /** * Check that a *new* client is created if asked from different threads even with * the same hive configuration * @throws ExecutionException * @throws InterruptedException */ @Test public void testMultipleThreadAccess() throws ExecutionException, InterruptedException { final HiveClientCache cache = new HiveClientCache(1000); class GetHiveClient implements Callable<IMetaStoreClient> { @Override public IMetaStoreClient call() throws IOException, MetaException, LoginException { return cache.get(hiveConf); } } ExecutorService executor = Executors.newFixedThreadPool(2); Callable<IMetaStoreClient> worker1 = new GetHiveClient(); Callable<IMetaStoreClient> worker2 = new GetHiveClient(); Future<IMetaStoreClient> clientFuture1 = executor.submit(worker1); Future<IMetaStoreClient> clientFuture2 = executor.submit(worker2); IMetaStoreClient client1 = clientFuture1.get(); IMetaStoreClient client2 = clientFuture2.get(); assertNotNull(client1); assertNotNull(client2); assertNotSame(client1, client2); } @Test public void testCloseAllClients() throws IOException, MetaException, LoginException { final HiveClientCache cache = new HiveClientCache(1000); HiveClientCache.ICacheableMetaStoreClient client1 = (HiveClientCache.ICacheableMetaStoreClient) cache.get(hiveConf); hiveConf.setVar(HiveConf.ConfVars.METASTOREURIS, " "); // URIs are checked for string equivalence, even spaces make them different HiveClientCache.ICacheableMetaStoreClient client2 = (HiveClientCache.ICacheableMetaStoreClient) cache.get(hiveConf); cache.closeAllClientsQuietly(); assertTrue(client1.isClosed()); assertTrue(client2.isClosed()); } /** * Test that a long table name actually breaks the HMSC. Subsequently check that isOpen() reflects * and tells if the client is broken */ @Ignore("hangs indefinitely") @Test public void testHMSCBreakability() throws IOException, MetaException, LoginException, TException, AlreadyExistsException, InvalidObjectException, NoSuchObjectException, InterruptedException { // Setup LocalMetaServer metaServer = new LocalMetaServer(); metaServer.start(); final HiveClientCache cache = new HiveClientCache(1000); HiveClientCache.CacheableHiveMetaStoreClient client = (HiveClientCache.CacheableHiveMetaStoreClient) cache.get(metaServer.getHiveConf()); assertTrue(client.isOpen()); final String DB_NAME = "test_db"; final String LONG_TABLE_NAME = "long_table_name_" + new BigInteger(200, new Random()).toString(2); try { client.dropTable(DB_NAME, LONG_TABLE_NAME); } catch (Exception e) { } try { client.dropDatabase(DB_NAME); } catch (Exception e) { } client.createDatabase(new Database(DB_NAME, "", null, null)); List<FieldSchema> fields = new ArrayList<FieldSchema>(); fields.add(new FieldSchema("colname", serdeConstants.STRING_TYPE_NAME, "")); Table tbl = new Table(); tbl.setDbName(DB_NAME); tbl.setTableName(LONG_TABLE_NAME); StorageDescriptor sd = new StorageDescriptor(); sd.setCols(fields); tbl.setSd(sd); sd.setSerdeInfo(new SerDeInfo()); // Break the client try { client.createTable(tbl); fail("Exception was expected while creating table with long name"); } catch (Exception e) { } assertFalse(client.isOpen()); metaServer.shutDown(); } private static class LocalMetaServer implements Runnable { public final int MS_PORT = 20101; private final HiveConf hiveConf; private final SecurityManager securityManager; public final static int WAIT_TIME_FOR_BOOTUP = 30000; public LocalMetaServer() { securityManager = System.getSecurityManager(); System.setSecurityManager(new NoExitSecurityManager()); hiveConf = new HiveConf(TestHiveClientCache.class); hiveConf.setVar(HiveConf.ConfVars.METASTOREURIS, "thrift://localhost:" + MS_PORT); hiveConf.setIntVar(HiveConf.ConfVars.METASTORETHRIFTCONNECTIONRETRIES, 3); hiveConf.setIntVar(HiveConf.ConfVars.METASTORETHRIFTFAILURERETRIES, 3); hiveConf.set(HiveConf.ConfVars.SEMANTIC_ANALYZER_HOOK.varname, HCatSemanticAnalyzer.class.getName()); hiveConf.set(HiveConf.ConfVars.PREEXECHOOKS.varname, ""); hiveConf.set(HiveConf.ConfVars.POSTEXECHOOKS.varname, ""); hiveConf.set(HiveConf.ConfVars.HIVE_SUPPORT_CONCURRENCY.varname, "false"); System.setProperty(HiveConf.ConfVars.PREEXECHOOKS.varname, " "); System.setProperty(HiveConf.ConfVars.POSTEXECHOOKS.varname, " "); } public void start() throws InterruptedException { Thread thread = new Thread(this); thread.start(); Thread.sleep(WAIT_TIME_FOR_BOOTUP); // Wait for the server to bootup } @Override public void run() { try { HiveMetaStore.main(new String[]{"-v", "-p", String.valueOf(MS_PORT)}); } catch (Throwable t) { LOG.error("Exiting. Got exception from metastore: ", t); } } public HiveConf getHiveConf() { return hiveConf; } public void shutDown() { System.setSecurityManager(securityManager); } } }