Como você resolve a questão da consistência na aplicação simultânea e distribuída (construída em torno do Bankers Dilemma)?

5

Este é um problema clássico que tenho certeza que foi resolvido muitas vezes por muitas pessoas diferentes. Eu não tenho nenhum treinamento formal (eu não estudei ciência da computação ou qualquer outro assunto acadêmico) e então não tenho certeza da melhor maneira de resolver o problema que estou prestes a descrever.

Se imaginarmos o diagrama abaixo é um exemplo de um dilema dos banqueiros (dois usuários Foo e Bar têm acesso a uma única conta bancária: Baz). Qual é o comportamento esperado ao seguir um dos caminhos mostrados?

Note: I'm assuming we're using a mutex (or some other form of synchronisation) on the Baz variable.

Exemplo 1: Baz inicialmente mantém o valor 10. Se Foo gravar um novo valor (que é o resultado da remoção de 5 do valor atual) antes de Bar; então Bar vai acabar levando 10 do novo valor 5, deixando um saldo negativo (ou seja, o valor final será -5). Significa que mais dinheiro foi gasto do que o disponível.

Exemplo 2: Baz inicialmente mantém o valor 10. Se Bar escreve um novo valor (que é o resultado da remoção de 10 do valor atual) antes de Foo; então Foo vai acabar tirando 5 do novo valor 0, deixando um saldo negativo (ou seja, o valor final será -5). Significa que mais dinheiro foi gasto do que o disponível.

Ambas as ações ( Foo (-5) e Bar (-10) ) são acionadas ao mesmo tempo. Então, como podemos garantir que Foo ou Bar seja alertado para o fato de que sua transação não pode ser concluída (já que não há fundos suficientes para que ela seja bem-sucedida)?

Parece que uma solução potencial é garantir que o chamador execute um método que use um mutex internamente para bloquear o valor primeiro; então, quando o valor é bloqueado, podemos ler o valor; e, em seguida, verifique se a ação é válida. Se a condição passar, atualizamos o valor e liberamos o bloqueio no valor. Isso significa que o próximo chamador poderá bloquear o valor e executar as mesmas etapas.

Mas como essa abordagem funcionaria com um sistema distribuído? Você poderia sugerir o uso de um armazenamento de dados global, mas ele teria de garantir a consistência (por exemplo, um serviço como o Dynamo DB da AWS oferece "consistência eventual" e, portanto, não funcionaria para uma instituição bancária); mas a consistência garantida é geralmente considerada muito lenta (dependendo do número de nós distribuídos que eu assumo).

Então, como tentamos resolver esse problema de design?

    
por Integralist 12.10.2014 / 17:59
fonte

2 respostas

3

Para um sistema distribuído, você poderia:

a) Use "quantidade de subtração ou erro de retorno se você não puder", onde o código responsável por baz retornará um erro se o resultado for negativo (ou retorna "sucesso" se não houver erro)

b) Use o equivalente de bloqueio; onde o código responsável por baz tem um "adquirir baz" e "release baz" que precisam ser usados antes e depois.

Observe que isso geralmente é apenas a ponta do iceberg. É mais provável que você tenha duas ou mais contas bancárias e queira transferir fundos de uma para as outras, de modo que todas as contas sejam atualizadas ou nenhuma seja atualizada. Nesse caso, você pode (por exemplo) acabar com uma combinação.

Por exemplo, se houver duas contas "Fred" e "Jane" e você quiser transferir US $ 5 de Fred para Jane; então você pode acabar com uma sequência como:

  • De você para a conta de Fred: "Se a conta de Fred tiver 5 ou mais bloqueio a conta de Fred e diga que eu posso prosseguir, caso contrário, diga que não posso continuar"

  • Da conta de Fred para você: "Você pode continuar"

  • De você para a conta de Jane: "Se a conta de Jane puder ser aumentada em 5 bloqueie a conta de Jane e diga que eu posso prosseguir, caso contrário, diga que não posso prosseguir"

  • Da conta de Jane para você: "Você pode continuar"

  • De você para a conta de Fred: "Subtraia 5 da conta de Fred e libere a trava que você me deu anteriormente"

  • De você para a conta de Jane: "Adicione 5 à conta de Jane e libere o bloqueio que você me deu anteriormente"

Observe que, para este exemplo; você, a conta de Fred e a conta de Jane podem estar todas rodando em computadores completamente diferentes se comunicando com mensagens / pacotes (sem nenhuma memória compartilhada).

    
por 12.10.2014 / 19:22
fonte
2

Eu entendo que o setor financeiro usa um sistema de verificação e correções "pós-fato" para resolver erros.

ie, você faz cada transação nos sistemas individuais independentemente (de forma que você saiba que cada sistema está correto) e você escreve a um registro os detalhes de cada transação. Esses logs são então comparados posteriormente, e se um erro ocorreu em um, o outro é instruído a reverter sua transação.

Então, o Banco A retira com sucesso o dinheiro, mas o banco B não credita isso. Posteriormente, as listas de transações de ambos são comparadas e o Banco A recebe um crédito para corrigir as coisas.

Você não pode implementar um "bloqueio" distribuído em sistemas como eles não respondem da maneira que você espera - e você não quer bloquear a conta de alguém enquanto faz uma retirada se você não puder dizer quanto tempo será antes do outro sistema envolvido na transação levará para concluir, você pode acabar com um bloqueio que permanece aberto bloqueando outras transações nessa conta.

    
por 13.10.2014 / 09:48
fonte