mirror of https://github.com/apache/activemq.git
Transactions, when recovered, need to be recovered in prepare order:
a) order determined by an unsorted map iterator might confuse people b) brokerSequence can get out of step for messages recovered then committed - e.g. recovery on startup git-svn-id: https://svn.apache.org/repos/asf/incubator/activemq/trunk@490795 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
b1c94aa00b
commit
4a16f4527a
|
@ -21,6 +21,9 @@ package org.apache.activemq.store.journal;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.transaction.xa.XAException;
|
import javax.transaction.xa.XAException;
|
||||||
|
|
||||||
|
@ -34,15 +37,14 @@ import org.apache.activemq.command.XATransactionId;
|
||||||
import org.apache.activemq.store.TransactionRecoveryListener;
|
import org.apache.activemq.store.TransactionRecoveryListener;
|
||||||
import org.apache.activemq.store.TransactionStore;
|
import org.apache.activemq.store.TransactionStore;
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public class JournalTransactionStore implements TransactionStore {
|
public class JournalTransactionStore implements TransactionStore {
|
||||||
|
|
||||||
private final JournalPersistenceAdapter peristenceAdapter;
|
private final JournalPersistenceAdapter peristenceAdapter;
|
||||||
ConcurrentHashMap inflightTransactions = new ConcurrentHashMap();
|
Map inflightTransactions = new LinkedHashMap();
|
||||||
ConcurrentHashMap preparedTransactions = new ConcurrentHashMap();
|
Map preparedTransactions = new LinkedHashMap();
|
||||||
private boolean doingRecover;
|
private boolean doingRecover;
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,26 +131,39 @@ public class JournalTransactionStore implements TransactionStore {
|
||||||
* @see org.apache.activemq.store.TransactionStore#prepare(TransactionId)
|
* @see org.apache.activemq.store.TransactionStore#prepare(TransactionId)
|
||||||
*/
|
*/
|
||||||
public void prepare(TransactionId txid) throws IOException{
|
public void prepare(TransactionId txid) throws IOException{
|
||||||
Tx tx = (Tx) inflightTransactions.remove(txid);
|
Tx tx=null;
|
||||||
|
synchronized(inflightTransactions){
|
||||||
|
tx=(Tx)inflightTransactions.remove(txid);
|
||||||
|
}
|
||||||
if(tx==null)
|
if(tx==null)
|
||||||
return;
|
return;
|
||||||
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_PREPARE,txid,false),true);
|
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_PREPARE,txid,false),true);
|
||||||
|
synchronized(preparedTransactions){
|
||||||
preparedTransactions.put(txid,tx);
|
preparedTransactions.put(txid,tx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @see org.apache.activemq.store.TransactionStore#prepare(TransactionId)
|
* @see org.apache.activemq.store.TransactionStore#prepare(TransactionId)
|
||||||
*/
|
*/
|
||||||
public void replayPrepare(TransactionId txid) throws IOException{
|
public void replayPrepare(TransactionId txid) throws IOException{
|
||||||
Tx tx = (Tx) inflightTransactions.remove(txid);
|
Tx tx=null;
|
||||||
|
synchronized(inflightTransactions){
|
||||||
|
tx=(Tx)inflightTransactions.remove(txid);
|
||||||
|
}
|
||||||
if(tx==null)
|
if(tx==null)
|
||||||
return;
|
return;
|
||||||
|
synchronized(preparedTransactions){
|
||||||
preparedTransactions.put(txid,tx);
|
preparedTransactions.put(txid,tx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Tx getTx(Object txid,RecordLocation location){
|
public Tx getTx(Object txid,RecordLocation location){
|
||||||
Tx tx = (Tx) inflightTransactions.get(txid);
|
Tx tx=null;
|
||||||
|
synchronized(inflightTransactions){
|
||||||
|
tx=(Tx)inflightTransactions.get(txid);
|
||||||
|
}
|
||||||
if(tx==null){
|
if(tx==null){
|
||||||
tx=new Tx(location);
|
tx=new Tx(location);
|
||||||
inflightTransactions.put(txid,tx);
|
inflightTransactions.put(txid,tx);
|
||||||
|
@ -163,17 +178,18 @@ public class JournalTransactionStore implements TransactionStore {
|
||||||
public void commit(TransactionId txid,boolean wasPrepared) throws IOException{
|
public void commit(TransactionId txid,boolean wasPrepared) throws IOException{
|
||||||
Tx tx;
|
Tx tx;
|
||||||
if(wasPrepared){
|
if(wasPrepared){
|
||||||
|
synchronized(preparedTransactions){
|
||||||
tx=(Tx)preparedTransactions.remove(txid);
|
tx=(Tx)preparedTransactions.remove(txid);
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
|
synchronized(inflightTransactions){
|
||||||
tx=(Tx)inflightTransactions.remove(txid);
|
tx=(Tx)inflightTransactions.remove(txid);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if(tx==null)
|
if(tx==null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(txid.isXATransaction()){
|
if(txid.isXATransaction()){
|
||||||
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_COMMIT, txid, wasPrepared),
|
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_COMMIT,txid,wasPrepared),true);
|
||||||
true);
|
|
||||||
}else{
|
}else{
|
||||||
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.LOCAL_COMMIT,txid,wasPrepared),
|
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.LOCAL_COMMIT,txid,wasPrepared),
|
||||||
true);
|
true);
|
||||||
|
@ -186,32 +202,37 @@ public class JournalTransactionStore implements TransactionStore {
|
||||||
*/
|
*/
|
||||||
public Tx replayCommit(TransactionId txid,boolean wasPrepared) throws IOException{
|
public Tx replayCommit(TransactionId txid,boolean wasPrepared) throws IOException{
|
||||||
if(wasPrepared){
|
if(wasPrepared){
|
||||||
|
synchronized(preparedTransactions){
|
||||||
return (Tx)preparedTransactions.remove(txid);
|
return (Tx)preparedTransactions.remove(txid);
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
|
synchronized(inflightTransactions){
|
||||||
return (Tx)inflightTransactions.remove(txid);
|
return (Tx)inflightTransactions.remove(txid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @see org.apache.activemq.store.TransactionStore#rollback(TransactionId)
|
* @see org.apache.activemq.store.TransactionStore#rollback(TransactionId)
|
||||||
*/
|
*/
|
||||||
public void rollback(TransactionId txid) throws IOException{
|
public void rollback(TransactionId txid) throws IOException{
|
||||||
|
Tx tx=null;
|
||||||
Tx tx = (Tx) inflightTransactions.remove(txid);
|
synchronized(inflightTransactions){
|
||||||
|
tx=(Tx)inflightTransactions.remove(txid);
|
||||||
|
}
|
||||||
if(tx!=null)
|
if(tx!=null)
|
||||||
|
synchronized(preparedTransactions){
|
||||||
tx=(Tx)preparedTransactions.remove(txid);
|
tx=(Tx)preparedTransactions.remove(txid);
|
||||||
|
}
|
||||||
if(tx!=null){
|
if(tx!=null){
|
||||||
if(txid.isXATransaction()){
|
if(txid.isXATransaction()){
|
||||||
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_ROLLBACK, txid, false),
|
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_ROLLBACK,txid,false),true);
|
||||||
true);
|
|
||||||
}else{
|
}else{
|
||||||
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.LOCAL_ROLLBACK,txid,false),
|
peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.LOCAL_ROLLBACK,txid,false),
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -219,9 +240,16 @@ public class JournalTransactionStore implements TransactionStore {
|
||||||
* @see org.apache.activemq.store.TransactionStore#rollback(TransactionId)
|
* @see org.apache.activemq.store.TransactionStore#rollback(TransactionId)
|
||||||
*/
|
*/
|
||||||
public void replayRollback(TransactionId txid) throws IOException{
|
public void replayRollback(TransactionId txid) throws IOException{
|
||||||
if (inflightTransactions.remove(txid) != null)
|
boolean inflight=false;
|
||||||
|
synchronized(inflightTransactions){
|
||||||
|
inflight=inflightTransactions.remove(txid)!=null;
|
||||||
|
}
|
||||||
|
if(inflight){
|
||||||
|
synchronized(preparedTransactions){
|
||||||
preparedTransactions.remove(txid);
|
preparedTransactions.remove(txid);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void start() throws Exception {
|
public void start() throws Exception {
|
||||||
}
|
}
|
||||||
|
@ -231,12 +259,18 @@ public class JournalTransactionStore implements TransactionStore {
|
||||||
|
|
||||||
synchronized public void recover(TransactionRecoveryListener listener) throws IOException{
|
synchronized public void recover(TransactionRecoveryListener listener) throws IOException{
|
||||||
// All the in-flight transactions get rolled back..
|
// All the in-flight transactions get rolled back..
|
||||||
|
synchronized(inflightTransactions){
|
||||||
inflightTransactions.clear();
|
inflightTransactions.clear();
|
||||||
|
}
|
||||||
this.doingRecover=true;
|
this.doingRecover=true;
|
||||||
try{
|
try{
|
||||||
for (Iterator iter = preparedTransactions.keySet().iterator(); iter.hasNext();) {
|
Map txs=null;
|
||||||
|
synchronized(preparedTransactions){
|
||||||
|
txs=new LinkedHashMap(preparedTransactions);
|
||||||
|
}
|
||||||
|
for(Iterator iter=txs.keySet().iterator();iter.hasNext();){
|
||||||
Object txid=(Object)iter.next();
|
Object txid=(Object)iter.next();
|
||||||
Tx tx = (Tx) preparedTransactions.get(txid);
|
Tx tx=(Tx)txs.get(txid);
|
||||||
listener.recover((XATransactionId)txid,tx.getMessages(),tx.getAcks());
|
listener.recover((XATransactionId)txid,tx.getMessages(),tx.getAcks());
|
||||||
}
|
}
|
||||||
}finally{
|
}finally{
|
||||||
|
@ -270,14 +304,13 @@ public class JournalTransactionStore implements TransactionStore {
|
||||||
|
|
||||||
|
|
||||||
public RecordLocation checkpoint() throws IOException{
|
public RecordLocation checkpoint() throws IOException{
|
||||||
|
|
||||||
// Nothing really to checkpoint.. since, we don't
|
// Nothing really to checkpoint.. since, we don't
|
||||||
// checkpoint tx operations in to long term store until they are committed.
|
// checkpoint tx operations in to long term store until they are committed.
|
||||||
|
|
||||||
// But we keep track of the first location of an operation
|
// But we keep track of the first location of an operation
|
||||||
// that was associated with an active tx. The journal can not
|
// that was associated with an active tx. The journal can not
|
||||||
// roll over active tx records.
|
// roll over active tx records.
|
||||||
RecordLocation rc=null;
|
RecordLocation rc=null;
|
||||||
|
synchronized(inflightTransactions){
|
||||||
for(Iterator iter=inflightTransactions.values().iterator();iter.hasNext();){
|
for(Iterator iter=inflightTransactions.values().iterator();iter.hasNext();){
|
||||||
Tx tx=(Tx)iter.next();
|
Tx tx=(Tx)iter.next();
|
||||||
RecordLocation location=tx.location;
|
RecordLocation location=tx.location;
|
||||||
|
@ -285,6 +318,8 @@ public class JournalTransactionStore implements TransactionStore {
|
||||||
rc=location;
|
rc=location;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
synchronized(preparedTransactions){
|
||||||
for(Iterator iter=preparedTransactions.values().iterator();iter.hasNext();){
|
for(Iterator iter=preparedTransactions.values().iterator();iter.hasNext();){
|
||||||
Tx tx=(Tx)iter.next();
|
Tx tx=(Tx)iter.next();
|
||||||
RecordLocation location=tx.location;
|
RecordLocation location=tx.location;
|
||||||
|
@ -294,6 +329,7 @@ public class JournalTransactionStore implements TransactionStore {
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDoingRecover() {
|
public boolean isDoingRecover() {
|
||||||
return doingRecover;
|
return doingRecover;
|
||||||
|
|
Loading…
Reference in New Issue