2009-06-29 15:01:20 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009 Scalable Solutions.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-09-02 09:10:21 +02:00
|
|
|
package se.scalablesolutions.akka.stm
|
2009-06-29 15:01:20 +02:00
|
|
|
|
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean
|
|
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
import se.scalablesolutions.akka.reactor.MessageInvocation
|
|
|
|
|
import se.scalablesolutions.akka.util.Logging
|
|
|
|
|
|
|
|
|
|
import org.codehaus.aspectwerkz.proxy.Uuid
|
|
|
|
|
|
|
|
|
|
import scala.collection.mutable.HashSet
|
|
|
|
|
|
|
|
|
|
// FIXME is java.util.UUID better?
|
2009-07-06 23:45:15 +02:00
|
|
|
|
2009-08-15 22:44:29 +02:00
|
|
|
import org.multiverse.utils.TransactionThreadLocal._
|
|
|
|
|
|
2009-07-06 23:45:15 +02:00
|
|
|
class TransactionRollbackException(msg: String) extends RuntimeException(msg)
|
2009-06-29 15:01:20 +02:00
|
|
|
|
|
|
|
|
class TransactionAwareWrapperException(val cause: Throwable, val tx: Option[Transaction]) extends RuntimeException(cause) {
|
|
|
|
|
override def toString(): String = "TransactionAwareWrapperException[" + cause + ", " + tx + "]"
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-29 17:33:38 +02:00
|
|
|
object TransactionManagement {
|
2009-10-06 00:07:27 +02:00
|
|
|
import se.scalablesolutions.akka.Config._
|
2009-09-02 09:10:21 +02:00
|
|
|
val TIME_WAITING_FOR_COMPLETION = config.getInt("akka.stm.wait-for-completion", 100)
|
|
|
|
|
val NR_OF_TIMES_WAITING_FOR_COMPLETION = config.getInt("akka.stm.wait-nr-of-times", 3)
|
2009-10-06 00:07:27 +02:00
|
|
|
val MAX_NR_OF_RETRIES = config.getInt("akka.stm.max-nr-of-retries", 10)
|
2009-09-30 20:08:55 +02:00
|
|
|
val TRANSACTION_ENABLED = new AtomicBoolean(config.getBool("akka.stm.service", false))
|
2009-07-06 23:45:15 +02:00
|
|
|
// FIXME reenable 'akka.stm.restart-on-collision' when new STM is in place
|
2009-09-02 09:10:21 +02:00
|
|
|
val RESTART_TRANSACTION_ON_COLLISION = false //akka.Kernel.config.getBool("akka.stm.restart-on-collision", true)
|
2009-06-29 17:33:38 +02:00
|
|
|
|
2009-07-04 12:06:07 +02:00
|
|
|
def isTransactionalityEnabled = TRANSACTION_ENABLED.get
|
|
|
|
|
def disableTransactions = TRANSACTION_ENABLED.set(false)
|
2009-06-29 15:01:20 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
private[akka] val currentTransaction: ThreadLocal[Option[Transaction]] = new ThreadLocal[Option[Transaction]]() {
|
2009-07-01 15:29:06 +02:00
|
|
|
override protected def initialValue: Option[Transaction] = None
|
2009-06-29 15:01:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trait TransactionManagement extends Logging {
|
2009-07-12 23:08:17 +02:00
|
|
|
var uuid = Uuid.newUuid.toString
|
2009-07-06 23:45:15 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
import TransactionManagement.currentTransaction
|
|
|
|
|
private[akka] val activeTransactions = new HashSet[Transaction]
|
2009-06-29 15:01:20 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
protected def startNewTransaction(message: MessageInvocation) = {
|
2009-06-29 15:01:20 +02:00
|
|
|
val newTx = new Transaction
|
2009-10-06 00:07:27 +02:00
|
|
|
newTx.begin(uuid, message)
|
|
|
|
|
activeTransactions += newTx
|
|
|
|
|
currentTransaction.set(Some(newTx))
|
|
|
|
|
setThreadLocalTransaction(newTx.transaction)
|
2009-06-29 15:01:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected def joinExistingTransaction = {
|
2009-10-06 00:07:27 +02:00
|
|
|
val cflowTx = currentTransaction.get
|
|
|
|
|
if (activeTransactions.isEmpty && cflowTx.isDefined) {
|
2009-06-29 15:01:20 +02:00
|
|
|
val currentTx = cflowTx.get
|
2009-07-02 18:07:29 +02:00
|
|
|
currentTx.join(uuid)
|
2009-10-06 00:07:27 +02:00
|
|
|
activeTransactions += currentTx
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected def tryToPrecommitTransactions = activeTransactions.foreach(_.precommit(uuid))
|
|
|
|
|
|
|
|
|
|
protected def tryToCommitTransactions = {
|
|
|
|
|
for (tx <- activeTransactions) {
|
|
|
|
|
if (tx.commit(uuid)) activeTransactions -= tx
|
|
|
|
|
else if (tx.isTopLevel) {
|
|
|
|
|
println("------------ COULD NOT COMMIT -- WAITING OR TIMEOUT? ---------")
|
|
|
|
|
//tx.retry
|
|
|
|
|
} else {
|
|
|
|
|
// continue, try to commit on next received message
|
|
|
|
|
// FIXME check if TX hase timed out => throw exception
|
|
|
|
|
}
|
2009-06-29 15:01:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-10-06 00:07:27 +02:00
|
|
|
|
2009-06-29 15:01:20 +02:00
|
|
|
protected def rollback(tx: Option[Transaction]) = tx match {
|
|
|
|
|
case None => {} // no tx; nothing to do
|
|
|
|
|
case Some(tx) =>
|
2009-07-02 18:07:29 +02:00
|
|
|
tx.rollback(uuid)
|
2009-06-29 15:01:20 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-04 12:06:07 +02:00
|
|
|
protected def rollbackForRescheduling(tx: Option[Transaction]) = tx match {
|
|
|
|
|
case None => {} // no tx; nothing to do
|
|
|
|
|
case Some(tx) =>
|
|
|
|
|
tx.rollbackForRescheduling(uuid)
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
protected def isInExistingTransaction = currentTransaction.get.isDefined
|
2009-06-29 15:01:20 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
protected def incrementTransaction = if (currentTransaction.get.isDefined) currentTransaction.get.get.increment
|
2009-06-29 15:01:20 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
protected def decrementTransaction = if (currentTransaction.get.isDefined) currentTransaction.get.get.decrement
|
2009-06-29 15:01:20 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
protected def removeTransactionIfTopLevel(tx: Transaction) = if (tx.isTopLevel) { activeTransactions -= tx }
|
2009-06-29 15:01:20 +02:00
|
|
|
}
|
|
|
|
|
|