mirror of https://github.com/apache/activemq.git
https://issues.apache.org/jira/browse/AMQ-2736 - KahaDB doesn't clean up old files (abortive shutdown)
After kill -9 with outstanding local transaction the transaction is recovered in error. fix and test git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1071732 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
14722f8627
commit
a6c51a4532
|
@ -493,6 +493,11 @@ public class KahaDBPersistenceAdapter implements PersistenceAdapter, BrokerServi
|
||||||
letter.setForceRecoverIndex(forceRecoverIndex);
|
letter.setForceRecoverIndex(forceRecoverIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for testing
|
||||||
|
public KahaDBStore getStore() {
|
||||||
|
return letter;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String path = getDirectory() != null ? getDirectory().toString() : "DIRECTORY_NOT_SET";
|
String path = getDirectory() != null ? getDirectory().toString() : "DIRECTORY_NOT_SET";
|
||||||
|
|
|
@ -960,29 +960,6 @@ public class KahaDBStore extends MessageDatabase implements PersistenceAdapter {
|
||||||
// Internal conversion methods.
|
// Internal conversion methods.
|
||||||
// /////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
KahaTransactionInfo createTransactionInfo(TransactionId txid) {
|
|
||||||
if (txid == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
KahaTransactionInfo rc = new KahaTransactionInfo();
|
|
||||||
|
|
||||||
if (txid.isLocalTransaction()) {
|
|
||||||
LocalTransactionId t = (LocalTransactionId) txid;
|
|
||||||
KahaLocalTransactionId kahaTxId = new KahaLocalTransactionId();
|
|
||||||
kahaTxId.setConnectionId(t.getConnectionId().getValue());
|
|
||||||
kahaTxId.setTransacitonId(t.getValue());
|
|
||||||
rc.setLocalTransacitonId(kahaTxId);
|
|
||||||
} else {
|
|
||||||
XATransactionId t = (XATransactionId) txid;
|
|
||||||
KahaXATransactionId kahaTxId = new KahaXATransactionId();
|
|
||||||
kahaTxId.setBranchQualifier(new Buffer(t.getBranchQualifier()));
|
|
||||||
kahaTxId.setGlobalTransactionId(new Buffer(t.getGlobalTransactionId()));
|
|
||||||
kahaTxId.setFormatId(t.getFormatId());
|
|
||||||
rc.setXaTransacitonId(kahaTxId);
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
KahaLocation convert(Location location) {
|
KahaLocation convert(Location location) {
|
||||||
KahaLocation rc = new KahaLocation();
|
KahaLocation rc = new KahaLocation();
|
||||||
rc.setLogId(location.getDataFileId());
|
rc.setLogId(location.getDataFileId());
|
||||||
|
|
|
@ -351,6 +351,11 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for testing
|
||||||
|
public LockFile getLockFile() {
|
||||||
|
return lockFile;
|
||||||
|
}
|
||||||
|
|
||||||
public void load() throws IOException {
|
public void load() throws IOException {
|
||||||
|
|
||||||
this.indexLock.writeLock().lock();
|
this.indexLock.writeLock().lock();
|
||||||
|
@ -417,10 +422,8 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// public for testing
|
||||||
* @return
|
public Location getFirstInProgressTxLocation() {
|
||||||
*/
|
|
||||||
private Location getFirstInProgressTxLocation() {
|
|
||||||
Location l = null;
|
Location l = null;
|
||||||
synchronized (inflightTransactions) {
|
synchronized (inflightTransactions) {
|
||||||
if (!inflightTransactions.isEmpty()) {
|
if (!inflightTransactions.isEmpty()) {
|
||||||
|
@ -474,6 +477,21 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
|
||||||
recoverIndex(tx);
|
recoverIndex(tx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// rollback any recovered inflight local transactions
|
||||||
|
Set<TransactionId> toRollback = new HashSet<TransactionId>();
|
||||||
|
synchronized (inflightTransactions) {
|
||||||
|
for (Iterator<TransactionId> it = inflightTransactions.keySet().iterator(); it.hasNext(); ) {
|
||||||
|
TransactionId id = it.next();
|
||||||
|
if (id.isLocalTransaction()) {
|
||||||
|
toRollback.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (TransactionId tx: toRollback) {
|
||||||
|
LOG.debug("rolling back recovered indoubt local transaction " + tx);
|
||||||
|
store(new KahaRollbackCommand().setTransactionInfo(createTransactionInfo(tx)), false, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}finally {
|
}finally {
|
||||||
this.indexLock.writeLock().unlock();
|
this.indexLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
@ -1987,6 +2005,32 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
|
||||||
this.databaseLockedWaitDelay = databaseLockedWaitDelay;
|
this.databaseLockedWaitDelay = databaseLockedWaitDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////
|
||||||
|
// Internal conversion methods.
|
||||||
|
// /////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
KahaTransactionInfo createTransactionInfo(TransactionId txid) {
|
||||||
|
if (txid == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
KahaTransactionInfo rc = new KahaTransactionInfo();
|
||||||
|
|
||||||
|
if (txid.isLocalTransaction()) {
|
||||||
|
LocalTransactionId t = (LocalTransactionId) txid;
|
||||||
|
KahaLocalTransactionId kahaTxId = new KahaLocalTransactionId();
|
||||||
|
kahaTxId.setConnectionId(t.getConnectionId().getValue());
|
||||||
|
kahaTxId.setTransacitonId(t.getValue());
|
||||||
|
rc.setLocalTransacitonId(kahaTxId);
|
||||||
|
} else {
|
||||||
|
XATransactionId t = (XATransactionId) txid;
|
||||||
|
KahaXATransactionId kahaTxId = new KahaXATransactionId();
|
||||||
|
kahaTxId.setBranchQualifier(new Buffer(t.getBranchQualifier()));
|
||||||
|
kahaTxId.setGlobalTransactionId(new Buffer(t.getGlobalTransactionId()));
|
||||||
|
kahaTxId.setFormatId(t.getFormatId());
|
||||||
|
rc.setXaTransacitonId(kahaTxId);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
class MessageOrderCursor{
|
class MessageOrderCursor{
|
||||||
long defaultCursorPosition;
|
long defaultCursorPosition;
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
/**
|
||||||
|
* 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.activemq.bugs;
|
||||||
|
|
||||||
|
import javax.jms.Connection;
|
||||||
|
import javax.jms.MessageProducer;
|
||||||
|
import javax.jms.Session;
|
||||||
|
import org.apache.activemq.ActiveMQConnectionFactory;
|
||||||
|
import org.apache.activemq.broker.BrokerService;
|
||||||
|
import org.apache.activemq.command.ActiveMQQueue;
|
||||||
|
import org.apache.activemq.store.kahadb.KahaDBPersistenceAdapter;
|
||||||
|
import org.apache.activemq.store.kahadb.KahaDBStore;
|
||||||
|
import org.apache.activemq.util.DefaultIOExceptionHandler;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
public class AMQ2736Test {
|
||||||
|
BrokerService broker;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRollbackOnRecover() throws Exception {
|
||||||
|
broker = createAndStartBroker(true);
|
||||||
|
DefaultIOExceptionHandler ignoreAllExceptionsIOExHandler = new DefaultIOExceptionHandler();
|
||||||
|
ignoreAllExceptionsIOExHandler.setIgnoreAllErrors(true);
|
||||||
|
broker.setIoExceptionHandler(ignoreAllExceptionsIOExHandler);
|
||||||
|
|
||||||
|
ActiveMQConnectionFactory f = new ActiveMQConnectionFactory("vm://localhost?async=false");
|
||||||
|
f.setAlwaysSyncSend(true);
|
||||||
|
Connection c = f.createConnection();
|
||||||
|
c.start();
|
||||||
|
Session s = c.createSession(true, Session.SESSION_TRANSACTED);
|
||||||
|
MessageProducer p = s.createProducer(new ActiveMQQueue("Tx"));
|
||||||
|
p.send(s.createTextMessage("aa"));
|
||||||
|
|
||||||
|
// kill journal without commit
|
||||||
|
KahaDBPersistenceAdapter pa = (KahaDBPersistenceAdapter) broker.getPersistenceAdapter();
|
||||||
|
KahaDBStore store = pa.getStore();
|
||||||
|
|
||||||
|
assertNotNull("last tx location is present " + store.getFirstInProgressTxLocation());
|
||||||
|
|
||||||
|
// test hack, close the journal to ensure no further journal updates when broker stops
|
||||||
|
// mimic kill -9 in terms of no normal shutdown sequence
|
||||||
|
store.getJournal().close();
|
||||||
|
try {
|
||||||
|
store.close();
|
||||||
|
} catch (Exception expectedLotsAsJournalBorked) {
|
||||||
|
}
|
||||||
|
store.getLockFile().unlock();
|
||||||
|
|
||||||
|
broker.stop();
|
||||||
|
broker.waitUntilStopped();
|
||||||
|
|
||||||
|
// restart with recovery
|
||||||
|
broker = createAndStartBroker(false);
|
||||||
|
|
||||||
|
pa = (KahaDBPersistenceAdapter) broker.getPersistenceAdapter();
|
||||||
|
store = pa.getStore();
|
||||||
|
|
||||||
|
// inflight non xa tx should be rolledback on recovery
|
||||||
|
assertNull("in progress tx location is present ", store.getFirstInProgressTxLocation());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void stopBroker() throws Exception {
|
||||||
|
if (broker != null) {
|
||||||
|
broker.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BrokerService createAndStartBroker(boolean deleteAll) throws Exception {
|
||||||
|
BrokerService broker = new BrokerService();
|
||||||
|
broker.setDeleteAllMessagesOnStartup(deleteAll);
|
||||||
|
broker.setUseJmx(false);
|
||||||
|
broker.getManagementContext().setCreateConnector(false);
|
||||||
|
broker.start();
|
||||||
|
return broker;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue