Erro SQL com `screened_ip_addresses` (API retorna 500)

Olá, amigos,

A API está retornando um erro 500 quando chamo para criar uma nova postagem (em um tópico existente). Nos logs, vejo:

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'

Falha ao lidar com a exceção no middleware do app de exceção: PG::InvalidTextRepresentation: ERROR:  invalid input syntax for type inet: ""
LINE 1: ..._addresses".* FROM "screened_ip_addresses" WHERE ('' <<= ip_...
                                                             ^

Aqui está minha lista de IPs filtrados — além da captura de tela abaixo, também coloquei na lista de permissões o IP da máquina que está chamando a API. (Estou usando uma chave de API em nível de sistema para importar em massa tópicos/mensagens antigas do meu antigo software de fórum.)

Apenas por curiosidade, também solicitei à API a lista de IPs filtrados… mesmos resultados. (https://mydiscourse.com/admin/logs/screened_ip_addresses.json)

Não tenho certeza do mais o que verificar. :man_shrugging:t2:

Alguém sabe:

1. O que está causando esse erro, e
2. Como posso corrigi-lo agora e evitar que isso aconteça no futuro?

Ajuda :slight_smile:

Obrigado!

Ou será que se trata de algum outro SQL tentando inserir dados nessa tabela?

Você pode postar o código que está usando para fazer uma requisição à API, com as credenciais removidas, para que possamos ver como você está fazendo a requisição à API?

Olá @blake, obrigado pela resposta. Apenas para ter certeza absoluta de que não era um problema no meu código, montei a chamada básica da API no Insomnia (similar ao Postman, mas, na minha opinião, mais simples/fácil). Infelizmente, os mesmos resultados, mas pelo menos agora está muito claro:

Aqui está a chamada que preparei para criar um novo post (já reduzi o “número mínimo de palavras nos posts” para 1):

E aqui estão os resultados

E as 2 mensagens de erro nos logs dessas chamadas de teste:

Erro 1:

Mensagem (15 cópias relatadas)

ActiveRecord::StatementInvalid (PG::InvalidTextRepresentation: ERRO: sintaxe de entrada inválida para tipo inet: ""
LINHA 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'

Erro 2:

Mensagem (15 cópias relatadas)

Falha ao lidar com exceção no middleware do aplicativo de exceção: PG::InvalidTextRepresentation: ERRO: sintaxe de entrada inválida para tipo inet: ""
LINHA 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'

Acabei de criar esse usuário há um momento, então me pergunto se isso é um bug relacionado aos níveis de confiança, de alguma forma, ou algo mais que não está totalmente autorizado para um usuário novo. Vou fazer alguns experimentos com isso e ver se consigo descobrir o que permitirá que o post seja aceito pela API.

Mas, de qualquer forma, parece que eu descobri algum tipo de bug… ?!

Então, é assim que contornei o bug… Comecei a experimentar:

  1. Mudei o usuário na minha chamada de API para ‘system’ e isso funcionou.
  2. Então pensei, hmm, e mudei o usuário para um terceiro usuário que eu ainda não tinha testado. Isso também funcionou.
  3. Depois, mudei o usuário de volta para o nome de usuário original, o que significa que eu estaria repetindo a chamada de API exata que havia gerado um erro 500 antes. Exceto que agora funcionou. :man_shrugging:t2:

Se isso acontecer novamente, vou ver se consigo descobrir como reproduzi-lo de forma confiável. Enquanto isso, talvez alguém que conheça o código do Discourse melhor do que eu :wink: possa dar uma olhada nessa parte e ter certeza de que não há bugs óbvios saltando aos olhos.

Hmmm, definitivamente há algo estranho acontecendo aqui. Reiniciei meu script, e ele começou a criar tópicos em massa e postar mensagens… e, mais uma vez, agora com um usuário diferente, estou recebendo o erro 500.

Levei esse nome de usuário para o Insomnia e testei a API… com certeza, erro 500. Mudei para ‘system’ e a requisição passou… então voltei ao nome de usuário original e falhou novamente com um erro 500!

O usuário que está causando o erro 500 (e todos os meus usuários sendo importados, exceto ‘system’) são TL1, e eu alterei os limites para que TL1 possa postar e

Isso definitivamente parece ter a ver com limitação de taxa, de alguma forma? Eu suspeitaria que o nginx, ou algo a montante, poderia estar causando o erro 500, mas removi toda a limitação de taxa do nginx, e o erro 500 está aparecendo claramente como esse bug de SQL nos logs de erro.

Notei que o erro de SQL está falhando em torno de uma coluna que referencia o endereço IP… poderia haver algo estranho acontecendo com a forma como a limitação de taxa está funcionando que poderia causar esse problema? Tentei fazer login com uma VPN (para mudar meu IP), mas ainda recebo o erro 500.

Enquanto isso, vejo que o problema está em rack-mini-profiler, que não é necessário. Vamos ver se consigo desativá-lo e se isso resolve o problema. … Não. Agora não vejo mais o mini-profiler na minha conta de administrador, mas ainda estou recebendo o mesmo HTTP 500 com os mesmos erros no log de erro :frowning:

Atualização: ao mudar o usuário para TL3, o problema parece desaparecer. Voltar para TL1 faz com que aconteça novamente. Estou testando essa teoria agora… não. Às vezes funciona, mas outras vezes, mesmo se eu editar manualmente o TL do usuário, o usuário parece ficar “preso”.

@blake ou alguém mais da @staff… ajuda! :wink: O que resta tentar? Há algo que eu possa limpar completamente aqui (relacionado ao código que está gerando o erro) e redefinir para as configurações de fábrica?

Parece-me que se trata de um possível problema de rede; com base no stack trace, às vezes nenhum endereço IP é detectado para o usuário, de alguma forma? Ou você tem dados incorretos no banco de dados, em admin, logs ou em endereços IP filtrados?

Concordo - <<= significa “LHS contido no RHS”, então parece que um endereço IP inválido/vazio está chegando ao aplicativo.

(Tenho curiosidade sobre como você não tem nenhum endereço IP disponível para o aplicativo; minha única suposição é que a solicitação está chegando por um socket Unix sem um cabeçalho de informação de IP encaminhado)

Concordo, mas não consigo entender por que, às vezes, o IP está presente e, outras vezes, não. No meu script de importação, recebo esse erro 500 após pelo menos 5 a 7 chamadas de API bem-sucedidas anteriores. É por isso que acho que pode ser algum dado corrompido no banco de dados.

Sim, é estranho. Mas, ao mesmo tempo, se eu chamar com um nome de usuário “inválido”, falha; se chamar com “system” ou outro nome de usuário, funciona. Então, acho que estamos apontando para dados corrompidos no meu banco de dados, possivelmente.

Alguém poderia me dar alguns comandos fáceis de linha de comando em Ruby que eu possa digitar para limpar/redefinir quaisquer tabelas do banco de dados que possam estar com problemas?

Acho que não há nada de errado com seu banco de dados, pois o erro está verificando se há um endereço IP em branco na tabela de endereços IP filtrados, e não se há endereços IP vazios no seu banco de dados.

O interessante, porém, é que esse código está verificando a existência de um ip_address antes de fazer essa solicitação SQL que está gerando o erro 500.

Você pode descrever como sua instância do Discourse está configurada? Ela veio de uma importação? Você está na versão mais recente? Quanto de memória RAM ela tem? Você seguiu este guia?

Você pode verificar essa configuração do site: max new accounts per registration ip? Não tenho certeza se isso importa neste caso, mas talvez esteja causando algum problema.

No seu script em lote, você poderia reduzir a velocidade e adicionar uma pausa de 1 segundo entre as solicitações para ver se isso faz alguma diferença?

Qual é o objetivo dessas chamadas de API? Se for um script único para importar usuários e posts, talvez um script de importação executado diretamente no servidor, sem fazer chamadas de API, seja melhor?

Desculpe por todas as perguntas, mas nunca nos deparamos com esse problema antes e o código em torno de screened_ip_addresses não foi atualizado há muito tempo. Não estou dizendo que não haja um bug em algum lugar da base de código, mas após uma rápida análise, nada se destaca no momento.

Claro — é totalmente Docker, na Digital Ocean. Segui aquele excelente guia à risca.

Claro, acabei de alterar essa configuração do padrão 3 para 99999. Sem diferença, ainda estou recebendo o erro 500.

Tentei isso, sem diferença. Note que ainda recebo o erro 500 com apenas uma conta “problemática”, usando o Insomnia. Então, neste ponto, é como se aquela conta estivesse “envenenada”, e mesmo que eu faça apenas aquela única chamada de API de “criar mensagem” com ela (nenhuma antes ou depois), ainda recebo o erro 500. Mas sim, meu script de importação também está recebendo o erro 500 :wink:

Sim, sou um programador experiente, mas não conheço nada de RoR/Ruby, então não consigo usar as opções prontas que vocês fornecem, embora reconheça que elas provavelmente sejam superiores à minha abordagem manual de percorrer meus fóruns existentes e criar usuários, etc., sob demanda via API. Daí meu post no marketplace… Eu adoraria conseguir fazer tudo isso funcionar por conta própria, mas também tenho um prazo rígido :wink:

Compreendo perfeitamente, e agradeço sua atenção a isso.

Então, aqui está algo que posso oferecer e que pode ajudar bastante: como se trata de uma instalação pronta, e eu não fiz quase nenhuma personalização, ALÉM de o bug ser facilmente reproduzível sem meu código (basta usar o Insomnia) E eu ainda não ter lançado os fóruns, eu poderia passar para você o login root da instância da Digital Ocean, minha chave de API, etc., e não tenho problema nenhum com você mexendo por aí. Meus fóruns do Discourse são atualmente um monte de categorias vazias e algumas outras mensagens de introdução especiais que configuramos, mas estão basicamente vazios e ainda não há usuários reais lá (apenas administradores). Então, não haveria problema se você quiser testar coisas, criar/apagar tópicos e mensagens, etc.

Isso definitivamente seria a maneira mais rápida de você ver o bug em primeira mão. E, como você estaria lá como root, também poderia mexer em qualquer coisa de baixo nível do Discourse que quiser, para descobrir por que isso está acontecendo.

E

O que acontece se você desativar o SSO?

Sem diferença, ainda retorna o erro 500.

Outra ideia: é possível que todas as contas para as quais funcionou sejam de administrador? Sei que alguns dos limites de taxa no nível da aplicação são contornados para administradores.

No entanto, de modo geral, é muito mais fácil simplesmente escrever um script de importação. Todo este tópico é o que “muito” significa :wink:

Hmm, pensei que já tivesse algo disso… meu usuário de teste “envenenado”, george21, estava no TL0. Então, mudei-o para TL1 e, em seguida, funcionou. Ok! Talvez seja isso! Então, mudei george21 de volta para TL0… e agora ele não está mais “envenenado” — ele consegue fazer a chamada da API mesmo estando no TL0.

Agora, vou executar meu script de importação novamente, e aha. Agora george21 está lançando o erro 500 no script de importação. E quando tento no Insomnia, falha. Então, agora vou colocar george21 de volta no TL1 e… sim, ele consegue executar a chamada HTTP.

Então, aqui está o que parece conseguir reproduzir:

  1. Se uma série de chamadas de API for feita (?), de alguma forma isso faz com que uma chamada subsequente falhe com um usuário TL0.
  2. Alterar o usuário TL0 para TL1 permite que a chamada de API seja processada.
  3. E, estranhamente, ao mudar esse mesmo usuário de volta para TL0, a chamada de API ainda consegue ser processada.
  4. Executar o script novamente funciona até falhar mais uma vez com outro usuário TL0.

Observe que:

  1. Até onde sei, todos os mínimos etc. para TL0 foram elevados (ou seja, tentei remover cada bloqueio que impediria um usuário TL0 de postar), e
  2. Mesmo que isso seja um problema com algum tipo de limite de taxa interno para usuários TL0, a API não deveria lançar um erro 500 e registrar um erro SQL no log de erros. Portanto, acho que podemos afirmar, neste ponto, que definitivamente há um bug em algum lugar.

Sim, hum, eu sei, e já expliquei quatro vezes por que não estou escrevendo meu próprio script de importação (com base nos exemplos fornecidos). :wink: Daí minha mudança de abordagem.

E, enquanto isso, continuo contribuindo aqui para tentar ajudar a encontrar e corrigir esse bug. Hoje, isso está afetando meu script de importação. Amanhã, pode ser algum script importante que você tenha no seu site e que precise da API…