/** * 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.server.execution.lng; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import org.diqube.data.column.ColumnType; import org.diqube.execution.ExecutablePlan; import org.diqube.execution.steps.GroupIdAdjustingStep; import org.diqube.function.AggregationFunction.ValueProvider; import org.diqube.function.IntermediaryResult; import org.diqube.function.aggregate.CountFunction; import org.diqube.queries.QueryRegistry.QueryResultHandler; import org.diqube.queries.QueryUuid; import org.diqube.server.execution.AbstractRemoteEmulatingDiqlExecutionTest; import org.testng.Assert; import org.testng.annotations.Test; /** * Tests correct data handling of {@link GroupIdAdjustingStep} in case that data is delivered in a "not usual" way. * * @author Bastian Gloeckle */ public class GroupIdAdjustingEmulatingDiqlExecutionTest extends AbstractRemoteEmulatingDiqlExecutionTest<Long> { public GroupIdAdjustingEmulatingDiqlExecutionTest() { super(ColumnType.LONG, new LongTestDataProvider()); } @Test public void groupIdAdjustReceivesUnneededInfoAfterProcessing() throws InterruptedException, ExecutionException { // GIVEN initializeSampleTableShards(2); ExecutablePlan executablePlan = buildExecutablePlan( // "Select " + COL_A + ", count() from " + TABLE + // " group by " + COL_A + // " order by count() desc LIMIT 1"); ExecutorService executor = executors.newTestExecutor(executablePlan.preferredExecutorServiceSize()); try { // WHEN // start execution. Future<Void> future = executablePlan.executeAsynchronously(executor); QueryResultHandler resultHandler = null; while (resultHandler == null) { if (queryRegistry.getQueryResultHandlers(QueryUuid.getCurrentQueryUuid()).size() > 0) resultHandler = queryRegistry.getQueryResultHandlers(QueryUuid.getCurrentQueryUuid()).iterator().next(); } String countCol = functionBasedColumnNameBuilderFactory.create().withFunctionName("count").build(); // let first shard returns some values Map<Long, Object> values = new HashMap<>(); values.put(0L, dp.v(5)); resultHandler.newColumnValues(COL_A, values); resultHandler.newIntermediaryAggregationResult(0L, countCol, intermediary(countCol, 0), intermediary(countCol, 3)); // wait until processed List<Long> expectedRun1 = Arrays.asList(new Long[] { 0L }); waitUntilOrFail(newOrderedRowIdsNotify, // () -> "Not correct ordering value. Was: " + resultOrderRowIds + " Expected: " + expectedRun1.toString(), // () -> resultOrderRowIds.equals(expectedRun1)); Long expectedValueRun1 = 3L; waitUntilOrFail(newValuesNotify, // () -> "Not correct value. Was: " + resultValues.get(countCol).get(0L) + " Expected: " + expectedValueRun1, // () -> resultValues.get(countCol) != null && expectedValueRun1.equals(resultValues.get(countCol).get(0L))); // we send another value for a different col but same row. values.clear(); values.put(0L, dp.v(100)); resultHandler.newColumnValues(COL_B, values); // receive another intermediary of another row. This might have been sent although its value has been cut off by a // remote order step for example (so we do not receive actual group-by values but we might have received the group // intermediary). resultHandler.newIntermediaryAggregationResult(1L, countCol, intermediary(countCol, 0), intermediary(countCol, 5)); // mark as "done", GroupIdAdjusting step should not block (to wait for more data on row 0 or 1). resultHandler.oneRemoteDone(); resultHandler.oneRemoteDone(); future.get(); // wait until fully done, this should not be killed by the timeout, but should basically return // immediately. // final result should only contain the row we had values of the group-by cols and intermediaries. Assert.assertEquals(resultOrderRowIds, Arrays.asList(new Long[] { 0L }), "Expected final ordering result to be correct"); } finally { executor.shutdownNow(); } } private IntermediaryResult intermediary(String outputColName, int count) { CountFunction fn = new CountFunction(); fn.addValues(new ValueProvider<Object>() { @Override public Object[] getValues() { return new Object[count]; } @Override public long size() { return count; } @Override public boolean isFinalSetOfValues() { return true; } }); IntermediaryResult res = new IntermediaryResult(outputColName, null); fn.populateIntermediary(res); return res; } }