/**
* 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.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.diqube.itest.AbstractDiqubeIntegrationTest;
import org.diqube.itest.annotations.NeedsServer;
import org.diqube.itest.util.QueryResultServiceTestUtil;
import org.diqube.itest.util.QueryResultServiceTestUtil.TestQueryResultService;
import org.diqube.itest.util.Waiter;
import org.diqube.itest.util.Waiter.WaitTimeoutException;
import org.diqube.server.ControlFileManager;
import org.diqube.thrift.base.thrift.RUUID;
import org.diqube.thrift.base.thrift.RValue;
import org.diqube.thrift.base.thrift.Ticket;
import org.diqube.thrift.base.util.RUuidUtil;
import org.diqube.thrift.base.util.RValueUtil;
import org.diqube.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* Tests simple deployments of data to a cluster.
*
* @author Bastian Gloeckle
*/
public class SimpleServerDeployIntegrationTest extends AbstractDiqubeIntegrationTest {
private static final Logger logger = LoggerFactory.getLogger(SimpleServerDeployIntegrationTest.class);
private static final String AGE_TABLE = "age";
private static final String AGE_JSON_FILE =
"/" + SimpleServerDeployIntegrationTest.class.getSimpleName() + "/age.json";
private static final String AGE_0_CONTROL_FILE = "/" + SimpleServerDeployIntegrationTest.class.getSimpleName()
+ "/age_rowId0" + ControlFileManager.CONTROL_FILE_EXTENSION;
private static final String AGE_11_CONTROL_FILE = "/" + SimpleServerDeployIntegrationTest.class.getSimpleName()
+ "/age_rowId11" + ControlFileManager.CONTROL_FILE_EXTENSION;
private static final String AGE_10_CONTROL_FILE = "/" + SimpleServerDeployIntegrationTest.class.getSimpleName()
+ "/age_rowId10" + ControlFileManager.CONTROL_FILE_EXTENSION;
@Test
@NeedsServer(servers = 1)
public void singleServerQuery() throws InterruptedException {
// WHEN
serverControl.get(0).deploy(cp(AGE_0_CONTROL_FILE), cp(AGE_JSON_FILE));
Ticket ticket = serverControl.get(0).loginSuperuser();
// THEN
try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) {
RUUID queryUuid = RUuidUtil.toRUuid(UUID.randomUUID());
logger.info("Executing query {}", RUuidUtil.toUuid(queryUuid));
serverControl.get(0).getSerivceTestUtil()
.queryService((queryService) -> queryService.asyncExecuteQuery(ticket, queryUuid,
"select age, count() from age group by age order by count() desc", true,
queryRes.getThisServicesAddr().toRNodeAddress()));
new Waiter().waitUntil("Final result of query received", 10, 500,
() -> queryRes.check() && queryRes.getFinalUpdate() != null);
List<Pair<Long, Long>> expected = new ArrayList<>();
expected.add(new Pair<>(5L, 5L));
expected.add(new Pair<>(3L, 3L));
expected.add(new Pair<>(2L, 2L));
expected.add(new Pair<>(1L, 1L));
List<Pair<Long, Long>> actual = new ArrayList<>();
for (List<RValue> row : queryRes.getFinalUpdate().getRows()) {
Assert.assertEquals(row.size(), 2, "Expected to get correct number of cols returned.");
actual.add(new Pair<>((Long) RValueUtil.createValue(row.get(0)), (Long) RValueUtil.createValue(row.get(1))));
}
Assert.assertEquals(actual, expected, "Expected to get correct results.");
} catch (IOException e) {
throw new RuntimeException("Could not execute query", e);
}
}
@Test
@NeedsServer(servers = 2)
public void twoServerQuery() throws InterruptedException {
// WHEN
serverControl.get(0).deploy(cp(AGE_0_CONTROL_FILE), cp(AGE_JSON_FILE));
serverControl.get(1).deploy(cp(AGE_11_CONTROL_FILE), cp(AGE_JSON_FILE));
Ticket ticket = serverControl.get(0).loginSuperuser();
// THEN
try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) {
RUUID queryUuid = RUuidUtil.toRUuid(UUID.randomUUID());
logger.info("Executing query {}", RUuidUtil.toUuid(queryUuid));
serverControl.get(0).getSerivceTestUtil()
.queryService((queryService) -> queryService.asyncExecuteQuery(ticket, queryUuid,
"select age, count() from age group by age order by count() desc", true,
queryRes.getThisServicesAddr().toRNodeAddress()));
new Waiter().waitUntil("Final result of query received", 10, 500,
() -> queryRes.check() && queryRes.getFinalUpdate() != null);
List<Pair<Long, Long>> expected = new ArrayList<>();
expected.add(new Pair<>(5L, 10L));
expected.add(new Pair<>(3L, 6L));
expected.add(new Pair<>(2L, 4L));
expected.add(new Pair<>(1L, 2L));
List<Pair<Long, Long>> actual = new ArrayList<>();
for (List<RValue> row : queryRes.getFinalUpdate().getRows()) {
Assert.assertEquals(row.size(), 2, "Expected to get correct number of cols returned.");
actual.add(new Pair<>((Long) RValueUtil.createValue(row.get(0)), (Long) RValueUtil.createValue(row.get(1))));
}
Assert.assertEquals(actual, expected, "Expected to get correct results.");
} catch (IOException e) {
throw new RuntimeException("Could not execute query", e);
}
}
@Test
@NeedsServer(servers = 1, manualStart = true)
public void deployThenStart() throws InterruptedException {
// WHEN
serverControl.get(0).deploy(cp(AGE_0_CONTROL_FILE), cp(AGE_JSON_FILE));
serverControl.get(0).start();
serverControl.get(0).waitUntilDeployed(cp(AGE_0_CONTROL_FILE));
Ticket ticket = serverControl.get(0).loginSuperuser();
// THEN
try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) {
RUUID queryUuid = RUuidUtil.toRUuid(UUID.randomUUID());
logger.info("Executing query {}", RUuidUtil.toUuid(queryUuid));
serverControl.get(0).getSerivceTestUtil()
.queryService((queryService) -> queryService.asyncExecuteQuery(ticket, queryUuid,
"select age, count() from age group by age order by count() desc", true,
queryRes.getThisServicesAddr().toRNodeAddress()));
new Waiter().waitUntil("Final result of query received", 10, 500,
() -> queryRes.check() && queryRes.getFinalUpdate() != null);
List<Pair<Long, Long>> expected = new ArrayList<>();
expected.add(new Pair<>(5L, 5L));
expected.add(new Pair<>(3L, 3L));
expected.add(new Pair<>(2L, 2L));
expected.add(new Pair<>(1L, 1L));
List<Pair<Long, Long>> actual = new ArrayList<>();
for (List<RValue> row : queryRes.getFinalUpdate().getRows()) {
Assert.assertEquals(row.size(), 2, "Expected to get correct number of cols returned.");
actual.add(new Pair<>((Long) RValueUtil.createValue(row.get(0)), (Long) RValueUtil.createValue(row.get(1))));
}
Assert.assertEquals(actual, expected, "Expected to get correct results.");
} catch (IOException e) {
throw new RuntimeException("Could not execute query", e);
}
}
@Test
@NeedsServer(servers = 2)
public void undeployInCluster() throws InterruptedException {
// WHEN
serverControl.get(0).deploy(cp(AGE_0_CONTROL_FILE), cp(AGE_JSON_FILE));
serverControl.get(1).deploy(cp(AGE_11_CONTROL_FILE), cp(AGE_JSON_FILE));
Ticket ticket = serverControl.get(0).loginSuperuser();
// THEN results from both cluster nodes
try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) {
RUUID queryUuid = RUuidUtil.toRUuid(UUID.randomUUID());
logger.info("Executing query {}", RUuidUtil.toUuid(queryUuid));
serverControl.get(0).getSerivceTestUtil()
.queryService((queryService) -> queryService.asyncExecuteQuery(ticket, queryUuid,
"select age, count() from age group by age order by count() desc", true,
queryRes.getThisServicesAddr().toRNodeAddress()));
new Waiter().waitUntil("Final result of query received", 10, 500,
() -> queryRes.check() && queryRes.getFinalUpdate() != null);
List<Pair<Long, Long>> expected = new ArrayList<>();
expected.add(new Pair<>(5L, 10L));
expected.add(new Pair<>(3L, 6L));
expected.add(new Pair<>(2L, 4L));
expected.add(new Pair<>(1L, 2L));
List<Pair<Long, Long>> actual = new ArrayList<>();
for (List<RValue> row : queryRes.getFinalUpdate().getRows()) {
Assert.assertEquals(row.size(), 2, "Expected to get correct number of cols returned.");
actual.add(new Pair<>((Long) RValueUtil.createValue(row.get(0)), (Long) RValueUtil.createValue(row.get(1))));
}
Assert.assertEquals(actual, expected, "Expected to get correct results.");
} catch (IOException e) {
throw new RuntimeException("Could not execute query", e);
}
// WHEN
serverControl.get(1).undeploy(cp(AGE_11_CONTROL_FILE));
// THEN result from one cluster node only
try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) {
RUUID queryUuid = RUuidUtil.toRUuid(UUID.randomUUID());
logger.info("Executing query {}", RUuidUtil.toUuid(queryUuid));
serverControl.get(0).getSerivceTestUtil()
.queryService((queryService) -> queryService.asyncExecuteQuery(ticket, queryUuid,
"select age, count() from age group by age order by count() desc", true,
queryRes.getThisServicesAddr().toRNodeAddress()));
new Waiter().waitUntil("Final result of query received", 10, 500,
() -> queryRes.check() && queryRes.getFinalUpdate() != null);
List<Pair<Long, Long>> expected = new ArrayList<>();
expected.add(new Pair<>(5L, 5L));
expected.add(new Pair<>(3L, 3L));
expected.add(new Pair<>(2L, 2L));
expected.add(new Pair<>(1L, 1L));
List<Pair<Long, Long>> actual = new ArrayList<>();
for (List<RValue> row : queryRes.getFinalUpdate().getRows()) {
Assert.assertEquals(row.size(), 2, "Expected to get correct number of cols returned.");
actual.add(new Pair<>((Long) RValueUtil.createValue(row.get(0)), (Long) RValueUtil.createValue(row.get(1))));
}
Assert.assertEquals(actual, expected, "Expected to get correct results.");
} catch (IOException e) {
throw new RuntimeException("Could not execute query", e);
}
}
@Test
@NeedsServer(servers = 1)
public void singleServerMultipleShards() throws InterruptedException {
// WHEN
serverControl.get(0).deploy(cp(AGE_0_CONTROL_FILE), cp(AGE_JSON_FILE));
serverControl.get(0).deploy(cp(AGE_11_CONTROL_FILE), cp(AGE_JSON_FILE));
Ticket ticket = serverControl.get(0).loginSuperuser();
// THEN
try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) {
RUUID queryUuid = RUuidUtil.toRUuid(UUID.randomUUID());
logger.info("Executing query {}", RUuidUtil.toUuid(queryUuid));
serverControl.get(0).getSerivceTestUtil()
.queryService((queryService) -> queryService.asyncExecuteQuery(ticket, queryUuid,
"select age, count() from age group by age order by count() desc", true,
queryRes.getThisServicesAddr().toRNodeAddress()));
new Waiter().waitUntil("Final result of query received", 10, 500,
() -> queryRes.check() && queryRes.getFinalUpdate() != null);
List<Pair<Long, Long>> expected = new ArrayList<>();
expected.add(new Pair<>(5L, 10L));
expected.add(new Pair<>(3L, 6L));
expected.add(new Pair<>(2L, 4L));
expected.add(new Pair<>(1L, 2L));
List<Pair<Long, Long>> actual = new ArrayList<>();
for (List<RValue> row : queryRes.getFinalUpdate().getRows()) {
Assert.assertEquals(row.size(), 2, "Expected to get correct number of cols returned.");
actual.add(new Pair<>((Long) RValueUtil.createValue(row.get(0)), (Long) RValueUtil.createValue(row.get(1))));
}
Assert.assertEquals(actual, expected, "Expected to get correct results.");
} catch (IOException e) {
throw new RuntimeException("Could not execute query", e);
}
// WHEN undeploy one of the shards
serverControl.get(0).undeploy(cp(AGE_11_CONTROL_FILE));
// THEN
try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) {
RUUID queryUuid = RUuidUtil.toRUuid(UUID.randomUUID());
logger.info("Executing query {}", RUuidUtil.toUuid(queryUuid));
serverControl.get(0).getSerivceTestUtil()
.queryService((queryService) -> queryService.asyncExecuteQuery(ticket, queryUuid,
"select age, count() from age group by age order by count() desc", true,
queryRes.getThisServicesAddr().toRNodeAddress()));
new Waiter().waitUntil("Final result of query received", 10, 500,
() -> queryRes.check() && queryRes.getFinalUpdate() != null);
List<Pair<Long, Long>> expected = new ArrayList<>();
expected.add(new Pair<>(5L, 5L));
expected.add(new Pair<>(3L, 3L));
expected.add(new Pair<>(2L, 2L));
expected.add(new Pair<>(1L, 1L));
List<Pair<Long, Long>> actual = new ArrayList<>();
for (List<RValue> row : queryRes.getFinalUpdate().getRows()) {
Assert.assertEquals(row.size(), 2, "Expected to get correct number of cols returned.");
actual.add(new Pair<>((Long) RValueUtil.createValue(row.get(0)), (Long) RValueUtil.createValue(row.get(1))));
}
Assert.assertEquals(actual, expected, "Expected to get correct results.");
} catch (IOException e) {
throw new RuntimeException("Could not execute query", e);
}
}
@Test(expectedExceptions = WaitTimeoutException.class)
@NeedsServer(servers = 1)
public void singleServerOverlappingShards() throws InterruptedException {
// GIVEN
try {
serverControl.get(0).deploy(cp(AGE_0_CONTROL_FILE), cp(AGE_JSON_FILE));
} catch (WaitTimeoutException e) {
Assert.fail("WaitTimeoutException happened in the wrong place: " + e.toString());
}
// WHEN deploy control file with overlapping rowIds
serverControl.get(0).deploy(cp(AGE_10_CONTROL_FILE), cp(AGE_JSON_FILE));
// THEN WaitTimeoutException is thrown as the control file cannot be deployed.
}
}