Problemas com TransactionScope e Oracle
nós escrevemos um cliente C # 3.5 conversando com um banco de dados Oracle (11g) usando o ODP.NET.
Esse aplicativo possui um processo em lote no qual uma tarefa de execução longa é realizada, fazendo várias chamadas para o banco de dados em um TransactionScope.
Em nosso ambiente de desenvolvimento, tudo corre bem, mas no ambiente UAT de um de nossos clientes (que possui muitos dados) ocorrem dois erros alternativos (às vezes um, às vezes o outro ...):
Não foi possível se inscrever em uma transação distribuídaA transação foi interrompida. (exceção interna: tempo limite da transação)Atualmente, usamos um tempo limite deum dia para a transação (para fins de teste).
A execução do referido processo no ambiente UAT interrompe após aprox. 10 minutos com uma das exceções acima, portanto, não há como chegar perto do valor do tempo limite.
Aqui está um trecho do rastreamento de pilha para o segundo erro:
at System.Transactions.TransactionStatePromotedAborted.CreateAbortingClone(InternalTransaction tx)
at System.Transactions.DependentTransaction..ctor(IsolationLevel isoLevel, InternalTransaction internalTransaction, Boolean blocking)
at System.Transactions.Transaction.DependentClone(DependentCloneOption cloneOption)
at System.Transactions.TransactionScope.SetCurrent(Transaction newCurrent)
at System.Transactions.TransactionScope.PushScope()
at System.Transactions.TransactionScope..ctor(TransactionScopeOption scopeOption)
at System.Transactions.TransactionScope..ctor()
at Application.Domain.DataAccess.Oracle.EntityDaoBase`2.SaveItem(TEntity item, EntityReference`1 user)
O processo tenta salvar um item no banco de dados dentro do escopo da transação, mas o rastreamento de pilha mostra que o construtor é atingido pela classe TransactionScope, o que significa que ele cria um novo TransactionScope.
Estou certo até agora?
Como eu não conheço muito do funcionamento interno do TransactionScope, mas parece que quando você chama um método dentro do escopo, ele cria uma nova transação (supostamente herdada da transação ambiental).
Será que, se eu estiver certo, essa nova transação não herda o tempo limite correto (mas o padrão), para que uma transação aninhada cause essa exceção de tempo limite?
Se não, algum pensamento sobre o que possivelmente pode ser? Em uma nota lateral, não há transações aninhadas definidas nos métodos chamados de dentro da transação do ambiente.
Qualquer ajuda seria muito apreciada!
Editar 1:
Fragmento de código simplificado da função:
public void SomeLengthyBatchProcess()
{
using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0, 0)))
{
foreach (var item in Items)
{
SaveItemToDB(item);
}
transaction.Complete();
}
}
public void SaveItemToDB(object item)
{
using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0, 0)))
{
// Performing data persistency here
transaction.Complete();
}
}
Edição 2:
Ok, então, como se vê, háé uma transação aninhada acontecendo no método 'SaveItemToDB'. Depois de analisar o código que um colega criou, vi que ele tinha seu próprio TransactionScope definido, mas sem opções e tempo limite.
Depois de modificar esse método para que ele tenha os mesmos parâmetros em relação ao tempo limite, executei o código novamente no servidor do cliente e ainda não tive sorte (novamente a transação interrompeu o erro com o tempo limite).
Então, minhas perguntas agora são as seguintes:
É necessário definir valores de tempo limite para transações aninhadas ou eles herdam isso da transação ambiental?Como é possível que uma exceção de tempo limite ocorra quando a configuração de tempo limite é (presumivelmente, além do funcionamento interno que eu não conheço) o mesmo para todos os escopos de transação e tem um valor de tempo limite definido de 1 dia, onde a exceção ocorre após aprox. 10 minutos?É possível impedir que o Oracle crie uma transação distribuída para transações nas quais a cadeia de conexão é a mesma?Pode ser que a sobrecarga adicionada de uma transação distribuída cause exceções como a transação anulada?Atualizei o snippet de código para refletir melhor a situação.
(btw: a segunda transação aninhada é necessária porque o DAL também persiste em parte alguns itens filhos, se presentes, e todo o item deve, é claro, ser revertido se algo der errado ao persistir os itens filhos)
Felizmente, com esta adição, será mais fácil lançar alguma luz sobre esta questão!