IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

Perl avec Threads, Moose et DBix


Sujet :

Langage Perl

  1. #1
    Membre averti Avatar de dca_marshall
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Âge : 63
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 46
    Par défaut Perl avec Threads, Moose et DBix
    Bonjour,

    Pour l’histoire
    Il y a maintenant 10 ans, j’ai développé un moteur de transformation de données.
    Il a bien entendu évolué pendant toutes ces années.
    Actuellement, ce moteur utilise des objets Moose et fonctionne à 100% en mémoire, dans un seul thread.
    Les objets sont donc tous persistants, pendant toute la durée d’execution.
    A ce jour cette méthode ne correspond plus à mes besoins futurs.

    De plus, la volumétrie des données que je reçois, est en constante évolution.
    Il n’est pas rare de voir le process, consommer de 30 à 50Go de mémoire, sur 64, (voire 79 au plus haut avec une méchante pagination qui met à genou la machine).
    En revanche tous les coeurs ne sont pas utilisés et cela démontre bien une mauvaise utilisation de cette super machine de 12 coeurs.

    Ce que je souhaite faire
    Bref, je souhaite donc maintenant, avoir des objets persistants, après exécution et gérer beaucoup plus de volume.
    J’ai donc opté pour la théorie suivante :
    - Postgres (persistance des données)
    - Moose + DBix::Class (gestion des objets, similaire à aujourd’hui avec persistance)
    - Threads Perl (exécution de règles de transformation en //)

    Le multi-threading, me permettra donc, de soumettre des règles de conversion en parallèle.
    Alors que dans la version actuelle, du moteur, elles sont sérialisées.
    Certaines de ces règles pourront tourner en parallèle et d’autres seront en attente de leurs « prédécesseurs », via une configuration déjà existante.
    Je vais donc mettre au moins un petit ordonnanceur, qui, avec les fonctions « list » de threads, garantira le bon ordonnancement.

    Les règles sont, ni plus ni moins, que de petites fonctions, qui feront du CRUD dans la base, au travers des objets Moose.
    Bien entendu, comme en principe je sais ce que fais (enfin je crois ), chaque règle aura son propre périmètre et ne devrait jamais mettre à jour les mêmes zones de la base ou même les lire avant un update, afin d’éviter des conflits d’update, (même si le SGBD sait le faire), mais surtout garantir la cohérence des données.

    Votre avis m'intéresse
    Avec ce principe, je pense donc répondre à mes futurs besoins et le multi-threads ferait gagner du temps, perdu par les I/O du SGBD.
    Je suis actuellement sur l’architecture fonctionnelle théorique de ce bousin et je souhaite vous solliciter sur différents points :
    • Est-ce que j’ai bon dans la théorie ?
    • Avez-vous des retours d’expérience de ce type d'architecture ?
    • Que pensez-vous de cela ?


    Je vous remercie par avance de vos retours.
    Bien cordialement,
    dca_marshall

  2. #2
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    je ne comprends pas bien ton architecture cible, mais je dirais que si ton principal problème est l'utilisation trop forte de la mémoire, alors je ne pense pas que le multithreading soit une bonne solution.

    Il faudrait d'abord, me semble-t-il, se demander pourquoi ton programme utilise tant de mémoire et voir s'il est possible de remédier à ce problème.

    Mais, encore une fois, je ne connais pas du tout ton programme actuel et ne comprends peut-être pas la solution que tu envisages, donc je dis peut-être des sottises

  3. #3
    Membre averti Avatar de dca_marshall
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Âge : 63
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 46
    Par défaut
    Bonjour,

    Merci pour ta réponse.

    Pour la taille mémoire, en fait le programme parse des fichiers de configuration de différents logiciels de gros datacenter.
    Chaque logiciel possède un langage différent pour sa configuration.
    Lorsque le logiciel doit être remplacé, il faut convertir ces configurations dans le langage du nouveau logiciel.
    Le nombre de fichiers peut être très important (plusieurs miliers) et le volume global peut atteindre plusieurs Go.

    A l’issue de la transformation, par des règles, la mémoire se retrouve avec la totalité des données en entrée, mais aussi, la totalité des données converties.
    En effet, chaque objets ayant en permanence, les propriétés du logiciel source, ainsi que celles du logiciel cible.
    A noter que chaque objet peut avoir plusieurs dizaines de propriétés (voire des centaines, pour certains).
    De plus, le moteur construit des relations entre tous les objets capturés par les Parser.

    Puis le moteur, passe par une phase d’ecriture des données converties, dans les nouveaux fichiers.
    Et pour terminer, tous les objets sont dumpés dans des structure Perl, pour debug, recherche ou rechargement.

    L’idée autjourdhui, est de ne plus mettre les objets persistants en mémoire, mais persistants dans une database, qui servira toute la durée de la conversion, au debug, recherche (via sql), mais aussi pour des stats et bien d’autres fonctionnalités.

    Mes questions sous-jacentes, sont plutôt orientées sur la faisabilité du multi threading avec DBix (réentrance et partage de connections)
    Je compte mettre en place un maître qui lancera des threads (fonction), en leur passant les seules informations nécessaires, relatives au requêtes DBix.
    En effet, je ne souhaite pas que chaque thread se connecte à la database. Cela ferai perdre un temps fou.

    J’avoue que ça peu ne pas être très clair
    Je vais tacher de faire un petit schéma pour résumer ce que je souhaite faire.

    Merci de retour.
    Cdlt,
    dca_marshall

  4. #4
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Sur le multi threading avec DBix (réentrance et partage de connections), je n'ai aucune expérience. Je dirais seulement que je suis assez méfiant avec les threads en Perl. Ils sont utilisables, mais assez difficiles à employer de manière correcte et fiable; de plus, il ne permettent une réelle amélioration des performances que quand ton processeur est sous-employé parce que ton application perd du temps à attendre des événements extérieurs (entrées/sorties, requête BdD, réponse réseau, etc.).

    N'est-il pas possible, dans ton application, de lire les données en entrée par blocs, par enregistrements ou par lignes, de les traiter au fur et à mesure et d'écrire le résultat dans des fichiers de sortie (ou une base de donnée), afin d'éviter de garder en mémoire l'ensemble des données en entrée et en sortie? C'est en tout cas ce que je fais régulièrement pour traiter des fichiers de données bien trop gros pour tenir en mémoire. En simplifiant, mon programme devient une sorte de filtre qui lit des lignes de données en entrée et écrit des lignes de données en sortie (dans un fichier ou une base de données, peu importe). A un moment quelconque, je n'ai en mémoire que quelques lignes du fichier en entrée et quelques lignes à écrire en sortie (plus bien sûr des structures de données persistantes nécessaire au traitement), mais jamais l'ensemble des données en entrée ou en sortie. Les vérifications et transformations que j'applique sont sans doute plus simples que celles que tu fais, mais je recommande néanmoins ce modèle de traitement s'il est utilisable dans ton cas.

  5. #5
    Membre averti Avatar de dca_marshall
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Âge : 63
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 46
    Par défaut
    Malheureusement, il est impossible de traiter les données à mesure de leur lecture.
    Ça, c’etait avant, mais ça à bien évolué.
    Des corrélations doivent être crées entre tous les objets et le seul moment de le faire, est une fois tous chargés.

    De plus la transformation se fait, pas-à-pas, de façon «*exponentielle*».
    Les zones à convertir se font à partir de zones connues, en prenant en compte les corrélations, plus des informations externes (table CSV).
    Puis d’autres transformations sont faites sur les mêmes bases, mais en plus avec les zones transformées préalablement.
    Ceci est répéteé jusqu’a L’obtention du résultat souhaité.
    De plus pendant la durée des projets de conversion, des évolutions de résultats me sont demandées, pour des périmètres plus restreints.

    Donc sans toucher aux règles déjà en place, j’ajoute d’autres règles, qui surcharge ce qui est déjà fait, pour répondre à ces nouvelles demandes.

    Mais je passe les détails.

    Je vais donc procéder à quelques tests basic, mais ça va me prendre du temps, car je ne maîtrise pas trop les Threads Perl et le DBix.

    Je donnerai alors mon retour d’expérience.

    Merci encore de ton implication et ta disponibilité

  6. #6
    Membre averti Avatar de dca_marshall
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Âge : 63
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 46
    Par défaut
    Bonjour,

    Schéma
    Alors voici un petit schéma qui explique le besoin :

    Nom : rulesGroups.png
Affichages : 128
Taille : 35,3 Ko

    Les groupes
    Un nom de groupe est unique et représente, un ensemble de tâches à exécuter (des règles de conversion).
    Les groupes passent dans l'ordre alphabétique de leur nom.
    Toutes les règles d'un groupe doivent être terminées, pour faire des trucs après chaque groupe.
    Et seulement une fois le groupe précédent terminé, on peut faire des trucs avant le groupe suivant et il peut démarrer.

    Les règles
    Un nom de règle est unique et doit exécuter du code de conversion.
    On doit pouvoir leur passer des paramètres.
    Les dépendances avec d'autres règles, sont fonctionnelles.
    Une règles peut être déplacée dans un autre groupe (la dépendance reste).

    Voici donc un petit Scheduler de groupes/règles qui fonctionne, comme je le souhaitais.

    Notes :
    • Ca tourne actuellement sur un Mac, mais je ne garanti pas le même succès sur d'autres plates-formes
    • Cela fonctionne avec la 5.22.3 du Perl



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
     
    #!/usr/bin/env perl -w
     
    use strict;
    use warnings;
    #use threads;
    use threads ('yield',
                 'stack_size' => 64*4096,
                 'exit' => 'threads_only',
                 'stringify');
    use Data::Dumper;
    $Data::Dumper::Sortkeys=1;
     
     
    #=========================================================================================#
    # Déclaration de globale
    #=========================================================================================#
    my $masterthr=threads->self;
    my %monitorRules=();
    my %thrIDForRules=();
    my %ruleNameForthrID=();
    my $sleepRuleGroup=3;
    my $sleepRulePred=3;
     
    my %scheduleRules=(
    	groups => {
    		group100 => { 
    			rules => {
    				rule01 => { param => [ 5 ] },
    				rule02 => { preds => [ ], param => [ 6 ] },
    			},
    		},
    		group200 => {
    			rules => {
    				rule06 => { preds => [ ], param => [ 5 ] },
    				rule07 => { preds => [ 'rule01' ], param => [ 6 ] },
    				rule09 => { param => [ 8 ] },
    				rule08 => { preds => [ 'rule02' ], param => [ 7 ] },
    			}
    		},
    		group300 => {
    			rules => {
    				rule03 => { preds => [ 'rule06' ], param => [ 5 ] },
    				rule04 => { preds => [ 'rule03' ], param => [ 6 ] },
    				rule05 => { preds => [ 'rule08', 'rule03' ], param => [ 7 ] },
    			}
    		}
    	}
    );
     
     
    sub _timeNow(@) {
    	use DateTime;
    	my $dt=DateTime->now;
    	return $dt->hms;
    }
     
    sub _trace(@) {
    	my ( $function, $message, $skipLine )=@_;
    	if ( $skipLine ) {
    		print $skipLine._timeNow." - $function - $message\n";
    	} else {
    		print _timeNow." - $function - $message\n";
    	}
    	return 1;
    }
     
    #=========================================================================================#
    # Attente des prédécesseurs de fin de groupe de règles
    #=========================================================================================#
    sub _waitAllThreadsForCurrentGroup(@) {																		#
    	#-----------------------------------------------------------------------------------------#
    	# Function de surveillance de status d'une liste de règle
    	# On s'appui sur le hash %thrIDForRules, pour obtenir la référence d'un Thread, 
    	# pour un nom de règle
    	#-----------------------------------------------------------------------------------------#
    																															#
    	my ( $objetcType, $objectName, @ruleNameList )=@_;														# Prend le type et le nom du contraint et la liste des règles contraignantes
    																															#
    	_trace( "_waitAllThreadsForCurrentGroup", "For $objetcType=$objectName, waiting Rules=".join(', ',@ruleNameList) );
    																															#
    	my $stop=0;																											# Positionne le flag d'arrêt de la boucle à faux
    	until ( $stop ) {																									# Boucle interminable...
    		foreach my $ruleName ( @ruleNameList ) {																	# Boucle sur la liste des règles à surveiller
    																																	#
    			unless ( exists( $monitorRules{rules}{$ruleName}{status} ) ) {										# Si la règle à surveiller, n'a pas de status dans le monitor...
    				$monitorRules{rules}{$ruleName}{status}='running';														# On lui donne le status 'running' par défaut
    			}																														#
    																																	#
    			foreach my $thr ( threads->list ) {																			# Boucle sur la liste des threads actifs
    																																		#
    				my $refThrCurrentRule=$thrIDForRules{$ruleName}{thr};													# Prend le pointeur du thread de la règle à surveiller
    				if ( $thr->equal( $refThrCurrentRule ) ) {																# Si le thread actif courant est le même que celui de la règle courante...
    					_trace( "_waitAllThreadsForCurrentGroup", "For $objetcType=$objectName, dependency constraint waiting Rule=$ruleName" );
    					$refThrCurrentRule->join;																						# On est donc obligé d'attendre la fin de cette règle
    					$monitorRules{rules}{$ruleName}{status}='terminated';														# On lui donne le status 'terminated'
    				}																														#
    																																	#
    			}																													#
    																																#
    		}																													#
    		sleep $sleepRuleGroup;																						# On attend une poignée de seconde (évite trop de charge)
    																															#
    		#--------------------------------------------------------------------------------------#
    		# On fait ce qu'il faut pour sortir de la boucle interminable									
    		#--------------------------------------------------------------------------------------#
    		my $countRules=scalar(@ruleNameList);																	# On compte le nombre de règles à surveiller
    		foreach my $ruleName ( @ruleNameList ) {																# Boucle sur la liste des règles à surveiller
    			if ( $monitorRules{rules}{$ruleName}{status} eq 'terminated' ) {										# Si la règle à un statut 'terminated'
    				$countRules--;																									# On décrémente le compteur des règles à surveiller
    				_trace( "_waitAllThreadsForCurrentGroup", "Rule $ruleName has been terminated" );
    			}																													#
    		}																													#
    		unless ( $countRules ) {																					# Si le compteur est à 0, il n'y a plus de règles à surveiller
    			$stop=1;																											# On met le flag de stop de la boucle interminable à vrai
    		}																													#
    	}																													#
    																														#
    	return 1;																										# Renvoi toujours vrai
    }																														#
    #=========================================================================================#
     
     
     
    #=========================================================================================#
    # Fonction fournissant une liste des règles toujours en exécution, d'après une liste de départ
    #=========================================================================================#
    sub _removeThreadFinished(@) {																				#
    	my ( $ruleName, @expectedPredRules )=@_;																	# En argument la règle contrainte, la liste des règles contraignantes
    	my @runningPredRules=();																						# Initialisation à vide, de la liste en sortie
    																															#
    	foreach my $predRule ( @expectedPredRules ) {															# Boucle sur la liste des règles contraignantes
    		my $currentRuleRunning=1;																						# Flag, considérant que la règle prédécesseur est toujours running
    																																#
    		if ( $monitorRules{rules}{$predRule}{status} eq 'terminated' ) {									# On regarde d'abord dans le monitor, si la règle contraignante courante n'est pas déjà terminée...
    			$currentRuleRunning=0;																							# On met le flag running à faux
    			next;																													# Et on passe à la règle contraignante suivante	
    		}																														#
    																																#
    		foreach my $terminatedThread ( threads->list(threads::joinable) ) {								# Boucle sur la liste des threads terminés et "joinable"
     
    			my $thrID=$terminatedThread->tid();
    			if ( exists( $ruleNameForthrID{$thrID} ) ) {
    				print "_removeThreadFinished - Test $thrID for Rule ".$ruleNameForthrID{$thrID}." joinable $terminatedThread=".$thrIDForRules{$predRule}{thr}." \n";
    			}
     
    			if ( $terminatedThread eq $thrIDForRules{$predRule}{thr} ) {										# Si le thread courant est le même que le threads de la règle contraignante courante...
    				$currentRuleRunning=0;																							# On met le flag running à faux
    				$monitorRules{rules}{$predRule}{status}='terminated';													# Dans la monitor, on change le status la règle contraignate à "terminated"
    				$terminatedThread->join();																						# On join ce threads, pour le marqué terminé
    			}																														#
    		}																														# Fin de boucle sur la list des threads "joinable"
    		if ( $currentRuleRunning ) {																					# Si le flag de "running" est toujours à vrai...
    			_trace( "_removeThreadFinished", "Rule $predRule still running" );
    			push( @runningPredRules, $predRule );																		# on ajoute la règle contraignante dans la list en sortie
    		}																														#
    	}																														# Fin de la boucle sur les règles contraignantes
    																															#
    	unless ( @runningPredRules ) {																				# Si la liste des règles contraignantes est vide...
    		_trace( "_removeThreadFinished", "No more rule running for $ruleName" );
    	} else {																												#	Si elle n'est pas vide...'
    		_trace( "_removeThreadFinished", "Rules still running for $ruleName (".join(', ', @runningPredRules ).")" );
    	}																														#
    	return @runningPredRules;																						# Renvoi de la liste des règles contraignates restantes
    }																														#
    #=========================================================================================#
     
     
     
    #=========================================================================================#
    # Fonction contrôle des règles contraignantes avant soumission de règle
    #=========================================================================================#
    sub _controlAndSubmitOneRule(@) {																			#
    	my ( $refHashWork, $ruleName, $groupName )=@_;															# Prend en argument : La référence du hash de travail, le nom de la règle à contrôler/soumettre, le groupe
    																															#
    	unless( _removeThreadFinished($ruleName, @{ $refHashWork->{$ruleName}{preds} }) ) {			# Si le contrôle des règles contraignantes est vide...
    		_submitlOneRule( $refHashWork, $ruleName, $groupName );												# Demande de soumission de la règle
    		delete( $refHashWork->{$ruleName} );																		# Suppression de la clé de la règle, du hash de travail
    	}																														#
    																															#
    	return 1;																											# Renvoi toujours vrai
    }																														#
    #=========================================================================================#
     
     
     
     
    #=========================================================================================#
    # Fonction de soumission d'une règle
    #=========================================================================================#
    sub _submitlOneRule(@) {																						#
    	my ( $refHashWork, $ruleName, $groupName )=@_;															# Prend en arguments : la référence du hash de travail, le nom de la règle, le nom du groupe
    																															#	
    	my $thr=threads->new(																							# On démarre un nouveau thread
    		\&$ruleName, 																										# Avec la référence à la fonction (par le nom de la règle courante)
    		$ruleName, 																											# Premier paramètre, le nom de la règle
    		@{ $refHashWork->{$ruleName}{param} }																		# Paramètres suivants, ceux du hash de déclaration des règles
    	);																														#
    	my $thrID=$thr->tid();																							# Au passage, on prend l'ID du thread
    																															#
    	$thrIDForRules{$ruleName}{tid}=$thrID;																		# On rapproche le nom de la règle et l'ID de son thread (ça peu servir)
    	$thrIDForRules{$ruleName}{thr}=$thr;																		# On rapproche le nom de la règle et la référence de son thread (ça peu servir)
    																															#
    	$ruleNameForthrID{$thrID}=$ruleName;																		# On rapproche l'ID du thread à la règle courante (ça peu servir)
    																															#
    	$monitorRules{rules}{$ruleName}{status}='started';														# On fixe le statut de la règle à 'started' dans le hash de monitoring
    	$monitorRules{rules}{$ruleName}{group}=$groupName;														# On fixe le nom du group courant pour la règle courante dans le hash de monitoring (ça peu servir)
    																															#
    	return 1;																											#
    }																														#
    #=========================================================================================#
     
     
     
     
     
    #=========================================================================================#
    # Fonction de soumission des thread pour toutes les règles d'un group
    #=========================================================================================#
    sub _submitAllRuleForCurrentGroup(@) {																		#
    	#-----------------------------------------------------------------------------------------#
    	# Function de création de thread pour chaque règle et mise à jour du hash de monoring
    	# Stocke aussi les références et ID de rapprochement des noms de règles et les leur thread
    	# permettant un accès plus rapide et concis, pour la surveillance
    	#-----------------------------------------------------------------------------------------#
    	my ($refGroup, $groupName)=@_;																				# Prend le pointeur du groupe de règle, du hash de déclaration des règles
    																															#
    	my %hashWork=%{ $refGroup->{rules} };																		# Copie de la list des règles (avec leurs paramètres) dans un hash de travail
    																															#	
    	while ( keys( %hashWork ) ) {																					# Boucle tant qu'il y a des règles à soumettre restante
    		#print Data::Dumper->Dump( [\%hashWork], ['hashWork'] );
    		#print Data::Dumper->Dump( [\%monitorRules], ['monitorRules'] );
    		foreach my $ruleName ( keys( %hashWork ) ) {																# Boucle sur la liste des règles du hash de travail
    			_controlAndSubmitOneRule( \%hashWork, $ruleName, $groupName );										# Demande de contrôle des contraignantes et soumission (si possible)
    		}																														# Fin de boucle sur le hash de travail
    		sleep $sleepRulePred;																							# Attente avant de rebalayer le hash de travail
    	}																														# Fin de boucle sur la liste des règles restantes
    																															#
    	return 1;																											# Renvoi toujours vrai
    }																														#
    #=========================================================================================#
     
     
     
     
    #=========================================================================================#
    # Fonction d'appel à l'attente de règles, pour un groupe
    #=========================================================================================#
    sub _hasPreviousGroup(@) {																						#
    	my ($group)=@_;																									# Prend le nom d'un group en paramètre
    	if ( scalar( threads->list ) ) {																				# Si des threads esclaves d'un groupe tournent...
    		my @ruleNameList=keys( %{ $scheduleRules{groups}{$group}{rules} } );								# On prend la liste des règles du groupe précédent
    		_trace( "_hasPreviousGroup", "Wait all rules on group $group" );
    		_waitAllThreadsForCurrentGroup( 																				# On demande d'attendre la fin de toutes ses règles
    			'group',																												# Passe le type de l'objet contraint
    			$group, 																												# Passe le nom de l'objet contraint
    			@ruleNameList 																										# Passe la liste des règles contraignantes
    		);																														#
    	} 																														#
    	return 1;																											# Renvoi toujours vrai
    }																														#
    #=========================================================================================#
     
     
    sub _doStuff(@) {
    	my ( $previousGroup, $currentGroup )=@_;
    	if ( $previousGroup and $previousGroup eq $currentGroup ) {
    		_trace( "_doStuff", "All groupe terminated : Do some stuff at the end !!!", "\n" );
    	}
     
    	elsif ( ! $previousGroup ) {
    		_trace( "_doStuff", "No group started : Do some stuff at the begin !!!" );
    	}
     
    	else {
    		_trace( "_doStuff", "Do stuff between group=$previousGroup and group=$currentGroup" );
    	}
    }
     
     
     
    #=========================================================================================#
    # Fonction principale du MASTER de gestion des groupes de règles
    #=========================================================================================#
    sub startManageGroup(@) {																						#
    	my $previousGroup;																								# Déclare le group précédent vide par défaut
    	foreach my $group ( sort keys( %{ $scheduleRules{groups} } ) ) {									# Boucle sur la liste des groupe de règles
    		my $refGroup=\%{ $scheduleRules{groups}{$group} };														# Pour alleger le code, on prend la ref du hash des règles du groupe courant
    		if ( $previousGroup ) {																							# Si il y avait un groupe précédent...
    			_trace( "startManageGroup", "New rules group, wait all rules previous group" );
    			_hasPreviousGroup( $previousGroup );																		# Y-a-t'il encore des règles en cours ?
    			_trace( "startManageGroup", "Rules group=$previousGroup terminated" );
    		}																														#
    																																#
    		#-----------------------------------------------------------------------------------------#
    		# Ici il n'y a pas (ou il n'y a plus) de threads esclaves actifs pour le group précédent
    		#-----------------------------------------------------------------------------------------#
    		_doStuff( $previousGroup, $group );																			# On peut faire des trucs entre le $previousGroup et le $group, ici
    		$previousGroup=$group;																							# Comme on débute un nouveau group, on met son nom dans le previousGroup (pour plus tard)
    		_trace( "startManageGroup", "Rules group=$group started", "\n" );
    		_submitAllRuleForCurrentGroup( $refGroup, $group );													# On demande la soumission de tous les threads des règles du group courant
    																																#
    	}																														#								
    																															#
    	print "startManageGroup - All group ended wait last rules\n";
    	_hasPreviousGroup( $previousGroup );																		# Attend la fin de toutes les règles du dernier groupe
    	_doStuff( $previousGroup, $previousGroup );																# On peut faire des trucs à la fin du dernier groupe, ici
    																															#
    	return 1;																											# Renvoi toujours vrai
    }																														#
    #=========================================================================================#
     
     
     
    sub makeRuleGraph(@) {
    }
     
     
     
    #=========================================================================================#
    # Déclaration toute les règles en local pour les tests
    #=========================================================================================#
    sub rule01 { my ($ruleName, $wait)=@_; _ruleContent( $ruleName, $wait ); return 1; }
    sub rule02 { my ($ruleName, $wait)=@_; _ruleContent( $ruleName, $wait ); return 1; }
    sub rule03 { my ($ruleName, $wait)=@_; _ruleContent( $ruleName, $wait ); return 1; }
    sub rule04 { my ($ruleName, $wait)=@_; _ruleContent( $ruleName, $wait ); return 1; }
    sub rule05 { my ($ruleName, $wait)=@_; _ruleContent( $ruleName, $wait ); return 1; }
    sub rule06 { my ($ruleName, $wait)=@_; _ruleContent( $ruleName, $wait ); return 1; }
    sub rule07 { my ($ruleName, $wait)=@_; _ruleContent( $ruleName, $wait ); return 1; }
    sub rule08 { my ($ruleName, $wait)=@_; _ruleContent( $ruleName, $wait ); return 1; }
    sub rule09 { my ($ruleName, $wait)=@_; _ruleContent( $ruleName, $wait ); return 1; }
     
     
     
    #=========================================================================================#
    # Pour les tests, toutes les règles executent le même code (avec un wait différent)
    #=========================================================================================#
    sub _ruleContent(@) {
    	my ($ruleName, $wait)=@_;
    	_trace( "===BEG=== $ruleName", "Start for wait $wait sec" );
    	sleep $wait;
    	_trace( "===END=== $ruleName", "terminated" );
    	return 1;
    }
     
    #=========================================================================================#
    # MAIN : Boucle de control des états entre les groupes
    #=========================================================================================#
    print "MAIN - Start scheduler\n";
    startManageGroup();
     
     
    #=========================================================================================#
    # Pour le debug, on regarde un peut si tout est OK...
    #=========================================================================================#
    #print Data::Dumper->Dump( [\%thrIDForRules], ['thrIDForRules'] );
    #print Data::Dumper->Dump( [\%ruleNameForthrID], ['ruleNameForthrID'] );
    #print Data::Dumper->Dump( [\%monitorRules], ['monitorRules'] );
     
    print "\nMAIN - End scheduler\n";
     
    exit 0;
    Et voici la trace que cela génère :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
     
    MAIN - Start scheduler
    15:16:29 - _doStuff - No group started : Do some stuff at the begin !!!
     
    15:16:29 - startManageGroup - Rules group=group100 started
    15:16:29 - _removeThreadFinished - No more rule running for rule01
    15:16:29 - _removeThreadFinished - No more rule running for rule02
    15:16:29 - ===BEG=== rule01 - Start for wait 5 sec
    15:16:29 - ===BEG=== rule02 - Start for wait 6 sec
    15:16:32 - startManageGroup - New rules group, wait all rules previous group
    15:16:32 - _hasPreviousGroup - Wait all rules on group group100
    15:16:32 - _waitAllThreadsForCurrentGroup - For group=group100, waiting Rules=rule02, rule01
    15:16:32 - _waitAllThreadsForCurrentGroup - For group=group100, dependency constraint waiting Rule=rule02
    15:16:34 - ===END=== rule01 - terminated
    15:16:35 - ===END=== rule02 - terminated
    15:16:35 - _waitAllThreadsForCurrentGroup - For group=group100, dependency constraint waiting Rule=rule01
    15:16:38 - _waitAllThreadsForCurrentGroup - Rule rule02 has been terminated
    15:16:38 - _waitAllThreadsForCurrentGroup - Rule rule01 has been terminated
    15:16:38 - startManageGroup - Rules group=group100 terminated
    15:16:38 - _doStuff - Do stuff between group=group100 and group=group200
     
    15:16:38 - startManageGroup - Rules group=group200 started
    15:16:38 - _removeThreadFinished - No more rule running for rule08
    15:16:38 - _removeThreadFinished - No more rule running for rule06
    15:16:38 - ===BEG=== rule08 - Start for wait 7 sec
    15:16:38 - _removeThreadFinished - No more rule running for rule09
    15:16:38 - ===BEG=== rule06 - Start for wait 5 sec
    15:16:38 - _removeThreadFinished - No more rule running for rule07
    15:16:38 - ===BEG=== rule09 - Start for wait 8 sec
    15:16:38 - ===BEG=== rule07 - Start for wait 6 sec
    15:16:41 - startManageGroup - New rules group, wait all rules previous group
    15:16:41 - _hasPreviousGroup - Wait all rules on group group200
    15:16:41 - _waitAllThreadsForCurrentGroup - For group=group200, waiting Rules=rule06, rule08, rule07, rule09
    15:16:41 - _waitAllThreadsForCurrentGroup - For group=group200, dependency constraint waiting Rule=rule06
    15:16:43 - ===END=== rule06 - terminated
    15:16:43 - _waitAllThreadsForCurrentGroup - For group=group200, dependency constraint waiting Rule=rule08
    15:16:44 - ===END=== rule07 - terminated
    15:16:45 - ===END=== rule08 - terminated
    15:16:45 - _waitAllThreadsForCurrentGroup - For group=group200, dependency constraint waiting Rule=rule07
    15:16:45 - _waitAllThreadsForCurrentGroup - For group=group200, dependency constraint waiting Rule=rule09
    15:16:46 - ===END=== rule09 - terminated
    15:16:49 - _waitAllThreadsForCurrentGroup - Rule rule06 has been terminated
    15:16:49 - _waitAllThreadsForCurrentGroup - Rule rule08 has been terminated
    15:16:49 - _waitAllThreadsForCurrentGroup - Rule rule07 has been terminated
    15:16:49 - _waitAllThreadsForCurrentGroup - Rule rule09 has been terminated
    15:16:49 - startManageGroup - Rules group=group200 terminated
    15:16:49 - _doStuff - Do stuff between group=group200 and group=group300
     
    15:16:49 - startManageGroup - Rules group=group300 started
    Use of uninitialized value in string eq at ./engine.pl line 134.
    15:16:49 - _removeThreadFinished - Rule rule03 still running
    15:16:49 - _removeThreadFinished - Rules still running for rule04 (rule03)
    Use of uninitialized value in string eq at ./engine.pl line 134.
    15:16:49 - _removeThreadFinished - Rule rule03 still running
    15:16:49 - _removeThreadFinished - Rules still running for rule05 (rule03)
    15:16:49 - _removeThreadFinished - No more rule running for rule03
    15:16:49 - ===BEG=== rule03 - Start for wait 5 sec
    15:16:52 - _removeThreadFinished - Rule rule03 still running
    15:16:52 - _removeThreadFinished - Rules still running for rule04 (rule03)
    15:16:52 - _removeThreadFinished - Rule rule03 still running
    15:16:52 - _removeThreadFinished - Rules still running for rule05 (rule03)
    15:16:54 - ===END=== rule03 - terminated
    _removeThreadFinished - Test 7 for Rule rule03 joinable 7=7 
    15:16:55 - _removeThreadFinished - No more rule running for rule04
    15:16:55 - ===BEG=== rule04 - Start for wait 6 sec
    15:16:55 - _removeThreadFinished - No more rule running for rule05
    15:16:55 - ===BEG=== rule05 - Start for wait 7 sec
    startManageGroup - All group ended wait last rules
    15:16:58 - _hasPreviousGroup - Wait all rules on group group300
    15:16:58 - _waitAllThreadsForCurrentGroup - For group=group300, waiting Rules=rule03, rule05, rule04
    15:16:58 - _waitAllThreadsForCurrentGroup - For group=group300, dependency constraint waiting Rule=rule05
    15:17:01 - ===END=== rule04 - terminated
    15:17:02 - ===END=== rule05 - terminated
    15:17:02 - _waitAllThreadsForCurrentGroup - For group=group300, dependency constraint waiting Rule=rule04
    15:17:05 - _waitAllThreadsForCurrentGroup - Rule rule03 has been terminated
    15:17:05 - _waitAllThreadsForCurrentGroup - Rule rule05 has been terminated
    15:17:05 - _waitAllThreadsForCurrentGroup - Rule rule04 has been terminated
     
    15:17:05 - _doStuff - All groupe terminated : Do some stuff at the end !!!
     
    MAIN - End scheduler
    Ca répond parfaitement à mes besoins (content Rosco)

    Petites restrictions
    Il n'y a pas de contrôle de boucle de dépendances (en principe, le paramétrage des règles, est entre mes mains et je devrais savoir ce que je fait).
    Mettre un prédécesseur dans un groupe en amont du successeur, provoque une boucle d'attente interminable.

    A venir
    1. Je bosse maintenant sur la conception d'un graph de ces enchainements (pour automatiser le schéma de début).
    2. Puis je passerai à des objets Moose/DBix toujours avec le même Scheduler.



    Cdlt...

  7. #7
    Membre averti Avatar de dca_marshall
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Âge : 63
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 46
    Par défaut
    Bonjour,

    Je continu d'avancer, sur l'auto paramétrage des règles et des groupes.
    Pour cela je passe par des Special Literals, comme suit.

    Avec le module :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    use Inline::Files;
    Le paramétrage de la règle est ici (en JSON) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    __PARAM__
    	{
    		"preds":   [  ], 
    		"class":   "user", 
    		"param":   [ 5 ], 
    		"label":   "Rule01", 
    		"active":  "active",
    		"status":  "unchanged",
    		"feature": "XXXX",
    		"group":   "group100",
    		"before" : [ { "function": "myFonction01", "param": [ "P1", "P2" ] } ],
    		"after" : [ { "function": "standardFunction02", "param": [ "P1", "P2" ] } ]
    	}
    et sa documentation (en Mardown) ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    __TOPIC__
    # h1 Règle nomDeLaRegleTresLongPourLesTests01
    ---
    ## h2 Description
    ---
    ## h2 Sous-projet
     
    ---
    ## h2 Zones utilisées
    | Zones | Classe | Utilisation | Remarques |
    | ------ | ----------- | ----------- | ----------- |
    | [field1](http:.user/fields/field1/description) | user | Lecture | remarque |
    | [field2](http:.user/fields/field2/description) | user | Lecture | remarque |
    ---
    ## h2 Pseudo-Code
    ```
    pseudocode ici...
    ```
    ---
    __Avertisement__
    ---
    le tout dans chaque règle.

    Avec cette méthode, que l'on créé une nouvelle règle, ou que l'on la modifie, ça se passe dans le même fichier.
    Il y a donc une grosse fonction qui récupère tous ceci, pour en construire les directives au mini scheduler et pour la documentation.
    Et du coup, comme je suis un gros fainéant, j'ai développé un petit tools, qui créé une règles vide, avec tout le bazar nécessaire à son auto paramétrage. Il y a juste à lui passer quelques paramètres de base.

    Je pense mettre la documentation dans une page web, avec GoJS pour la partie graphique.
    Avant j'utilisai Doxygen, en y ajoutant des SVG en Graphviz, mais le design de tous ceci n'est, aujourd'hui, pas assez moderne à mon goût.
    Doxygen était torturé pour s'adapter au Perl.
    Et Graphviz ne permet pas quelque chose de concis, dynamique et interdit le HMTL5 et CSS.
    En revanche, j'utilise toujours Graphviz pour de très gros graphiques, mais avec peu d'information (il rend quand même bien service).

    Je n'ai pas encore débuté les tests avec DBix.
    Je pense le faire dans une dizaine de jours.

    Le truc c'est que j'ai aussi du boulot au quotidien, donc ça avance doucement.

    Je voulais juste savoir si mes travaux vous intéressent toujours.
    Dans l'affirmative je vous posterai les sources à mesure de mon avancée, sinon je pense que j'arrêterai ici.
    Merci de votre compréhension et de vos retours.

    Cdlt,
    dca_marshall

  8. #8
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    oui, ça m'intéresse toujours de voir comment d'autres personnes résolvent leurs problèmes. C'est très souvent une excellente source d'idées nouvelles.

    Même si ne ne comprends pas forcément très bien la "big picture" de ton application, la façon dont tu fais les choses donne des idées

  9. #9
    Membre averti Avatar de dca_marshall
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Âge : 63
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 46
    Par défaut
    Bonjour,

    Merci de ton retour

    Je continuerai donc à poster les sources, avec quelques explications sur les principes.
    Je suis désolé, mais je ne peux trop en dire plus sur l'application.

    Merci encore, pour ton implication ici

Discussions similaires

  1. perl avec easyphp
    Par HULK dans le forum Web
    Réponses: 18
    Dernier message: 09/01/2008, 16h19
  2. Réponses: 6
    Dernier message: 23/05/2005, 08h33
  3. [MFC] Cherche Timer avec thread
    Par romeo9423 dans le forum MFC
    Réponses: 17
    Dernier message: 09/03/2005, 10h33
  4. Variable static avec thread
    Par oxor3 dans le forum Threads & Processus
    Réponses: 7
    Dernier message: 27/08/2004, 11h45
  5. [langage] Comparer Perl avec d'autres langages comme C ?
    Par Anonymous dans le forum Langage
    Réponses: 3
    Dernier message: 10/08/2002, 23h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo