/* * 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.hive.hcatalog.streaming.mutate.client; import org.apache.hadoop.hive.metastore.IMetaStoreClient; import org.apache.hadoop.hive.metastore.api.NoSuchTxnException; import org.apache.hadoop.hive.metastore.api.TxnAbortedException; import org.apache.hive.hcatalog.streaming.TransactionBatch.TxnState; import org.apache.hive.hcatalog.streaming.mutate.client.lock.Lock; import org.apache.hive.hcatalog.streaming.mutate.client.lock.LockException; import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Transaction { private static final Logger LOG = LoggerFactory.getLogger(Transaction.class); private final Lock lock; private final IMetaStoreClient metaStoreClient; private final long transactionId; private TxnState state; Transaction(IMetaStoreClient metaStoreClient, Lock.Options lockOptions) throws TransactionException { this(metaStoreClient, new Lock(metaStoreClient, lockOptions)); } /** Visible for testing only. */ Transaction(IMetaStoreClient metaStoreClient, Lock lock) throws TransactionException { this.metaStoreClient = metaStoreClient; this.lock = lock; transactionId = open(lock.getUser()); } public long getTransactionId() { return transactionId; } public TxnState getState() { return state; } /** * Begin the transaction. Acquires a {@link Lock} for the transaction and {@link AcidTable AcidTables}. */ public void begin() throws TransactionException { try { lock.acquire(transactionId); } catch (LockException e) { throw new TransactionException("Unable to acquire lock for transaction: " + transactionId, e); } state = TxnState.OPEN; LOG.debug("Begin. Transaction id: {}", transactionId); } /** Commits the transaction. Releases the {@link Lock}. */ public void commit() throws TransactionException { try { lock.release(); } catch (LockException e) { // This appears to leave the remove transaction in an inconsistent state but the heartbeat is now // cancelled and it will eventually time out throw new TransactionException("Unable to release lock: " + lock + " for transaction: " + transactionId, e); } try { metaStoreClient.commitTxn(transactionId); state = TxnState.COMMITTED; } catch (NoSuchTxnException e) { throw new TransactionException("Invalid transaction id: " + transactionId, e); } catch (TxnAbortedException e) { throw new TransactionException("Aborted transaction cannot be committed: " + transactionId, e); } catch (TException e) { throw new TransactionException("Unable to commit transaction: " + transactionId, e); } LOG.debug("Committed. Transaction id: {}", transactionId); } /** Aborts the transaction. Releases the {@link Lock}. */ public void abort() throws TransactionException { try { lock.release(); } catch (LockException e) { // This appears to leave the remove transaction in an inconsistent state but the heartbeat is now // cancelled and it will eventually time out throw new TransactionException("Unable to release lock: " + lock + " for transaction: " + transactionId, e); } try { metaStoreClient.rollbackTxn(transactionId); state = TxnState.ABORTED; } catch (NoSuchTxnException e) { throw new TransactionException("Unable to abort invalid transaction id : " + transactionId, e); } catch (TException e) { throw new TransactionException("Unable to abort transaction id : " + transactionId, e); } LOG.debug("Aborted. Transaction id: {}", transactionId); } @Override public String toString() { return "Transaction [transactionId=" + transactionId + ", state=" + state + "]"; } private long open(String user) throws TransactionException { long transactionId = -1; try { transactionId = metaStoreClient.openTxn(user); state = TxnState.INACTIVE; } catch (TException e) { throw new TransactionException("Unable to open transaction for user: " + user, e); } LOG.debug("Opened transaction with id: {}", transactionId); return transactionId; } }