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

org.apache.phoenix.mapreduce.PhoenixTTLDeleteJobMapper 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.phoenix.mapreduce;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.phoenix.compile.QueryPlan;
import org.apache.phoenix.coprocessor.BaseScannerRegionObserver;
import org.apache.phoenix.exception.SQLExceptionCode;
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.jdbc.PhoenixResultSet;
import org.apache.phoenix.jdbc.PhoenixStatement;
import org.apache.phoenix.mapreduce.util.ConnectionUtil;
import org.apache.phoenix.mapreduce.util.PhoenixConfigurationUtil;
import org.apache.phoenix.mapreduce.util.ViewInfoTracker;
import org.apache.phoenix.mapreduce.util.ViewInfoWritable.ViewInfoJobState;
import org.apache.phoenix.mapreduce.util.MultiViewJobStatusTracker;
import org.apache.phoenix.mapreduce.util.DefaultMultiViewJobStatusTracker;
import org.apache.phoenix.query.QueryConstants;
import org.apache.phoenix.schema.PTable;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.util.PhoenixRuntime;
import org.apache.phoenix.util.SchemaUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class PhoenixTTLDeleteJobMapper extends Mapper {
    private static final Logger LOGGER = LoggerFactory.getLogger(PhoenixTTLDeleteJobMapper.class);
    private MultiViewJobStatusTracker multiViewJobStatusTracker;
    private static final int DEFAULT_MAX_RETRIES = 3;
    private static final int DEFAULT_RETRY_SLEEP_TIME_IN_MS = 10000;

    private void initMultiViewJobStatusTracker(Configuration config) throws Exception {
        try {
            Class defaultViewDeletionTrackerClass = DefaultMultiViewJobStatusTracker.class;
            if (config.get(
                    PhoenixConfigurationUtil.MAPREDUCE_MULTI_INPUT_MAPPER_TRACKER_CLAZZ) != null) {
                LOGGER.info("Using customized tracker class : " + config.get(
                        PhoenixConfigurationUtil.MAPREDUCE_MULTI_INPUT_MAPPER_TRACKER_CLAZZ));
                defaultViewDeletionTrackerClass = Class.forName(config.get(
                        PhoenixConfigurationUtil.MAPREDUCE_MULTI_INPUT_MAPPER_TRACKER_CLAZZ));
            } else {
                LOGGER.info("Using default tracker class ");
            }
            this.multiViewJobStatusTracker = (MultiViewJobStatusTracker)
                    defaultViewDeletionTrackerClass.newInstance();
        } catch (Exception e) {
            LOGGER.error("Getting exception While initializing initMultiViewJobStatusTracker " +
                    "with error message " + e.getMessage());
            throw e;
        }
    }

    @Override
    protected void map(NullWritable key, ViewInfoTracker value, Context context)
            throws IOException {
        try {
            final Configuration config = context.getConfiguration();

            if (this.multiViewJobStatusTracker == null) {
                initMultiViewJobStatusTracker(config);
            }

            LOGGER.debug(String.format("Deleting from view %s, TenantID %s, and TTL value: %d",
                    value.getViewName(), value.getTenantId(), value.getPhoenixTtl()));

            deleteExpiredRows(value, config, context);

        } catch (SQLException e) {
            LOGGER.error("Mapper got an exception while deleting expired rows : "
                    + e.getMessage() );
            throw new IOException(e.getMessage(), e.getCause());
        } catch (Exception e) {
            LOGGER.error("Getting IOException while running View TTL Deletion Job mapper " +
                    "with error : " + e.getMessage());
            throw new IOException(e.getMessage(), e.getCause());
        }
    }

    private void deleteExpiredRows(ViewInfoTracker value, Configuration config, Context context)
            throws Exception {
        try (PhoenixConnection connection =
                     (PhoenixConnection) ConnectionUtil.getInputConnection(config)) {
            if (value.getTenantId() != null && !value.getTenantId().equals("NULL")) {
                Properties props = new Properties();
                props.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, value.getTenantId());

                try (PhoenixConnection tenantConnection = (PhoenixConnection)
                        DriverManager.getConnection(connection.getURL(), props)) {
                    deleteExpiredRows(tenantConnection, value, config, context);
                }
            } else {
                deleteExpiredRows(connection, value, config, context);
            }
        }
    }

    /*
     * Each Mapper that receives a MultiPhoenixViewInputSplit will execute a DeleteMutation/Scan
     * (With DELETE_TTL_EXPIRED attribute) per view for all the views and view indexes in the split.
     * For each DeleteMutation, it bounded by the view start and stop keys for the region and
     *  TTL attributes and Delete Hint.
     */
    private void deleteExpiredRows(PhoenixConnection connection, ViewInfoTracker viewInfoTracker,
                                   Configuration config, Context context) throws Exception {
        try (PhoenixStatement pstmt =
                     new PhoenixStatement(connection).unwrap(PhoenixStatement.class)) {
            PTable pTable = PhoenixRuntime.getTable(connection, viewInfoTracker.getViewName());
            String deleteIfExpiredStatement = "SELECT /*+ NO_INDEX */ count(*) FROM " +
                    viewInfoTracker.getViewName();

            if (viewInfoTracker.isIndexRelation()) {
                pTable = PhoenixRuntime.getTable(connection, viewInfoTracker.getRelationName());
                deleteIfExpiredStatement = "SELECT count(*) FROM " +
                        viewInfoTracker.getRelationName();
            }

            String sourceTableName = pTable.getTableName().getString();
            this.multiViewJobStatusTracker.updateJobStatus(viewInfoTracker, 0,
                    ViewInfoJobState.INITIALIZED.getValue(), config, 0, context.getJobName());
            final QueryPlan queryPlan = pstmt.optimizeQuery(deleteIfExpiredStatement);
            final Scan scan = queryPlan.getContext().getScan();
            byte[] emptyColumnFamilyName = SchemaUtil.getEmptyColumnFamily(pTable);
            byte[] emptyColumnName = pTable.getEncodingScheme() ==
                    PTable.QualifierEncodingScheme.NON_ENCODED_QUALIFIERS ?
                            QueryConstants.EMPTY_COLUMN_BYTES :
                            pTable.getEncodingScheme().encode(
                                    QueryConstants.ENCODED_EMPTY_COLUMN_NAME);

            scan.setAttribute(
                    BaseScannerRegionObserver.EMPTY_COLUMN_FAMILY_NAME, emptyColumnFamilyName);
            scan.setAttribute(
                    BaseScannerRegionObserver.EMPTY_COLUMN_QUALIFIER_NAME, emptyColumnName);
            scan.setAttribute(
                    BaseScannerRegionObserver.DELETE_PHOENIX_TTL_EXPIRED, PDataType.TRUE_BYTES);
            scan.setAttribute(
                    BaseScannerRegionObserver.PHOENIX_TTL,
                    Bytes.toBytes(viewInfoTracker.getPhoenixTtl()));
            scan.setAttribute(
                    BaseScannerRegionObserver.PHOENIX_TTL_SCAN_TABLE_NAME,
                    Bytes.toBytes(sourceTableName));

            this.multiViewJobStatusTracker.updateJobStatus(viewInfoTracker, 0,
                    ViewInfoJobState.RUNNING.getValue(), config, 0, context.getJobName());

            addingDeletionMarkWithRetries(pstmt, viewInfoTracker, config, context,
                    queryPlan);
        } catch (Exception e) {
            if (e instanceof SQLException && ((SQLException) e).getErrorCode() ==
                    SQLExceptionCode.TABLE_UNDEFINED.getErrorCode()) {
                this.multiViewJobStatusTracker.updateJobStatus(viewInfoTracker, 0,
                        ViewInfoJobState.DELETED.getValue(), config, 0, context.getJobName());
            }
            LOGGER.error(String.format("Had an issue to process the view: %s, " +
                    "see error %s ", viewInfoTracker.toString(),e.getMessage()));
        }
    }

    private boolean addingDeletionMarkWithRetries(PhoenixStatement stmt,
                                                  ViewInfoTracker viewInfoTracker,
                                                  Configuration config, Context context,
                                                  QueryPlan queryPlan)
            throws Exception {
        int retry = 0;
        long startTime = System.currentTimeMillis();
        String viewInfo = viewInfoTracker.getTenantId() == null ?
                viewInfoTracker.getViewName() : viewInfoTracker.getTenantId()
                + "." + viewInfoTracker.getViewName();

        while (retry < DEFAULT_MAX_RETRIES) {
            try {
                PhoenixResultSet rs = stmt.newResultSet(
                        queryPlan.iterator(), queryPlan.getProjector(), queryPlan.getContext());

                long numberOfDeletedRows = 0;
                if (rs.next()) {
                    numberOfDeletedRows = rs.getLong(1);
                }
                this.multiViewJobStatusTracker.updateJobStatus(viewInfoTracker, numberOfDeletedRows,
                        ViewInfoJobState.SUCCEEDED.getValue(), config,
                        System.currentTimeMillis() - startTime, context.getJobName());
                PhoenixTTLTool.MR_COUNTER_METRICS metricsStatus =
                        viewInfoTracker.isIndexRelation() ?
                        PhoenixTTLTool.MR_COUNTER_METRICS.VIEW_INDEX_SUCCEED :
                        PhoenixTTLTool.MR_COUNTER_METRICS.VIEW_SUCCEED;
                context.getCounter(metricsStatus).increment(1);
                return true;
            } catch (Exception e) {
                PhoenixTTLTool.MR_COUNTER_METRICS metricsStatus =
                        viewInfoTracker.isIndexRelation() ?
                        PhoenixTTLTool.MR_COUNTER_METRICS.VIEW_INDEX_FAILED :
                        PhoenixTTLTool.MR_COUNTER_METRICS.VIEW_FAILED;
                if (e instanceof SQLException && ((SQLException) e).getErrorCode() ==
                        SQLExceptionCode.TABLE_UNDEFINED.getErrorCode()) {
                    LOGGER.info(viewInfo + " has been deleted : " + e.getMessage());
                    this.multiViewJobStatusTracker.updateJobStatus(viewInfoTracker, 0,
                            ViewInfoJobState.DELETED.getValue(), config, 0, context.getJobName());
                    context.getCounter(metricsStatus).increment(1);
                    return false;
                }
                retry++;

                if (retry == DEFAULT_MAX_RETRIES) {
                    LOGGER.error("Deleting " + viewInfo + " expired rows has an exception for : "
                            + e.getMessage());
                    this.multiViewJobStatusTracker.updateJobStatus(viewInfoTracker, 0,
                            ViewInfoJobState.FAILED.getValue(), config, 0, context.getJobName());
                    context.getCounter(metricsStatus).increment(1);
                    throw e;
                } else {
                    Thread.sleep(DEFAULT_RETRY_SLEEP_TIME_IN_MS);
                }
            }
        }
        return false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy