domingo, 15 de abril de 2012

Como resolver problemas com transações Two-Phase-Commit 1/2


Esse artigo sera dividido em duas partes a primeira relatando o problema e possíveis soluções e na segunda parte apresentaremos como remover transações distribuídas usando COMMIT e ROLLBACK force.

A CAUSA

Como resultado de uma falha na confirmação de uma transação distribuída, algumas entradas podem ser gravadas na visão DBA_2PC_PENDING e na DBA_2PC_NEIGHBORS. O evento acontece quando o processo de recover (RECO) verifica essas visões para recuperar a transação que falhou. No entanto, em alguns casos não é possível executar a recuperação. Isso ocorre porque todos os locais que estavam envolvidos na transação não estão mais acessíveis. Outra causa é a visão DBA_2PC_PENDING ficar com transações inconsistentes, que é o tema deste artigo. Essa causa pode ainda ser classificada da seguinte maneira:

1. Entradas na visão DBA_2PC_PENDING para uma transação distribuída inexistente;

2. Há uma transação distribuída para os quais não existem entradas no dba_2pc exibições (inverso do item 1);

3. Como “limpar” (purgar) as transações distribuídas que estão com o “status” como PREPARED. Esse erro ocorre quando uma transação distribuída trava. As visões DBA_2PC_PENDING e DBA_2PC_NEIGHBORS armazenam os detalhes das transações que não foram adequadamente resolvidas.

SOLUÇÕES
Para as entradas na visão DBA_2PC_PENDING sem uma transação correspondente.

Neste caso a visão DBA_2PC_PENDING apresenta as transações distribuídas, mas não há nenhum transação pendente na realidade. Se o “status” da transação for confirmado, a transação pode ser confirmada realizando um “COMMIT FORCE” ou pode-se voltar a transação executando um “ROLLBACK FORCE”. Pode-se utilizar a package “DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY”, para fazer o trabalho.

No entanto, se o “status” da transação está em PREPARED e não há nenhuma entrada na tabela de transação, então esta entrada pode ser limpa manualmente utilizando os passos abaixo:.

SQL> set transaction use rollback segment SYSTEM;
SQL> delete from sys.pending_trans$ where local_tran_id = [valor_local_tran_id];
SQL> delete from sys.pending_sessions$ where local_tran_id = [valor_local_tran_id];

SQL> delete from sys.pending_sub_sessions$ where local_tran_id = [valor_local_tran_id];

SQL> commit;
Exemplo: A consulta a seguir relata uma transação distribuída com o “status” PREPARED.
SQL> select local_tran_id, state from dba_2pc_pending;
LOCAL_TRAN_ID          STATE
---------------------- ----------------
1.92.66874             prepared


Dado que a LOCAL_TRAN_ID é composta por, '1.92.66874' está localizada no segmento de rollback #1 (SYSTEM). Para encontrar a transação que está em execução deve-se utilizar a consulta abaixo:

SQL> SELECT KTUXEUSN, KTUXESLT, KTUXESQN, /* Transaction ID */,
            KTUXESTA Status, KTUXECFL Flags
       FROM x$ktuxe
      WHERE ktuxesta!='INACTIVE'
        AND ktuxeusn= 1; <== este é o número do segmento de rollback em uso.

no rows selected
Para forçar o “ROLLBACK”, usar:

SQL> rollback force '1.92.66874';
ORA-02058: no prepared transaction found with ID 1.92.66874

Neste caso, não é possível executar o “ROLLBACK FORCE”, então deve-se realizar a limpeza desta transação manualmente:

SQL> set transaction use rollback segment SYSTEM;
SQL>
delete from sys.pending_trans$ where local_tran_id = '1.92.66874';
SQL>
delete from sys.pending_sessions$ where local_tran_id = '1.92.66874';
SQL>
delete from sys.pending_sub_sessions$ where local_tran_id = '1.92.66874';
SQL>
commit;

Em nosso próximo artigo apresentarei a continuação deste assunto…


Rubens Oliveira
DBA Oracle Consultor
olivert.dba@consultant.com