Dépannage efficace des erreurs courantes des commandes MongoDB

Dépannez les erreurs courantes des commandes MongoDB dans mongosh, y compris les problèmes de syntaxe, d'authentification, de connexion, d'écriture et de jeu de réplicas.

Dépannage efficace des erreurs courantes des commandes MongoDB

Les erreurs de commande MongoDB sont plus faciles à corriger lorsque vous ralentissez et identifiez où se produit l'échec. Est-ce que mongosh n'a pas réussi à analyser ce que vous avez tapé ? Le serveur a-t-il rejeté la commande parce que l'utilisateur n'a pas le rôle nécessaire ? Le client s'est-il connecté à la mauvaise base de données ? Le primaire est-il indisponible ? Ce sont des problèmes différents, même s'ils apparaissent tous comme du texte rouge dans le shell.

J'aime commencer par trois vérifications : à quel serveur suis-je connecté, quelle base de données est-ce que j'utilise, et quel utilisateur suis-je authentifié ?

db.getName()
db.hello()
db.runCommand({ connectionStatus: 1 })

Ces commandes évitent beaucoup de débogage inutile. De nombreuses "erreurs de commande MongoDB" sont en réalité des erreurs de contexte : exécuter une commande administrative depuis la mauvaise base de données, s'authentifier contre le mauvais authSource, ou tester contre un secondaire alors que vous vouliez écrire sur le primaire.

Comprendre les catégories d'erreurs de commande MongoDB

Les erreurs de commande MongoDB peuvent généralement être classées en quelques types principaux :

  • Erreurs de syntaxe : Commandes mal formées que le shell ou le pilote MongoDB ne peut pas analyser.
  • Erreurs de permission : Tentatives d'effectuer des opérations sans les privilèges utilisateur nécessaires.
  • Erreurs opérationnelles : Problèmes survenant lors de l'exécution d'une commande, tels que des problèmes réseau, des limitations de ressources ou des incohérences de données.
  • Erreurs de connexion : Problèmes pour établir une connexion au serveur MongoDB.

Erreurs de syntaxe courantes et solutions

Les erreurs de syntaxe sont souvent les plus simples à corriger, provenant généralement de fautes de frappe, de caractères manquants ou d'une utilisation incorrecte des paramètres. Le shell MongoDB (mongosh) est généralement bon pour fournir des messages d'erreur informatifs pour ces problèmes.

1. Noms de champs ou structure de document invalides

Lors de l'insertion ou de la mise à jour de documents, l'utilisation de noms de champs incorrects ou d'une structure de document invalide peut entraîner des erreurs.

Exemple d'erreur :

db.users.insertOne({ "bad\u0000field": "Alice" })
// MongoServerError: The dotted field 'bad\u0000field' ... is not valid for storage

Explication : Les noms de champs MongoDB ne peuvent pas contenir de caractère nul. Les noms de champs avec des traits d'union, comme "email-address", sont autorisés, bien qu'ils puissent être difficiles à interroger car vous devez les mettre entre guillemets. Le vrai problème est un caractère invalide ou une structure que MongoDB ne peut pas stocker.

Solution :

Examinez attentivement les noms de champs générés par les importations, les entrées utilisateur ou le code de sérialisation. Choisissez une convention de nommage cohérente, comme emailAddress ou email_address, et validez les données avant qu'elles n'atteignent MongoDB.

> db.users.insertOne({ name: "Alice", age: 30, emailAddress: "[email protected]" })
{ acknowledged: true, insertedId: ObjectId('...') }

2. Virgules manquantes ou supplémentaires

Comme en JavaScript, les commandes du shell MongoDB sont sensibles au placement correct des virgules dans les objets et les tableaux.

Exemple d'erreur :

db.products.insertOne({ name: "Laptop", price: 1200,, })
// SyntaxError: Unexpected token

Solution :

Supprimez la virgule superflue. Assurez un formatage cohérent pour la lisibilité.

> db.products.insertOne({ name: "Laptop", price: 1200 })
{ acknowledged: true, insertedId: ObjectId('...') }

3. Syntaxe de commande incorrecte (par exemple, find vs findOne)

Utiliser la mauvaise commande ou fournir des arguments dans un ordre incorrect peut également entraîner des erreurs.

Exemple :

db.inventory.find({ item: "notebook" }, { qty: 1, size: 1, _id: 0 })
// Cette commande est syntaxiquement correcte pour find, mais si vous aviez l'intention de trouver un seul document :

Solution :

Si vous avez l'intention de récupérer un seul document, utilisez findOne. find retourne un curseur, tandis que findOne retourne le document lui-même.

> db.inventory.findOne({ item: "notebook" }, { qty: 1, size: 1, _id: 0 })
{
  qty: 20,
  size: { h: 14, w: 21, uom: "cm" },
  ... // autres champs si la projection ne les a pas exclus et qu'ils ne sont pas spécifiquement projetés
}

Erreurs de permission courantes

Les erreurs de permission se produisent généralement lorsqu'un utilisateur tente d'effectuer une opération pour laquelle il ne dispose pas des rôles ou privilèges nécessaires.

1. Privilèges insuffisants pour exécuter la commande

Ce message d'erreur est explicite quant au manque de permissions.

Exemple d'erreur :

> db.adminCommand({ listDatabases: 1 })
Error: listDatabases requires authentication

Explication : listDatabases est une commande administrative qui nécessite une authentification et les bons privilèges. Un utilisateur peut être valide pour une base de données d'application mais toujours incapable d'inspecter l'ensemble du déploiement.

Solution :

  • Authentifiez-vous avec des identifiants appropriés : Connectez-vous à MongoDB en utilisant un utilisateur disposant des rôles nécessaires. Pour listDatabases, vous pourriez avoir besoin de vous connecter en tant qu'administrateur.
    mongosh "mongodb://<adminUser>:<adminPassword>@<host>:<port>/admin?authSource=admin"
    
    Ensuite, réessayez la commande :
    db.adminCommand({ listDatabases: 1 })
    
  • Accordez des rôles : Si vous êtes un administrateur de base de données, accordez les rôles requis à l'utilisateur rencontrant le problème.
    // Exemple : Accorder le rôle readAnyDatabase à l'utilisateur 'myUser' sur la base de données 'admin'
    use admin
    db.grantRolesToUser("myUser", [ { role: "readAnyDatabase", db: "admin" } ])
    

2. Opération d'écriture refusée

Tentative d'insérer, de mettre à jour ou de supprimer des documents dans une collection ou une base de données sans permissions d'écriture.

Exemple d'erreur :

> db.myCollection.insertOne({ name: "Test" })
WriteError: Not enough privileges to execute on "myCollection" with operation "insert"

Solution :

  • Authentifiez-vous en tant qu'utilisateur disposant de privilèges d'écriture pour la base de données/collection cible.
  • Accordez des rôles d'écriture (par exemple, readWrite, dbOwner) à l'utilisateur.
  • Vérifiez la base de données dans l'invite. Un utilisateur ayant reçu readWrite sur appdb n'a pas automatiquement accès en écriture sur test, même si le nom de la collection est le même.

Authentification et erreurs de authSource

Les erreurs d'authentification semblent souvent déroutantes car les utilisateurs MongoDB appartiennent à une base de données d'authentification spécifique. De nombreux utilisateurs d'application sont créés dans la base de données de l'application. Les utilisateurs administrateurs sont souvent créés dans admin.

Si vous voyez :

MongoServerError: Authentication failed.

vérifiez la chaîne de connexion avant de changer les mots de passe :

mongosh "mongodb://appUser:secret@db01:27017/appdb?authSource=appdb"

Si l'utilisateur a été créé dans admin, utilisez :

mongosh "mongodb://adminUser:secret@db01:27017/appdb?authSource=admin"

Le chemin de la base de données après l'hôte (/appdb) est la base de données par défaut que votre shell ouvre. authSource est l'endroit où MongoDB recherche le compte utilisateur. Les confondre est une cause courante d'échecs de connexion après le passage d'une base de données de développement locale à un serveur sécurisé.

Vous pouvez confirmer les utilisateurs authentifiés actuels avec :

db.runCommand({ connectionStatus: 1 })

Erreurs opérationnelles courantes et solutions

Les erreurs opérationnelles peuvent être plus complexes, souvent liées à l'état du déploiement MongoDB, à des problèmes réseau ou à des contraintes de ressources.

1. Délai d'attente réseau ou connexion refusée

Ces erreurs indiquent que le client n'a pas pu établir ou maintenir une connexion avec le serveur MongoDB.

Exemple d'erreur (côté client) :

Error: connect ECONNREFUSED 127.0.0.1:27017

Explication : Le client a tenté de se connecter à l'hôte et au port spécifiés, mais la connexion a été refusée. Cela pourrait signifier que le serveur MongoDB ne fonctionne pas, qu'il fonctionne sur un port différent, ou qu'un pare-feu bloque la connexion.

Solution :

  • Vérifiez l'état du serveur MongoDB : Assurez-vous que le processus mongod est en cours d'exécution sur le serveur.
    • Sous Linux : sudo systemctl status mongod ou sudo service mongod status
    • Sous macOS (avec Homebrew) : brew services list
    • Sous Windows : Vérifiez l'application Services.
  • Vérifiez la configuration MongoDB : Confirmez que mongod est configuré pour écouter sur la bonne adresse IP et le bon port (par défaut 27017). Vérifiez le fichier mongod.conf.
  • Règles de pare-feu : Assurez-vous qu'aucun pare-feu (au niveau du serveur ou du réseau) ne bloque le trafic sur le port MongoDB.
  • Chaîne de connexion correcte : Vérifiez deux fois votre chaîne de connexion pour les fautes de frappe dans l'hôte et le port.

Pour les jeux de réplicas, incluez le nom du jeu de réplicas lorsque votre pilote l'attend :

mongosh "mongodb://db01:27017,db02:27017,db03:27017/appdb?replicaSet=rs0"

Si DNS est impliqué, testez le nom d'hôte exact depuis la machine cliente :

nc -vz db01.example.com 27017

Une connexion réussie depuis le serveur de base de données lui-même ne prouve pas que les serveurs d'application, les exécuteurs CI ou les bastions peuvent l'atteindre.

2. Limite de taille de document dépassée

Les documents MongoDB ont une limite de taille BSON maximale (actuellement 16 Mo).

Exemple d'erreur :

> db.largeDocs.insertOne({ data: "... très grande chaîne ..." })
Error: BSONObj size: 17000000 bytes is too large, max 16777216 bytes

Solution :

  • Divisez les grands documents : Décomposez le grand document en documents plus petits et liés. Utilisez des références (par exemple, ObjectId) pour les lier.
  • Utilisez GridFS : Pour stocker de gros fichiers binaires (comme des images ou des vidéos) qui dépassent la limite de taille de document, utilisez la spécification GridFS de MongoDB.
  • Évitez les tableaux illimités : Un document qui croît indéfiniment, comme un document utilisateur avec chaque événement intégré pour toujours, finira par devenir lent ou atteindre des limites. Stockez les enregistrements enfants à volume élevé dans leur propre collection.

3. Erreurs de write concern

Les write concerns spécifient les garanties d'accusé de réception requises de MongoDB pour les opérations d'écriture. Si ces garanties ne sont pas respectées dans un délai d'attente, une erreur de write concern se produit.

Exemple :

// Exemple d'une opération d'écriture avec un write concern spécifique
db.myCollection.insertOne({ name: "Item" }, { writeConcern: { w: "majority", wtimeout: 1000 } });

Erreur potentielle :

WriteConcernError: waiting for replication timed out

Explication : L'opération d'écriture a échoué car le nombre requis de nœuds (dans ce cas, majority) n'a pas accusé réception de l'écriture dans le wtimeout spécifié (1000 ms).

Solution :

  • Examinez la santé du jeu de réplicas : Vérifiez la santé et l'état de votre jeu de réplicas MongoDB. Les nœuds sont-ils en retard ? Y a-t-il des problèmes réseau entre les nœuds ?
  • Augmentez wtimeout : Si une latence réseau temporaire ou des retards de réplication en sont la cause, vous pourriez envisager d'augmenter la valeur wtimeout, mais cela doit être fait avec prudence car cela peut masquer des problèmes sous-jacents.
  • Révisez le write concern : Assurez-vous que le niveau de write concern (w) est approprié pour les besoins de votre application. w: 1 (par défaut) nécessite un accusé de réception du primaire uniquement, ce qui est moins sujet aux problèmes de délai d'attente mais offre une garantie de durabilité moindre.

4. Erreurs "Not Primary" ou de préférence de lecture

Les écritures doivent aller au primaire dans un jeu de réplicas. Si votre shell ou application est connecté à un secondaire et que vous essayez d'écrire, vous pouvez voir une erreur similaire à :

MongoServerError: not primary

Vérifiez où vous êtes connecté :

db.hello()

Si isWritablePrimary est faux, connectez-vous via un URI de jeu de réplicas approprié ou acheminez les écritures vers le primaire. Pour les lectures, décidez si les lectures secondaires sont acceptables pour votre application. Les lectures secondaires peuvent être obsolètes, ne les activez donc pas simplement pour faire taire une erreur, sauf si les lectures obsolètes sont sûres pour ce cas d'utilisation.

5. Erreurs de clé en double

Les erreurs de clé en double signifient généralement qu'un index unique fait son travail.

E11000 duplicate key error collection: app.users index: email_1 dup key

Ne "corrigez" pas cela en supprimant l'index unique à moins que la règle d'unicité ne soit réellement erronée. Identifiez d'abord l'index :

db.users.getIndexes()

Ensuite, décidez si l'application doit mettre à jour le document existant, rejeter le doublon ou utiliser un upsert :

db.users.updateOne(
  { email: "[email protected]" },
  { $set: { lastLoginAt: new Date() } },
  { upsert: true }
)

Avec les upserts, assurez-vous que le filtre correspond à la clé unique qui vous intéresse. Un filtre lâche peut toujours créer des doublons sur un champ unique différent et échouer.

6. Erreurs d'opérateur de requête

Certaines erreurs proviennent de l'utilisation d'opérateurs de mise à jour ou de requête au mauvais endroit.

Ceci est incorrect pour les mises à jour de type remplacement :

db.users.updateOne(
  { email: "[email protected]" },
  { lastLoginAt: new Date() }
)

MongoDB traite ce deuxième document comme un document de remplacement. Si vous vouliez modifier un champ, utilisez $set :

db.users.updateOne(
  { email: "[email protected]" },
  { $set: { lastLoginAt: new Date() } }
)

Cette distinction est importante car les mises à jour de remplacement peuvent supprimer des champs qui ne sont pas inclus dans le document de remplacement.

Une routine de débogage pratique

Lorsqu'une commande échoue, capturez l'erreur exacte, puis répondez à ces questions dans l'ordre :

  1. mongosh est-il connecté à l'hôte attendu ?

    db.hello().me
    
  2. Est-ce que j'utilise la base de données attendue ?

    db.getName()
    
  3. Suis-je authentifié, et avec quels rôles ?

    db.runCommand({ connectionStatus: 1 })
    
  4. S'agit-il d'une erreur d'analyse, d'une erreur serveur ou d'une erreur de write concern ?

    Les erreurs d'analyse apparaissent généralement avant que la commande n'atteigne le serveur. Les erreurs de permission, de clé en double, de non-primaire, de validation et de write concern proviennent de MongoDB après avoir évalué la commande.

  5. La même commande fonctionne-t-elle avec un document minimal ?

    Si une petite insertion fonctionne mais que la véritable insertion échoue, inspectez la forme du document, la taille, les noms de champs, la validation de schéma et les index uniques.

  6. L'échec dépend-il du nœud ?

    Les problèmes de jeu de réplicas et de cluster shardé peuvent apparaître uniquement sur certains nœuds ou via certains routeurs. Pour les clusters shardés, le trafic applicatif doit normalement passer par mongos, pas directement vers les membres du shard.

Lire les logs sans deviner

Les logs MongoDB expliquent souvent la raison côté serveur plus clairement que la sortie du shell. Sur les installations Linux par paquet, vérifiez les logs du service :

journalctl -u mongod --since "30 minutes ago"

ou le chemin du log MongoDB configuré dans mongod.conf.

Recherchez les messages autour du même horodatage que la commande qui a échoué. Les indices utiles incluent les échecs d'authentification, les réinitialisations de connexion, les opérations lentes, les changements d'état de réplication, les erreurs de disque plein et les échecs de validation de schéma.

Pour les requêtes lentes ou échouées, explain() est plus utile que de deviner :

db.orders.find({ customerId: 123, status: "open" }).explain("executionStats")

Si la requête scanne un grand nombre de documents pour retourner quelques résultats, la commande peut être syntaxiquement correcte mais opérationnellement coûteuse. Ce n'est pas un problème de shell ; c'est un problème d'indexation ou de forme de requête.

Meilleures pratiques pour prévenir les erreurs de commande

  • Utilisez mongosh et ses fonctionnalités : Tirez parti de la complétion par tabulation, de l'historique des commandes et des messages d'erreur clairs fournis par le shell MongoDB moderne.
  • Comprenez votre modèle de données : Concevez votre schéma et vos structures de document avec soin pour éviter des problèmes comme des documents surdimensionnés ou des requêtes inefficaces.
  • Implémentez une authentification et une autorisation appropriées : Définissez des utilisateurs avec le moindre privilège nécessaire pour leurs rôles.
  • Surveillez votre déploiement : Vérifiez régulièrement les logs MongoDB, les métriques de performance et l'état du jeu de réplicas pour identifier de manière proactive les problèmes potentiels.
  • Testez les commandes : Avant de déployer des commandes complexes ou des modifications en production, testez-les minutieusement dans un environnement de développement ou de staging.
  • Maintenez MongoDB à jour selon un calendrier planifié : Les versions plus récentes peuvent inclure des corrections de bugs et des changements de comportement. Lisez les notes de version et testez les mises à niveau avant le déploiement en production.

La plupart des erreurs de commande MongoDB deviennent gérables une fois que vous séparez les problèmes de syntaxe du shell, de contexte d'authentification, d'autorisation, de topologie et de forme des données. Commencez par prouver où vous êtes connecté et qui vous êtes. Laissez ensuite le message d'erreur exact vous orienter vers la couche suivante au lieu de réécrire la commande au hasard.