LOXODATA

Profilage de fonctions PL/PgSQL (partie 2)

2017-04-05   932 mots, 5 minutes de lecture   Sébastien Lardière

Profilage de fonctions PL/PgSQL

Contexte

Le langage PL/PgSQL est présent depuis près de 20 ans dans PostgreSQL.

Il ajoute des instructions procédurales au langage SQL. On peut ainsi écrire des procédures dans une base PostgreSQL.

Ses structures de contrôle sont très communes pour un développeur. Il intègre parfaitement bien le langage SQL, ce qui en fait un outil idéal lorsqu’un développeur veut implémenter ses algorithmes au plus proche des données.

Un premier article a montré l’installation et l’utilisation basique de ce profiler, ce second article se propose de montrer l’utilisation de l’outil dans le cadre de tests de charge, aussi appelés « benchmarks ».

Test de Charge

Il n’est pas recommandé d’utiliser le profiler dans un système de production, en raison de la charge supplémentaire que le suivi du code apporte.

Le profiler est conçu pour être utilisé dans le cadre de tests de charge avec, par exemple, pgbench ou tsung. Mais il peut aussi être utilisé pour vérifier le bon fontionnement des procédures « maison ».

Pour illustrer cet article, nous avons choisi un simple script SQL qui insère des données dans une table partitionnée. Le partitionnement de données étant réalisé par des triggers écrits en PL/PgSQL, le profiler montrera le temps passé dans ces fonctions.

Installation de la table partitionnée

L’outil de partitionnement est disponible à l’adresse suivante : (https://github.com/slardiere/PartMgr)

La table partitionnée peut-être créée avec les commandes suivantes :

create schema partmgr;
create extension partmgr with schema partmgr;

drop schema if exists test cascade ;

create schema test ;

create table test.test1jour ( id int primary key,
    ev_date timestamptz default now(), label int );
create sequence test.test1jour_id_seq ;
alter table test.test1jour alter column id
    set default nextval('test.test1jour_id_seq');

create trigger update_row before update
    on test.test1jour for each row
	execute procedure  partmgr.update_tuple();

create or replace function test.test_trigger ()
returns trigger
language plpgsql
as $BODY$
begin
  if TG_OP = 'INSERT' then
    raise notice 'Fct triggered on %', TG_OP ;
    return new ;
  elsif TG_OP = 'UPDATE' then
    raise notice 'Fct triggered on %', TG_OP ;
    return new ;
  elsif TG_OP = 'DELETE' then
    raise notice 'Fct triggered on %', TG_OP ;
    return old ;
  end if;
  return null;
end;
$BODY$ ;

create trigger _insupdev before insert or update
  on test.test1jour
  for each row
  execute procedure test.test_trigger () ;

insert into partmgr.part_table (schemaname, tablename, keycolumn,
    pattern, cleanable, retention_period)
    values ('test','test1jour','ev_date','D','t','2 month') ;
select partmgr.create_part_trigger('test','test1jour') ;
select * from partmgr.create ( (current_date - interval '2 month')::date,
                             (current_date + interval ' 2 month')::date ) ;

Le script testpart.sql est le suivant :

insert into test.test1jour ( ev_date, label ) values
    ( now() + interval '1d' *  ((random() - 0.5) * 100)::int, 1);

Le script SQL est appelé par l’outil pgbench. En effet, au dela de l’usage habituel, pgbench peut lancer n’importe quel script, avec de nombreuses possibilités dépassant le cadre de cet article. Dans notre cas, l’interêt est de lancer le test avec quelques clients (10) pendant un temps donné (10sec), comme dans l’exemple suivant :

pgbench -c 10 -j 10 -T 10 -f testpart.sql

Le résultat du test de charge ne nous intéresse pas dans le cadre de cet article. Nous nous intéressons uniquement aux appels de procédures stockées.

Profilage d’une instance PostgreSQL

Le module plprofiler doit être chargé dès le démarrage de l’instance PostgreSQL, permettant le profilage global. Pour cela, il faut modifier le fichier de configuration postgresql.conf :

shared_preload_libraries = 'plprofiler'

L’instance doit alors être redémarrée.

Surveillance globale d’une instance

Lorsqu’un test de charge est en cours, il est alors possible de suivre l’activité de toute l’instance PostgreSQL, en utilisant l’outil client plprofiler :

plprofiler reset
plprofiler monitor  --interval 10 --duration 300

La commande reset nettoie les données déjà collectées. La commande monitor permet de collecter les données de profilage, ici pendant 300 secondes.

Les données de profilage peuvent alors être exportées pour être examiné dans un navigateur web :

plprofiler report --from-shared --output parttest01.html

Ce mode permet de ne pas modifier le test de charge, ce qui est particulièrement pertinent lors du test d’une application existante, ou il peut être difficile d’insérer les déclenchements du profilage.

L’extrait suivant montre la liste des fonctions, utilisée dans le test de charge, et donc identifiées par le profiler :

Rapport plprofiler

Puis, pour chaque fonction, le code est détaillé avec les informations associées aux lignes de code :

Rapport plprofiler

Pilotage depuis le test de charge

Lorsque le test de charge est “ouvert”, il est plus simple de déclencher le profilage aux endroits strictement nécessaires, afin d’obtenir des données pertinentes sur ce qui doit être testé.

Pour cela, il suffit d’activer le profilage à la demande, comme dans l’exemple testpart.sql suivant :

SET plprofiler.enabled TO true;
SET plprofiler.collect_interval TO 10;
insert into test.test1jour ( ev_date, label )
   values ( now() + interval '1d' *
          ((random() - 0.5) * 100)::int, 1);

Le simple lancement du test de charge déclenche alors la collecte des données de profilage :

pgbench -c 10 -j 10 -T 10 -f testpart.sql

Enregistrement d’une session

Les données « partagées » du profiler peuvent être enregistrées dans une session, ce qui permet de cumuler plusieurs sessions, correspondant à des tests de charge distincts :

plprofiler save --name=parttest01

Production d’un rapport

Enfin, il est possible de produire un rapport à partir d’une session enregistrée :

plprofiler report --name=parttest01 > parttest01.html

Conclusion

Au terme de ces 2 articles, on comprend que l’outil est d’une grande aide pour les développeurs qui cherchent à comprendre l’exécution dans le détail de leurs procédures stockées.

De même, l’administrateur de base de données confronté à des problèmes de performances y trouve un outil précieux, en complément de bien d’autres outils, pour identifier les points bloquants de l’instance.