Pourquoi et comment nous avons migré de CouchDB vers MongoDB
Après quelques hésitations et suite à de nombreuses discussions, nous avons décidé de migrer l'application sur laquelle je travaille de CouchDB vers MongoDB.
Je vais tenter, dans cet article, de vous expliquer pourquoi et comment nous avons décidé de faire cela.
Le pourquoi du comment
L'image ci-contre est l'interface de recherche de l'application sur laquelle je travaille.
Dans cette application, nous avons divers enregistrements (chacun représentant un forage). Chacun de ces enregistrements possède divers attributs tels que ceux qui sont affichés ici (nom; longueur et date de forage).
La partie haute de l'image correspond aux filtres. On peut ajouter un ou plusieurs filtres sur les attributs. Ici par exemple, nous avons deux filtres : un sur le numéro de série de l'appareil qui a analysé le forage et un sur une intervalle de dates.
Les enregistrements affichés sont ceux qui correspondent à tous les filtres présents. La raison principale nous ayant poussé à migrer vers MongoDB est ici !
Fonctionnement sous CouchDB
Afin de rechercher l'enregistrement approprié avec CouchDB, nous créons dynamiquement diverses vues.
Par exemple la recherche ci-dessus fait appel à la vue by_boreholename_creation_date. Vue qui nous retourne "simplement" tous les enregistrements avec, comme clé, un tableau de tous les attributs présentés. A l'appel de cette vue, nous faisons une recherche avec startkey et endkey, qui nous permet alors de filtrer suivant les valeurs fournies.
Cependant l'article startkey et endkey dont le lien est plus haut dit :
CouchDB fait en effet une comparaison d'égalité pour chacun des éléments du tableau, sauf le dernier.
Du coup supposons la recherche suivante :
- Sur une intervalle de date de début.
- Puis sur un nom de forage.
Le second élément de la clé sera cherché correctement. Cependant pour l'intervale, la recherche faite sera en ==. En conséquent nous n'aurons absolument pas les résultats escomptés. Nous n'aurons que les enregistrements correspondant à la date appropriée.
Lors du WebWorkersCamp, j'ai abordé ce problème à Benoit Chesneau qui est contributeur CouchDB. Bien que nous n'ayons pas vraiment eu le temps d'en parler en détail, la solution qu'il semblait me proposer était de créer une vue pour chaque filtre et de faire autant d'appels que de filtres.
A l'heure actuelle, certains de nos clients ont plus de 700 enregistrements en base et cela va en grimpant de manière exponentielle. Nos requêtes sont déjà (trop) lentes. Il n'est pas envisageable de faire une requête pour chaque filtre.
Fonctionnement sous MongoDB
Du coup nous avons décidé de migrer sous MongoDB. J'ai commencé à travailler sur cette migration le 26 juillet et celle-ci a été déployée en production cette semaine même (30 aout).
Les requêtes sous MongoDB sont beaucoup plus simples. Déjà car nous n'avons pas à nous préoccuper de créer des vues en javascript. Elles sont générées automatiquement par le moteur. Les opérateurs permettent de gérer tous nos besoins.
Nous utilisons mongoid (sous rails 2.3).
Et un simple appel à la méthode where() avec un hash d'arguments correspondant à la recherche suffit pour récupérer la liste de nos enregistrements.
Nous n'avons même plus de problématique d'ordre des filtres. Nos recherches sont toujours effectuées sur un intervalle lorsqu'il y en a un ou une expression régulière lorsqu'il s'agit d'une chaine.
Le comment du pourquoi
Avec CouchDB, nous utilisions CouchRest. Avec MongoDB, nous utilisons Mongoid.
La principale probématique du "comment" fût la migration des données. Je ne souhaitais pas vraiment faire du trop bas niveau.
J'ai donc migré les modèles CouchDB vers le dossier lib/ (afin qu'ils ne soient pas chargés automatiquement en production) et je leur ai mis le contenu suivant :
module Couchdb
class Record < CouchRest::ExtendedDocument
def self.to_s
"Record"
end
end
end
Ainsi mon modèle récupère les données de CouchDB correspondant au namespace "Record" et non pas "Couchdb::Record" comme cela aurait été le cas originellement.
Puis dans une tâche rake, je parcours tous les documents CouchDB et les ajoute dans MongoDB.
Couchdb::Record.all.each do |record|
record.delete '_rev'
record.delete '_attachments'
record.delete 'couchrest-type'
r = Record.new record
puts "Record saved"
end
puts "There are now #{Record.count} records in the mongo db"
CouchRest, faisant étendre les modèles de Hashes (ce qui est une décision fortement discutable) rends la chose particulièrement aisée puisqu'il nous suffit de transmettre le document sans les paramètres métiers.
Vous noterez que nous ne supprimons pas le _id de CouchRest. Ainsi, notre document conserve le même id.
Cette tâche rake est exécutée manuellement lors du déploiement en production.
Conclusion
N'abandonnez pas CouchDB à cause de cet article ! Bien que ce moteur ne convienne, in fine, pas à nos besoins de recherche multi attributs, il n'en reste pas moins quelque chose d'excellent que je n'hésiterai pas à recommander.
Par ailleurs la bonne nouvelle est que, MongoDB stockant ses données en json binaire, l'accès à celles-ci est beaucoup plus rapide. Nous n'avons pas encore de réelles statistiques en production. Mais le temps d'exécution de nos tests a été réduit de 20% !

