Eu tenho este seguinte cenário:
- Um usuário faz uma solicitação GET
/projects/1
e recebe um ETag . - O usuário faz uma solicitação PUT para
/projects/1
com o ETag da etapa 1. - O usuário faz outra solicitação de PUT
/projects/1
com o ETag da etapa 1.
Normalmente, a segunda solicitação PUT receberia uma resposta 412, já que o ETag agora está obsoleto - a primeira solicitação PUT modificou o recurso, para que o ETag não corresponda mais.
Mas e se as duas solicitações PUT forem enviadas ao mesmo tempo (ou exatamente uma após a outra)? A primeira solicitação PUT não tem tempo para processar e atualizar o recurso antes que a PUT # 2 chegue, o que faz com que a PUT # 2 substitua a PUT # 1. O objetivo do bloqueio otimista é que isso não aconteça ...
rest
language-agnostic
concurrency
http
maximedupre
fonte
fonte
Respostas:
O mecanismo ETag especifica apenas o protocolo de comunicação para bloqueio otimista. É responsabilidade do serviço de aplicativo implementar o mecanismo para detectar atualizações simultâneas e reforçar o bloqueio otimista.
Em um aplicativo típico que usa um banco de dados, você normalmente faria isso abrindo uma transação ao processar uma solicitação PUT. Você normalmente lê o estado existente do banco de dados dentro dessa transação (para obter um bloqueio de leitura), verifica a validade do Etag e sobrescreve os dados (de maneira a causar um conflito de gravação quando houver uma transação simultânea incompatível), depois confirme. Se você configurar a transação corretamente, uma das confirmações falhará porque ambas tentarão atualizar os mesmos dados simultaneamente. Você poderá usar essa falha de transação para retornar 412 ou tentar novamente a solicitação, se fizer sentido para o aplicativo.
fonte
AND ETag = ...
suaUPDATE
instruçãoWHERE
e verificando a contagem de linhas atualizada posteriormente. (Ou usando um nível de isolamento transação mais rigorosa, mas eu realmente não recomendo isso.)Você deve executar o seguinte par atomicamente:
Outros estão chamando isso de transação - mas, fundamentalmente, a execução atômica dessas duas operações é o que impede que uma substitua a outra por acidente de tempo; sem isso, você tem uma condição de corrida, como observa.
Isso ainda é considerado bloqueio otimista, se você observar o cenário geral: que o recurso em si não está bloqueado pela leitura inicial (GET) de qualquer Usuário ou Usuário que esteja visualizando os dados, com a intenção de atualizar ou não.
Algum comportamento atômico é necessário, mas isso acontece dentro de uma única solicitação (a PUT), em vez de tentar manter um bloqueio em várias interações de rede; isso é bloqueio otimista: o objeto não está bloqueado pelo GET e ainda pode ser atualizado com segurança pelo PUT.
Também existem muitas maneiras de obter a execução atômica dessas duas operações - bloquear o recurso não é a única opção; por exemplo, um bloqueio leve de thread ou objeto pode ser suficiente e depende da arquitetura e do contexto de execução do aplicativo.
fonte
Cabe ao desenvolvedor do aplicativo verificar o E-Tag e fornecer essa lógica. Não é mágico que o servidor da Web faça por você, porque ele só sabe calcular
E-Tag
cabeçalhos para conteúdo estático. Então, vamos analisar o cenário acima e detalhar como a interação deve acontecer.O servidor recebe a solicitação, determina o E-Tag para esta versão do registro, retornando-o com o conteúdo real.
Como o cliente agora tem o valor de E-Tag, ele pode ser incluído na
PUT
solicitação:Nesse ponto, seu aplicativo deve fazer o seguinte:
Envie a resposta com sucesso.
Se outra solicitação surgir e tentar executar uma
PUT
semelhante à solicitação acima, na segunda vez que o código do servidor avaliar, você será responsável por fornecer a mensagem de erro.Em caso de falha, envie a resposta da falha.
Este é o código que você realmente precisa escrever. O E-Tag pode de fato ser qualquer texto (dentro dos limites definidos na especificação HTTP). Não precisa ser um número. Também pode ser um valor de hash.
fonte
Como complemento às outras respostas, publicarei uma das melhores citações na documentação do ZeroMQ que descreve fielmente o problema subjacente:
fonte