Traccia la perdita di memoria in Ruby on Rails 3 / Postgres / Apache Passenger
-
27-10-2019 - |
Domanda
Ciao,
Abbiamo recentemente aggiornato un'applicazione a Rails 3.0.4 (3.0.5 su Online Devel Server). La maggior parte delle modifiche da 2.3.10 a 3.0.4 erano dovute a plugin e gemme obsoleti o obsoleti e erano risolvibili con relativa facilità. Ma una cosa mi fa impazzire:
Ogni singola richiesta Web, in modalità di sviluppo, fa sì che il processo del server allocasse circa 50-60 MB più memoria di prima. Questo ricordo è non liberato dopo la richiesta, almeno non tutto. Dopo 10-20 richieste, ogni istanza di Ruby consumava 500 MB di RAM, mentre i nostri precedenti casi di Rails 2.3.10 erano raramente al di sopra di 200 MB.
Ciò rende impossibile eseguire i nostri test 1300, perché i 4 GB di RAM della macchina dello sviluppo sono riempiti prima della fine dei test. Succede solo in modalità di sviluppo con cache_classes = false
. Se cambio cache_class su true, le istanze delle rotaie consumano circa 200 MB di memoria e poi rimarranno lì. Tuttavia, durante i test, anche con cache_classes = true, l'utilizzo della memoria crescerà.
Ho interrogato ObjectSpace e ho scoperto che con ogni richiesta, circa 3500 nuovi proc, fino a 50'000 nuove stringhe e 3000 nuovi hash e array vengono creati e non liberati. Queste stringhe (quando scaricate) contenevano il mio intero codice sorgente, inclusi plugin e gemme, documentazione, commenti sul codice sorgente e nomi dei percorsi. (Perché?)
Per trovare la causa per questo, ecco cosa ho provato: (Dopo ogni cambiamento, ho martellato le app ab -n 50
.)
- Ho creato un Applicazione Fresh Rails 3 con una singola risorsa e controller e sqlite3 dB. L'utilizzo della memoria è iniziato a 60 MB e è rimasto al di sotto di 80 MB.
- Ho cambiato 'sqlite3' a 'pg' e ho puntato l'app New Rails 3 al mio DB Postgres esistente. L'utilizzo della memoria è iniziato a 110 MB e non è cresciuto oltre 130 MB. (Domanda laterale: perché la gemma di Postgres usa così molta più memoria della gemma SQLite3?)
- Ho copiato il mio Gemfile e gemfile.lock Dall'app rotto Rails3 all'app Bare Bones e ha funzionato Installazione del fascio. Nessun cambiamento, la memoria è rimasta a circa 115 MB, indipendentemente da quante richieste sono state fatte.
- Ho creato un vuoto "def foocontroller; def foo; render: text => 'foo' end; end" nell'app rotto Rails3. L'utilizzo della memoria è cresciuto più lentamente, ma non ha mai smesso di crescere dopo le richieste.
- Ho eliminato ogni percorso ad eccezione della rotta Foocontroller. Nessun cambiamento.
- Ho disabilitato tutte le gemme tranne le seguenti:
pg, rails, aasm, will_paginate, geokit-rails3, koala, omniauth, paperclip
. Nessun cambiamento. - Ho disabilitato ogni prima_filter e dopo_filter in ApplicationController e ogni non essenziale
include
In Environment.rb. Ho anche sincronizzato Boot.rb, Environment.rb e Application.rb con la mia app Bare-Bones Rails 3, ad eccezione di cinque osservatori relativamente semplici, file di caricamento automatico in /lib e filtro_parameters. Nessun cambiamento. Ogni nuova richiesta consumava ancora un ulteriore 10-50 MB di RAM.
Se hai un'idea di cosa non va storto qui e dove potrebbe essere la perdita di memoria, apprezzerei davvero qualsiasi aiuto. Sto correndo Rails 3.0.4 su OS X Snow Leopard, Rails 3.0.5 su Debian Lenny e
Grazie!
Avvicinandosi:
Ho rimosso ogni plug -in, ogni gemma, ogni estensione e tutto ciò che non ho scritto personalmente, in modo che la mia applicazione sia sostanzialmente nuda. Soprattutto, ho rimosso questi plugin: acts_as_list, acts_as_tree, asset_packager, forgot_password, fudge_form, fudge_scaffold, paperclippolymorph, query_trace, rails_upgrade, repeated_auto_complete-0.1.0, role_requirement, to_select, validates_url, and ym4r_gm
.
Ora la mia applicazione: solo il foocontroller sopra funziona ancora! - Avvia 65 MB e non va mai oltre i 75 MB di RAM, anche dopo averlo martellato con ab -n 1000 -c1
(1000 richieste HTTP a /foo usando ApacheBench). Sfortunatamente, senza i plugin, questo è anche l'unico URI che funziona affatto.
Dopo un po 'di scavo, sembra che una combinazione tra l'autenticazione riposante e funge da plug -in AMAST (State Machine (AASM) provoca la perdita di memoria. Guarda anche https://github.com/satish/restful-authentication/issues#issue/11. Non sono ancora sicuro del perché, e solo "includere aasm" nel mio progetto nudo non causa la crescita dell'utilizzo di RAM da solo.
Indagherò ulteriormente.
Colpevole trovato
È Aasm. In Rails 3 sembra perdere istanze dell'oggetto Aasm :: XXX. vedere
- https://github.com/jeff/enumerated_attribute/issues/#issue/20
- https://github.com/rubyist/aasm/issues/31
- https://github.com/satish/restful-authentication/issues/#issue/11
Secondo colpevole trovato
C'è stata un'altra perdita di memoria in RSPEC. Ciò ha reso i miei test quasi insopportabilmente lenti, anche dopo aver rimosso Aasm, perché due compiti RSPEC in esecuzione parallela (usando https://github.com/grosser/parallel_tests) ha preso quasi 3 GB di memoria alla fine. Vedere https://github.com/rspec/rspec-core/issues/#issue/321.
Soluzione
Alcune buone risorse per aiutarti a rintracciare la fonte delle perdite:
Più nuovo:
Trova la perdita di memoria in un progetto Ruby on Rails
Più vecchio:
Rilevamento di perdite di memoria Ruby/Ruby on Rails
http://xdotcommer.wordpress.com/2009/03/03/tracking-down-a-memory-leak-performance-issues-in-rails/