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);
}
}
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());
}
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());
}
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);
}
});
}
}
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);
}
});
}
}
}
Aggregations