Banco de Dados

21 ago, 2018

MongoDB e as aplicações resilientes

Publicidade

Em uma busca rápida no Google, o primeiro significado que eu encontrei para a palavra resiliência, foi: “Capacidade de se recobrar facilmente ou se adaptar à má sorte ou às mudanças.”

Palavra forte e empoderadora, que trouxe à tona a seguinte questão: você tem se preocupado em criar aplicações resilientes?

O MongoDB é um SGBD fantástico! Mas se ele for a sua escolha, você precisa estar atento as características dele para criar uma aplicação resiliente.

Em artigos anteriores eu falei um pouco sobre as transações multidocumento no MongoDB 4.0. Este recurso é muito legal e pode ser um diferencial para as suas aplicações… E este post tem como objetivo principal fazer com que as transações multidocumento sejam um diferencial positivo!!!

De acordo com a documentação da MongoDB “Independentemente do SGBD, seja o MongoDB ou bancos de dados relacionais, os aplicativos devem tomar medidas para lidar com erros durante os commits de transação e incorporar a lógica necessária para a repetição dos comandos.”

Quando ocorre um erro em uma operação, dentro transação multidocumento, as operações não podem ser executadas novamente de maneira automática (não chorem por isso!). Caberá a você criar uma aplicação que se recupere deste estado. E acredite em mim, não é difícil!

Quando ocorre um erro transitório ele deve ser identificado para que a transação seja automaticamente repetida.

E como identificar este tipo de erro? É só verificar se a matriz errorLabels contém um elemento “TransientTransactionError”.

O exemplo de código abaixo, extraído da documentação oficial do MongoDB, vemos como tratar os erros transientes

// Executa a transação txnFunc e tenta novamente se encontrar TransientTransactionError 

function runTransactionWithRetry(txnFunc, session) {
    while (true) {
        try {
            txnFunc(session);  // executa a transação
            break;
        } catch (error) {
            print("Transaction aborted. Caught exception during transaction.");

            // Se ocorrer um erro transitório, tente novamente toda a transação
            if ( error.hasOwnProperty("errorLabels") && error.errorLabels.includes( "TransientTransactionError")  ) {
                print("TransientTransactionError, retrying transaction ...");
                continue;
            } else {
                throw error;
            }
        }
    }
}

Retryable Writes

Este recurso não é uma novidade da versão 4.0 do MongoDB (foi lançado na versão 3.6), mas para que você faça uma aplicação resiliente é muito bom você conhecê-lo.

As Retryable Writes permitem que o driver do MongoDB tente automaticamente fazer uma nova tentativa da operação realizada sobre dados se encontrar erros (por exemplo, não encontrou um primary, erros de rede, etc).

E combina com as transações? Sim e não.

As operações de commit e o abort são operações que podem ser repetidas, ou seja, se ocorrer um erro em uma destas etapas, os drivers do MongoDB tentarão novamente fazer a operação, independentemente da propriedade retryWrites estar definida como true.

Contudo, todavia e entretanto as operações de gravação dentro de uma transação não podem ser repetidas individualmente, independentemente de retryWrites estar definida como true.

Para habilitar o retryWrites no Mongo Shell:

Mongo --retryWrites

Resumindo, se ocorrer um erro em uma operação de commit ou abort, o MongoDB automaticamente irá fazer uma nova tentativa.
Mas e se na nova tentativa ocorrer um erro, a operação não será executada novamente. Cabe a você criar o código para que esta situação não ocorra, deixando sua aplicação mais resiliente.

Erros no commit da transação são identificados no elemento “UnknownTransactionCommitResult” da matriz errorLabels.

A seguir temos um exemplo de tratamento para este erro, extraído da documentação oficial do MongoDB:

// Tenta novamente fazer o commit de uma transação se encontrar um erro

function commitWithRetry(session) {
    while (true) {
        try {
            session.commitTransaction();
            print("Transaction committed.");
            break;
        } catch (error) {
            // Can retry commit
            if (error.hasOwnProperty("errorLabels") && error.errorLabels.includes( "UnknownTransactionCommitResult") ) {
                print("UnknownTransactionCommitResult, retrying commit operation ...");
                continue;
            } else {
                print("Error during commit ...");
                throw error;
            }
       }
    }
}

Conclusão

O MongoDB 4.0 tem muitas possibilidades fantásticas! Mas para utilizá-lo com sucesso caberá a você conhecer as características dele e construir aplicações cada vez melhores e mais resilientes!

Referências