web-dev-qa-db-fra.com

Comment représenter au mieux une synchronisation bidirectionnelle dans une API REST?

En supposant un système où il existe une application Web avec une ressource et une référence à une application distante avec une autre ressource similaire, comment représentez-vous une action de synchronisation bidirectionnelle qui synchronise la ressource `` locale '' avec la ressource `` distante ''?

Exemple:

J'ai une API qui représente une liste de tâches.

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

Cette API peut référencer des services TODO distants.

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

Je peux manipuler les todos du service distant via mon API en tant que proxy via

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

Je veux pouvoir effectuer une synchronisation bidirectionnelle entre un ensemble local de todos et l'ensemble distant de TODOS.

D'une manière rpc, on pourrait faire

POST/todo_services/abc123/sync /

Mais, dans l'idée "les verbes sont mauvais", existe-t-il une meilleure façon de représenter cette action?

25
Edward M Smith

Où et quelles sont les ressources?

REST consiste à adresser les ressources de manière apatride et détectable. Il n'a pas besoin d'être implémenté sur HTTP, ni de s'appuyer sur JSON ou XML, bien qu'il soit fortement recommandé d'utiliser un format de données hypermédia (voir HATEOAS principe) car les liens et les identifiants sont souhaitables.

Alors, la question devient: comment penser la synchronisation en termes de ressources?

Qu'est-ce que la synchronisation bidirectionnelle? **

La synchronisation bidirectionnelle est le processus de mise à jour des ressources présentes sur un graphe de nœuds afin qu'à la fin du processus, tous les nœuds aient mis à jour leurs ressources conformément aux règles régissant ces ressources. En règle générale, cela signifie que tous les nœuds disposeraient de la dernière version des ressources telles qu'elles sont présentes dans le graphique. Dans le cas le plus simple, le graphique se compose de deux nœuds: local et distant. Local lance la synchronisation.

Ainsi, la ressource clé qui doit être adressée est un journal des transactions et, par conséquent, un processus de synchronisation peut ressembler à ceci pour la collection "items" sous HTTP:

Étape 1 - Local récupère le journal des transactions

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

À distance: 200 OK avec un corps contenant le journal des transactions contenant des champs similaires à celui-ci.

  • itemId - un UUID pour fournir une clé primaire partagée

  • updatedAt - horodatage pour fournir un point coordonné lors de la dernière mise à jour des données (en supposant qu'aucun historique de révision n'est requis)

  • fingerprint - un hachage SHA1 du contenu des données pour une comparaison rapide si updateAt est dans quelques secondes

  • itemURI - un URI complet à l'élément pour permettre la récupération plus tard

Étape 2 - Local compare le journal des transactions distant avec le sien

Il s'agit de l'application des règles commerciales de synchronisation. Généralement, le itemId identifiera la ressource locale, puis comparera l'empreinte digitale. S'il y a une différence, une comparaison de updatedAt est effectuée. Si ceux-ci sont trop proches pour appeler, une décision devra être prise pour tirer en fonction de l'autre nœud (peut-être que c'est plus important), ou pour pousser vers l'autre nœud (ce nœud est plus important). Si la ressource distante n'est pas présente localement, une entrée Push est effectuée (elle contient les données réelles pour l'insertion/la mise à jour). Toutes les ressources locales non présentes dans le journal des transactions distant sont supposées être inchangées.

Les demandes d'extraction sont effectuées sur le nœud distant afin que les données existent localement à l'aide de itemURI. Ils ne sont appliqués localement que plus tard.

Étape 3 - Envoyer le journal des transactions de synchronisation locale à distance

Local: PUT /remotehost/items/transactions Avec corps contenant le journal des transactions de synchronisation local.

Le nœud distant peut traiter cela de manière synchrone (s'il est petit et rapide) ou asynchrone (pensez 202 ACCEPTÉ) s'il est susceptible d'entraîner beaucoup de frais généraux. En supposant une opération synchrone, le résultat sera soit 200 OK ou 409 CONFLIT selon le succès ou l'échec. Dans le cas d'un 409 CONFLICT, alors le processus doit être redémarré car il y a eu un échec de verrouillage optimiste au niveau du nœud distant (quelqu'un a changé les données pendant la synchronisation). Les mises à jour à distance sont traitées dans le cadre de leur propre transaction d'application.

Étape 4 - Mettre à jour localement

Les données extraites à l'étape 2 sont appliquées localement dans le cadre d'une transaction d'application.

Bien que ce qui précède ne soit pas parfait (il existe plusieurs situations où local et distant peuvent avoir des problèmes et avoir des données d'extraction à distance du local est probablement plus efficace que de les mettre dans un gros PUT), il montre comment REST peut être utilisé lors d'un processus de synchronisation bidirectionnelle.

18
Gary Rowe

Je considérerais une opération de synchronisation comme une ressource accessible (GET) ou créée (POST). Dans cet esprit, l'URL de l'API pourrait être:

/todo_services/abc123/synchronization

(L'appeler "synchronisation", pas "sync" pour qu'il soit clair que ce n'est pas un verbe)

Alors fais:

POST /todo_services/abc123/synchronization

Pour lancer une synchronisation. Étant donné qu'une opération de synchronisation est une ressource, cet appel peut potentiellement renvoyer un ID qui peut ensuite être utilisé pour vérifier l'état de l'opération:

GET /todo_services/abc123/synchronization?id=12345
6
laurent

C'est un problème difficile. Je ne pense pas que REST est un niveau approprié pour implémenter la synchronisation. Une synchronisation robuste devrait essentiellement être une transaction distribuée. REST n'est pas l'outil pour cela) emploi.

(Hypothèse: par "synchronisation", vous indiquez que l'une ou l'autre des ressources peut changer indépendamment de l'autre à tout moment, et vous voulez pouvoir les réaligner sans perdre les mises à jour.)

Vous voudrez peut-être envisager de faire de l'un "maître" et de l'autre "esclave" afin de pouvoir assommer l'esclave périodiquement en toute confiance avec les données du maître.

Vous pouvez également envisager le Microsoft Sync Framework si vous avez absolument besoin de prendre en charge des magasins de données à changement indépendant. Cela ne fonctionnerait pas via REST, mais dans les coulisses.

5
codingoutloud

Apache CouchDB est une base de données basée sur REST, HTTP et JSON. Les développeurs effectuent des opérations CRUD de base sur HTTP. Il fournit également un mécanisme de réplication qui est peer-to-peer en utilisant uniquement des méthodes HTTP.

Pour fournir cette réplication, CouchDB doit avoir certaines conventions spécifiques à CouchDB. Aucun de ceux-ci n'est opposé au REST. Il fournit à chaque document (c'est-à-dire une ressource REST dans une base de données) un numéro de révision . Cela fait partie de la représentation JSON de ce document, mais se trouve également dans l'en-tête HTTP ETag. Chaque base de données a également un numéro de séquence qui permet suivi des modifications à la base de données dans son ensemble.

Pour résolution des conflits , ils notent simplement qu'un document est en conflit et conservent les versions en conflit, laissant aux développeurs utilisant la base de données le soin de fournir un algorithme de résolution des conflits.

Vous pouvez soit utiliser CouchDB comme votre REST, qui vous donnera une synchronisation prête à l'emploi, ou jeter un œil à la façon dont il fournit la réplication pour fournir un point de départ pour créer votre propre algorithme.

2
David V