2012-07-26 12 views
9

Ho bisogno di eseguire alcuni SQL raw nella mia app Rails. La query causerà un commit implicito se viene eseguito all'interno di una transazione. Usiamo MySQL con InnoDB e la query includerà per es. crea tabella.Come posso eseguire un'operazione di database al di fuori di una transazione in Rails/ActiveRecord

L'esecuzione della query con ActiveRecord::Base.connection.execute attiva il commit implicito che è un problema.

Mi sembra di avere solo bisogno di una connessione separata per eseguire le mie domande. ActiveRecord può fornire questo? Ho visto discussioni sulla connessione a più database ma non su più connessioni allo stesso database.

Una soluzione non deve coinvolgere ActiveRecord se c'è un modo migliore.

La nostra versione di Rails e ActiveRecord è 3.2.3.

risposta

16

connessioni al database sono fatti su una base per filo (questo è fondamentalmente necessario per la sicurezza dei thread), che si può usare a vostro vantaggio: basta eseguire il codice in un thread separato, ad esempio

ActiveRecord::Base.transaction do 
    # ... 
    Thread.new do 
    ActiveRecord::Base.connection.execute "..." # in a new connection 
    end.join 
end 

Come di rails 4, activerecord non raccoglie più connessioni create in questo modo automaticamente. Per evitare perdite di connessioni è necessario restituirle alla piscina. Come suggerisce Matt Connelly, il modo più semplice per farlo è quello di utilizzare il metodo with_connection che controllerà il collegamento di nuovo in alla fine del blocco, per esempio

Thread.new do 
    ActiveRecord::Base.connection_pool.with_connection do 
    ... 
    end 
end 
+0

Penso che tu abbia capito meglio il problema :) – PriteshJ

+1

Al termine, assicurati di restituire la connessione al pool di connessioni! –

+0

buona chiamata! - la risposta era scaduta. –

1

DDL e di alcuni più query fuoco implicita commettere modo che non possano essere ripristinate secondo la documentazione di MySQL

http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html

Questi implicitamente terminare qualsiasi transazione attiva nella sessione corrente, come se si fosse fatto un commit prima di eseguire la dichiarazione.

Se non ci sono tali query intermedie, è possibile utilizzare la funzione SAVEPOINT. (This does not work with DDL statements)

C'è un'opzione attiva record che contribuisce a creare operazioni di sub, che utilizza punti di salvataggio

ActiveRecord::Base.transaction do 
    # ... 
    ActiveRecord::Base.transaction(:requires_new => true) do #creates save point 
     # perform task 
     # if error occurs rollbacks only till the save point. 
    end 
    end 

Controlla rails doc per maggiori dettagli.

+0

L'autore sta parlando di dichiarazioni che sarà sempre implicitamente commit di una transazione (ad esempio alter table) –

+0

@FrederickCheung, Dalla domanda "ActiveRecord :: Base.connection.execute innesca il commit implicito che è un problema. " mi fa credere che voglia controllare esplicitamente la transazione da solo. correggimi se sbaglio – PriteshJ

2

E 'importante che se si utilizza una connessione a un thread che restituisce la connessione al pool di connessioni al termine. Il modo più semplice per farlo è come questo:

Thread.new do 
    ActiveRecord::Base.connection_pool.with_connection do |connection| 
    connection.execute "..." 
    # ensures the connection is returned to the pool when the thread is done.   
    end 
end.join 
Problemi correlati