@Override public int getStmtIdAndIncrement() { assert isTxnOpen(); return stmtId++; } @Override
@Override public int getCurrentStmtId() { assert isTxnOpen(); return stmtId; } @Override
@Override public boolean isImplicitTransactionOpen() { if(!isTxnOpen()) { //some commands like "show databases" don't start implicit transactions return false; } if(!isExplicitTransaction) { assert numStatements == 1 : "numStatements=" + numStatements; return true; } return false; } @Override
@Override public int getWriteIdAndIncrement() { assert isTxnOpen(); return statementId++; }
@Override public long getTableWriteId(String dbName, String tableName) throws LockException { assert isTxnOpen(); return getTableWriteId(dbName, tableName, true); }
@Override public long getAllocatedTableWriteId(String dbName, String tableName) throws LockException { assert isTxnOpen(); // Calls getTableWriteId() with allocateIfNotYet being false // to return 0 if the dbName:tableName's writeId is yet allocated. // This happens when the current context is before // Driver.acquireLocks() is called. return getTableWriteId(dbName, tableName, false); }
/** * @param delay time to delay for first heartbeat */ @VisibleForTesting void acquireLocksWithHeartbeatDelay(QueryPlan plan, Context ctx, String username, long delay) throws LockException { LockState ls = acquireLocks(plan, ctx, username, true); if (ls != null && !isTxnOpen()) { // If there's no lock, we don't need to do heartbeat // Start heartbeat for read-only queries which don't open transactions but requires locks. // For those that require transactions, the heartbeat has already been started in openTxn. ctx.setHeartbeater(startHeartbeat(delay)); } }
@Override protected void destruct() { try { stopHeartbeat(); if (shutdownRunner != null) { ShutdownHookManager.removeShutdownHook(shutdownRunner); } if (isTxnOpen()) rollbackTxn(); if (lockMgr != null) lockMgr.close(); } catch (Exception e) { LOG.error("Caught exception " + e.getClass().getName() + " with message <" + e.getMessage() + ">, swallowing as there is nothing we can do with it."); // Not much we can do about it here. } }
@Override protected void destruct() { try { stopHeartbeat(); if (shutdownRunner != null) { ShutdownHookManager.removeShutdownHook(shutdownRunner); } if (isTxnOpen()) rollbackTxn(); if (lockMgr != null) lockMgr.close(); } catch (Exception e) { LOG.error("Caught exception " + e.getClass().getName() + " with message <" + e.getMessage() + ">, swallowing as there is nothing we can do with it."); // Not much we can do about it here. } }
/** * In an explicit txn start_transaction is the 1st statement and we record the snapshot at the * start of the txn for Snapshot Isolation. For Read Committed (not supported yet) we'd record * it before executing each statement (but after lock acquisition if using lock based concurrency * control). * For implicit txn, the stmt that triggered/started the txn is the first statement */ @Override public boolean recordSnapshot(QueryPlan queryPlan) { assert isTxnOpen(); assert numStatements > 0 : "was acquireLocks() called already?"; if(queryPlan.getOperation() == HiveOperation.START_TRANSACTION) { //here if start of explicit txn assert isExplicitTransaction; assert numStatements == 1; return true; } else if(!isExplicitTransaction) { assert numStatements == 1 : "numStatements=" + numStatements + " in implicit txn"; if (queryPlan.hasAcidResourcesInQuery()) { //1st and only stmt in implicit txn and uses acid resource return true; } } return false; }
@Override public ValidTxnWriteIdList getValidWriteIds(List<String> tableList, String validTxnList) throws LockException { assert isTxnOpen(); assert validTxnList != null && !validTxnList.isEmpty(); try { return TxnCommonUtils.createValidTxnWriteIdList( txnId, getMS().getValidWriteIds(tableList, validTxnList)); } catch (TException e) { throw new LockException(ErrorMsg.METASTORE_COMMUNICATION_FAILED.getMsg(), e); } }
/** * @param delay time to delay for first heartbeat */ @VisibleForTesting void acquireLocksWithHeartbeatDelay(QueryPlan plan, Context ctx, String username, long delay) throws LockException { LockState ls = acquireLocks(plan, ctx, username, true); if (ls != null && !isTxnOpen()) { // If there's no lock, we don't need to do heartbeat // Start heartbeat for read-only queries which don't open transactions but requires locks. // For those that require transactions, the heartbeat has already been started in openTxn. ctx.setHeartbeater(startHeartbeat(delay)); } }
@Override public ValidTxnList getValidTxns() throws LockException { assert isTxnOpen(); init(); try { return getMS().getValidTxns(txnId); } catch (TException e) { throw new LockException(ErrorMsg.METASTORE_COMMUNICATION_FAILED.getMsg(), e); } }
@Override public void rollbackTxn() throws LockException { if (!isTxnOpen()) { throw new RuntimeException("Attempt to rollback before opening a transaction"); } try { lockMgr.clearLocalLockRecords(); stopHeartbeat(); LOG.debug("Rolling back " + JavaUtils.txnIdToString(txnId)); getMS().rollbackTxn(txnId); } catch (NoSuchTxnException e) { LOG.error("Metastore could not find " + JavaUtils.txnIdToString(txnId)); throw new LockException(e, ErrorMsg.TXN_NO_SUCH_TRANSACTION, JavaUtils.txnIdToString(txnId)); } catch(TxnAbortedException e) { throw new LockException(e, ErrorMsg.TXN_ABORTED, JavaUtils.txnIdToString(txnId)); } catch (TException e) { throw new LockException(ErrorMsg.METASTORE_COMMUNICATION_FAILED.getMsg(), e); } finally { txnId = 0; stmtId = -1; numStatements = 0; tableWriteIds.clear(); } }
@VisibleForTesting long openTxn(Context ctx, String user, long delay) throws LockException { /*Q: why don't we lock the snapshot here??? Instead of having client make an explicit call whenever it chooses A: If we want to rely on locks for transaction scheduling we must get the snapshot after lock acquisition. Relying on locks is a pessimistic strategy which works better under high contention.*/ init(); getLockManager(); if(isTxnOpen()) { throw new LockException("Transaction already opened. " + JavaUtils.txnIdToString(txnId)); } try { txnId = getMS().openTxn(user); stmtId = 0; numStatements = 0; tableWriteIds.clear(); isExplicitTransaction = false; startTransactionCount = 0; LOG.info("Opened " + JavaUtils.txnIdToString(txnId)); ctx.setHeartbeater(startHeartbeat(delay)); return txnId; } catch (TException e) { throw new LockException(e, ErrorMsg.METASTORE_COMMUNICATION_FAILED); } }
@Override public void commitTxn() throws LockException { if (!isTxnOpen()) { throw new RuntimeException("Attempt to commit before opening a transaction"); } try { // do all new clear in clearLocksAndHB method to make sure that same code is there for replCommitTxn flow. clearLocksAndHB(); LOG.debug("Committing txn " + JavaUtils.txnIdToString(txnId)); getMS().commitTxn(txnId); } catch (NoSuchTxnException e) { LOG.error("Metastore could not find " + JavaUtils.txnIdToString(txnId)); throw new LockException(e, ErrorMsg.TXN_NO_SUCH_TRANSACTION, JavaUtils.txnIdToString(txnId)); } catch (TxnAbortedException e) { LockException le = new LockException(e, ErrorMsg.TXN_ABORTED, JavaUtils.txnIdToString(txnId), e.getMessage()); LOG.error(le.getMessage()); throw le; } catch (TException e) { throw new LockException(ErrorMsg.METASTORE_COMMUNICATION_FAILED.getMsg(), e); } finally { // do all new reset in resetTxnInfo method to make sure that same code is there for replCommitTxn flow. resetTxnInfo(); } } @Override
@Override public void rollbackTxn() throws LockException { if (!isTxnOpen()) { throw new RuntimeException("Attempt to rollback before opening a transaction"); } try { lockMgr.clearLocalLockRecords(); stopHeartbeat(); LOG.debug("Rolling back " + JavaUtils.txnIdToString(txnId)); getMS().rollbackTxn(txnId); } catch (NoSuchTxnException e) { LOG.error("Metastore could not find " + JavaUtils.txnIdToString(txnId)); throw new LockException(e, ErrorMsg.TXN_NO_SUCH_TRANSACTION, JavaUtils.txnIdToString(txnId)); } catch(TxnAbortedException e) { throw new LockException(e, ErrorMsg.TXN_ABORTED, JavaUtils.txnIdToString(txnId)); } catch (TException e) { throw new LockException(ErrorMsg.METASTORE_COMMUNICATION_FAILED.getMsg(), e); } finally { txnId = 0; statementId = -1; } }
@Override public void commitTxn() throws LockException { if (!isTxnOpen()) { throw new RuntimeException("Attempt to commit before opening a transaction"); } try { lockMgr.clearLocalLockRecords(); stopHeartbeat(); LOG.debug("Committing txn " + JavaUtils.txnIdToString(txnId)); getMS().commitTxn(txnId); } catch (NoSuchTxnException e) { LOG.error("Metastore could not find " + JavaUtils.txnIdToString(txnId)); throw new LockException(e, ErrorMsg.TXN_NO_SUCH_TRANSACTION, JavaUtils.txnIdToString(txnId)); } catch (TxnAbortedException e) { LockException le = new LockException(e, ErrorMsg.TXN_ABORTED, JavaUtils.txnIdToString(txnId), e.getMessage()); LOG.error(le.getMessage()); throw le; } catch (TException e) { throw new LockException(ErrorMsg.METASTORE_COMMUNICATION_FAILED.getMsg(), e); } finally { txnId = 0; statementId = -1; } }
@VisibleForTesting long openTxn(Context ctx, String user, long delay) throws LockException { //todo: why don't we lock the snapshot here??? Instead of having client make an explicit call //whenever it chooses init(); if(isTxnOpen()) { throw new LockException("Transaction already opened. " + JavaUtils.txnIdToString(txnId)); } try { txnId = getMS().openTxn(user); statementId = 0; LOG.debug("Opened " + JavaUtils.txnIdToString(txnId)); ctx.setHeartbeater(startHeartbeat(delay)); return txnId; } catch (TException e) { throw new LockException(e, ErrorMsg.METASTORE_COMMUNICATION_FAILED); } }
@Override public void replCommitTxn(CommitTxnRequest rqst) throws LockException { try { if (rqst.isSetReplLastIdInfo()) { if (!isTxnOpen()) { throw new RuntimeException("Attempt to commit before opening a transaction"); } // For transaction started internally by repl load command, heartbeat needs to be stopped. clearLocksAndHB(); } getMS().replCommitTxn(rqst); } catch (NoSuchTxnException e) { LOG.error("Metastore could not find " + JavaUtils.txnIdToString(rqst.getTxnid())); throw new LockException(e, ErrorMsg.TXN_NO_SUCH_TRANSACTION, JavaUtils.txnIdToString(rqst.getTxnid())); } catch (TxnAbortedException e) { LockException le = new LockException(e, ErrorMsg.TXN_ABORTED, JavaUtils.txnIdToString(rqst.getTxnid()), e.getMessage()); LOG.error(le.getMessage()); throw le; } catch (TException e) { throw new LockException(ErrorMsg.METASTORE_COMMUNICATION_FAILED.getMsg(), e); } finally { if (rqst.isSetReplLastIdInfo()) { // For transaction started internally by repl load command, needs to clear the txn info. resetTxnInfo(); } } }