SOCIÉTÉ

BLOGS

Attachez-vous aux liens !

09.04.2010
par Olivier Deschanels
Lien

...Ou comment booster l'affichage de vos listes.

Au cours de mes rencontres avec les développeurs j'ai souvent entendu dire que les liens n'étaient pas très utiles dans une base de données et qu'un bon vieux CHERCHER faisait l'affaire en faisant correctement le boulot sans avoir à se poser trop de questions ! (sic). Vous vous en doutez, je suis en total désaccord avec cette position et je vais dans ce billet vous expliquer pourquoi je suis attaché aux liens. J'oserais même dire qu'un bon lien va libérer les performances de votre base de données !
 

 

Plaçons-nous dans le contexte d'une base de données simple, créée pour l'occasion, avec trois tables comme ci-dessous :


 

Tous les liens sont définis en "manuel" tant pour l'aller que pour le retour.


Nous allons générer des données automatiquement via la méthode suivante :
 

$lettre:="ABCDEFGHIJKLMNOPQRSTUVWXZ"

Boucle ($soc;1;10)
    Repeter
        $nom:="SOC_"
        Boucle ($i;1;10;1)
            $nom:=$nom+$lettre{Hasard%Longueur($lettre)+1}
        Fin de boucle
    Jusque(Trouver dans champ([Societe]Nom;$nom)<0)
    CREER ENREGISTREMENT([Societe])
    [Societe]ID:=Numerotation automatique([Societe])
    [Societe]Nom:=$nom
    STOCKER ENREGISTREMENT([Societe])
   
    Boucle ($serv;1;10;1)
        Repeter
            $nom:="SERV_"
            Boucle ($i;1;10;1)
                $nom:=$nom+$lettre{Hasard%Longueur($lettre)+1}
            Fin de boucle
        Jusque (Trouver dans champ([Service]Nom;$nom)<0)
        CREER ENREGISTREMENT([Service])
        [Service]ID:=Numerotation automatique([Service])
        [Service]ID_Societe:=[Societe]ID
        [Service]Nom:=$nom
        STOCKER ENREGISTREMENT([Service])
       
        Boucle ($person;1;10;1)
            Repeter
                $nom:="PERS_"
                Boucle ($i;1;10;1)
                    $nom:=$nom+$lettre{Hasard%Longueur($lettre)+1}
                Fin de boucle
            Jusque (Trouver dans champ([Personne]Nom;$nom)<0)
            CREER ENREGISTREMENT([Personne])
            [Personne]ID:=Numerotation automatique([Personne])
            [Personne]ID_Service:=[Service]ID
            [Personne]Nom:=$nom
            STOCKER ENREGISTREMENT([Personne])
        Fin de boucle
    Fin de boucle
Fin de boucle


A la fin de l'exécution de cette méthode nous obtenons 10 sociétés, 100 services et 1.000 personnes. La base n'est donc pas très volumineuse en soi, mais ce volume suffira largement pour illustrer nos propos. Laissons 4D créer les formulaires par défaut, et vérifions que les données sont correctement créées.


Afficher des données liées

Créons à présent un formulaire permettant d'afficher, pour une personne, son service et sa société. Pour cela utilisons l'assistant de création de formulaire pour obtenir un formulaire liste ressemblant à ceci :
 

 

Si nous utilisons ce formulaire, voici le résultat :

Nous remarquons que les informations sur le service et la société sont absentes. Effectivement, il nous faut ajouter dans la méthode formulaire le chargement des enregistrements liés via le code suivant :

 

$evt:=Evenement formulaire

Au cas ou
    : ($evt=Sur affichage corps)
        CHARGER SUR LIEN([Personne]ID_Service)
        CHARGER SUR LIEN([Service]ID_Societe)
       
Fin de cas


L'affichage est à présent correct :

 

Finissons la préparation de notre base de test en créant une méthode (nommée "Affiche_Liste_Personne") permettant d'afficher cette liste dans un process :

 

C_ENTIER($1)

Au cas ou
    : (Nombre de parametres=0)
        $ref_process:=Nouveau process("Affiche_Liste_Personne";64*1024;"Affiche_Liste_Personne";1;*)
       
    Sinon
       
        FORMULAIRE SORTIE([Personne];"Formulaire1")
        FORMULAIRE ENTREE([Personne];"Entrée")
       
        TOUT SELECTIONNER([Personne])
       
        $ref_fenetre:=Creer fenetre(100;100;1100;800;Fenêtre standard de taille fixe )
        MODIFIER SELECTION([Personne])
        FERMER FENETRE($ref_fenetre)
Fin de cas


Enfin, associons cette méthode à un menu.

 

 

Affichage de la liste dans un process en client-serveur

Nous pouvons maintenant passer aux choses intéressantes ! Si ce n'est déjà le cas, ouvrons l'application en contexte client-serveur.

Regardons à nouveau la méthode formulaire écrite un peu plus haut. L'événement formulaire traité est "sur affichage corps", c'est-à-dire qu'à l'affichage de chaque ligne (de chaque enregistrement), deux commandes 4D sont effectuées entraînant deux échanges entre le client et le serveur.

 

Pour nous en convaincre, nous allons activer le log des requêtes en client-serveur. Nous utilisons pour ce faire la commande FIXER PARAMETRE BASE avec le sélecteur adéquat et modifions la méthode "Affiche_Liste_Personne" en conséquence :

 

C_ENTIER($1)

Au cas ou
    : (Nombre de parametres=0)
        $ref_process:=Nouveau process("Affiche_Liste_Personne";64*1024;"Affiche_Liste_Personne";1;*)
       
    Sinon
       
        FORMULAIRE SORTIE([Personne];"Formulaire1")
        FORMULAIRE ENTREE([Personne];"Entrée")
       
        TOUT SELECTIONNER([Personne])
        FIXER PARAMETRE BASE(Enreg requêtes 4D Server ;1)
        $ref_fenetre:=Creer fenetre(100;100;1100;800;Fenêtre standard de taille fixe )
        MODIFIER SELECTION([Personne])
        FERMER FENETRE($ref_fenetre)
        FIXER PARAMETRE BASE(Enreg requêtes 4D Server ;0)
Fin de cas

 


Protocole de test

A présent, lançons la méthode de test via le menu (donc en mode application) et appuyons uniquement sur la touche "page suivante" afin de faire défiler l'ensemble de la liste des enregistrements. Lorsque la liste est entièrement parcourue, refermons la liste et la fenêtre. Il est important de respecter ce processus de test afin de comparer par la suite des résultats de situations globalement équivalentes ; nous verrons que le niveau de précision requis n’est pas énorme, mais il est quand même important de faire attention au déroulement des tests !

 

Dans le dossier Log, situé à coté de la structure de votre base, vous devez à présent trouver un fichier nommé "4DRequestsLog_1.txt". Le contenu du fichier doit ressembler à cela :

 

Log Session Identifer    51C3EBCE8EFD43C794CE254D46A6D06A
1 nth log opened on Wednesday, March 31, 2010 10:19:12 AM
time    task id    component    process info index    request    bytes in    bytes out    duration
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    150    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    140    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    150    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    140    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    150    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    140    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    150    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    140    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    150    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    140    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    150    0
03/31/10, 10:19:12    10    'dbmg'    1    11050    105    140    0

 


Lecture du fichier de log

Peu nous importe aujourd'hui le détail de ce fichier. En effet, l'explication serait beaucoup trop longue pour être faite dans le cadre d'un billet de blog ; une formation est là pour cela !


Non, ce qui nous intéresse, c'est de compter le nombre de lignes dans le fichier. Pour cela il suffit d'ouvrir le fichier dans un éditeur de texte sachant compter les lignes ou dans un tableur. Quelle que soit la méthode choisie, le nombre de lignes, au delà des trois lignes d'entête, est de l'ordre de 2190.


Ce nombre exprime le volume d'échanges effectués entre le client et le serveur au cours du test. Il y a une ligne par échange entre le client et le serveur (échange aller et retour). Si la procédure de test a été respectée, nous avons demandé l'affichage des 1000 enregistrements provoquant chacun 2 appels à la commande CHARGER SUR LIEN soit 2000 échanges sur le réseau.


Les 190 autres échanges correspondent à la gestion des pages (affichage d'une série d'enregistrements suite à l'appui sur la touche "page suivante"), plus d'autres tâches de gestion.

 

 

CHERCHER ou CHARGER SUR LIEN

Répondons de suite à ceux qui préfèrent utiliser un CHERCHER en lieu et place du CHARGER SUR LIEN. Modifions donc la méthode formulaire pour obtenir ce code :

 

$evt:=Evenement formulaire

Au cas ou
    : ($evt=Sur affichage corps )
          `CHARGER SUR LIEN([Personne]ID_Service)
        CHERCHER([Service];[Service]ID=[Personne]ID_Service)
          `CHARGER SUR LIEN([Service]ID_Societe)
        CHERCHER([Societe];[Societe]ID=[Service]ID_Societe)
       
Fin de cas


Reprenons notre protocole de test et mesurons le nombre de lignes obtenu dans le fichier "4DRequestsLog_1.txt". Nous obtenons, à quelques unités près, le même nombre d'échanges entre le client et le serveur. La différence est simplement liée aux échanges nécessaires au moteur de 4D et que nous ne pouvons prévoir, mais qui ne sont pas signifiants.


Les deux choix sont donc équivalents, ce qui pourrait laisser entendre que les partisans du "sans lien" ne se trompent pas !

 

 

Sans chercher ni charger !

Reprenons donc notre méthode formulaire pour mettre les appels à CHARGER SUR LIEN et à CHERCHER en commentaires :

 

$evt:=Evenement formulaire

Au cas ou
    : ($evt=Sur affichage corps )
          `CHARGER SUR LIEN([Personne]ID_Service)
          `CHERCHER([Service];[Service]ID=[Personne]ID_Service)
          `CHARGER SUR LIEN([Service]ID_Societe)
          `CHERCHER([Societe];[Societe]ID=[Service]ID_Societe)

       
Fin de cas


Reprenons notre protocole de test. Certes l'affichage n'est pas correct, mais continuons quand même sans broncher ! Le décompte des échanges est de l'ordre de 50 à présent. Si vous rapprochez ce nombre de la quantité de clics sur la touche "page suivante", vous aurez une idée de la source des échanges. En effet, à chaque page demandée 4D va charger les éléments nécessaires à l'affichage de la page, en l'occurrence ici il s'agit principalement du nom de la personne.

 

L'amélioration en terme de performance (qui est directement liée au nombre d'échanges entre le client et le serveur) est donc nette, mais l'affichage n'est pas correct. Comment obtenir l'affichage désiré, sans pour autant obérer les performances ? La première idée est de se dire que puiqu'il y a un lien, pourquoi ne pas le rendre automatique, pour que le chargement se fasse tout seul ? Le plus simple à ce stade est de tester cette solution. Pour cela rendons les liens automatiques. Nous nous contenterons des liens aller.


Relançons notre protocole de test. Nous constatons qu'a présent l'affichage est correct et le nombre d'échange n'a pas évolué de manière significative. Donc nous avons bien optimisé l'affichage de la liste. Certes, mais nous avons mis des liens automatiques en structure ce qui n'est pas souhaitable car nous ne pouvons plus maîtriser le chargement des enregistrements liés et ce qui peut être bénéfique dans le cadre de cette liste peut être pénalisant ailleurs. Alors comment faire ? Il faut conserver l'automatisme lorsqu'il est nécessaire et seulement dans ce cas.

 

 

Automatiser à la demande

Pour répondre à la question, il nous faut écrire du code, mais au préalable enlevons l'automatisme des liens en structure. Une fois que les liens sont manuels en structure, activons l'automatisme via la méthode FIXER LIEN CHAMP dans la méthode "Affiche_Liste_Personne" :

 

C_ENTIER($1)

Au cas ou
    : (Nombre de parametres=0)
        $ref_process:=Nouveau process("Affiche_Liste_Personne";64*1024;"Affiche_Liste_Personne";1;*)
       
    Sinon
       
        FORMULAIRE SORTIE([Personne];"Formulaire1")
        FORMULAIRE ENTREE([Personne];"Entrée")
       
        TOUT SELECTIONNER([Personne])
       
        FIXER LIEN CHAMP([Personne]ID_Service;Automatique ;Ne pas changer )
        FIXER LIEN CHAMP([Service]ID_Societe;Automatique ;Ne pas changer )
        FIXER PARAMETRE BASE(Enreg requêtes 4D Server ;1)
        $ref_fenetre:=Creer fenetre(100;100;1100;800;Fenêtre standard de taille fixe )
        MODIFIER SELECTION([Personne])
        FERMER FENETRE($ref_fenetre)
        FIXER PARAMETRE BASE(Enreg requêtes 4D Server ;0)
        FIXER LIEN CHAMP([Personne]ID_Service;Configuration structure ;Ne pas changer )
        FIXER LIEN CHAMP([Service]ID_Societe;Configuration structure ;Ne pas changer )
       
Fin de cas


Dans ce code nous fixons les deux liens concernés par la liste en automatique, puis à la sortie de la méthode nous les re-configurons comme ils sont définis par défaut dans la structure.


Notons au passage que cette commande ne fixe l'automatisme que pour le process courant, ce qui différe grandement de l'automatisme en structure dont la portée est globale à l'application.


Notre protocole de test nous donne alors un nombre d'échanges entre le client et le serveur toujours de l'ordre de 50.
Nous avons donc par ce code utilisé la puissance des liens automatiques, sans pour autant perturber le reste de la base.
 

 

De l’importance de l’utilisation du lien

Pourquoi est-ce plus important qu'il n'y paraît ? Cette optimisation du code permet d'envisager de nouvelles utilisations. En effet notre protocole de test montre que le nombre d'échanges varie entre 50 et 2190. Supposons à présent que le code soit exécuté non pas à travers un réseau local, mais à travers un réseau de type "WAN". Dans ce cas il faudrait tenir compte du temps de latence inhérent à ce type de réseau. Pour faciliter les calculs prenons le cas d'un réseau moyen (en performance) ou le temps de latence est de 50 ms. Ce temps signifie qu'en une seconde un maximum de 20 échanges pourront être menés à bien. Dans le cas optimisé, notre protocole de test nécessitera un minimum de 2,5 secondes. Ce temps sera en grande partie masqué par les actions de l'utilisateur qui ne sont pas instantanées. Dans le cas non optimisé nécessitant 2190 échanges, le temps minimum pour les transferts sur le réseau est de 1 min 49 sec. Ce temps n'est désormais plus négligeable et est largement percevable par l'utilisateur. Si l'on rapporte cette mesure à la page, en supposant comme nous l'avons vu plus haut qu'il y a une cinquantaine de pages, le temps nécessaire pour les transferts sur le réseau varie entre 1/20ème de seconde et 2,2 secondes.
 

RSS 5 commentaire(s) pour ce billet
Je crois bien que suite à ce billet, je vais devoir me réconcilier avec les liens...

 @Anne : C'est bien le but de ce billet et j'en suis ravi ! 

Encore une fois ce billet nous ouvre une toute perspective dans l'optimisation des requêtes en client-serveur. Grand merci
Schade. Es wäre wahrscheinlich sehr interessant gewesen zu lesen, was hier steht. Leider geht das nicht, weil der Autor zu glauben scheint, 4D wäre eine rein französische Lösung. Wer interessiert sich schon für den Rest der Welt. Und so ist der Blog-Eintrag für 75% der 4D-Benutzer vollkommen nutzlos.

Hello.

 

Actually blogs are not a part of the official documentation, even if they bring a useful information. As usual for a blog, those posts are a free and spontaneous contribution from their authors and are written with a very personal tone.

As such, they must be considered as literary texts rather than technical notes. Olivier Deschanels uses a rich and colourful vocabulary, with idiomatic expressions, references to French history and culture, etc... that make impossible the immediate translation of his (long!) contributions.

 

Of course it's totally impossible to translate these posts to all the languages we support for documentation for example. We make our best to provide an English version. But an English native writer who understands perfectly the plays on word and French allusions must do the job in order to keep the same intention and tone.The code has to be translated as well.

 

We are done with some last year posts that you can find under the german blog section of 4D.com. We'll continue to translate Olivier's and other writers' posts with a certain delay when we think the topic can be universal.

 

Thank you for your interest.

Poster un nouveau commentaire

  • Les adresses de pages web et de messagerie électronique sont transformées en liens automatiquement.
  • You may use [view:viewname] tags to display listings of nodes.
  • Each email address will be obfuscated in a human readable fashion or (if JavaScript is enabled) replaced with a spamproof clickable link.

Plus d'informations sur les options de formatage