/** * 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.Arrays; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; import org.diqube.itest.AbstractDiqubeIntegrationTest; 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.Waiter; import org.diqube.permission.Permissions; import org.diqube.server.ControlFileManager; import org.diqube.thrift.base.thrift.AuthorizationException; 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.tool.im.AddPermissionActualIdentityToolFunction; import org.diqube.tool.im.CreateUserActualIdentityToolFunction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.Test; /** * Integration test which flattens data, requests it, adjusts the original table by loading more shards and then * re-flattens and re-requests. * * @author Bastian Gloeckle */ public class FlattenIntegrationTest extends AbstractDiqubeIntegrationTest { private static final Logger logger = LoggerFactory.getLogger(FlattenIntegrationTest.class); private static final String TABLE = "flattendata"; private static final String DATA0_JSON_FILE = "/" + FlattenIntegrationTest.class.getSimpleName() + "/flattendata0.json"; private static final String DATA0_CONTROL_FILE = "/" + FlattenIntegrationTest.class.getSimpleName() + "/flattendata0" + ControlFileManager.CONTROL_FILE_EXTENSION; private static final String DATA3_JSON_FILE = "/" + FlattenIntegrationTest.class.getSimpleName() + "/flattendata3.json"; private static final String DATA3_CONTROL_FILE = "/" + FlattenIntegrationTest.class.getSimpleName() + "/flattendata3" + ControlFileManager.CONTROL_FILE_EXTENSION; private static final String DATA6_JSON_FILE = "/" + FlattenIntegrationTest.class.getSimpleName() + "/flattendata6.json"; private static final String DATA6_CONTROL_FILE = "/" + FlattenIntegrationTest.class.getSimpleName() + "/flattendata6" + ControlFileManager.CONTROL_FILE_EXTENSION; private static final String USER = "user1"; private static final String PASSWD = "passwd"; private static final String EMAIL = "a@b.c"; @Test @NeedsServer(servers = 2) public void flattenTest() throws IOException { // GIVEN serverControl.get(0).deploy(cp(DATA0_CONTROL_FILE), cp(DATA0_JSON_FILE)); serverControl.get(1).deploy(cp(DATA6_CONTROL_FILE), cp(DATA6_JSON_FILE)); Ticket ticket = serverControl.get(0).loginSuperuser(); // WHEN: query 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 a.b, count() from flatten(" + TABLE + ", a[*]) group by a.b order by a.b asc", true, queryRes.getThisServicesAddr().toRNodeAddress())); new Waiter().waitUntil("Final result of query received", 10, 500, () -> queryRes.check() && queryRes.getFinalUpdate() != null); // THEN: valid result. Assert.assertTrue(queryRes.getFinalUpdate().isSetRows(), "Expected to get a result"); List<List<Long>> expectedResult = new ArrayList<>(); expectedResult.add(Arrays.asList(1L, 3L)); // a.b = 1, count = 3 expectedResult.add(Arrays.asList(2L, 3L)); // a.b = 2, count = 3 expectedResult.add(Arrays.asList(5L, 3L)); // a.b = 5, count = 3 expectedResult.add(Arrays.asList(6L, 3L)); // a.b = 6, count = 3 List<List<Long>> actual = transformResult(queryRes.getFinalUpdate().getRows()); Assert.assertEquals(actual, expectedResult, "Expected to get correct result BEFORE adding additional shard."); } catch (IOException e) { throw new RuntimeException("Could not execute query", e); } // WHEN: deploy another shard in the middle -> flattened data should be invalidated and re-calculated, since // otherwise rowIds would overlap. serverControl.get(0).deploy(cp(DATA3_CONTROL_FILE), cp(DATA3_JSON_FILE)); // THEN: query again, receive updated results. 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 a.b, count() from flatten(" + TABLE + ", a[*]) group by a.b order by a.b asc", true, queryRes.getThisServicesAddr().toRNodeAddress())); new Waiter().waitUntil("Final result of query received", 10, 500, () -> queryRes.check() && queryRes.getFinalUpdate() != null); // THEN: valid result. Assert.assertTrue(queryRes.getFinalUpdate().isSetRows(), "Expected to get a result"); List<List<Long>> expectedResult = new ArrayList<>(); expectedResult.add(Arrays.asList(1L, 3L)); // a.b = 1, count = 3 expectedResult.add(Arrays.asList(2L, 3L)); // a.b = 2, count = 3 expectedResult.add(Arrays.asList(3L, 3L)); // a.b = 3, count = 3 expectedResult.add(Arrays.asList(4L, 3L)); // a.b = 4, count = 3 expectedResult.add(Arrays.asList(5L, 3L)); // a.b = 5, count = 3 expectedResult.add(Arrays.asList(6L, 3L)); // a.b = 6, count = 3 List<List<Long>> actual = transformResult(queryRes.getFinalUpdate().getRows()); Assert.assertEquals(actual, expectedResult, "Expected to get correct result AFTER adding additional shard."); } catch (IOException e) { throw new RuntimeException("Could not execute query", e); } } @Test @NeedsServer public void flattenTestNonRoot() throws IOException { // GIVEN serverControl.get(0).deploy(cp(DATA0_CONTROL_FILE), cp(DATA0_JSON_FILE)); logger.info("Creating new user with access to test table"); toolControl.im(serverControl.get(0).getAddr(), CreateUserActualIdentityToolFunction.FUNCTION_NAME, // ServerControl.ROOT_USER, ServerControl.ROOT_PASSWORD, // USER, // paramUser PASSWD, // paramPassword EMAIL, // paramEmail null, // paramPermission null // paramPermissionObject ); toolControl.im(serverControl.get(0).getAddr(), AddPermissionActualIdentityToolFunction.FUNCTION_NAME, // ServerControl.ROOT_USER, ServerControl.ROOT_PASSWORD, // USER, // paramUser null, // paramPassword null, // paramEmail Permissions.TABLE_ACCESS, // paramPermission TABLE // paramPermissionObject ); Ticket ticket = serverControl.get(0).login(USER, PASSWD); // WHEN: query 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 a.b, count() from flatten(" + TABLE + ", a[*]) group by a.b order by a.b asc", true, queryRes.getThisServicesAddr().toRNodeAddress())); new Waiter().waitUntil("Final result of query received", 10, 500, () -> queryRes.check() && queryRes.getFinalUpdate() != null); // THEN: valid result. Assert.assertTrue(queryRes.getFinalUpdate().isSetRows(), "Expected to get a result"); List<List<Long>> expectedResult = new ArrayList<>(); expectedResult.add(Arrays.asList(1L, 3L)); // a.b = 1, count = 3 expectedResult.add(Arrays.asList(2L, 3L)); // a.b = 2, count = 3 List<List<Long>> actual = transformResult(queryRes.getFinalUpdate().getRows()); Assert.assertEquals(actual, expectedResult, "Expected to get correct result BEFORE adding additional shard."); } catch (IOException e) { throw new RuntimeException("Could not execute query", e); } } @Test @NeedsServer public void flattenTestNonRootNoAccess() throws IOException { // GIVEN serverControl.get(0).deploy(cp(DATA0_CONTROL_FILE), cp(DATA0_JSON_FILE)); logger.info("Creating new user WITHOUT access to test table"); toolControl.im(serverControl.get(0).getAddr(), CreateUserActualIdentityToolFunction.FUNCTION_NAME, // ServerControl.ROOT_USER, ServerControl.ROOT_PASSWORD, // USER, // paramUser PASSWD, // paramPassword EMAIL, // paramEmail null, // paramPermission null // paramPermissionObject ); Ticket ticket = serverControl.get(0).login(USER, PASSWD); // WHEN: query try (TestQueryResultService queryRes = QueryResultServiceTestUtil.createQueryResultService()) { RUUID queryUuid = RUuidUtil.toRUuid(UUID.randomUUID()); logger.info("Executing query {}", RUuidUtil.toUuid(queryUuid)); serverControl.get(0).getSerivceTestUtil().queryService((queryService) -> { try { queryService.asyncExecuteQuery(ticket, queryUuid, "select a.b, count() from flatten(" + TABLE + ", a[*]) group by a.b order by a.b asc", true, queryRes.getThisServicesAddr().toRNodeAddress()); Assert.fail("Expected to receive an AuthorizationException, but none received!"); } catch (AuthorizationException e) { // fine, this is expected! } }); } catch (IOException e) { throw new RuntimeException("Could not execute query", e); } } private List<List<Long>> transformResult(List<List<RValue>> in) { List<List<Long>> res = new ArrayList<>(); for (List<RValue> inRow : in) res.add(inRow.stream().map(rvalue -> (Long) RValueUtil.createValue(rvalue)).collect(Collectors.toList())); return res; } }