O REST é limitado apenas ao controle de concorrência otimista?

9

Contexto

Devido à falta de estado do estilo de arquitetura REST, cada uma das solicitações fica completamente sozinha, levando o servidor a nunca armazenar informações sobre o cliente.

Portanto, o controle de simultaneidade pessimista não é adequado porque exigiria que o servidor armazene qual cliente obtém o bloqueio em um recurso. O controle de concorrência otimista é então usado, com a ajuda do Etagcabeçalho. (btw, como eu pedi lá /programming/30080634/concurrency-in-a-rest-api )

Problema

O principal problema com um mecanismo de controle de concorrência otimista é que você permite o tempo todo, todos os clientes, executar quaisquer operações.

E eu gostaria de evitar isso sem violar o princípio da apatridia do REST. Quero dizer que todos os clientes não podem executar nenhuma operação a qualquer momento.

Questão

Na minha opinião, seria possível com um mecanismo de controle de concorrência semi-otimista , assim:

  • Os clientes podem solicitar um token
  • Somente um token pode ser gerado e tem um período de validade limitado
  • Para executar operações em recursos (como POST ou PUT ), o cliente deve fornecer esse token como parte do corpo (ou cabeçalho?) Da solicitação. O cliente que não possui o token não pode executar essas operações.

É muito semelhante ao controle de concorrência otimista, exceto que apenas um cliente pode executar algumas operações (a que recebeu o token) ... ao contrário de "todos os clientes podem executar todas as operações".

Esse mecanismo é compatível com um estilo de arquitetura REST? Ele quebra alguma de suas restrições? Eu estava pensando em perguntar sobre SO, mas isso parece mais uma pergunta de alto nível, relacionada ao design de software.

AilurusFulgens
fonte
11
Na verdade, é bastante simples: você precisa modelar um Transactionexplicitamente como um Recurso.
Jörg W Mittag
O que isso muda? Não tenho certeza de entender seu comentário. Não estou fazendo controle de simultaneidade para transação, mas por dizer ao cliente "ok agora, você tem certeza absoluta de que ninguém alteraria o recurso" (pois recebeu um token exclusivo).
AilurusFulgens
11
Qual é a diferença? Por um lado, você usa o Etag para verificar quem tem permissão para atualizar a versão "atual"; se houver conflito, você deverá atualizar sua versão e fazer a atualização depois disso. Por outro lado, você tem seu mecanismo de "bloqueio": um é permitido, outros têm que esperar. Parece o mesmo. A desvantagem da segunda abordagem: 2 solicitações - 1 para o bloqueio e 1 para a atualização. Em um caso ideal, você tem apenas uma atualização através da abordagem Etag.
Thomas Junk
Bem, de um modo geral sobre controle de concorrência ... o segundo que "perdeu a corrida" sempre terá que esperar (apenas por razões diferentes, eu concordo com isso). Diferença com Etag? Se Etagvocê nunca tiver certeza de que suas operações serão concluídas, você poderá ter uma situação em que nunca executará nenhuma operação. Com uma trava, você tem certeza de pelo menos para executar sua operação. Portanto, ter um bloqueio simples é apenas o meio termo entre um ambiente em que "altos conflitos" e "raros conflitos" podem acontecer.
AilurusFulgens

Respostas:

3

Você nunca deve (como nunca NUNCA) bloquear qualquer recurso enquanto aguarda uma interação do usuário.

Em algum momento, alguns de seus usuários decolam por um longo fim de semana, deixando alguns registros vitais bloqueados.

Ah, mas você não permitirá que isso aconteça porque você tem algum esquema inteligente de resolução de tempo limite / impasse; então, em algum momento, isso ficará terrivelmente errado e um usuário que tenha recebido uma boa mensagem "seu widget foi solicitado" estará gritando no suporte técnico exigindo saber por que o widget não foi entregue.

A maioria das pessoas pode lidar com a mensagem "desculpe, outro usuário acabou de encomendar esta parte".

James Anderson
fonte
Você explicou bem. Embora eu não esteja completamente convencido com sua primeira frase: em algum momento você garantiria que ninguém mais que você possa executar uma ordem. Mas bem, sua última frase é um bom resumo, em 99% das vezes é o caso.
AilurusFulgens
11
Se você precisar bloquear um registro para um usuário específico, faça-o no nível do aplicativo. Ou um simples atributo "LockedBy" ou, com algum mecanismo de versão e fluxo de trabalho mais sofisticado.
James Anderson
O que você quer dizer com "no nível do aplicativo"? Se você adicionar um atributo "LockedBy" ao seu recurso, você armazenará informações sobre o cliente no seu servidor. Ou talvez eu não tenha entendido o seu comentário. Se você pode fornecer alguns detalhes, seria ótimo!
AilurusFulgens
11
@AilurusFulgens - Você adiciona um atributo LockedBy (e possivelmente um carimbo de data / hora LockedOn) ao seu banco de dados. No código do aplicativo, você define o nome do usuário e a hora sempre que o cliente inicia uma interação de atualização. Você unset-lo quando o cliente está terminado - então você precisa para trabalhar fora algumas regras de negócios para resolver conflitos e tempos de espera etc.
James Anderson
2

O uso de tokens é muito comum nas APIs, geralmente esses tokens são enviados como um cabeçalho e têm um ciclo de vida claro. Pense, por exemplo, em OAuth.

Independentemente da sua linguagem ou estrutura de programação, as APIs REST são semelhantes.

Posso pensar em vários cenários em que você deseja limitar a simultaneidade, dois deles são:

  1. Vários clientes atualizando os mesmos recursos, como uma linha do banco de dados. Por exemplo: duas solicitações simultâneas, uma exclui um registro e a outra tenta atualizar o mesmo registro. Dependendo do seu banco de dados e de como você o configurou, é possível bloquear os registros ou a operação inválida, pois os dados serão diferentes ou não existirão.

  2. Um superusuário ou administrador executando ações especiais com a API.


O que fazer nesses casos?

  1. Use transações no banco de dados, singletons, bloqueios e mecanismos semelhantes para sincronizar o acesso aos recursos.

  2. O token pode funcionar, acho que será melhor se você não armazenar informações sobre o cliente, apenas sobre o próprio token. Em uma etapa, você pode validar o usuário e atribuir o token. Apenas valide se o token está ativo e é válido. Use um token que pode ser descriptografado para obter informações extras. Você pode armazenar se esse é um token especial e permitir apenas por vez. Dessa forma, você valida o token, não o usuário.

Eu espero que isso ajude.

Pablo Granados
fonte
Sim, eu estava esperando uma resposta sobre a semelhança de um token e o mecanismo OAuth. Embora o último seja dedicado à autenticação. Não entendi o seu cenário 1. A parte complicada de lidar com a simultaneidade em uma API REST é manter a restrição de apatridia ... o que significa que o servidor não armazena informações sobre o cliente. Seu cenário 2 é exatamente o que estou fazendo atualmente! :)
AilurusFulgens
Eu respondi sua pergunta?
Pablo Granados
Desculpe. Votou sua resposta porque ela dá uma boa visão geral do problema, mas infelizmente, na verdade, não está realmente resolvendo o problema.
AilurusFulgens
0

Somente o REST é muito primitivo, na verdade. Você pode iniciar o REST, mas, eventualmente, seu aplicativo avançado precisará de consultas com junções e atualizações com transações. Todo desenvolvedor que tenta adicionar essas coisas por conta própria seria propenso a erros e inconsistente. Felizmente, existe um padrão emergente chamado OData que faz exatamente isso. Ele se sobrepõe ao REST e fornece (1) linguagem de consulta que permite junções simples usando propriedades de navegação (sem a necessidade de expor chaves estrangeiras) e (2) processamento em lote que inclui conjuntos de alterações atômicas.

Consulte aqui (1) https://stackoverflow.com/a/3921423/471129 e,

Consulte aqui e (2) https://stackoverflow.com/a/21939972/471129

Erik Eidt
fonte