/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package org.voltdb.client; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.util.concurrent.CountDownLatch; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.voltdb.BackendTarget; import org.voltdb.TheHashinator; import org.voltdb.VoltTable; import org.voltdb.compiler.VoltProjectBuilder; import org.voltdb.regressionsuites.JUnit4LocalClusterTest; import org.voltdb.regressionsuites.LocalCluster; import org.voltdb.utils.VoltFile; /** * Test client all partition calls * */ public class TestAllPartitionProcedureCalls extends JUnit4LocalClusterTest { static final int ROWS = 1000; private LocalCluster cluster; private Client client; private Client clientWithAffinity; @Before public void setUp() throws Exception { VoltFile.recursivelyDelete(new File("/tmp/" + System.getProperty("user.name"))); File f = new File("/tmp/" + System.getProperty("user.name")); f.mkdirs(); if (TheHashinator.getConfiguredHashinatorType() != TheHashinator.HashinatorType.ELASTIC) return; cluster = new LocalCluster("client-all-partitions.jar", 4, 2, 0, BackendTarget.NATIVE_EE_JNI); cluster.overrideAnyRequestForValgrind(); cluster.setHasLocalServer(false); VoltProjectBuilder project = new VoltProjectBuilder(); project.setUseDDLSchema(true); project.addSchema(TestAllPartitionProcedureCalls.class.getResource("allpartitioncall.sql")); boolean success = cluster.compile(project); assertTrue(success); cluster.startUp(); ClientConfig config = new ClientConfig(); config.setClientAffinity(false); client = ClientFactory.createClient(config); client.createConnection("", cluster.port(0)); load(client, "TABLE_INT_PARTITION"); load(client, "TABLE_STRING_PARTITION"); clientWithAffinity = ClientFactory.createClient(); clientWithAffinity.createConnection("", cluster.port(0)); } @After public void tearDown() throws Exception { if(client != null){ client.close(); } if(clientWithAffinity != null){ clientWithAffinity.close(); } if (cluster != null) { cluster.shutDown(); } } @Test public void testSyncCallAllPartitionProcedureWithIntPartition() throws Exception { ClientResponseWithPartitionKey[] responses = client.callAllPartitionProcedure("PartitionIntegerTestProc"); validateResults(responses, 8); responses = clientWithAffinity.callAllPartitionProcedure("PartitionIntegerTestProc"); validateResults(responses, 8); } @Test public void testAsyncCallAllPartitionProcedureWithIntPartition() throws Exception { asyncTest(client, "PartitionIntegerTestProc"); asyncTest(clientWithAffinity, "PartitionIntegerTestProc"); } @Test public void testCallInvalidPartitionProcedure() throws Exception { ClientResponseWithPartitionKey[] responses; // check sysproc responses = client.callAllPartitionProcedure("@Statistics", "MEMORY"); for (ClientResponseWithPartitionKey response : responses) { assertEquals(ClientResponse.GRACEFUL_FAILURE, response.response.getStatus()); String msg = response.response.getStatusString(); assertTrue(msg.contains("Invalid procedure for all-partition execution")); } // check multipart responses = client.callAllPartitionProcedure("MultiPartitionProcedureSample", 0); for (ClientResponseWithPartitionKey response : responses) { assertEquals(ClientResponse.GRACEFUL_FAILURE, response.response.getStatus()); String msg = response.response.getStatusString(); assertTrue(msg.contains("Invalid procedure for all-partition execution")); } // check wrong-partitioning responses = client.callAllPartitionProcedure("PartitionedTestProcNonZeroPartitioningParam", 0, 1); for (ClientResponseWithPartitionKey response : responses) { assertEquals(ClientResponse.GRACEFUL_FAILURE, response.response.getStatus()); String msg = response.response.getStatusString(); assertTrue(msg.contains("Invalid procedure for all-partition execution")); } } @Test public void testSyncCallAllPartitionProcedureWithStringPartition() throws Exception { ClientResponseWithPartitionKey[] responses = client.callAllPartitionProcedure("PartitionStringTestProc"); validateResults(responses, 8); responses = clientWithAffinity.callAllPartitionProcedure("PartitionStringTestProc"); validateResults(responses, 8); } private void asyncTest(Client cleint, String procName) throws Exception { CountDownLatch latch = new CountDownLatch(1); CallBack cb = new CallBack(8, latch); client.callAllPartitionProcedure(cb, procName); try{ latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } } @Test public void testAsyncCallAllPartitionProcedureWithStringPartition() throws Exception{ asyncTest(client, "PartitionStringTestProc"); asyncTest(clientWithAffinity, "PartitionStringTestProc"); } @Test public void testCallAllPartitionProcedureFailuerProc() throws Exception { ClientResponseWithPartitionKey[] responses = client.callAllPartitionProcedure("PartitionFailureTestProc"); for (ClientResponseWithPartitionKey resp: responses) { int key = (int)(resp.partitionKey); if (key == 7) { assertTrue(resp.response.getStatus() == 1); } else { assertFalse(resp.response.getStatus() == 1); } } } private void validateResults(ClientResponseWithPartitionKey[] responses, int partitionCount) { assertNotNull("responses are null", responses); assertEquals ("response array size is not equal to the number of partitions", partitionCount, responses.length); long total = 0; for (ClientResponseWithPartitionKey resp: responses) { VoltTable results = resp.response.getResults()[0]; long count = results.fetchRow(0).getLong(0); assertTrue(count > 0); total += count; } assertTrue(total == ROWS); } private void load(Client client, String tableName) throws NoConnectionsException, IOException, ProcCallException { for (int i = 0; i < 1000; i++) { StringBuilder builder = new StringBuilder(); builder.append("insert into " + tableName + " values (" + i); builder.append(", '" + i + "', " + i + ", " + i + ")"); client.callProcedure("@AdHoc", builder.toString()); } String sql = "SELECT count(*) from " + tableName; VoltTable vt = client.callProcedure("@AdHoc", sql).getResults()[0]; assertTrue(1000 == vt.fetchRow(0).getLong(0)); } public static class CallBack implements AllPartitionProcedureCallback { final int m_partitionCount; final CountDownLatch m_latch; CallBack(int partitionCount, CountDownLatch latch) { m_partitionCount = partitionCount; m_latch = latch; } @Override public void clientCallback(ClientResponseWithPartitionKey[] responses) throws Exception { assertEquals("response array size is not equal to the number of partitions", m_partitionCount, responses.length); long total = 0; try { for (ClientResponseWithPartitionKey resp: responses) { VoltTable results = resp.response.getResults()[0]; long count = results.fetchRow(0).getLong(0); total += count; assertTrue(count > 0); } assertTrue(total == ROWS); } finally { m_latch.countDown(); } } } }