Réplication

CouchDB intègre un mécanisme de réplication de haute qualité. La réplication consiste à synchroniser deux copies de la même base de données, ce qui permet aux utilisateurs de profiter d’une faible latence où qu’ils soient. Ces bases peuvent être hébergées sur le même serveur ou sur deux serveurs ; CouchDB ne fait pas de différence. Si vous apportez une modification aux données sur une instance, la réplication transmettra ce changement à son homologue.

La réplication est une opération ponctuelle : vous soumettez une requête HTTP à CouchDB en désignant la base de données source et celle de destination ; CouchDB transmettra les modifications de la source à la cible. C’est aussi simple que cela. Je vous accorde qu’expliquer en une phrase un système de haute qualité, c’est étrange. Toutefois, une des raisons fondamentales qui font la puissance du mécanisme de réplication de CouchDB est sa simplicité.

Regardons à quoi ressemble une réplication :

POST /_replicate HTTP/1.1
{"source":"database","target":"http://example.org/database"}

Cette requête envoie tous les documents présents dans la base locale database vers la base distante http://example.org/database. Une base de données est considérée comme étant locale quand elle se trouve sur l’instance de CouchDB qui a reçu la requête HTTP POST /_replicate. Toutes les autres instances sont distantes.

Si vous désirez transmettre les modifications apportées sur la cible à la source, vous envoyez la même requête en intervertissant la source et la cible. C’est tout.

POST /_replicate HTTP/1.1
{"source":"http://example.org/database","target":"database"}

Une base de données distante est identifiée par la même URL que celle utilisée pour y accéder. Le mécanisme de réplication de CouchDB exploite le même protocole HTTP et les mêmes fonctions que celles que vous pouvez utiliser. D’autre part, cet exemple met en évidence que la réplication est un processus unidirectionnel. Les documents sont copiés d’une base de données à une autre, ce qui n’implique pas l’échange dans le sens inverse. Si vous désirez obtenir une réplication bidirectionnelle, vous devez déclencher deux réplications en intervertissant la source et la cible.

La magie

Quand vous demandez à CouchDB de répliquer une base vers une autre, il va commencer par comparer les deux bases pour lister les documents qui diffèrent sur la source, puis il va les transmettre et ne s’arrêtera qu’une fois ce transfert terminé. Les modifications ainsi répliquées concernent les nouveaux documents, ceux altérés et ceux supprimés. Les documents qui sont déjà présents avec le même numéro de version sur la cible ne sont pas transférés ; seules les nouvelles versions le sont.

Dans CouchDB, les bases ont un numéro d’ordre (sequence number en anglais), lequel est incrémenté chaque fois que la base est mise à jour. CouchDB est à même de dire quelles modifications sont intervenues pour un numéro d’ordre donné. C’est ainsi qu’il est possible de répondre à la question « Quels changements sont intervenus entre le numéro d’ordre 212 et maintenant ? » par une liste de documents nouveaux et modifiés. Aussi, obtenir la liste des différences entre les bases de données est une opération très efficace, ce qui contribue à la robustesse du mécanisme de réplication.

Le système de vues de CouchDB exploite le même mécanisme pour déterminer si une vue nécessite d’être mise à jour. Vous pourriez aussi l’exploiter pour répondre à vos besoins.

Vous pouvez répliquer une instance CouchDB pour créer un instantané de vos bases et y éprouver vos codes sans risquer de compromettre vos données d’origine, ou bien pour pouvoir exploiter les anciennes et les nouvelles données. Toutefois, la réplication devient très intéressante lorsqu’elle est utilisée pour synchroniser plusieurs bases dans des lieux éloignés les uns des autres.

Si vous avez des serveurs différents, peut-être à ces centaines ou milliers de kilomètres les un des autres, une chose est certaine : vous serez confronté à des problèmes tels que les crash et les coupures de réseau. En cas d’interruption de la réplication, vos bases de données sont temporairement incohérentes. Toutefois, lorsque la situation se rétablit et que vous relancez la réplication, le mécanisme reprend où il en était et achève son travail.

Réplication simple à l’aide de l’interface d’administration

Vous pouvez déclencher une réplication depuis Futon, l’interface d’administration web intégrée à CouchDB. Démarrez CouchDB et pointez votre navigateur sur http://127.0.0.1:5984/_utils/. Sur le côté droit, vous verrez une liste d’actions possibles. Cliquez sur « Replication ».

Futon vous proposera alors de déclencher une réplication. Vous pourrez indiquer la source et la cible soit en sélectionnant parmi la liste proposée, soit en saisissant l’URL d’une base distante.

Cliquez sur le bouton « Replicate », patientez quelque peu, puis consulter la deuxième partie de l’écran, là où CouchDB vous indique quelques statistiques quant à la réplication qui vient d’être conduite ou, le cas échéant, le message correspondant à l’erreur survenue.

Félicitations ! Vous venez d’effectuer votre première réplication.

Détails sur la réplication

Jusqu’à présent, nous avons passé sous silence le résultat d’une requête de réplication. Il est temps d’y venir avec l’exemple ci-dessous :

{
  "ok": true,
  "source_last_seq": 10,
  "session_id": "c7a2bbbf9e4af774de3049eb86eaa447",
  "history": [
    {
      "session_id": "c7a2bbbf9e4af774de3049eb86eaa447",
      "start_time": "Mon, 24 Aug 2009 09:36:46 GMT",
      "end_time": "Mon, 24 Aug 2009 09:36:47 GMT",
      "start_last_seq": 0,
      "end_last_seq": 1,
      "recorded_seq": 1,
      "missing_checked": 0,
      "missing_found": 1,
      "docs_read": 1,
      "docs_written": 1,
      "doc_write_failures": 0,
    }
  ]
}

Le "ok": true est semblable aux autres réponses et indique que tout s’est bien passé. source_last_seq indique le numéro d’ordre update_seq de la source qui a été retenu pour la réplication. À chaque réplication correspond un identifiant de session de réplication session_id, lequel est un simple UUID et identifie le transfert.

La partie suivante couvre l’historique de la réplication. CouchDB tient à jour une liste des historiques pour un usage futur. Cette liste est actuellement limitée à 50 entrées. Chaque déclencheur de réplication (la chaîne JSON qui identifie la source et la cible, ainsi que les éventuelles options) possède son propre historique. Voyons de quoi cela parle.

L’identifiant de session de réplication session_id est conservé par agrément. Les dates et heures de début et de fin de l’opération sont archivées. Le _last_seq indique le numéro d’ordre update_seq qui était valide au début et à la fin de la session. recorded_seq est le numéro d’ordre update_seq de la cible. Sa valeur sera différente du end_last_seq si une réplication échoue et est relancée. missing_checked donne le nombre de documents déjà présents et ne nécessitant pas de mises à jour.

Les trois derniers : docs_read, docs_written, et doc_write_failures indique, respectivement, le nombre de documents lus sur la source, écrits sur la cible et combien ont échoué. Si tout se passe bien, _read et _written sont identiques, et doc_write_failures est à zéro. Dans le cas contraire, quelque chose ne s’est pas bien passé lors de la réplication. Les cas possibles sont un crash du serveur source ou cible, une perte du réseau, ou une fonction validate_doc_update qui rejette l’écriture d’un document.

Un scénario courant consiste à déclencher la réplication sur les nœuds où les comptes d’administration sont activés. En effet, la création des design documents est réservée aux administrateurs, aussi si la réplication est déclenchée sans ces privilèges, l’écriture de ces documents lors de la réplication échouera, ce qui se retrouvera dans doc_write_failures. Si vous possédez les privilèges d’administration, pensez à les ajouter dans la requête :

> curl -X POST http://127.0.0.1:5984/_replicate  -d '{"source":"http://example.org/database", "target":"http://admin:password@e127.0.0.1:5984/database"}'

Réplication permanente

Puisque vous savez désormais comment fonctionne la réplication, partageons un petit truc utile. Quand vous ajoutez "continuous":true à l’objet de déclenchement de la réplication, CouchDB ne s’arrêtera pas après avoir achevé la copie des documents manquants de la source à la cible. Il écoutera les notifications de l’API CouchDB _changes (cf. chapitre 20, Notification des modifications) et répliquera automatiquement les nouveaux changements intervenant sur la source. En vérité, ils ne sont pas répliqués immédiatement. Il y a un algorithme complexe qui détermine le moment idéal pour répliquer le plus efficacement possible. Cet algorithme est complexe et fonctionne très bien. Il est ajusté de temps à autre et le documenter ici n’apporterait pas grand-chose.

> curl -X POST http://127.0.0.1:5984/_replicate -d '{"source":"db", "target":"db-replica", "continuous":true}'

Au moment où nous écrivons ce livre, CouchDB ne se souvient pas des réplications permanentes après un redémarrage. Aussi, vous devez les redéclencher quand vous redémarrez CouchDB. À l’avenir, CouchDB vous permettra de définir des réplications permanentes qui survivent à l’arrêt du service.

C’est tout ?

La réplication est la brique fondamentale qui permet de comprendre les chapitres suivants. Soyez certain d’avoir compris ce chapitre. Si vous ne vous sentez pas à votre aise, lisez-le à nouveau et jouez un peu avec l’interface de réplication disponible dans Futon.

Nous ne vous avons pas encore tout dit sur la réplication. Les chapitres suivants vous montreront comment gérer les conflits de réplication (chapitre 17, Gestion des conflits), comment exploiter un ensemble d’instances CouchDB synchronisées (chapitre 18, Équilibrage de charge) et de quelle manière construire un groupement de serveurs capable de traiter un plus grand nombre d’opérations de lecture et d’écriture qu’un nœud seul (chapitre 19, Groupement de serveurs).