Search in sources :

Example 6 with WriteEventInfo

use of org.apache.hadoop.hive.metastore.api.WriteEventInfo in project hive by apache.

the class TestGetAllWriteEventInfo method testGetByWrongDB.

@Test
public void testGetByWrongDB() throws Exception {
    GetAllWriteEventInfoRequest req = new GetAllWriteEventInfoRequest();
    req.setTxnId(TXN_ID);
    req.setDbName("wrong_db");
    List<WriteEventInfo> writeEventInfoList = client.getAllWriteEventInfo(req);
    Assert.assertTrue(writeEventInfoList.isEmpty());
}
Also used : GetAllWriteEventInfoRequest(org.apache.hadoop.hive.metastore.api.GetAllWriteEventInfoRequest) WriteEventInfo(org.apache.hadoop.hive.metastore.api.WriteEventInfo) Test(org.junit.Test) MetastoreCheckinTest(org.apache.hadoop.hive.metastore.annotation.MetastoreCheckinTest)

Example 7 with WriteEventInfo

use of org.apache.hadoop.hive.metastore.api.WriteEventInfo in project hive by apache.

the class ObjectStore method getAllWriteEventInfo.

@Override
public List<WriteEventInfo> getAllWriteEventInfo(long txnId, String dbName, String tableName) throws MetaException {
    List<WriteEventInfo> writeEventInfoList = null;
    boolean commited = false;
    Query query = null;
    try {
        openTransaction();
        List<String> parameterVals = new ArrayList<>();
        StringBuilder filterBuilder = new StringBuilder(" txnId == " + Long.toString(txnId));
        if (dbName != null && !"*".equals(dbName)) {
            // * means get all database, so no need to add filter
            appendSimpleCondition(filterBuilder, "database", new String[] { dbName }, parameterVals);
        }
        if (tableName != null && !"*".equals(tableName)) {
            appendSimpleCondition(filterBuilder, "table", new String[] { tableName }, parameterVals);
        }
        query = pm.newQuery(MTxnWriteNotificationLog.class, filterBuilder.toString());
        query.setOrdering("database,table ascending");
        List<MTxnWriteNotificationLog> mplans = (List<MTxnWriteNotificationLog>) query.executeWithArray(parameterVals.toArray(new String[0]));
        pm.retrieveAll(mplans);
        commited = commitTransaction();
        if (mplans != null && mplans.size() > 0) {
            writeEventInfoList = Lists.newArrayList();
            for (MTxnWriteNotificationLog mplan : mplans) {
                WriteEventInfo writeEventInfo = new WriteEventInfo(mplan.getWriteId(), mplan.getDatabase(), mplan.getTable(), mplan.getFiles());
                writeEventInfo.setPartition(mplan.getPartition());
                writeEventInfo.setPartitionObj(mplan.getPartObject());
                writeEventInfo.setTableObj(mplan.getTableObject());
                writeEventInfoList.add(writeEventInfo);
            }
        }
    } finally {
        rollbackAndCleanup(commited, query);
    }
    return writeEventInfoList;
}
Also used : ScheduledQuery(org.apache.hadoop.hive.metastore.api.ScheduledQuery) Query(javax.jdo.Query) MScheduledQuery(org.apache.hadoop.hive.metastore.model.MScheduledQuery) WriteEventInfo(org.apache.hadoop.hive.metastore.api.WriteEventInfo) ArrayList(java.util.ArrayList) ValidWriteIdList(org.apache.hadoop.hive.common.ValidWriteIdList) ReplicationMetricList(org.apache.hadoop.hive.metastore.api.ReplicationMetricList) LinkedList(java.util.LinkedList) MStringList(org.apache.hadoop.hive.metastore.model.MStringList) ArrayList(java.util.ArrayList) ValidReaderWriteIdList(org.apache.hadoop.hive.common.ValidReaderWriteIdList) List(java.util.List) MTxnWriteNotificationLog(org.apache.hadoop.hive.metastore.model.MTxnWriteNotificationLog)

Example 8 with WriteEventInfo

use of org.apache.hadoop.hive.metastore.api.WriteEventInfo in project hive by apache.

the class CommitTxnHandler method handle.

@Override
public List<Task<?>> handle(Context context) throws SemanticException {
    if (!AcidUtils.isAcidEnabled(context.hiveConf)) {
        context.log.error("Cannot load transaction events as acid is not enabled");
        throw new SemanticException("Cannot load transaction events as acid is not enabled");
    }
    CommitTxnMessage msg = deserializer.getCommitTxnMessage(context.dmd.getPayload());
    int numEntry = (msg.getTables() == null ? 0 : msg.getTables().size());
    List<Task<?>> tasks = new ArrayList<>();
    String dbName = context.dbName;
    String tableNamePrev = null;
    String tblName = null;
    ReplTxnWork work = new ReplTxnWork(HiveUtils.getReplPolicy(context.dbName), context.dbName, null, msg.getTxnId(), ReplTxnWork.OperationType.REPL_COMMIT_TXN, context.eventOnlyReplicationSpec(), context.getDumpDirectory(), context.getMetricCollector());
    if (numEntry > 0) {
        context.log.debug("Commit txn handler for txnid " + msg.getTxnId() + " databases : " + msg.getDatabases() + " tables : " + msg.getTables() + " partitions : " + msg.getPartitions() + " files : " + msg.getFilesList() + " write ids : " + msg.getWriteIds());
    }
    for (int idx = 0; idx < numEntry; idx++) {
        String actualTblName = msg.getTables().get(idx);
        String actualDBName = msg.getDatabases().get(idx);
        String completeName = Table.getCompleteName(actualDBName, actualTblName);
        // grouped together in commit txn message.
        if (tableNamePrev == null || !(completeName.equals(tableNamePrev))) {
            // The data location is created by source, so the location should be formed based on the table name in msg.
            Path location = HiveUtils.getDumpPath(new Path(context.location), actualDBName, actualTblName);
            tblName = actualTblName;
            // for warehouse level dump, use db name from write event
            dbName = (context.isDbNameEmpty() ? actualDBName : context.dbName);
            Context currentContext = new Context(context, dbName, context.getDumpDirectory(), context.getMetricCollector());
            currentContext.setLocation(location.toUri().toString());
            // Piggybacking in Import logic for now
            TableHandler tableHandler = new TableHandler();
            tasks.addAll((tableHandler.handle(currentContext)));
            readEntitySet.addAll(tableHandler.readEntities());
            writeEntitySet.addAll(tableHandler.writeEntities());
            getUpdatedMetadata().copyUpdatedMetadata(tableHandler.getUpdatedMetadata());
            tableNamePrev = completeName;
        }
        try {
            WriteEventInfo writeEventInfo = new WriteEventInfo(msg.getWriteIds().get(idx), dbName, tblName, msg.getFiles(idx));
            if (msg.getPartitions().get(idx) != null && !msg.getPartitions().get(idx).isEmpty()) {
                writeEventInfo.setPartition(msg.getPartitions().get(idx));
            }
            work.addWriteEventInfo(writeEventInfo);
        } catch (Exception e) {
            throw new SemanticException("Failed to extract write event info from commit txn message : " + e.getMessage());
        }
    }
    Task<ReplTxnWork> commitTxnTask = TaskFactory.get(work, context.hiveConf);
    // Anyways, if this event gets executed again, it is taken care of.
    if (!context.isDbNameEmpty()) {
        updatedMetadata.set(context.dmd.getEventTo().toString(), context.dbName, null, null);
    }
    context.log.debug("Added Commit txn task : {}", commitTxnTask.getId());
    if (tasks.isEmpty()) {
        // will be used for setting the last repl id.
        return Collections.singletonList(commitTxnTask);
    }
    DAGTraversal.traverse(tasks, new AddDependencyToLeaves(commitTxnTask));
    return tasks;
}
Also used : Path(org.apache.hadoop.fs.Path) Task(org.apache.hadoop.hive.ql.exec.Task) ArrayList(java.util.ArrayList) CommitTxnMessage(org.apache.hadoop.hive.metastore.messaging.CommitTxnMessage) SemanticException(org.apache.hadoop.hive.ql.parse.SemanticException) WriteEventInfo(org.apache.hadoop.hive.metastore.api.WriteEventInfo) ReplTxnWork(org.apache.hadoop.hive.ql.plan.ReplTxnWork) SemanticException(org.apache.hadoop.hive.ql.parse.SemanticException) AddDependencyToLeaves(org.apache.hadoop.hive.ql.exec.repl.util.AddDependencyToLeaves)

Example 9 with WriteEventInfo

use of org.apache.hadoop.hive.metastore.api.WriteEventInfo in project hive by apache.

the class TxnHandler method commitTxn.

/**
 * Concurrency/isolation notes:
 * This is mutexed with {@link #openTxns(OpenTxnRequest)} and other {@link #commitTxn(CommitTxnRequest)}
 * operations using select4update on NEXT_TXN_ID. Also, mutexes on TXNS table for specific txnid:X
 * see more notes below.
 * In order to prevent lost updates, we need to determine if any 2 transactions overlap.  Each txn
 * is viewed as an interval [M,N]. M is the txnid and N is taken from the same NEXT_TXN_ID sequence
 * so that we can compare commit time of txn T with start time of txn S.  This sequence can be thought of
 * as a logical time counter. If S.commitTime < T.startTime, T and S do NOT overlap.
 *
 * Motivating example:
 * Suppose we have multi-statement transactions T and S both of which are attempting x = x + 1
 * In order to prevent lost update problem, then the non-overlapping txns must lock in the snapshot
 * that they read appropriately. In particular, if txns do not overlap, then one follows the other
 * (assuming they write the same entity), and thus the 2nd must see changes of the 1st.  We ensure
 * this by locking in snapshot after
 * {@link #openTxns(OpenTxnRequest)} call is made (see org.apache.hadoop.hive.ql.Driver.acquireLocksAndOpenTxn)
 * and mutexing openTxn() with commit(). In other words, once a S.commit() starts we must ensure
 * that txn T which will be considered a later txn, locks in a snapshot that includes the result
 * of S's commit (assuming no other txns).
 * As a counter example, suppose we have S[3,3] and T[4,4] (commitId=txnid means no other transactions
 * were running in parallel). If T and S both locked in the same snapshot (for example commit of
 * txnid:2, which is possible if commitTxn() and openTxnx() is not mutexed)
 * 'x' would be updated to the same value by both, i.e. lost update.
 */
@Override
@RetrySemantics.Idempotent("No-op if already committed")
public void commitTxn(CommitTxnRequest rqst) throws NoSuchTxnException, TxnAbortedException, MetaException {
    char isUpdateDelete = 'N';
    long txnid = rqst.getTxnid();
    long sourceTxnId = -1;
    boolean isReplayedReplTxn = TxnType.REPL_CREATED.equals(rqst.getTxn_type());
    boolean isHiveReplTxn = rqst.isSetReplPolicy() && TxnType.DEFAULT.equals(rqst.getTxn_type());
    try {
        Connection dbConn = null;
        Statement stmt = null;
        Long commitId = null;
        try {
            lockInternal();
            dbConn = getDbConn(Connection.TRANSACTION_READ_COMMITTED);
            stmt = dbConn.createStatement();
            if (rqst.isSetReplLastIdInfo()) {
                updateReplId(dbConn, rqst.getReplLastIdInfo());
            }
            if (isReplayedReplTxn) {
                assert (rqst.isSetReplPolicy());
                sourceTxnId = rqst.getTxnid();
                List<Long> targetTxnIds = getTargetTxnIdList(rqst.getReplPolicy(), Collections.singletonList(sourceTxnId), dbConn);
                if (targetTxnIds.isEmpty()) {
                    // Idempotent case where txn was already closed or commit txn event received without
                    // corresponding open txn event.
                    LOG.info("Target txn id is missing for source txn id : " + sourceTxnId + " and repl policy " + rqst.getReplPolicy());
                    return;
                }
                assert targetTxnIds.size() == 1;
                txnid = targetTxnIds.get(0);
            }
            /**
             * Runs at READ_COMMITTED with S4U on TXNS row for "txnid".  S4U ensures that no other
             * operation can change this txn (such acquiring locks). While lock() and commitTxn()
             * should not normally run concurrently (for same txn) but could due to bugs in the client
             * which could then corrupt internal transaction manager state.  Also competes with abortTxn().
             */
            TxnType txnType = getOpenTxnTypeAndLock(stmt, txnid);
            if (txnType == null) {
                // if here, txn was not found (in expected state)
                TxnStatus actualTxnStatus = findTxnState(txnid, stmt);
                if (actualTxnStatus == TxnStatus.COMMITTED) {
                    if (isReplayedReplTxn) {
                        // in case of replication, idempotent is taken care by getTargetTxnId
                        LOG.warn("Invalid state COMMITTED for transactions started using replication replay task");
                    }
                    /**
                     * This makes the operation idempotent
                     * (assume that this is most likely due to retry logic)
                     */
                    LOG.info("Nth commitTxn(" + JavaUtils.txnIdToString(txnid) + ") msg");
                    return;
                }
                raiseTxnUnexpectedState(actualTxnStatus, txnid);
            }
            String conflictSQLSuffix = "FROM \"TXN_COMPONENTS\" WHERE \"TC_TXNID\"=" + txnid + " AND \"TC_OPERATION_TYPE\" IN (" + OperationType.UPDATE + "," + OperationType.DELETE + ")";
            long tempCommitId = generateTemporaryId();
            if (txnType == TxnType.SOFT_DELETE || txnType == TxnType.COMPACTION) {
                acquireTxnLock(stmt, false);
                commitId = getHighWaterMark(stmt);
            } else if (txnType != TxnType.READ_ONLY && !isReplayedReplTxn) {
                String writeSetInsertSql = "INSERT INTO \"WRITE_SET\" (\"WS_DATABASE\", \"WS_TABLE\", \"WS_PARTITION\"," + "   \"WS_TXNID\", \"WS_COMMIT_ID\", \"WS_OPERATION_TYPE\")" + " SELECT DISTINCT \"TC_DATABASE\", \"TC_TABLE\", \"TC_PARTITION\", \"TC_TXNID\", " + tempCommitId + ", \"TC_OPERATION_TYPE\" ";
                if (isUpdateOrDelete(stmt, conflictSQLSuffix)) {
                    isUpdateDelete = 'Y';
                    // if here it means currently committing txn performed update/delete and we should check WW conflict
                    /**
                     * "select distinct" is used below because
                     * 1. once we get to multi-statement txns, we only care to record that something was updated once
                     * 2. if {@link #addDynamicPartitions(AddDynamicPartitions)} is retried by caller it may create
                     *  duplicate entries in TXN_COMPONENTS
                     * but we want to add a PK on WRITE_SET which won't have unique rows w/o this distinct
                     * even if it includes all of its columns
                     *
                     * First insert into write_set using a temporary commitID, which will be updated in a separate call,
                     * see: {@link #updateWSCommitIdAndCleanUpMetadata(Statement, long, TxnType, Long, long)}}.
                     * This should decrease the scope of the S4U lock on the next_txn_id table.
                     */
                    Savepoint undoWriteSetForCurrentTxn = dbConn.setSavepoint();
                    stmt.executeUpdate(writeSetInsertSql + (useMinHistoryLevel ? conflictSQLSuffix : "FROM \"TXN_COMPONENTS\" WHERE \"TC_TXNID\"=" + txnid + " AND \"TC_OPERATION_TYPE\" <> " + OperationType.COMPACT));
                    /**
                     * This S4U will mutex with other commitTxn() and openTxns().
                     * -1 below makes txn intervals look like [3,3] [4,4] if all txns are serial
                     * Note: it's possible to have several txns have the same commit id.  Suppose 3 txns start
                     * at the same time and no new txns start until all 3 commit.
                     * We could've incremented the sequence for commitId as well but it doesn't add anything functionally.
                     */
                    acquireTxnLock(stmt, false);
                    commitId = getHighWaterMark(stmt);
                    if (!rqst.isExclWriteEnabled()) {
                        /**
                         * see if there are any overlapping txns that wrote the same element, i.e. have a conflict
                         * Since entire commit operation is mutexed wrt other start/commit ops,
                         * committed.ws_commit_id <= current.ws_commit_id for all txns
                         * thus if committed.ws_commit_id < current.ws_txnid, transactions do NOT overlap
                         * For example, [17,20] is committed, [6,80] is being committed right now - these overlap
                         * [17,20] committed and [21,21] committing now - these do not overlap.
                         * [17,18] committed and [18,19] committing now - these overlap  (here 18 started while 17 was still running)
                         */
                        try (ResultSet rs = checkForWriteConflict(stmt, txnid)) {
                            if (rs.next()) {
                                // found a conflict, so let's abort the txn
                                String committedTxn = "[" + JavaUtils.txnIdToString(rs.getLong(1)) + "," + rs.getLong(2) + "]";
                                StringBuilder resource = new StringBuilder(rs.getString(3)).append("/").append(rs.getString(4));
                                String partitionName = rs.getString(5);
                                if (partitionName != null) {
                                    resource.append('/').append(partitionName);
                                }
                                String msg = "Aborting [" + JavaUtils.txnIdToString(txnid) + "," + commitId + "]" + " due to a write conflict on " + resource + " committed by " + committedTxn + " " + rs.getString(7) + "/" + rs.getString(8);
                                // remove WRITE_SET info for current txn since it's about to abort
                                dbConn.rollback(undoWriteSetForCurrentTxn);
                                LOG.info(msg);
                                // todo: should make abortTxns() write something into TXNS.TXN_META_INFO about this
                                if (abortTxns(dbConn, Collections.singletonList(txnid), false, isReplayedReplTxn) != 1) {
                                    throw new IllegalStateException(msg + " FAILED!");
                                }
                                dbConn.commit();
                                throw new TxnAbortedException(msg);
                            }
                        }
                    }
                } else if (!useMinHistoryLevel) {
                    stmt.executeUpdate(writeSetInsertSql + "FROM \"TXN_COMPONENTS\" WHERE \"TC_TXNID\"=" + txnid + " AND \"TC_OPERATION_TYPE\" <> " + OperationType.COMPACT);
                    commitId = getHighWaterMark(stmt);
                }
            } else {
                /*
           * current txn didn't update/delete anything (may have inserted), so just proceed with commit
           *
           * We only care about commit id for write txns, so for RO (when supported) txns we don't
           * have to mutex on NEXT_TXN_ID.
           * Consider: if RO txn is after a W txn, then RO's openTxns() will be mutexed with W's
           * commitTxn() because both do S4U on NEXT_TXN_ID and thus RO will see result of W txn.
           * If RO < W, then there is no reads-from relationship.
           * In replication flow we don't expect any write write conflict as it should have been handled at source.
           */
                assert true;
            }
            if (txnType != TxnType.READ_ONLY && !isReplayedReplTxn) {
                moveTxnComponentsToCompleted(stmt, txnid, isUpdateDelete);
            } else if (isReplayedReplTxn) {
                if (rqst.isSetWriteEventInfos()) {
                    String sql = String.format(COMPL_TXN_COMPONENTS_INSERT_QUERY, txnid, quoteChar(isUpdateDelete));
                    try (PreparedStatement pstmt = dbConn.prepareStatement(sql)) {
                        int insertCounter = 0;
                        for (WriteEventInfo writeEventInfo : rqst.getWriteEventInfos()) {
                            pstmt.setString(1, writeEventInfo.getDatabase());
                            pstmt.setString(2, writeEventInfo.getTable());
                            pstmt.setString(3, writeEventInfo.getPartition());
                            pstmt.setLong(4, writeEventInfo.getWriteId());
                            pstmt.addBatch();
                            insertCounter++;
                            if (insertCounter % maxBatchSize == 0) {
                                LOG.debug("Executing a batch of <" + sql + "> queries. Batch size: " + maxBatchSize);
                                pstmt.executeBatch();
                            }
                        }
                        if (insertCounter % maxBatchSize != 0) {
                            LOG.debug("Executing a batch of <" + sql + "> queries. Batch size: " + insertCounter % maxBatchSize);
                            pstmt.executeBatch();
                        }
                    }
                }
                deleteReplTxnMapEntry(dbConn, sourceTxnId, rqst.getReplPolicy());
            }
            updateWSCommitIdAndCleanUpMetadata(stmt, txnid, txnType, commitId, tempCommitId);
            removeTxnsFromMinHistoryLevel(dbConn, ImmutableList.of(txnid));
            if (rqst.isSetKeyValue()) {
                updateKeyValueAssociatedWithTxn(rqst, stmt);
            }
            if (!isHiveReplTxn) {
                createCommitNotificationEvent(dbConn, txnid, txnType);
            }
            LOG.debug("Going to commit");
            dbConn.commit();
            if (MetastoreConf.getBoolVar(conf, MetastoreConf.ConfVars.METASTORE_ACIDMETRICS_EXT_ON)) {
                Metrics.getOrCreateCounter(MetricsConstants.TOTAL_NUM_COMMITTED_TXNS).inc();
            }
        } catch (SQLException e) {
            LOG.debug("Going to rollback: ", e);
            rollbackDBConn(dbConn);
            checkRetryable(e, "commitTxn(" + rqst + ")");
            throw new MetaException("Unable to update transaction database " + StringUtils.stringifyException(e));
        } finally {
            close(null, stmt, dbConn);
            unlockInternal();
        }
    } catch (RetryException e) {
        commitTxn(rqst);
    }
}
Also used : TxnType(org.apache.hadoop.hive.metastore.api.TxnType) SQLException(java.sql.SQLException) PreparedStatement(java.sql.PreparedStatement) Statement(java.sql.Statement) Connection(java.sql.Connection) Savepoint(java.sql.Savepoint) PreparedStatement(java.sql.PreparedStatement) TxnAbortedException(org.apache.hadoop.hive.metastore.api.TxnAbortedException) WriteEventInfo(org.apache.hadoop.hive.metastore.api.WriteEventInfo) ResultSet(java.sql.ResultSet) MetaException(org.apache.hadoop.hive.metastore.api.MetaException)

Example 10 with WriteEventInfo

use of org.apache.hadoop.hive.metastore.api.WriteEventInfo in project hive by apache.

the class TestGetAllWriteEventInfo method testGetByTxnIdAndTableName.

@Test
public void testGetByTxnIdAndTableName() throws Exception {
    GetAllWriteEventInfoRequest req = new GetAllWriteEventInfoRequest();
    req.setTxnId(TXN_ID);
    req.setDbName(DB_NAME);
    req.setTableName(TABLE_NAME);
    List<WriteEventInfo> writeEventInfoList = client.getAllWriteEventInfo(req);
    Assert.assertEquals(1, writeEventInfoList.size());
    WriteEventInfo writeEventInfo = writeEventInfoList.get(0);
    Assert.assertEquals(TXN_ID, writeEventInfo.getWriteId());
    Assert.assertEquals(DB_NAME, writeEventInfo.getDatabase());
    Assert.assertEquals(TABLE_NAME, writeEventInfo.getTable());
}
Also used : GetAllWriteEventInfoRequest(org.apache.hadoop.hive.metastore.api.GetAllWriteEventInfoRequest) WriteEventInfo(org.apache.hadoop.hive.metastore.api.WriteEventInfo) Test(org.junit.Test) MetastoreCheckinTest(org.apache.hadoop.hive.metastore.annotation.MetastoreCheckinTest)

Aggregations

WriteEventInfo (org.apache.hadoop.hive.metastore.api.WriteEventInfo)11 GetAllWriteEventInfoRequest (org.apache.hadoop.hive.metastore.api.GetAllWriteEventInfoRequest)6 MetastoreCheckinTest (org.apache.hadoop.hive.metastore.annotation.MetastoreCheckinTest)5 Test (org.junit.Test)5 ArrayList (java.util.ArrayList)4 List (java.util.List)3 Path (org.apache.hadoop.fs.Path)2 MetaException (org.apache.hadoop.hive.metastore.api.MetaException)2 CommitTxnMessage (org.apache.hadoop.hive.metastore.messaging.CommitTxnMessage)2 Partition (org.apache.hadoop.hive.ql.metadata.Partition)2 Table (org.apache.hadoop.hive.ql.metadata.Table)2 SemanticException (org.apache.hadoop.hive.ql.parse.SemanticException)2 DumpMetaData (org.apache.hadoop.hive.ql.parse.repl.load.DumpMetaData)2 Collections2 (com.google.common.collect.Collections2)1 Lists (com.google.common.collect.Lists)1 File (java.io.File)1 IOException (java.io.IOException)1 Connection (java.sql.Connection)1 PreparedStatement (java.sql.PreparedStatement)1 ResultSet (java.sql.ResultSet)1