33

Modifica principale: Da quando ho trovato questo problema, l'ho ridotto a un livello inferiore. Penso che questa sia ora una marginalmente descrizione più precisa del problema. I commenti sul PO potrebbero pertanto non essere interamente correlati.Errore di filettatura quando si utilizza `ActiveRecord with_connection do` e ActionController :: Live

Modifica versione leggermente modificata pubblicato in Rails/progetti puma: https://github.com/rails/rails/issues/21209, https://github.com/puma/puma/issues/758

Modifica Ora riprodotto con OS X e Rainbows

Sommario:Quando si utilizza Puma e correre lungo connessioni in esecuzione Sto ricevendo costantemente errori relativi alle connessioni ActiveRecord che attraversano i thread. Questo si manifesta in un messaggio comemessage type 0x## arrived from server while idlee un server bloccato (arrestato).

Il set up:

  • Ubuntu 15/OSX Yosemite
  • PostgreSQL (9.4)/MySQL (mysqld 5.6.25-0ubuntu0.15.04.1)
  • Rubino - MRI 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]/Rubinius rbx-2.5.8
  • Rails (4.2.3, 4.2.1
  • Puma (2.12.2, 2.11)
  • pg (pg-0.18.2)/mysql2

nota, non tutte le combinazioni delle versioni di cui sopra sono state provate. La prima versione elencata è ciò che sto attualmente testando contro.

  • rails new issue-test
  • Aggiungere un percorso get 'events' => 'streaming#events'
  • Aggiungere un controller streaming_controller.rb
  • Impostare roba database (pool: 2, ma visto con diverse dimensioni della piscina)

Codice:

class StreamingController < ApplicationController 

    include ActionController::Live 

    def events 
    begin 
     response.headers["Content-Type"] = "text/event-stream" 
     sse = SSE.new(response.stream) 
     sse.write({:data => 'starting'} , {:event => :version_heartbeat}) 
     ActiveRecord::Base.connection_pool.release_connection 
     while true do 
     ActiveRecord::Base.connection_pool.with_connection do |conn| 
      ActiveRecord::Base.connection.query_cache.clear 
      logger.info 'START' 
      conn.execute 'SELECT pg_sleep(3)' 
      logger.info 'FINISH' 
      sse.write({:data => 'continuing'}, {:event => :version_heartbeat}) 
      sleep 0.5 
     end 
     end 
    rescue IOError 
    rescue ClientDisconnected 
    ensure 
     logger.info 'Ensuring event stream is closed' 
     sse.close 
    end 
    render nothing: true 
    end 
end 
Configurazione

Puma:

workers 1 
threads 2, 2 
#... 
bind "tcp://0.0.0.0:9292" 

#... 
activate_control_app 

on_worker_boot do 
    require "active_record" 
    ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished 
    ActiveRecord::Base.establish_connection(YAML.load_file("#{app_dir}/config/database.yml")[rails_env]) 
end 
  • eseguire il server puma -e production -C path/to/puma/config/production.rb

script di test:

#!/bin/bash 

timeout 30 curl -vS http://0.0.0.0/events & 
timeout 5 curl -vS http://0.0.0.0/events & 
timeout 30 curl -vS http://0.0.0.0/events 

Questo ragionevolmente risultati sempre in un blocco completo del server di applicazione (in PostgreSQL, vedere le note).Il messaggio viene da paura libpq:

message type 0x44 arrived from server while idle 
message type 0x43 arrived from server while idle 
message type 0x5a arrived from server while idle 
message type 0x54 arrived from server while idle 

Nel 'mondo reale' Ho un paio di elementi abbastanza in più e il problema si presenta in modo casuale. La mia ricerca indica che questo messaggio proviene da libpq ed è sottotesto per il problema di comunicazione ', eventualmente utilizzando la connessione in thread diversi'. Infine, durante la stesura di questo, ho avuto il blocco del server senza un singolo messaggio in qualsiasi registro.

Quindi, la domanda (s):

  1. è il modello che sto seguendo non è legale in qualche modo? Che cosa ho mis [sed | capito]?
  2. Che cos'è lo "standard" per lavorare con le connessioni di database qui che dovrebbe evitare questi problemi?
  3. Riesci a vedere un modo per riprodurlo in modo affidabile?

o

  1. Qual è il problema di fondo qui e come posso risolverlo?

MySQL

Se MySQL in esecuzione, il messaggio è un po 'diverso, e l'applicazione recupera (anche se non sono sicuro se è poi in qualche stato non definito):

F, [2015-07-30T14:12:07.078215 #15606] FATAL -- : 
ActiveRecord::StatementInvalid (Mysql2::Error: This connection is in use by: #<Thread:[email protected]/home/dev/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/live.rb:269 sleep>: SELECT `tasks`.* FROM `tasks` ORDER BY `tasks`.`id` ASC LIMIT 1): 
+1

Da ActiveRecord, i documenti che utilizzano with_connection si basano sul blocco passato come argomento che termina. Sei sicuro che sia completo? Che ne dici di gestire la connessione con Base.connection o checkout? – Grasshopper

+1

@Grasshopper - grazie! Sono preoccupato che ciò manterrà la connessione aperta per tutta la durata della richiesta (ore), in modo da mangiare il mio pool di connessioni abbastanza rapidamente? Suppongo che il modo in cui potrebbe non essere completato è se sse.write blocca per qualche motivo e il thread si trova lì, cioè se la connessione è andata e non ritorna per qualche motivo? (Detto questo, non sono sicuro che spieghi completamente i messaggi basati su problemi thread da libpq). (sperimenterà alcune cose in quella direzione) – button

+1

In effetti il ​​problema che descrivi può accadere in scenari in cui le connessioni non vengono rilasciate. Puoi provare a rimuovere la chiamata a sse.write dal blocco with_connection? – Grasshopper

risposta

1

Attenzione: leggere 'risposta' come 'sembra fare la differenza'


non vedo il problema accade se cambio il blocco di controllo per assomigliare:

begin 
    #... 
    while true do 
    t = Thread.new do #<<<<<<<<<<<<<<<<< 
     ActiveRecord::Base.connection_pool.with_connection do |conn| 
      #... 
     end 
    end 
    t.join #<<<<<<<<<<<<<<<<< 
    end 
    #... 
rescue IOError 
#... 

Ma io non so se questo è stato effettivamente risolto il problema o semplicemente reso estremamente improbabile. Né posso davvero capire perché questo farebbe la differenza.

Inserendo questo come soluzione nel caso in cui aiuta, ma continua a scavare sul problema.

+0

Quindi ho anche un lungo processo in esecuzione e questo è il modo in cui lo uso anch'io. la mia domanda è, perché inizi una discussione per ogni iterazione? non ha senso iniziare il thread e controllare la connessione per ogni client fuori dal loop? –

Problemi correlati