Como resolver Weblogic com Oracle ORA-01591 lock held by in-doubt distributed transaction
Essa semana tive muitos problemas no trabalho por causa desse ORA-01591, chegou ao ponto dele travar minha aplicação no weblogic em produção. Portanto decidi compartilhar a solução que custamos a chegar, inclusive o suporte da Oracle teve que ser ativado para conseguir essas informações. Importante: reiniciar o banco NÃO limpa essas transações in-doubt.
Descrição
A descrição desse erro pode ser encontrada no site oficial http://ora-01591.ora-code.com/, simplificando esse problema ocorre quando uma transação distribuída perde seu coordenador de transação. Ou seja, o Oracle fica em dúvida sobre o que deve ser feito com a transação que agora está sem coordenador, assim ele mantém ativo o lock que essa transação estava fazendo no registro e se outra transação tentar alterar essa mesma linha o oracle lança o erro ORA-01591.
Se você caiu aqui procurando por esse erro você deve ter entendido o que eu expliquei acima, já que estará utilizando os recursos avançados como transação distribuída.
Solução
A solução para esse problema é forçar o rollback ou commit da transação para liberar os locks do banco. Eu recomendo o rollback para evitar inconsistências nas tabelas.
Bem para conseguir fazer um rollback da transação é preciso saber qual o transaction ID da transação que está segurando o lock. Existem duas possibilidades e em ambas é necessário ser SYS para executar:
Possibilidade 1
Fazer um select na sys.pending_trans$ ou na sys.pending_sessions$ para obter o local_tran_id, será um String como essa 166.5.1546.
Pronto, agora basta fazer o rollback com o comando:
rollback force ‘166.5.1546’;
Se não ocorrer nenhum erro, parabéns seu lock foi removido e sua aplicação deve voltar ao normal. Entretanto, se ocorreu algum erro dizendo que não encontrou essa transação, vá para a possibilidade 2.
Possibilidade 2
Nesse caso o problema é mais grave, pois sua transação nem está mais nas tabelas de controle do oracle. Foi nesse caso que eu esbarrei e a solução é mais complicada.
- Passo 1: Fazer seguinte select para obter o número da transação:
SELECT KTUXEUSN, KTUXESLT, KTUXESQN, KTUXESTA Status, KTUXECFL Flags FROM x$ktuxe WHERE ktuxesta!=’INACTIVE’;
- Passo 2: Agora some os campos da seguinte maneira (já pode fazer isso direto no select):
KTUXEUSN + ‘.’ + KTUXESLT + ‘.’ + KTUXESQN, isso vai te dar o número da transação no formato esperado166.5.1546
- Passo 3: Temos que inserir esse valor nas tabelas de controle, sim isso mesmo! Tem que inserir nas tabelas internas do Oracle!
INSERT INTO sys.pending_trans$
(local_tran_id, global_tran_fmt, global_oracle_id, state, status, session_vector, reco_vector, type#, fail_time, reco_time)
VALUES
(!!!SEU_ID_TRANSACAO, 306206, ‘XXXXXXX.12345.1.2.3’, ‘prepared’, ‘P’, HEXTORAW(‘00000001’), HEXTORAW(‘00000000’), 0, SYSDATE, SYSDATE);
INSERT INTO sys.pending_sessions$
VALUES
(!!!SEU_ID_TRANSACAO,1,HEXTORAW(‘05004F003A1500000104′),’C’, 0, 30258592, ”, 146);
COMMIT;
- Passo 4: executar o comando
rollback force ‘166.5.1546’;
COMMIT; - Passo 5: verificar se deu tudo certo e se os registros que você inseriu sumiram das tabelas sys.pending_trans$ e sys.pending_sessions$. Se sumiu o registro pode parar por aqui, problema resolvido, caso contrário tentar fazer o passo 6.
- Passo 6: Execute o comando: dbms_transaction.purge_lost_db_entry(!!!SEU_ID_TRANSACAO);
Faça as verificações do passo 5, se falhar tente fazer o passo 7. - Passo 7: Execute os deletes para limpar os registros fake:
DELETE FROM sys.pending_trans$ WHERE local_tran_id = !!!SEU_ID_TRANSACAO;
DELETE FROM sys.pending_sessions$ WHERE local_tran_id = !!!SEU_ID_TRANSACAO;
Nesse ponto sua transação deve ter sido liberada, senão funcionou contate o suporte da Oracle. Lembrando que reiniciar o banco NÃO limpa essas transações in-doubt
Parabéns cara, me ajudou muito.
esse tipo de situação a gente nunca mais esquece 🙂