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

org.apache.ignite.internal.table.AbstractTableView Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.ignite.internal.table;

import static java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.function.Function.identity;
import static org.apache.ignite.internal.lang.IgniteExceptionMapperUtil.convertToPublicFuture;
import static org.apache.ignite.internal.table.criteria.CriteriaExceptionMapperUtil.mapToPublicCriteriaException;
import static org.apache.ignite.internal.table.distributed.TableUtils.isDirectFlowApplicableTx;
import static org.apache.ignite.internal.util.ExceptionUtils.isOrCausedBy;
import static org.apache.ignite.internal.util.ExceptionUtils.unwrapCause;
import static org.apache.ignite.internal.util.ViewUtils.sync;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.ignite.internal.lang.IgniteExceptionMapperUtil;
import org.apache.ignite.internal.marshaller.MarshallersProvider;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.SchemaRegistry;
import org.apache.ignite.internal.table.criteria.CursorAdapter;
import org.apache.ignite.internal.table.criteria.QueryCriteriaAsyncCursor;
import org.apache.ignite.internal.table.criteria.SqlSerializer;
import org.apache.ignite.internal.table.criteria.SqlSerializer.Builder;
import org.apache.ignite.internal.table.distributed.replicator.IncompatibleSchemaVersionException;
import org.apache.ignite.internal.table.distributed.replicator.InternalSchemaVersionMismatchException;
import org.apache.ignite.internal.table.distributed.schema.SchemaVersions;
import org.apache.ignite.internal.tx.InternalTransaction;
import org.apache.ignite.lang.AsyncCursor;
import org.apache.ignite.lang.Cursor;
import org.apache.ignite.sql.IgniteSql;
import org.apache.ignite.sql.ResultSetMetadata;
import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.sql.Statement;
import org.apache.ignite.table.criteria.Criteria;
import org.apache.ignite.table.criteria.CriteriaQueryOptions;
import org.apache.ignite.table.criteria.CriteriaQuerySource;
import org.apache.ignite.tx.Transaction;
import org.jetbrains.annotations.Nullable;

/**
 * Base class for Table views.
 */
abstract class AbstractTableView implements CriteriaQuerySource {
    /** Internal table. */
    protected final InternalTable tbl;

    private final SchemaVersions schemaVersions;

    /** Table row view converter. */
    protected final TableViewRowConverter rowConverter;

    /** Ignite SQL facade. */
    protected final IgniteSql sql;

    /** Marshallers provider. */
    protected final MarshallersProvider marshallers;

    /**
     * Constructor.
     *
     * @param tbl Internal table.
     * @param schemaVersions Schema versions access.
     * @param schemaReg Schema registry.
     * @param sql Ignite SQL facade.
     * @param marshallers Marshallers provider.
     */
    AbstractTableView(
            InternalTable tbl,
            SchemaVersions schemaVersions,
            SchemaRegistry schemaReg,
            IgniteSql sql,
            MarshallersProvider marshallers
    ) {
        this.tbl = tbl;
        this.schemaVersions = schemaVersions;
        this.sql = sql;
        this.marshallers = marshallers;

        this.rowConverter = new TableViewRowConverter(schemaReg);
    }

    /**
     * Combines the effect of {@link #withSchemaSync(Transaction, KvAction)} and
     * {@link IgniteExceptionMapperUtil#convertToPublicFuture(CompletableFuture)}.
     *
     * @param  Type of the data the action returns.
     * @param tx Transaction or {@code null}.
     * @param action Action to execute.
     * @return Future of whatever the action returns.
     */
    protected final  CompletableFuture doOperation(@Nullable Transaction tx, KvAction action) {
        return convertToPublicFuture(withSchemaSync(tx, action));
    }

    /**
     * Executes the provided KV action in the given transaction, maintaining Schema Synchronization semantics: that is, before executing
     * the action, a check is made to make sure that the current node has schemas complete wrt timestamp corresponding to the operation
     * (if not, a wait is made till this condition is satisfied) and then the action is provided with the table schema version corresponding
     * to the operation.
     *
     * 

If a transaction is given, the operation timestamp will be equal to the transaction timestamp; otherwise, 'now' will * be used. * *

If no transaction is provided, it might happen that schema version obtained at 'now' is different than a schema version * obtained when processing the implicit transaction (that will be created further). If this happens, we'll get an exception * saying that we need to make a retry; it will be handled by this method. * * @param Type of the data the action returns. * @param tx Transaction or {@code null}. * @param action Action to execute. * @return Whatever the action returns. */ protected final CompletableFuture withSchemaSync(@Nullable Transaction tx, KvAction action) { return withSchemaSync((InternalTransaction) tx, null, action); } private CompletableFuture withSchemaSync(@Nullable InternalTransaction tx, @Nullable Integer previousSchemaVersion, KvAction action) { CompletableFuture schemaVersionFuture = isDirectFlowApplicableTx(tx) ? schemaVersions.schemaVersionAtCurrentTime(tbl.tableId()) : schemaVersions.schemaVersionAt(tx.startTimestamp(), tbl.tableId()); return schemaVersionFuture .thenCompose(schemaVersion -> action.act(schemaVersion) .handle((res, ex) -> { if (ex == null) { return completedFuture(res); } if (isOrCausedBy(InternalSchemaVersionMismatchException.class, ex)) { // There is no transaction, and table version was changed between taking the table version (that was used // to marshal inputs and would be used to unmarshal outputs) and starting an implicit transaction // in InternalTable. A transaction must always work with binary rows of the same table version matching the // version corresponding to the transaction creation moment, so this mismatch is not tolerable: we need // to retry the operation here. assert isDirectFlowApplicableTx(tx) : "Only for direct flow applicable tx a retry might be requested"; assertSchemaVersionIncreased(previousSchemaVersion, schemaVersion); // Repeat. return withSchemaSync(tx, schemaVersion, action); } else if (tx == null && isOrCausedBy(IncompatibleSchemaVersionException.class, ex)) { // Table version was changed while we were executing an implicit transaction (between it had been created // and the moment when the operation actually touched the partition), let's retry. assertSchemaVersionIncreased(previousSchemaVersion, schemaVersion); return withSchemaSync(tx, schemaVersion, action); } else { return CompletableFuture.failedFuture(ex); } })) .thenCompose(identity()); } private static void assertSchemaVersionIncreased(@Nullable Integer previousSchemaVersion, Integer schemaVersion) { assert previousSchemaVersion == null || !Objects.equals(schemaVersion, previousSchemaVersion) : "Same schema version (" + schemaVersion + ") on a retry: something is wrong, is this caused by the test setup?"; } /** * Map columns to it's names. * * @param columns Target columns. * @return Column names. */ protected static String[] columnNames(List columns) { String[] columnNames = new String[columns.size()]; for (int i = 0; i < columns.size(); i++) { columnNames[i] = columns.get(i).name(); } return columnNames; } /** * Create conversion function for objects contained by result set to criteria query objects. * * @param meta Result set columns' metadata. * @param schema Schema. * @return Conversion function (if {@code null} conversions isn't required). */ protected @Nullable Function queryMapper(ResultSetMetadata meta, SchemaDescriptor schema) { return null; } /** {@inheritDoc} */ @Override public Cursor query(@Nullable Transaction tx, @Nullable Criteria criteria, @Nullable String indexName, CriteriaQueryOptions opts) { return new CursorAdapter<>(sync(queryAsync(tx, criteria, indexName, opts))); } /** {@inheritDoc} */ @Override public CompletableFuture> queryAsync( @Nullable Transaction tx, @Nullable Criteria criteria, @Nullable String indexName, @Nullable CriteriaQueryOptions opts ) { CriteriaQueryOptions opts0 = opts == null ? CriteriaQueryOptions.DEFAULT : opts; CompletableFuture> future = doOperation(tx, (schemaVersion) -> { SchemaDescriptor schema = rowConverter.registry().schema(schemaVersion); SqlSerializer ser = new Builder() .tableName(tbl.name()) .columns( schema.columns().stream() .map(Column::name) .collect(Collectors.toList()) ) .indexName(indexName) .where(criteria) .build(); Statement statement = sql.statementBuilder().query(ser.toString()).pageSize(opts0.pageSize()).build(); return sql.executeAsync(tx, statement, ser.getArguments()) .thenApply(resultSet -> { ResultSetMetadata meta = resultSet.metadata(); assert meta != null : "Metadata can't be null."; return new QueryCriteriaAsyncCursor<>( resultSet, queryMapper(meta, schema), () -> {/* NO-OP */} ); }); }); return future.exceptionally(th -> { throw new CompletionException(mapToPublicCriteriaException(unwrapCause(th))); }); } /** * Action representing some KV operation. When executed, the action is supplied with schema version corresponding * to the operation timestamp (see {@link #doOperation(Transaction, KvAction)} for details). * * @param Type of the result. * @see #doOperation(Transaction, KvAction) */ @FunctionalInterface protected interface KvAction { /** * Executes the action. * * @param schemaVersion Schema version corresponding to the operation. * @return Action result. */ CompletableFuture act(int schemaVersion); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy