Search in sources :

Example 1 with TransactionOperation

use of org.apache.activemq.artemis.core.transaction.TransactionOperation in project activemq-artemis by apache.

the class ScaleDownHandler method scaleDownTransactions.

public void scaleDownTransactions(ClientSessionFactory sessionFactory, ResourceManager resourceManager, String user, String password) throws Exception {
    ClientSession session = sessionFactory.createSession(user, password, true, false, false, false, 0);
    ClientSession queueCreateSession = sessionFactory.createSession(user, password, false, true, true, false, 0);
    List<Xid> preparedTransactions = resourceManager.getPreparedTransactions();
    Map<String, Long> queueIDs = new HashMap<>();
    for (Xid xid : preparedTransactions) {
        logger.debug("Scaling down transaction: " + xid);
        Transaction transaction = resourceManager.getTransaction(xid);
        session.start(xid, XAResource.TMNOFLAGS);
        List<TransactionOperation> allOperations = transaction.getAllOperations();
        // Get the information of the Prepared TXs so it could replay the TXs
        Map<Message, Pair<List<Long>, List<Long>>> queuesToSendTo = new HashMap<>();
        for (TransactionOperation operation : allOperations) {
            if (operation instanceof PostOfficeImpl.AddOperation) {
                PostOfficeImpl.AddOperation addOperation = (PostOfficeImpl.AddOperation) operation;
                List<MessageReference> refs = addOperation.getRelatedMessageReferences();
                for (MessageReference ref : refs) {
                    Message message = ref.getMessage();
                    Queue queue = ref.getQueue();
                    long queueID;
                    String queueName = queue.getName().toString();
                    if (queueIDs.containsKey(queueName)) {
                        queueID = queueIDs.get(queueName);
                    } else {
                        queueID = createQueueIfNecessaryAndGetID(queueCreateSession, queue, message.getAddressSimpleString());
                        // store it so we don't have to look it up every time
                        queueIDs.put(queueName, queueID);
                    }
                    Pair<List<Long>, List<Long>> queueIds = queuesToSendTo.get(message);
                    if (queueIds == null) {
                        queueIds = new Pair<List<Long>, List<Long>>(new ArrayList<Long>(), new ArrayList<Long>());
                        queuesToSendTo.put(message, queueIds);
                    }
                    queueIds.getA().add(queueID);
                }
            } else if (operation instanceof RefsOperation) {
                RefsOperation refsOperation = (RefsOperation) operation;
                List<MessageReference> refs = refsOperation.getReferencesToAcknowledge();
                for (MessageReference ref : refs) {
                    Message message = ref.getMessage();
                    Queue queue = ref.getQueue();
                    long queueID;
                    String queueName = queue.getName().toString();
                    if (queueIDs.containsKey(queueName)) {
                        queueID = queueIDs.get(queueName);
                    } else {
                        queueID = createQueueIfNecessaryAndGetID(queueCreateSession, queue, message.getAddressSimpleString());
                        // store it so we don't have to look it up every time
                        queueIDs.put(queueName, queueID);
                    }
                    Pair<List<Long>, List<Long>> queueIds = queuesToSendTo.get(message);
                    if (queueIds == null) {
                        queueIds = new Pair<List<Long>, List<Long>>(new ArrayList<Long>(), new ArrayList<Long>());
                        queuesToSendTo.put(message, queueIds);
                    }
                    queueIds.getA().add(queueID);
                    queueIds.getB().add(queueID);
                }
            }
        }
        ClientProducer producer = session.createProducer();
        for (Map.Entry<Message, Pair<List<Long>, List<Long>>> entry : queuesToSendTo.entrySet()) {
            List<Long> ids = entry.getValue().getA();
            ByteBuffer buffer = ByteBuffer.allocate(ids.size() * 8);
            for (Long id : ids) {
                buffer.putLong(id);
            }
            Message message = entry.getKey();
            message.putBytesProperty(Message.HDR_ROUTE_TO_IDS.toString(), buffer.array());
            ids = entry.getValue().getB();
            if (ids.size() > 0) {
                buffer = ByteBuffer.allocate(ids.size() * 8);
                for (Long id : ids) {
                    buffer.putLong(id);
                }
                message.putBytesProperty(Message.HDR_ROUTE_TO_ACK_IDS.toString(), buffer.array());
            }
            producer.send(message.getAddressSimpleString().toString(), message);
        }
        session.end(xid, XAResource.TMSUCCESS);
        session.prepare(xid);
    }
}
Also used : ClientMessage(org.apache.activemq.artemis.api.core.client.ClientMessage) Message(org.apache.activemq.artemis.api.core.Message) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) SimpleString(org.apache.activemq.artemis.api.core.SimpleString) ClientSession(org.apache.activemq.artemis.api.core.client.ClientSession) ArrayList(java.util.ArrayList) List(java.util.List) Queue(org.apache.activemq.artemis.core.server.Queue) ClientProducer(org.apache.activemq.artemis.api.core.client.ClientProducer) Pair(org.apache.activemq.artemis.api.core.Pair) PostOfficeImpl(org.apache.activemq.artemis.core.postoffice.impl.PostOfficeImpl) TransactionOperation(org.apache.activemq.artemis.core.transaction.TransactionOperation) MessageReference(org.apache.activemq.artemis.core.server.MessageReference) ByteBuffer(java.nio.ByteBuffer) Xid(javax.transaction.xa.Xid) Transaction(org.apache.activemq.artemis.core.transaction.Transaction) HashMap(java.util.HashMap) Map(java.util.Map)

Example 2 with TransactionOperation

use of org.apache.activemq.artemis.core.transaction.TransactionOperation in project activemq-artemis by apache.

the class TransactionImplTest method testTimeoutAndThenCommitWithARollback.

@Test
public void testTimeoutAndThenCommitWithARollback() throws Exception {
    TransactionImpl tx = new TransactionImpl(newXID(), new FakeSM(), 10);
    Assert.assertTrue(tx.hasTimedOut(System.currentTimeMillis() + 60000, 10));
    final AtomicInteger commit = new AtomicInteger(0);
    final AtomicInteger rollback = new AtomicInteger(0);
    tx.addOperation(new TransactionOperation() {

        @Override
        public void beforePrepare(Transaction tx) throws Exception {
        }

        @Override
        public void afterPrepare(Transaction tx) {
        }

        @Override
        public void beforeCommit(Transaction tx) throws Exception {
        }

        @Override
        public void afterCommit(Transaction tx) {
            System.out.println("commit...");
            commit.incrementAndGet();
        }

        @Override
        public void beforeRollback(Transaction tx) throws Exception {
        }

        @Override
        public void afterRollback(Transaction tx) {
            System.out.println("rollback...");
            rollback.incrementAndGet();
        }

        @Override
        public List<MessageReference> getRelatedMessageReferences() {
            return null;
        }

        @Override
        public List<MessageReference> getListOnConsumer(long consumerID) {
            return null;
        }
    });
    for (int i = 0; i < 2; i++) {
        try {
            tx.commit();
            Assert.fail("Exception expected!");
        } catch (ActiveMQException expected) {
        }
    }
    // it should just be ignored!
    tx.rollback();
    Assert.assertEquals(0, commit.get());
    Assert.assertEquals(1, rollback.get());
}
Also used : TransactionOperation(org.apache.activemq.artemis.core.transaction.TransactionOperation) Transaction(org.apache.activemq.artemis.core.transaction.Transaction) ActiveMQException(org.apache.activemq.artemis.api.core.ActiveMQException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) List(java.util.List) RouteContextList(org.apache.activemq.artemis.core.server.RouteContextList) ActiveMQException(org.apache.activemq.artemis.api.core.ActiveMQException) Test(org.junit.Test)

Example 3 with TransactionOperation

use of org.apache.activemq.artemis.core.transaction.TransactionOperation in project activemq-artemis by apache.

the class TransactionImplTest method testTimeoutThenRollbackWithRollback.

@Test
public void testTimeoutThenRollbackWithRollback() throws Exception {
    TransactionImpl tx = new TransactionImpl(newXID(), new FakeSM(), 10);
    Assert.assertTrue(tx.hasTimedOut(System.currentTimeMillis() + 60000, 10));
    final AtomicInteger commit = new AtomicInteger(0);
    final AtomicInteger rollback = new AtomicInteger(0);
    tx.addOperation(new TransactionOperation() {

        @Override
        public void beforePrepare(Transaction tx) throws Exception {
        }

        @Override
        public void afterPrepare(Transaction tx) {
        }

        @Override
        public void beforeCommit(Transaction tx) throws Exception {
        }

        @Override
        public void afterCommit(Transaction tx) {
            System.out.println("commit...");
            commit.incrementAndGet();
        }

        @Override
        public void beforeRollback(Transaction tx) throws Exception {
        }

        @Override
        public void afterRollback(Transaction tx) {
            System.out.println("rollback...");
            rollback.incrementAndGet();
        }

        @Override
        public List<MessageReference> getRelatedMessageReferences() {
            return null;
        }

        @Override
        public List<MessageReference> getListOnConsumer(long consumerID) {
            return null;
        }
    });
    tx.rollback();
    // This is a case where another failure was detected (In parallel with the TX timeout for instance)
    tx.markAsRollbackOnly(new ActiveMQException("rollback only again"));
    tx.rollback();
    Assert.assertEquals(0, commit.get());
    Assert.assertEquals(1, rollback.get());
}
Also used : TransactionOperation(org.apache.activemq.artemis.core.transaction.TransactionOperation) Transaction(org.apache.activemq.artemis.core.transaction.Transaction) ActiveMQException(org.apache.activemq.artemis.api.core.ActiveMQException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) List(java.util.List) RouteContextList(org.apache.activemq.artemis.core.server.RouteContextList) ActiveMQException(org.apache.activemq.artemis.api.core.ActiveMQException) Test(org.junit.Test)

Example 4 with TransactionOperation

use of org.apache.activemq.artemis.core.transaction.TransactionOperation in project activemq-artemis by apache.

the class TransactionImpl method internalRollback.

private void internalRollback() throws Exception {
    if (logger.isTraceEnabled()) {
        logger.trace("TransactionImpl::internalRollback " + this);
    }
    beforeRollback();
    try {
        doRollback();
        state = State.ROLLEDBACK;
    } catch (IllegalStateException e) {
        // Something happened before and the TX didn't make to the Journal / Storage
        // We will like to execute afterRollback and clear anything pending
        ActiveMQServerLogger.LOGGER.failedToPerformRollback(e);
    }
    // We want to make sure that nothing else gets done after the commit is issued
    // this will eliminate any possibility or races
    final List<TransactionOperation> operationsToComplete = this.operations;
    this.operations = null;
    final List<TransactionOperation> storeOperationsToComplete = this.storeOperations;
    this.storeOperations = null;
    // We use the Callback even for non persistence
    // If we are using non-persistence with replication, the replication manager will have
    // to execute this runnable in the correct order
    storageManager.afterCompleteOperations(new IOCallback() {

        @Override
        public void onError(final int errorCode, final String errorMessage) {
            ActiveMQServerLogger.LOGGER.ioErrorOnTX(errorCode, errorMessage);
        }

        @Override
        public void done() {
            afterRollback(operationsToComplete);
        }
    });
    if (storeOperationsToComplete != null) {
        storageManager.afterStoreOperations(new IOCallback() {

            @Override
            public void onError(final int errorCode, final String errorMessage) {
                ActiveMQServerLogger.LOGGER.ioErrorOnTX(errorCode, errorMessage);
            }

            @Override
            public void done() {
                afterRollback(storeOperationsToComplete);
            }
        });
    }
}
Also used : ActiveMQIllegalStateException(org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException) TransactionOperation(org.apache.activemq.artemis.core.transaction.TransactionOperation) IOCallback(org.apache.activemq.artemis.core.io.IOCallback)

Example 5 with TransactionOperation

use of org.apache.activemq.artemis.core.transaction.TransactionOperation in project activemq-artemis by apache.

the class TransactionImpl method commit.

@Override
public void commit(final boolean onePhase) throws Exception {
    if (logger.isTraceEnabled()) {
        logger.trace("TransactionImpl::commit::" + this);
    }
    synchronized (timeoutLock) {
        if (state == State.COMMITTED) {
            // I don't think this could happen, but just in case
            logger.debug("TransactionImpl::commit::" + this + " is being ignored");
            return;
        }
        if (state == State.ROLLBACK_ONLY) {
            internalRollback();
            if (exception != null) {
                throw exception;
            } else {
                // Do nothing
                return;
            }
        }
        if (xid != null) {
            if (onePhase && state != State.ACTIVE || !onePhase && state != State.PREPARED) {
                throw new ActiveMQIllegalStateException("Transaction is in invalid state " + state);
            }
        } else {
            if (state != State.ACTIVE) {
                throw new ActiveMQIllegalStateException("Transaction is in invalid state " + state);
            }
        }
        beforeCommit();
        doCommit();
        // We want to make sure that nothing else gets done after the commit is issued
        // this will eliminate any possibility or races
        final List<TransactionOperation> operationsToComplete = this.operations;
        this.operations = null;
        // We use the Callback even for non persistence
        // If we are using non-persistence with replication, the replication manager will have
        // to execute this runnable in the correct order
        // This also will only use a different thread if there are any IO pending.
        // If the IO finished early by the time we got here, we won't need an executor
        storageManager.afterCompleteOperations(new IOCallback() {

            @Override
            public void onError(final int errorCode, final String errorMessage) {
                ActiveMQServerLogger.LOGGER.ioErrorOnTX(errorCode, errorMessage);
            }

            @Override
            public void done() {
                afterCommit(operationsToComplete);
            }
        });
        final List<TransactionOperation> storeOperationsToComplete = this.storeOperations;
        this.storeOperations = null;
        if (storeOperationsToComplete != null) {
            storageManager.afterStoreOperations(new IOCallback() {

                @Override
                public void onError(final int errorCode, final String errorMessage) {
                    ActiveMQServerLogger.LOGGER.ioErrorOnTX(errorCode, errorMessage);
                }

                @Override
                public void done() {
                    afterCommit(storeOperationsToComplete);
                }
            });
        }
    }
}
Also used : TransactionOperation(org.apache.activemq.artemis.core.transaction.TransactionOperation) ActiveMQIllegalStateException(org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException) IOCallback(org.apache.activemq.artemis.core.io.IOCallback)

Aggregations

TransactionOperation (org.apache.activemq.artemis.core.transaction.TransactionOperation)5 List (java.util.List)3 Transaction (org.apache.activemq.artemis.core.transaction.Transaction)3 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)2 ActiveMQException (org.apache.activemq.artemis.api.core.ActiveMQException)2 ActiveMQIllegalStateException (org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException)2 IOCallback (org.apache.activemq.artemis.core.io.IOCallback)2 RouteContextList (org.apache.activemq.artemis.core.server.RouteContextList)2 Test (org.junit.Test)2 ByteBuffer (java.nio.ByteBuffer)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 Xid (javax.transaction.xa.Xid)1 Message (org.apache.activemq.artemis.api.core.Message)1 Pair (org.apache.activemq.artemis.api.core.Pair)1 SimpleString (org.apache.activemq.artemis.api.core.SimpleString)1 ClientMessage (org.apache.activemq.artemis.api.core.client.ClientMessage)1 ClientProducer (org.apache.activemq.artemis.api.core.client.ClientProducer)1 ClientSession (org.apache.activemq.artemis.api.core.client.ClientSession)1