All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.trino.testing.ExtendedFailureRecoveryTest Maven / Gradle / Ivy

There is a newer version: 458
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.trino.testing;

import com.google.common.collect.ImmutableList;
import io.trino.Session;
import io.trino.operator.RetryPolicy;
import io.trino.server.DynamicFilterService.DynamicFilterDomainStats;
import io.trino.server.DynamicFilterService.DynamicFiltersStats;
import io.trino.spi.ErrorType;
import org.intellij.lang.annotations.Language;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.parallel.Execution;

import java.util.List;
import java.util.Optional;

import static com.google.common.collect.Iterables.getOnlyElement;
import static io.trino.SystemSessionProperties.ENABLE_DYNAMIC_FILTERING;
import static io.trino.SystemSessionProperties.JOIN_DISTRIBUTION_TYPE;
import static io.trino.SystemSessionProperties.JOIN_REORDERING_STRATEGY;
import static io.trino.execution.FailureInjector.FAILURE_INJECTION_MESSAGE;
import static io.trino.execution.FailureInjector.InjectedFailureType.TASK_FAILURE;
import static io.trino.execution.FailureInjector.InjectedFailureType.TASK_MANAGEMENT_REQUEST_FAILURE;
import static io.trino.execution.FailureInjector.InjectedFailureType.TASK_MANAGEMENT_REQUEST_TIMEOUT;
import static io.trino.spi.predicate.Domain.singleValue;
import static io.trino.spi.type.BigintType.BIGINT;
import static io.trino.sql.planner.OptimizerConfig.JoinDistributionType.PARTITIONED;
import static io.trino.sql.planner.OptimizerConfig.JoinReorderingStrategy.NONE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;

@TestInstance(PER_CLASS)
@Execution(CONCURRENT)
public abstract class ExtendedFailureRecoveryTest
        extends BaseFailureRecoveryTest
{
    private static final String PARTITIONED_LINEITEM = "partitioned_lineitem";

    protected ExtendedFailureRecoveryTest(RetryPolicy retryPolicy)
    {
        super(retryPolicy);
    }

    @BeforeAll
    public void initTables()
            throws Exception
    {
        // setup partitioned fact table for dynamic partition pruning
        createPartitionedLineitemTable(PARTITIONED_LINEITEM, ImmutableList.of("orderkey", "partkey", "suppkey"), "suppkey");
    }

    protected abstract void createPartitionedLineitemTable(String tableName, List columns, String partitionColumn);

    @Test
    protected void testSimpleSelect()
    {
        testSelect("SELECT * FROM nation");
    }

    @Test
    protected void testAggregation()
    {
        testSelect("SELECT orderStatus, count(*) FROM orders GROUP BY orderStatus");
    }

    @Test
    protected void testJoinDynamicFilteringDisabled()
    {
        @Language("SQL") String selectQuery = "SELECT * FROM partitioned_lineitem JOIN supplier ON partitioned_lineitem.suppkey = supplier.suppkey " +
                "AND supplier.name = 'Supplier#000000001'";
        testSelect(selectQuery, Optional.of(enableDynamicFiltering(false)));
    }

    @Test
    protected void testJoinDynamicFilteringEnabled()
    {
        @Language("SQL") String selectQuery = "SELECT * FROM partitioned_lineitem JOIN supplier ON partitioned_lineitem.suppkey = supplier.suppkey " +
                "AND supplier.name = 'Supplier#000000001'";
        testSelect(
                selectQuery,
                Optional.of(enableDynamicFiltering(true)),
                queryId -> {
                    DynamicFiltersStats dynamicFiltersStats = getDynamicFilteringStats(queryId);
                    assertThat(dynamicFiltersStats.getLazyDynamicFilters())
                            .as("Dynamic filter is missing")
                            .isEqualTo(1);
                    DynamicFilterDomainStats domainStats = getOnlyElement(dynamicFiltersStats.getDynamicFilterDomainStats());
                    assertThat(domainStats.getSimplifiedDomain())
                            .isEqualTo(singleValue(BIGINT, 1L).toString(getSession().toConnectorSession()));
                });
    }

    @Test
    protected void testUserFailure()
    {
        // Some connectors have pushdowns enabled for arithmetic operations (like SqlServer),
        // so exception will come not from trino, but from datasource itself
        Session withoutPushdown = Session.builder(this.getSession())
                .setSystemProperty("allow_pushdown_into_connectors", "false")
                .build();

        assertThatThrownBy(() -> getQueryRunner().execute(withoutPushdown, "SELECT * FROM nation WHERE regionKey / nationKey - 1 = 0"))
                .hasMessageMatching("(?i).*Division by zero.*"); // some errors come back with different casing.

        assertThatQuery("SELECT * FROM nation")
                .experiencing(TASK_FAILURE, Optional.of(ErrorType.USER_ERROR))
                .at(leafStage())
                .failsAlways(failure -> failure.hasMessageContaining(FAILURE_INJECTION_MESSAGE));
    }

    @Test
    @Override
    protected void testRequestTimeouts()
    {
        // extra test cases not covered by general timeout cases scattered around
        assertThatQuery("SELECT * FROM nation")
                .experiencing(TASK_MANAGEMENT_REQUEST_TIMEOUT)
                .at(leafStage())
                .failsWithoutRetries(failure -> failure.hasMessageContaining("Encountered too many errors talking to a worker node"))
                .finishesSuccessfully();

        assertThatQuery("SELECT * FROM nation")
                .experiencing(TASK_MANAGEMENT_REQUEST_TIMEOUT)
                .at(boundaryDistributedStage())
                .failsWithoutRetries(failure -> failure.hasMessageContaining("Encountered too many errors talking to a worker node"))
                .finishesSuccessfully();

        super.testRequestTimeouts();
    }

    @Override
    protected void testNonSelect(Optional session, Optional setupQuery, String query, Optional cleanupQuery, boolean writesData)
    {
        super.testNonSelect(session, setupQuery, query, cleanupQuery, writesData);

        assertThatQuery(query)
                .withSession(session)
                .withSetupQuery(setupQuery)
                .withCleanupQuery(cleanupQuery)
                .experiencing(TASK_FAILURE, Optional.of(ErrorType.INTERNAL_ERROR))
                .at(leafStage())
                .failsWithoutRetries(failure -> failure.hasMessageContaining(FAILURE_INJECTION_MESSAGE))
                .finishesSuccessfully();

        assertThatQuery(query)
                .withSession(session)
                .withSetupQuery(setupQuery)
                .withCleanupQuery(cleanupQuery)
                .experiencing(TASK_FAILURE, Optional.of(ErrorType.INTERNAL_ERROR))
                .at(intermediateDistributedStage())
                .failsWithoutRetries(failure -> failure.hasMessageContaining(FAILURE_INJECTION_MESSAGE))
                .finishesSuccessfully();

        assertThatQuery(query)
                .withSession(session)
                .withSetupQuery(setupQuery)
                .withCleanupQuery(cleanupQuery)
                .experiencing(TASK_MANAGEMENT_REQUEST_FAILURE)
                .at(boundaryDistributedStage())
                .failsWithoutRetries(failure -> failure.hasMessageFindingMatch("Error 500 Internal Server Error|Error closing remote buffer, expected 204 got 500"))
                .finishesSuccessfully();
    }

    private Session enableDynamicFiltering(boolean enabled)
    {
        Session defaultSession = getQueryRunner().getDefaultSession();
        return Session.builder(defaultSession)
                .setSystemProperty(ENABLE_DYNAMIC_FILTERING, Boolean.toString(enabled))
                .setSystemProperty(JOIN_REORDERING_STRATEGY, NONE.name())
                .setSystemProperty(JOIN_DISTRIBUTION_TYPE, PARTITIONED.name())
                // Ensure probe side scan wait until DF is collected
                .setCatalogSessionProperty(defaultSession.getCatalog().orElseThrow(), "dynamic_filtering_wait_timeout", "1h")
                .build();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy