Gestion des documents de conception
Le magasin de données JSON évolutif pour IBM Cloudant possède plusieurs mécanismes d'interrogation, chacun générant des index qui sont créés et conservés séparément depuis les données coeur.
Article signé Glynn Bird, chargé du pôle de développement pour IBM Cloudant, glynn@cloudant.com.
L'indexation n'est pas réalisée immédiatement lorsqu'un document est sauvegardé. Son exécution est prévue ultérieurement afin d'offrir un débit d'écriture non-bloquant plus rapide.
- Les vues MapReduce sont des index dans l'ensemble de données avec des paires clé-valeur qui sont stockées dans un arbre B afin d'être plus rapidement extraites par la clé ou la plage de clés.
- Les index de recherche sont construits à l'aide d'Apache Lucene pour permettre la recherche de texte libre, la création de facettes et les requêtes ad hoc complexes.
Les index de recherche et les vues MapReduce d'IBM® Cloudant® for IBM Cloud® sont configurés en ajoutant des documents de conception à une base de données. Les documents de conception sont des documents JSON qui contiennent des instructions sur la façon dont la vue ou l'index doivent être créés. Prenons un exemple simple. Supposons que vous disposez d'une simple collection de documents de données, comme dans l'exemple suivant.
Voici un exemple de document de données simple :
{
"_id": "23966717-5A6F-E581-AF79-BB55D6BBB613",
"_rev": "1-96daf2e7c7c0c277d0a63c49b57919bc",
"doc_name": "Markdown Reference",
"body": "Lorem Ipsum",
"ts": 1422358827
}
Chaque document de données comporte un nom, un corps de texte et un horodatage. Vous créez une vue MapReduce pour trier vos documents par horodatage.
Vous pouvez trier vos documents par horodatage en créant une fonction de mappe.
Voici un exemple de fonction de mappe qui renvoie la zone d'horodatage d'un document, le cas échéant :
function(doc) {
if (doc.ts) {
emit( doc.ts, null);
}
}
La fonction émet l'horodatage du document pour que vous puissiez l'utiliser comme clé d'index. Puisque nous ne sommes pas intéressés par la valeur de l'index,
null est émis. L'objectif est de fournir un index ordonné chronologiquement dans l'ensemble du document.
Nous allons appeler cette vue by_ts et la placer dans un document de conception appelé fetch.
Voici un exemple de document de conception qui définit une vue à l'aide d'une fonction de mappe :
{
"_id": "_design/fetch",
"views": {
"by_ts": {
"map": "function(doc) {
if (doc.ts) {
emit( doc.ts, null);
}
}"
}
},
"language": "javascript"
}
Il en résulte que le code de mappage est transformé en chaîne compatible JSON, puis inclus dans un document de conception.
Une fois le document de conception enregistré, IBM Cloudant déclenche des processus côté serveur pour générer la vue fetch/by_ts. Pour créer la vue, il procède à l'itération de chaque document dans la base de données, puis il envoie
chacun d'eux vers la fonction de mappe JavaScript. La fonction renvoie la paire key-value émise. A mesure que l'itération se poursuit, chaque paire key-value est stockée dans un index B-Tree. Lorsque l'index est généré
pour la première fois, la réindexation suivante s'applique uniquement aux documents nouveaux et mis à jour. Les documents supprimés ne sont plus indexés. Ce processus rapide est appelé Incremental MapReduce, tel qu'illustré dans le diagramme
ci-dessous :
Il convient de rappeler les points suivants :
- La construction d'un index est asynchrone. IBM Cloudant confirme que le document de conception a été sauvegardé. Pour vérifier la progression de la construction de l'index, vous devez interroger le noeud final
_active_tasksd'IBM Cloudant. - Plus votre quantité de données est importante, plus il faut de temps avant que l'index ne soit prêt.
- Pendant le procédé de génération d'index initial, toutes les requêtes sur cet index seront bloquées.
- L'interrogation d'une vue déclenche le "mappage" des documents qui ne sont pas indexés de manière incrémentielle. Cette pratique garantit l'obtention d'une vue actualisée des données. Consultez la discussion suivante relative au
paramètre
stalepour en savoir plus sur les exceptions à cette règle.
Vues multiples dans un même document de conception
Si vous définissez plusieurs vues dans un même document de conception, celles-ci sont générées de manière efficace et simultanée. Chaque document est lu une seule fois, avant d'être soumis à la fonction de mappe de la vue. Si vous utilisez cette approche, gardez à l'esprit que la modification d'un document de conception invalide toutes les vues MapReduce existantes définies dans le document. Ce processus invalide les vues MapReduce même si certaines vues ne sont pas altérées.
Si les vues MapReduce doivent être modifiées indépendamment les unes des autres, placez leurs définitions dans des documents de conception séparés.
Ce comportement ne s'applique pas aux index de recherche Lucene. Ces derniers peuvent être modifiés dans le même document de conception sans invalider d'autres index qui, eux, n'ont pas été modifiés au sein du même document.
Gestion des changements dans un document de conception
Imaginons que vous décidiez plus tard de modifier la conception de la vue. A présent, au lieu de retourner le résultat de l'horodatage réel, nous allons nous intéresser exclusivement au nombre de documents remplissant les critères. Pour obtenir
ce nombre, la fonction de mappe est inchangée, mais vous utilisez maintenant un paramètre reduce ayant pour valeur_count.
Voici un exemple de document de conception qui utilise une fonction de réduction :
{
"_id": "_design/fetch",
"_rev": "2-a2324c9e74a76d2a16179c56f5315dba",
"views": {
"by_ts": {
"map": "function(doc) {
if (doc.ts) {
emit( doc.ts, null);
}
}
}",
"reduce": "_count"
},
"language": "javascript"
}
Lorsque ce document de conception est enregistré, IBM Cloudant invalide complètement l'ancien index et commence à construire le nouvel index à partir de zéro, en répétant l'opération sur chaque document un par un. Comme pour la génération initiale, le délai dépend du nombre de documents dans la base de données. Les requêtes entrantes sont également bloquées sur cette vue par la génération tant que celle-ci n'est pas terminée.
Mais il y a un hic...
Si une application accède à cette vue en temps réel, il se peut que vous soyez face à un dilemme en matière de déploiement :
- La version 1 du code, qui s'appuie sur le document de conception original, risque de ne plus fonctionner car l'ancienne vue a été invalidée.
- La version 2 du code utilise le nouveau document de conception. Cette version ne peut pas être publiée immédiatement car la génération de la nouvelle vue n'est pas encore terminée. N'oubliez pas que le processus de génération prend plus de temps si la base de données contient de nombreux documents.
- Un problème plus subtil qui affecte le code est que les versions 1 et 2 attendent des données de résultat différentes de la vue: La version 1 attend une liste de documents correspondants, tandis que la version 2 prévoit une " réduction " du nombre de résultats.
Coordination des changements dans les documents de conception
Vous pouvez traiter ce problème de contrôle des changements de deux manières.
Documents de conception avec version
Une solution consiste à utiliser des noms de documents de conception avec version :
- Le code est initialement écrit pour utiliser une vue nommée
_design/fetchv1. - Lorsque vous publiez une nouvelle version, vous créez une vue appelée
_design/fetchv2, puis vous interrogez cette vue pour vérifier que sa génération est en cours. - IBM Cloudant interroge
_active_tasksjusqu'à ce que le travail de création du nouvel index soit terminé. - Vous êtes maintenant prêt à publier le code qui dépend de la seconde vue.
- Supprimez
_design/fetchv1lorsque vous êtes certain de ne plus en avoir besoin.
L'utilisation de documents de conception avec version constitue une manière simple de gérer le contrôle des changements dans vos documents de conception, mais vous devez penser à supprimer les anciennes versions ultérieurement.
Move and switchDocuments de conception
Une autre approche repose sur le fait qu'IBM Cloudant reconnaît quand deux documents de conception sont identiques et qu'il ne perd donc pas inutilement du temps et des ressources à reconstruire des vues qu'il possède déjà. En d'autres termes,
si vous prenez votre document de conception _design/fetch et que vous en créez une réplique exacte nommée _design/fetch_OLD, les deux noeuds finaux fonctionnent de manière interchangeable sans déclencher de réindexation.
Pour passer à la nouvelle vue, procédez comme suit :
- Créez une copie en double du document de conception que vous souhaitez modifier, par exemple en ajoutant
_OLDà son nom :_design/fetch_OLD. - Placez le document de conception (nouveau ou "entrant") dans la base de données en utilisant un nom avec le suffixe
_NEW:_design/fetch_NEW. - Interrogez la vue
fetch_NEWpour vous assurer que sa génération démarre. - Interrogez le noeud final
_active_taskset patientez jusqu'à la fin de la génération d'index. - Placez une copie du nouveau document de conception dans
_design/fetch. - Supprimez le document de conception
_design/fetch_NEW. - Supprimez le document de conception
_design/fetch_OLD.
Move and switch outils
Le script couchmigrate de la ligne de commande Node.js automatise la procédure de Move and switch. Pour l'installer, utilisez la commande suivante :
npm install -g couchmigrate
Pour utiliser le script couchmigrate, définissez d'abord l'instance URL de CouchDB/{{site. data.keyword.cloudant_short_notm }} en définissant une variable d'environnement appelée COUCH_URL. Exécutez la commande suivante
afin de définir l'URL pour l'instance IBM Cloudant :
export COUCH_URL=https://127.0.0.1:5984
Le site URL doit commencer par https:// et peut inclure des informations d'authentification. Exécutez la commande suivante afin de définir l'URL de l'instance IBM Cloudant avec des données d'authentification :
export COUCH_URL="https://$ACCOUNT:$PASSWORD@$HOST.cloudant.com"
En admettant que vous disposiez d'un document de conception au format JSON stocké dans un fichier, vous pouvez ensuite exécuter la commande de migration.
Dans cet exemple,
db spécifie le nom de la base de données à modifier, et dd indique le chemin d'accès au fichier du document de conception. Exécutez la commande couchmigrate :
couchmigrate --db mydb --dd /path/to/my/dd.json
Le script coordonne la procédure Move and switch, attendant que la vue soit générée avant de retourner à son point d'origine. Si le document de conception entrant est identique à celui d'origine, le script est retourné presque immédiatement.
Le code source du script est disponible ici :
couchmigrate.
Paramètre 'stale'
Si un index est terminé, mais que de nouveaux enregistrements sont ajoutés à la base de données, la mise à jour de l'index en arrière-plan est planifiée. L'état de la base de données est illustré dans le diagramme suivant :
Lorsque vous interrogez la vue, trois choix sont possibles :
- Le comportement par défaut consiste à vérifier que l'index est à jour, et que la base de données contient les documents les plus récents, avant que la réponse ne soit renvoyée. Lorsque vous interrogez la vue, IBM Cloudant indexe d'abord les 250 nouveaux documents, puis renvoie la réponse.
- Une deuxième solution consiste à ajouter le paramètre
stale=okà l'appel API. Ce paramètre signifie,return me the data that is already indexed. I don't care about the latest updates.En d'autres termes, lorsque vous interrogez la vue avecstale=ok, IBM Cloudant renvoie la réponse immédiatement, sans réindexation supplémentaire. - Une troisième solution consiste à ajouter le paramètre
stale=update_afterà l'appel API. Ce paramètre signifie,return me the data that is already indexed, and then reindex any new documents.En d'autres termes, lorsque vous interrogez la vue avecstale=update_after, IBM Cloudant renvoie la réponse immédiatement, puis planifie une tâche d'arrière-plan pour indexer les nouvelles données.
L'ajout de stale=ok ou stale=update_after peut être un bon moyen d'obtenir une réponse rapide de la vue, mais au détriment de l'obtention de données actualisées.
Le comportement par défaut répartit équitablement la charge entre les noeuds du cluster d'IBM Cloudant. Si vous utilisez les options stale=ok oustale=update_after alternatives, celles-ci peuvent favoriser un sous-ensemble
de noeuds du cluster, afin de renvoyer des résultats cohérents depuis l'ensemble cohérent à terme. Le paramètre stale n'est pas la solution parfaite pour tous les cas d'utilisation. Il peut toutefois permettre d'obtenir des réponses
rapides sur des ensembles de données en perpétuel changement, si votre application accepte des résultats périmés. Si le taux de changement de vos données est faible, l'ajout de stale=ok ou de stale=update_after ne
présente aucun avantage en termes de performances, et risque même de répartir inégalement la charge sur des clusters plus importants.
Evitez le paramètre stale=ok ou stale=update_after dans la mesure du possible car le comportement par défaut fournit les données les plus récentes et répartit les données dans le cluster. Vous pouvez indiquer à une
application client qu'une longue tâche de traitement de données est en cours d'exécution (par exemple pendant une mise à jour de données non formatées) en basculant temporairement vers le paramètre stale=ok pendant ces périodes.
L'application peut revenir au comportement par défaut par la suite.
L'option stale est toujours disponible, mais les options les plus utiles, stable et update, sont également disponibles et leur utilisation doit être privilégiée. Pour plus d'informations, voir Accessing a stale view.