Qual a melhor forma de representar uma sincronização bidirecional em uma API REST?


23

Supondo que um sistema em que haja um aplicativo Web com um recurso e uma referência a um aplicativo remoto com outro recurso semelhante, como você representa uma ação de sincronização bidirecional que sincroniza o recurso 'local' com o recurso 'remoto'?

Exemplo:

Eu tenho uma API que representa uma lista de tarefas.

GET / POST / PUT / DELETE / todos /, etc.

Essa API pode fazer referência a serviços remotos TODO.

GET / POST / PUT / DELETE / todo_services /, etc.

Eu posso manipular todos do serviço remoto através da minha API como proxy via

GET / POST / PUT / DELETE / todo_services / abc123 /, etc.

Quero a capacidade de fazer uma sincronização bidirecional entre um conjunto local de todos e o conjunto remoto de TODOS.

De uma maneira rpc, alguém poderia fazer

POST / todo_services / abc123 / sync /

Mas, na idéia "os verbos são ruins", existe uma maneira melhor de representar essa ação?


4
Eu acho que um bom design de API depende absolutamente de uma compreensão muito concreta do que você quer dizer com sincronização. A "sincronização" de duas fontes de dados geralmente é um problema muito complexo, muito fácil de simplificar, mas muito difícil de pensar em todas as suas implicações. Faça uma sincronização "bidirecional" e, de repente, a dificuldade é muito maior. Comece pensando nas questões muito difíceis que surgem.
Adam Crossland

Certo - suponha que o algoritmo de sincronização tenha sido projetado e funcional na API "em nível de código" - como expô-lo através do REST. A sincronização unidirecional parece muito mais fácil de expressar: eu GET /todo/1/e POSTela /todo_services/abc123/ , mas, a bidirecional - não estou pegando um conjunto de dados e colocando-o em um recurso, a ação que estou realizando realmente resulta na modificação potencial de dois recursos. Eu acho que eu poderia voltar a ter "todas as sincronizações" sendo recursos eles mesmos POST /todo_synchronizations/ {"todos":["/todo/1/","/todo_services/abc123/1"],"schedule":"now"}
Edward M Smith

Ainda temos um problema de carroça antes do cavalo. Meu argumento era que você não pode assumir que a sincronização funcione e crie a API. O design da API será orientado por inúmeras preocupações sobre exatamente como o algoritmo de sincronização funciona.
Adam Crossland

Isso potencialmente expõe resultados úteis: GET /todo_synchronizations/1=>{"todos":["/todo/1/","/todo_services/abc123/1"],"schedule":"now","ran_at":"datetime","result":"success"}
Edward M Smith

2
Eu concordo com @Adam. Você sabe como implementará sua sincronização? Como você está lidando com as mudanças? Você simplesmente tem dois conjuntos de itens que deseja reconciliar ou possui um log das ações que fizeram com que os dois conjuntos divergissem desde a última sincronização? A razão pela qual pergunto é que pode ser complicado detectar adições e exclusões (independentemente do REST). Se você tem um objeto no lado do servidor e não no lado do cliente, você deve se perguntar: "O cliente o excluiu ou o servidor o criou?" Somente quando você sabe exatamente como o "recurso" se comporta, é possível representá-lo com precisão no REST.
Raymond Saltrelli

Respostas:


17

Onde e quais são os recursos?

O REST tem tudo a ver com endereçamento de recursos de maneira apátrida e detectável. Ele não precisa ser implementado por HTTP, nem deve depender de JSON ou XML, embora seja altamente recomendável que um formato de dados hipermídia seja usado (consulte o princípio HATEOAS ), pois os links e os IDs são desejáveis.

Então, a pergunta se torna: como alguém pensa em sincronização em termos de recursos?

O que é sincronização bidirecional? **

A sincronização bidirecional é o processo de atualização dos recursos presentes em um gráfico de nós, para que, no final do processo, todos os nós tenham atualizado seus recursos de acordo com as regras que regem esses recursos. Normalmente, entende-se que todos os nós teriam a versão mais recente dos recursos, conforme presente no gráfico. No caso mais simples, o gráfico consiste em dois nós: local e remoto. Local inicia a sincronização.

Portanto, o principal recurso que precisa ser endereçado é um log de transações e, portanto, um processo de sincronização pode ser assim para a coleção "items" em HTTP:

Etapa 1 - Local recupera o log de transações

Local: GET /remotehost/items/transactions?earliest=2000-01-01T12:34:56.789Z

Remoto: 200 OK com o corpo contendo o log de transações contendo campos semelhantes a este.

  • itemId - um UUID para fornecer uma chave primária compartilhada

  • updatedAt - carimbo de data / hora para fornecer um ponto coordenado quando os dados foram atualizados pela última vez (assumindo que um histórico de revisão não seja necessário)

  • fingerprint- um hash SHA1 do conteúdo dos dados para comparação rápida se updateAthouver alguns segundos fora

  • itemURI - um URI completo para o item para permitir a recuperação posterior

Etapa 2 - Local compara o log de transações remotas com seus próprios

Esta é a aplicação das regras de negócios de como sincronizar. Normalmente, o itemIdidentifica o recurso local e depois compara a impressão digital. Se houver uma diferença, updatedAté feita uma comparação de . Se estes estiverem muito perto de serem chamados, será necessário tomar uma decisão para puxar com base no outro nó (talvez seja mais importante) ou empurrar para o outro nó (este nó é mais importante). Se o recurso remoto não estiver presente localmente, será feita uma entrada push (ela contém os dados reais para inserção / atualização). Todos os recursos locais que não estão presentes no log de transações remotas são assumidos como inalterados.

As solicitações pull são feitas no nó remoto para que os dados existam localmente usando o itemURI. Eles não são aplicados localmente até mais tarde.

Etapa 3 - Enviar o log de transações de sincronização local para o remoto

Local: PUT /remotehost/items/transactions com o corpo que contém o log de transações de sincronização local.

O nó remoto pode processar isso de forma síncrona (se for pequena e rápida) ou de forma assíncrona (pense 202 ACEITADO ) se for provável que ocorra muita sobrecarga. Supondo uma operação síncrona, o resultado será 200 OK ou 409 CONFLITO, dependendo do sucesso ou falha. No caso de um 409 CONFLICT , o processo deve ser iniciado novamente, pois houve uma falha de bloqueio otimista no nó remoto (alguém alterou os dados durante a sincronização). As atualizações remotas são processadas sob sua própria transação de aplicativo.

Etapa 4 - Atualize localmente

Os dados extraídos na Etapa 2 são aplicados localmente em uma transação de aplicativo.

Embora o exposto acima não seja perfeito (há várias situações em que local e remoto podem ter problemas e ter dados de extração remota do local é provavelmente mais eficiente do que colocá-lo em uma grande PUT), ele demonstra como o REST pode ser usado durante processo de sincronização direcional.


6

Eu consideraria uma operação de sincronização como um recurso que pode ser acessado (GET) ou criado (POST). Com isso em mente, o URL da API pode ser:

/todo_services/abc123/synchronization

(Chamando de "sincronização", não de "sincronização" para deixar claro que não é um verbo)

Então faça:

POST /todo_services/abc123/synchronization

Para iniciar uma sincronização. Como uma operação de sincronização é um recurso, essa chamada pode retornar um ID que pode ser usado para verificar o status da operação:

GET /todo_services/abc123/synchronization?id=12345

3
Esta resposta simples é a resposta. Transforme seus verbos em substantivos e siga em frente ...
HDave

5

Este é um problema díficil. Não acredito que o REST seja um nível apropriado para implementar a sincronização. Uma sincronização robusta precisaria essencialmente ser uma transação distribuída. O REST não é a ferramenta para esse trabalho.

(Suposição: por "sincronização", você está sugerindo que um dos recursos pode mudar independentemente do outro a qualquer momento e deseja realinhar os recursos sem perder as atualizações.)

Você pode considerar tornar um o "mestre" e o outro o "escravo", para poder confundir o escravo com confiança periodicamente com os dados do mestre.

Você também pode considerar o Microsoft Sync Framework se precisar absolutamente oferecer suporte a repositórios de dados com alterações independentes. Isso não funcionaria no REST, mas nos bastidores.


5
+1 para "problema grave". A sincronização bidirecional é uma daquelas coisas que você não percebe o quão difícil é até que você esteja profundamente na lama.
21412 Ray Dan

2

O Apache CouchDB é um banco de dados baseado em REST, HTTP e JSON. Os desenvolvedores realizam operações básicas de CRUD sobre HTTP. Ele também fornece um mecanismo de replicação ponto a ponto usando apenas métodos HTTP.

Para fornecer essa replicação, o CouchDB precisa ter algumas convenções específicas do CouchDB. Nada disso se opõe ao REST. Ele fornece a cada documento (que é um recurso REST dentro de um banco de dados) um número de revisão . Isso faz parte da representação JSON desse documento, mas também está no cabeçalho HTTP ETag. Cada banco de dados também possui um número de sequência que permite rastrear alterações no banco de dados como um todo.

Para resolução de conflitos , eles simplesmente observam que um documento está em conflito e mantém as versões em conflito, deixando para os desenvolvedores que usam o banco de dados para fornecer um algoritmo de resolução de conflitos.

Você pode usar o CouchDB como sua API REST, que fornecerá a sincronização imediata ou verificar como ele fornece replicação para fornecer um ponto de partida para criar seu próprio algoritmo.


Eu amo o CouchDB, e é o sucessor CouchBase + SyncGateway. 1
Leonid Usov

-1

Você pode resolver o problema "os verbos são ruins" com uma simples renomeação - use "updates" em vez de "sync".

O processo de sincronização está realmente enviando uma lista de atualizações locais feitas desde a última sincronização e recebendo uma lista de atualizações feitas no servidor no mesmo tempo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.