Erreur SQL avec `screened_ip_addresses` (l'API renvoie 500)

Salut les amis,

L’API me renvoie une erreur 500 lorsque j’appelle pour créer un nouveau message (dans un sujet existant). Dans les journaux, je vois :

ActiveRecord::StatementInvalid (PG::InvalidTextRepresentation: ERROR:  invalid input syntax for type inet: ""
LINE 1: ..._addresses".* FROM "screened_ip_addresses" WHERE ('' <<= ip_...
                                                             ^
)
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'

Échec de la gestion de l'exception dans le middleware de l'application d'exception : PG::InvalidTextRepresentation: ERROR:  invalid input syntax for type inet: ""
LINE 1: ..._addresses".* FROM "screened_ip_addresses" WHERE ('' <<= ip_...
                                                             ^

Voici ma liste d’adresses IP filtrées : outre la capture d’écran ci-dessous, j’ai également mis en liste blanche l’IP de la machine qui appelle l’API. (J’utilise une clé API au niveau du système pour importer en masse d’anciens sujets/messages de mon ancien logiciel de forum.)

Par curiosité, j’ai également demandé à l’API la liste des adresses IP filtrées… mêmes résultats. (https://mydiscourse.com/admin/logs/screened_ip_addresses.json)

Je ne sais pas quoi d’autre vérifier. :man_shrugging:t2:

Quelqu’un sait-il :

1. Quelle est la cause de cette erreur, et
2. Comment puis-je la corriger maintenant et l’empêcher de se reproduire à l’avenir ?

Aide :slight_smile:

Merci !

Ou s’agit-il plutôt d’une autre requête SQL tentant d’insérer des données dans cette table ?

Pouvez-vous poster le code que vous utilisez pour effectuer une requête API sans les informations d’identification, afin que nous puissions voir comment vous effectuez la requête API ?

Salut @blake, merci pour ta réponse. Pour être absolument certain que ce n’était pas un problème de mon code, j’ai préparé un appel API basique dans Insomnia (similaire à Postman mais, à mon avis, plus simple et plus facile). Malheureusement, mêmes résultats, mais au moins c’est maintenant très clair :

Voici l’appel que j’ai préparé pour créer un nouveau post (j’ai déjà réduit le “nombre minimum de mots dans les posts” à 1) :

Et voici les résultats

Et les 2 messages d’erreur dans les journaux issus de ces appels de test :

Erreur 1 :

Message (15 copies signalées)

ActiveRecord::StatementInvalid (PG::InvalidTextRepresentation: ERREUR : syntaxe d'entrée invalide pour le type inet : ""
LIGNE 1 : ..._addresses".* FROM "screened_ip_addresses" WHERE ('' <<= ip_...
                                                             ^
)
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'

Backtrace

rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'
rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'
activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:672:in `block (2 levels) in exec_no_cache'
activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:48:in `block in permit_concurrent_loads'
activesupport-6.0.1/lib/active_support/concurrency/share_lock.rb:187:in `yield_shares'
activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:47:in `permit_concurrent_loads'
activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:671:in `block in exec_no_cache'
activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:718:in `block (2 levels) in log'
/usr/local/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:717:in `block in log'

Erreur 2 :

Message (15 copies signalées)

Échec de la gestion de l'exception dans le middleware de l'application d'exception : PG::InvalidTextRepresentation: ERREUR : syntaxe d'entrée invalide pour le type inet : ""
LIGNE 1 : ..._addresses".* FROM "screened_ip_addresses" WHERE ('' <<= ip_...
                                                             ^


Backtrace

rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'
rack-mini-profiler-2.0.1/lib/patches/db/pg.rb:69:in `exec_params'
activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:672:in `block (2 levels) in exec_no_cache'
activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:48:in `block in permit_concurrent_loads'
activesupport-6.0.1/lib/active_support/concurrency/share_lock.rb:187:in `yield_shares'
activesupport-6.0.1/lib/active_support/dependencies/interlock.rb:47:in `permit_concurrent_loads'
activerecord-6.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:671:in `block in exec_no_cache'
activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:718:in `block (2 levels) in log'
/usr/local/lib/ruby/2.6.0/monitor.rb:235:in `mon_synchronize'
activerecord-6.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:717:in `block in log'

J’ai créé cet utilisateur il y a un instant, alors je me demande si c’est un bug lié aux niveaux de confiance, d’une manière ou d’une autre, ou quelque chose d’autre qui n’est pas entièrement autorisé pour un nouvel utilisateur. Je vais faire quelques essais à ce sujet et voir si je peux comprendre ce qui permettra au post de passer via l’API.

Mais dans tous les cas, il semble que j’ai mis en lumière un certain type de bug… ?!

Voici comment j’ai contourné le bug… J’ai commencé à expérimenter :

  1. J’ai changé l’utilisateur dans mon appel API en ‘system’ et cela a fonctionné
  2. Alors j’ai pensé, hmm, et j’ai changé l’utilisateur vers un 3e utilisateur que je n’avais pas encore essayé. Cela a aussi fonctionné.
  3. Ensuite, j’ai remis l’utilisateur à son nom d’origine, ce qui signifie que je refaisais exactement le même appel API qui avait généré une erreur 500 auparavant. Sauf que cette fois, ça a marché. :man_shrugging:t2:

Si cela se reproduit, je verrai si je peux trouver comment le reproduire de manière fiable. En attendant, peut-être que quelqu’un qui connaît mieux le code de Discourse que moi :wink: pourra jeter un coup d’œil à cette partie et s’assurer qu’il n’y a pas de bug évident qui saute aux yeux.

Hmm, il y a définitivement quelque chose d’étrange en train de se passer ici. J’ai relancé mon script, et il commence à créer massivement des sujets et à publier des messages… et encore une fois, cette fois avec un autre utilisateur, je reçois une erreur 500.

J’utilise ce nom d’utilisateur dans Insomnia pour tester l’API… effectivement, erreur 500. Je passe à ‘system’ et tout fonctionne… puis je reviens au nom d’utilisateur d’origine et l’échec avec une erreur 500 se reproduit !

L’utilisateur qui provoque l’erreur 500 (ainsi que tous mes utilisateurs importés, sauf ‘system’) sont de niveau TL1, et j’ai modifié les limites pour que les TL1 puissent poster et…

Cela ressemble vraiment à un problème lié à la limitation du débit, d’une manière ou d’une autre ? Je soupçonnais nginx ou autre chose en amont de provoquer l’erreur 500, mais j’ai supprimé toute limitation du débit de nginx, et l’erreur 500 apparaît clairement comme ce bug SQL dans les journaux d’erreur.

J’ai remarqué que l’erreur SQL se produit autour d’une colonne qui fait référence à une adresse IP… y aurait-il quelque chose d’étrange dans le fonctionnement de la limitation du débit qui pourrait causer ce problème ? J’ai essayé de me connecter avec un VPN (pour changer mon adresse IP), mais j’obtiens toujours l’erreur 500.

En attendant, je vois que le problème vient de rack-mini-profiler, qui n’est pas nécessaire. Voyons si je peux le désactiver et si cela résout le problème. … Non. Maintenant, je ne vois plus le mini-profiler sur mon compte administrateur, mais je reçois toujours la même erreur HTTP 500 avec les mêmes erreurs dans le journal d’erreur :frowning:

Mise à jour : en changeant l’utilisateur en TL3, le problème semble disparaître. En revenant à TL1, le problème réapparaît. Je teste cette théorie maintenant… non. Parfois cela fonctionne, mais d’autres fois, même si je modifie manuellement le niveau TL de l’utilisateur, celui-ci semble rester “coincé”.

@blake ou quelqu’un d’autre de l’équipe @staff… aide ! :wink: Que reste-t-il à essayer ? Y a-t-il quelque chose que je peux complètement vider ici (lié au code qui provoque l’erreur) et réinitialiser aux paramètres d’usine ?

Il semble que ce soit un problème de réseau, d’après la pile d’appels : parfois, aucune adresse IP n’est détectée pour l’utilisateur, d’une manière ou d’une autre ? Ou bien avez-vous des données de base de données incorrectes dans admin, les journaux ou les adresses IP filtrées ?

D’accord - <<= signifie « LHS contenu dans RHS », donc il semble qu’une adresse IP invalide ou vide parvienne à l’application.

(Je suis curieux de savoir comment aucune adresse IP n’est disponible pour l’application ; mon seul soupçon est que la requête arrive via un socket Unix sans en-tête d’information d’IP transférée)

Je suis d’accord, mais je ne vois pas pourquoi l’adresse IP serait parfois présente et parfois absente. Dans mon script d’importation, j’obtiens cette erreur 500 après au moins 5 à 7 appels API réussis précédents. C’est pourquoi je pense qu’il s’agit de données corrompues dans la base de données, d’une manière ou d’une autre.

Oui, c’est étrange. Mais en même temps, si j’appelle avec un « mauvais » nom d’utilisateur, cela échoue ; si j’appelle avec « system » ou un autre nom d’utilisateur, cela fonctionne. Donc je pense que nous pointons vers des données corrompues dans ma base de données, peut-être.

Quelqu’un peut-il me donner quelques commandes Ruby simples en ligne de commande que je peux taper pour effacer/réinitialiser les tables de base de données qui pourraient être endommagées ?

Je ne pense pas qu’il y ait un problème avec votre base de données, car l’erreur vérifie s’il existe une adresse IP vide dans la table des adresses IP filtrées, et non s’il y a des adresses IP vides dans votre base de données.

Ce qui est intéressant, c’est que ce code vérifie l’existence d’une adresse IP avant d’effectuer cette requête SQL qui provoque l’erreur 500.

Pouvez-vous décrire comment votre instance Discourse est configurée ? Provient-elle d’une importation ? Utilisez-vous la dernière version ? De quelle quantité de RAM dispose-t-elle ? Avez-vous suivi ce guide ?

Pouvez-vous vérifier ce paramètre du site : max new accounts per registration ip ? Je ne suis pas sûr que cela compte dans ce cas, mais cela pourrait peut-être causer un problème.

Dans votre script en lot, pourriez-vous le ralentir et ajouter une pause de 1 seconde entre les requêtes pour voir si cela fait une différence ?

Quel est l’objectif de ces appels API ? S’il s’agit d’un script ponctuel pour importer des utilisateurs et des publications, alors peut-être qu’un script d’importation exécuté directement sur le serveur, sans faire d’appels API, serait préférable ?

Désolé pour toutes ces questions, mais nous n’avons jamais rencontré ce problème auparavant et le code autour de screened_ip_addresses n’a pas été mis à jour depuis longtemps. Je ne dis pas qu’il n’y a pas de bug quelque part dans la base de code, mais après un rapide examen, rien ne ressort pour le moment.

Bien sûr — c’est entièrement Docker, sur Digital Ocean. J’ai suivi ce guide excellent à la lettre.

Bien sûr, je viens de changer ce paramètre de la valeur par défaut 3 à 99999. Aucune différence, j’obtiens toujours l’erreur 500.

J’ai essayé, aucune différence. Notez que j’obtiens toujours l’erreur 500 avec UN seul « mauvais » compte, via Insomnia. Donc à ce stade, c’est comme si ce seul compte était empoisonné, et même si je fais juste un seul appel API « créer message » avec lui (aucun avant ou après), j’obtiens toujours l’erreur 500. Mais oui, mon script d’importation obtient aussi l’erreur 500 :wink:

Oui, je suis un codeur expérimenté mais je ne connais pas du tout RoR/Ruby, donc je ne peux pas vraiment utiliser les options prêtes à l’emploi que vous proposez, même si je reconnais qu’elles sont probablement supérieures à ce que je ferais en parcourant manuellement mes forums existants et en créant des utilisateurs, etc. à la volée via l’API. D’où mon post sur la marketplace… J’aimerais beaucoup que tout cela fonctionne pour moi, mais j’ai aussi une date limite stricte :wink:

Je comprends tout à fait, et j’apprécie ton attention portée à ce problème.

Voici donc quelque chose que je peux offrir et qui pourrait beaucoup aider : comme il s’agit d’une installation standard, je n’ai pas fait beaucoup de personnalisations, ET le bug est facilement reproductible sans mon code (il suffit d’utiliser Insomnia) ET je n’ai pas encore lancé les forums, je pourrais te transmettre l’identifiant root de l’instance Digital Ocean, ma clé API, etc., et je n’ai aucun problème si tu veux fouiller dedans. Mes forums Discourse sont actuellement un tas de catégories vides et quelques autres messages d’introduction spéciaux que nous avons configurés, mais c’est essentiellement vide et il n’y a pas encore de vrais utilisateurs dedans (juste des administrateurs). Donc, cela ne me dérange pas si tu veux tester des choses, créer/effacer des sujets et des messages, etc.

Ce serait certainement le moyen le plus rapide de voir le bug de tes propres yeux. Et ensuite, comme tu seras dedans en tant que root, tu pourras aussi manipuler tout ce que tu veux au niveau bas de Discourse pour comprendre pourquoi cela se produit.

E

Que se passe-t-il si vous désactivez l’authentification unique (SSO) ?

Aucune différence, cela génère toujours l’erreur 500.

Une autre idée : est-il possible que tous les comptes pour lesquels cela a fonctionné soient des administrateurs ? Je sais que certaines limites de taux au niveau de l’application sont contournées pour les administrateurs.

En général, cependant, il est beaucoup plus simple d’écrire simplement un script d’importation. Tout ce sujet illustre à quoi ressemble « beaucoup » :wink:

Hmm, je pensais avoir trouvé quelque chose… mon utilisateur de test « empoisonné », george21, était au niveau TL0. Alors je l’ai passé à TL1 et cela a fonctionné. Ok ! Peut-être que c’est ça ! Alors j’ai remis george21 à TL0… et maintenant il n’est plus « empoisonné » — il peut effectuer l’appel API même en tant que TL0.

Maintenant, je relance mon script d’importation, et aha. Maintenant, george21 génère une erreur 500 dans le script d’importation. Et quand j’essaie dans Insomnia, cela échoue. Alors je remets george21 à TL1 et… oui, il peut exécuter l’appel HTTP.

Voici donc ce que je parviens apparemment à reproduire :

  1. Si une série d’appels API est effectuée (?), cela provoque d’une manière ou d’une autre l’échec d’un appel API ultérieur avec un utilisateur TL0.
  2. Changer l’utilisateur TL0 en TL1 permet à l’appel API de passer.
  3. Étrangement, changer ce même utilisateur de nouveau en TL0 permet toujours à l’appel API de passer.
  4. Relancer le script fonctionne bien jusqu’à ce qu’il échoue à nouveau avec un autre utilisateur TL0.

Notez que :

  1. À ma connaissance, tous les minimums, etc., pour TL0 ont été relevés (c’est-à-dire que j’ai essayé de supprimer chaque blocage empêchant un utilisateur TL0 de poster), et
  2. Même s’il s’agit d’un problème lié à une sorte de limite de taux interne pour les utilisateurs TL0, l’API ne devrait pas renvoyer une erreur 500 et inscrire une erreur SQL dans le journal des erreurs. Nous pouvons donc affirmer à ce stade qu’il y a définitivement un bug quelque part.

Oui, euh, je sais, et j’ai déjà expliqué quatre fois pourquoi je n’écris pas mon propre script d’importation (basé sur les exemples donnés). :wink: D’où mon changement d’approche.

En attendant, je continue de contribuer ici pour essayer d’aider à trouver et corriger ce bug. Aujourd’hui, cela impacte mon script d’importation. Demain, cela pourrait être un script important que vous avez sur votre site et qui a besoin de l’API…