Problème Mémoire

Bonjour,
je développe une application qui récupère des infos d’un fichier XML
Dans le fichier, un réseau et des connexions sont décrits
Une connexion est composée d’un ou plusieurs chemins
Un chemin est composé d’une ou plusieurs cross-connections.
Une cross-connexions est attaché à un équipement.

J’ai mis en place une relation Has Many Through entre les connexions et
les équipements via les cross connexions.

J’ai une méthode qui calcule l’impact de la mise hors tension d’un
équipement. Selon le placement de la cross connexion dans le chemin, la
connexion sera plus ou moins impactée.

Au démarrage de mon appli, sans rien lancé comme vue, j’utilise 72 Mo
RAM (avec ruby 1.8.7 entreprise edition x64 et webrick comme serveur et
en mode production).
J’utilise authlogic pour la gestion de l’identité et active scaffold
pour montrer les listes (sauf celle de l’accueil)
Afficher la page d’acceuil (contenu essentiellement statique sauf une
liste des des fichiers XML) me fait passer à 550Mo RAM !
Après ce calcul d’impact, je passe à 1Go Ram utilisée.
Quand je reviens sur ma page d’accueil, je passe à 1,5Go.
Re calcul d’impact, je repasse à 1,6Go.
En me baladant dans les différentes vue (rendues la plupart par Active
Scaffold), j’arrive sans problème à 2,5Go de RAM utilisée.
De plus, l’affichage des pages se fait assez lentement (une page avec
très peu de données met 20s à s’afficher alors que les logs parlent
d’1,4s…)

J’ai pas mal de before_filter pour authlogic, pensez vous que cela peut
jouer ?

Merci d’avance si vous avez des idées/conseils à me donner

PS : j’ai essayé d’utiliser bleak_house mais il ne marche qu’avec ruby
1.8.6 et j’ai ruby 1.9 ou jruby ou ruby 1.8.7…

Bonjour Sylvain,

Une page qui met 20 secondes à s’afficher, je regarde direct :

  • si j’ai bien mes “add_index” sur toutes mes clés étrangères dans
    mes migrations
  • si je fais bien mes “include” quand je manipule mes ActiveRecord

Ceci dit, dans les logs en environnement de dév, un oubli d’include
saute aux yeux.

S’il s’agit vraiment d’un problème de volumétrie, je cherche d’abord à
dénormaliser mes relations : stocker un raccourci sur un modèle, ce
qui oblige à synchroniser cette donnée (via des callbacks). Dans ton
cas, si j’ai bien lu, un équipement n’appartient qu’à une seule
connexion. On peut songer y coller un “connection_id”.

En ultime recours, je fais mes requêtes à la main…

Enfin, je te conseille d’opter pour autre chose que Webrick (je n’ai
pas de benchmarks sous la main pour appuyer ce que je pense de
Webrick…). Personnellement, j’utilise Mongrel pour le dév, Phusion
Passenger (aka mod_rails) pour la prod.

Julien

On 24 juin, 16:45, Sylvain D. [email protected]

Bonjour Julien et merci pour ta réponse,
mes commentaires dans le texte :

Meshak wrote:

Bonjour Sylvain,

Une page qui met 20 secondes � s’afficher, je regarde direct :

surtout sur les 20 secondes, j’ai rien dans le log sur les 18 premières
(que la RAM et le proc qui s’affole). Par contre j’ai viré les requêtes
SQL du Log

  • si j’ai bien mes “add_index” sur toutes mes cl�s �trang�res dans
    mes migrations

Oui ça c’est fait (avec des contraintes de clefs car je suis sous
PostgreSQL)

  • si je fais bien mes “include” quand je manipule mes ActiveRecord

euh là je suis pas sur de comprendre le “include”

Ceci dit, dans les logs en environnement de d�v, un oubli d’include
saute aux yeux.

S’il s’agit vraiment d’un probl�me de volum�trie, je cherche d’abord �
d�normaliser mes relations : stocker un raccourci sur un mod�le, ce
qui oblige � synchroniser cette donn�e (via des callbacks). Dans ton
cas, si j’ai bien lu, un �quipement n’appartient qu’� une seule
connexion. On peut songer y coller un “connection_id”.

malheureusement l’équipement peut appartenir à plusieurs connexions donc
c’est bien une relation many-to-many

En ultime recours, je fais mes requ�tes � la main…

Enfin, je te conseille d’opter pour autre chose que Webrick (je n’ai
pas de benchmarks sous la main pour appuyer ce que je pense de
Webrick…). Personnellement, j’utilise Mongrel pour le d�v, Phusion
Passenger (aka mod_rails) pour la prod.

j’ai utilisé Webrick en exemple car j’avais testé sous nginx + thin +
ruby 1.9.1 et nginx + “mod_rails” + ruby ee 1.8.7 et j’avais les mêmes
résultats

Julien

On 24 juin, 16:45, Sylvain D. [email protected]

En voulant utiliser ruby-prof, j’ai par mégarde détruit ma base. En la
recréant et en ajoutant des fichiers, mon utilisation mémoire est bien
moindre (je dépasse pas le Go après pas mal de manip…) et les temps de
réponses ne dépasse pas les 5secondes. J’ai regardé les logs SQL de la
partie developement et hormis le fait que je fasse un update à chaque
fois sur les Users (ce qui posait peut être problème), je vois pas…
C’est vraiment bizarre.

Sylvain D. a écrit :

  • si je fais bien mes “include” quand je manipule mes ActiveRecord

euh là je suis pas sur de comprendre le “include”

Il s’agit de mettre en place du eager loading. Imagine que tu as un
modèle category avec n products. Tu as une vue ou tu liste tes
catégories avec les produits associés.

Dans ta vue tu vas faire un appel genre category.products.
Sans include pour chaque catégorie tu vas faire une requête SQL pour
récupérer les produits, ce qui est lourd et lent.

Au lieu de ça tu peux le faire en amont dans ton contrôleur avec un:
Category.find(:all, :include => :products)


Martin C. || fuse
http://www.noremember.org

Martin C. wrote:

Sylvain D. a écrit :

  • si je fais bien mes “include” quand je manipule mes ActiveRecord

euh là je suis pas sur de comprendre le “include”

Il s’agit de mettre en place du eager loading. Imagine que tu as un
modèle category avec n products. Tu as une vue ou tu liste tes
catégories avec les produits associés.

Dans ta vue tu vas faire un appel genre category.products.
Sans include pour chaque catégorie tu vas faire une requête SQL pour
récupérer les produits, ce qui est lourd et lent.

Au lieu de ça tu peux le faire en amont dans ton contrôleur avec un:
Category.find(:all, :include => :products)


Martin C. || fuse
http://www.noremember.org

Ouh là j’étais pas réveillé ce matin ;-). J’ai bien utilisé les include
pour faire du eager loading quand j’en avais besoin.
Mais comme dit au dessus, depuis que la base est fraiche et reloadée
avec de nouvelles valeurs, il ne me semble plus avoir ce genre de
problème…

2009/6/25 Sylvain D. [email protected]

  • si je fais bien mes “include” quand je manipule mes ActiveRecord

euh là je suis pas sur de comprendre le “include”

Il parle des include ActiveRecord lors des différents select (find) [1]
avec
jointures: ces includes te permettent de faire du prefetch et donc
d’économiser des requêtes et donc du temps de fetch lorsque tu
réutilises
ces résultats.

Sinon Meshak va se faire fouetter en arrivant au bureau: avant de
dénormaliser, ou pire de faire du SQL à la main, il faut optimiser avec
les
moyens d’active record, aka: les named_scope! Les named_scope, c’est la
vie
(ça permet d’optimiser tes requêtes via ActiveRecord en agrégeant les
contraintes les unes aux autres: la grande force, c’est que ça reste une
seule requête SQL en backend, quel que soit le nombre de named_scope que
tu
combines. De plus tu gardes toute la sémantique de ta requête dans ton
code:
c’est juste délicieux! )

[1] find (ActiveRecord::Base) - APIdock (8ème sur les
params)

moyens d’active record, aka: les named_scope! Les named_scope, c’est la
vie
(ça permet d’optimiser tes requêtes via ActiveRecord en agrégeant les
contraintes les unes aux autres: la grande force, c’est que ça reste une
seule requête SQL en backend,
Ca optimise rien du tout, ça permet d’être un peu plus dry tout au plus,
jusqu’à ce que tu te rendes compte que chaque named_scope n’est utilisé
que par une seule méthode :smiley:

ook? ook! wrote:

Sinon Meshak va se faire fouetter en arrivant au bureau: avant de
dénormaliser, ou pire de faire du SQL à la main, il faut optimiser avec
les
moyens d’active record, aka: les named_scope! Les named_scope, c’est la
vie
(ça permet d’optimiser tes requêtes via ActiveRecord en agrégeant les
contraintes les unes aux autres: la grande force, c’est que ça reste une
seule requête SQL en backend, quel que soit le nombre de named_scope que
tu
combines. De plus tu gardes toute la sémantique de ta requête dans ton
code:
c’est juste délicieux! )

je connaissais pas et ça a l’air super interressant !

Merci !

Bon j’ai fait deux trois tests pour essayer de comprendre mon problème

L’affichage de ma page index (en requête SQL, un select * d’un table (8
lignes), récupération de l’utilisateur pour authlogic, mise à jour des
infos de l’utilisateur, récupération de son groupe) prend 16s (en dev
pour pouvoir regarder ce qui se passe au niveau SQL et espérer savoir
par où je passe…)

Le select * de la table se fait au bout de 4s
le select / update user + groupe se fait 2s après
Puis il ne se passe ‘rien’ (rien dans les logs) pendant 9s jusqu’Ã
l’affichage dans les logs de “Processing AspenTopoFilesController#index”
qui me dit
“Completed in 1140ms (View: 131, DB: 2071)”

J’ai mis des “puts” dans mon code pour voir et la fin du rendu de la vue
se fait en gros au même moment que le dernier select.
J’ai donc 4s de trou au départ et 9s de trou à l’arrivée ou je sais pas
ce qu’il se passe (avec ruby EE, il faut doubler ces temps…)

Au niveau plugin, j’utilise active scaffold (pas dans cette page),
authlogic, piggy_back (pas pour cette page), memory_test_fix (pour les
tests) et render_component (toujours pas pour cette page)

Quelqu’un aurait une idée pour avoir plus de débug?

2009/6/25 Fernando P. [email protected]

moyens d’active record, aka: les named_scope! Les named_scope, c’est la
vie
(ça permet d’optimiser tes requêtes via ActiveRecord en agrégeant les
contraintes les unes aux autres: la grande force, c’est que ça reste une
seule requête SQL en backend,
Ca optimise rien du tout, ça permet d’être un peu plus dry tout au plus,
jusqu’à ce que tu te rendes compte que chaque named_scope n’est utilisé
que par une seule méthode :smiley:

C’est sûr que si tu les utilises pas ou mal, ça ne risque pas
d’améliorer
tes performances… Pour ma part, passer de 8 requêtes qui cumulent 3s
d’exécution en une seule de 120ms, y’a pas photo!

Le select * de la table se fait au bout de 4s
le select / update user + groupe se fait 2s après
Puis il ne se passe ‘rien’ (rien dans les logs) pendant 9s jusqu’Ã
l’affichage dans les logs de “Processing AspenTopoFilesController#index”
qui me dit
“Completed in 1140ms (View: 131, DB: 2071)”

Sur quel OS es-tu ? Si c’et Linux (et OSX je suppose ?) lance un “top”
sur
une nouvelle fenêtre, tu verras quels sont les process qui tournent
pendant
ce temps là .

gUI


Pour la santé de votre ordinateur, préférez les logiciels libres.
Lire son mail : http://www.mozilla-europe.org/fr/products/thunderbird/
Browser le web : http://www.mozilla-europe.org/fr/products/firefox/
Suite bureautique : http://fr.openoffice.org/

C’est sûr que si tu les utilises pas ou mal, ça ne risque pas
d’améliorer
tes performances… Pour ma part, passer de 8 requêtes qui cumulent 3s
d’exécution en une seule de 120ms, y’a pas photo!

Ca n’a rien mais alors rien à voir du tout avec les named scoped. Tu es
passé du :include (qui fait une requête par table ou un inner join dans
le cas le plus imple) Ã du :joins qui fait des jointures donc une seule
requête.

Guillaume B. wrote:

Sur quel OS es-tu ? Si c’et Linux (et OSX je suppose ?) lance un “top”
sur
une nouvelle fenêtre, tu verras quels sont les process qui tournent
pendant
ce temps là .

gUI

je suis sous Linux et j’avais un Top en //
C’est le script rails qui prend 100% du temps quasiment tout le long

C’est le script rails qui prend 100% du temps quasiment tout le long

Si c’est 100% de CPU, c’est pas un pb d’IO, donc pas de manque de RAM ni
d’attente de la BdD…

A la limite je verrais bien un bout de code pourri (style grosse
boucle)…

gUI


Pour la santé de votre ordinateur, préférez les logiciels libres.
Lire son mail : http://www.mozilla-europe.org/fr/products/thunderbird/
Browser le web : http://www.mozilla-europe.org/fr/products/firefox/
Suite bureautique : http://fr.openoffice.org/

Ca existe pas un outil qui donne le chemin par ou on passe ?

Je connais très peu, mais il doit bien y avoir des outils de profiling
capable de te donner le temps passé dans chaque controleur, action…

gUI


Pour la santé de votre ordinateur, préférez les logiciels libres.
Lire son mail : http://www.mozilla-europe.org/fr/products/thunderbird/
Browser le web : http://www.mozilla-europe.org/fr/products/firefox/
Suite bureautique : http://fr.openoffice.org/

Guillaume B. wrote:

C’est le script rails qui prend 100% du temps quasiment tout le long

Si c’est 100% de CPU, c’est pas un pb d’IO, donc pas de manque de RAM ni
d’attente de la BdD…

A la limite je verrais bien un bout de code pourri (style grosse
boucle)…

gUI


Pour la santé de votre ordinateur, préférez les logiciels libres.
Lire son mail : http://www.mozilla-europe.org/fr/products/thunderbird/
Browser le web : http://www.mozilla-europe.org/fr/products/firefox/
Suite bureautique : http://fr.openoffice.org/

Ben j’aimerais bien mais je la vois pas la boucle :wink:
Ca existe pas un outil qui donne le chemin par ou on passe ?

Fabien J. wrote:

Ton parsing XML, tu as regardé en le lançant séparemment (console ou
autre)
si son execution est rapide ?
Les 2 choses que je vois possibles dans ton cas : un parsing XML trop
long
ou une instanciation de trop d’objets ActiveRecord.

Dans les outils de profiling, je ne sais pas si ruby-prof est encore
compatible avec les dernières versions de rails, mais c’est un très bon
outil pour ton cas.


http://fabien.jakimowicz.com

Mon Parsing XML n’est lancé qu’une fois de temps en temps (quand je crée
un onjet “fichierXMLM”). Autrement il n’est pas lancé.

J’ai essayé ruby-prof sur ruby ee, ça a l’air de marcher mais pas encore
comme je voudrais, il faut que je regarde

Le 25 juin 09 à 10:49, Sylvain D. a écrit :

Le select * de la table se fait au bout de 4s
le select / update user + groupe se fait 2s après
Puis il ne se passe ‘rien’ (rien dans les logs) pendant 9s jusqu’à
l’affichage dans les logs de “Processing
AspenTopoFilesController#index”
qui me dit
“Completed in 1140ms (View: 131, DB: 2071)”

Vu le temps de réponse de ta requête, il doit y avoir un nombre de
ligne incroyable. Sachant que pour chaque ligne 1 objet est créer. Ca
peux expliquer le temps de création des objets après lecture de ta BDD.


Cyril M.

2009/6/25 Sylvain D. [email protected]

gUI

Ton parsing XML, tu as regardé en le lançant séparemment (console ou
autre)
si son execution est rapide ?
Les 2 choses que je vois possibles dans ton cas : un parsing XML trop
long
ou une instanciation de trop d’objets ActiveRecord.

Dans les outils de profiling, je ne sais pas si ruby-prof est encore
compatible avec les dernières versions de rails, mais c’est un très bon
outil pour ton cas.


http://fabien.jakimowicz.com

Sylvain D. wrote:

J’ai essayé ruby-prof sur ruby ee, ça a l’air de marcher mais pas encore
comme je voudrais, il faut que je regarde

j’ai réussi à faire marcher ruby-prof (en mode test seulement) :
Thread ID: 5707440
Total: 0.440000

%self total self wait child calls name
22.73 0.10 0.10 0.00 0.00 5061
Pathname#chop_basename
6.82 0.14 0.03 0.00 0.11 912
Pathname#cleanpath_aggressive
2.27 0.01 0.01 0.00 0.00 2048 String#sub
2.27 0.15 0.01 0.00 0.14 912 Pathname#cleanpath
2.27 0.01 0.01 0.00 0.00 400
Rack::Lint::Assertion#assert
2.27 0.04 0.01 0.00 0.03 417 Array#each
2.27 0.01 0.01 0.00 0.00 1824
Pathname#initialize
2.27 0.01 0.01 0.00 0.00 87 Kernel#hash
2.27 0.01 0.01 0.00 0.00 680 Enumerable#any?
2.27 0.18 0.01 0.00 0.17 434 String#gsub
2.27 0.21 0.01 0.00 0.20 3
ActionView::Template#render_template
2.27 0.01 0.01 0.00 0.00 11651 String#==
2.27 0.04 0.01 0.00 0.03 1901 Class#new

Par contre le test dure 51 seconde et il trouve un process time de
440ms…