/**
* diqube: Distributed Query Base.
*
* Copyright (C) 2015 Bastian Gloeckle
*
* This file is part of diqube.
*
* diqube is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diqube.itest.tests;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.UUID;
import org.diqube.config.ConfigKey;
import org.diqube.itest.AbstractDiqubeIntegrationTest;
import org.diqube.itest.annotations.NeedsProcessPid;
import org.diqube.itest.annotations.NeedsServer;
import org.diqube.itest.control.ServerControl;
import org.diqube.itest.util.QueryResultServiceTestUtil;
import org.diqube.itest.util.QueryResultServiceTestUtil.TestQueryResultService;
import org.diqube.itest.util.TestDataGenerator;
import org.diqube.itest.util.Waiter;
import org.diqube.server.ControlFileManager;
import org.diqube.thrift.base.thrift.RUUID;
import org.diqube.thrift.base.thrift.Ticket;
import org.diqube.thrift.base.util.RUuidUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.Test;
/**
* Tests that queries timeout.
*
* @author Bastian Gloeckle
*/
public class QueryTimeoutIntegrationTest extends AbstractDiqubeIntegrationTest {
private static final Logger logger = LoggerFactory.getLogger(QueryTimeoutIntegrationTest.class);
private static final String BIG_TABLE = "big";
private static final String BIG_CONTROL_FILE =
"/" + QueryTimeoutIntegrationTest.class.getSimpleName() + "/big" + ControlFileManager.CONTROL_FILE_EXTENSION;
private static final String BIG_DATA_FILE_WORK = "big.json";
@Test
@NeedsServer(servers = 1, manualStart = true)
@NeedsProcessPid
public void queryProjectedWhereEmptyResult() throws InterruptedException, FileNotFoundException, IOException {
// GIVEN
// a table which has enough data that the first queries on it might time out
TestDataGenerator.generateJsonTestData(work(BIG_DATA_FILE_WORK), 10, 2, new String[] { "a", "b" }, 30);
// WHEN
// start server with a very low timeout
serverControl.get(0).start(prop -> prop.setProperty(ConfigKey.QUERY_EXECUTION_TIMEOUT_SECONDS, "1"));
serverControl.get(0).deploy(cp(BIG_CONTROL_FILE), work(BIG_DATA_FILE_WORK));
Ticket ticket = serverControl.get(0).loginSuperuser();
// THEN
try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) {
UUID queryUuid = UUID.randomUUID();
RUUID queryRUuid = RUuidUtil.toRUuid(queryUuid);
logger.info("Executing query {}", RUuidUtil.toUuid(queryRUuid));
// execute a long-running query. It should just take longer than the timeout we set above...
serverControl.get(0).getSerivceTestUtil()
.queryService((queryService) -> queryService.asyncExecuteQuery(ticket, queryRUuid,
"select avg(add(a[*].a[*], 1)), avg(add(a[*].b[*], 1)), avg(add(b[*].a[*], 1)) from " + BIG_TABLE, true,
queryRes.getThisServicesAddr().toRNodeAddress()));
new Waiter().waitUntil("Expected to receive a timeout exception.", 10, 500,
() -> queryRes.getException() != null);
// we received an exception, validate that all threads of the query have been shut down!
// Actually, we should not wait too long here, otherwise the worker threads could end because they finished their
// work (but we want them to be terminated here!), but in this test case, the worker threads will be busy building
// columns (with the results of the projections/colAggregations and that process does not have enough Object#wait
// calls inside to recognoze the interruption early. So: Stick with 10s although its not perfect :/
new Waiter().waitUntil("Expected that all threads of the query were shut down", 10, 1000,
() -> !threadDumpContainsQueryUuid(serverControl.get(0), queryUuid));
logger.info("Successfully received exception and all threads of the query were shut down.",
queryRes.getException());
} catch (IOException e) {
throw new RuntimeException("Could not execute query", e);
}
}
private boolean threadDumpContainsQueryUuid(ServerControl serverControl, UUID queryUuid) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serverControl.createThreadDump(baos);
String s = baos.toString();
logger.info("Gathered thread dump of server process: {}", s);
return s.contains(queryUuid.toString());
}
}