Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
445 lines (381 sloc) 14.3 KB
<?xml version="1.0" encoding="UTF-8"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<sect1 id="usingslonik">
<title>Utiliser Slonik</title>
<indexterm><primary>utiliser slonik</primary></indexterm>
<para>
Il est parfois pénible d'écrire les scripts <application>Slonik</application>
à la main, en particulier lorsqu'on travaille avec des clusters &slony1;
dont le nombre de n&oelig;uds et d'ensemble de réplication augmente
régulièrement. Les problèmes suivants ont été identifiés.
</para>
<itemizedlist>
<listitem>
<para>
Si vous utilisez &slony1; pour une réplication
<quote>maître/esclave</quote> avec un n&oelig;ud <quote>maître</quote>
et un n&oelig;ud <quote>esclave</quote>, le plus simple est de nommer le
n&oelig;ud <quote>maître</quote> n&oelig;ud 1 et le n&oelig;ud
<quote>esclave</quote> n&oelig;ud 2.
</para>
<para>
Malheureusement, lorsque le nombre de n&oelig;ud augmente, la
correspondance entre les identifiants et les n&oelig;uds devient moins
évidente, en particulier si vous avez un cluster dont l'origine est
déplacée d'un n&oelig;ud à l'autre de temps en temps.
</para>
</listitem>
<listitem>
<para>
De la même façon, s'il n'y a qu'un seul ensemble de réplication, il est
facile de le nommer <quote>ensemble 1</quote> mais s'il y a de multiples
ensembles, la numérotation des ensembles sera moins intuitive.
</para>
</listitem>
<listitem>
<para>
On observe que <application>Slonik</application> ne fournit pas la notion
d'itération. Il est courant de vouloir créer un ensemble d'entrées <xref
linkend="stmtstorepath"/> similaires car, la plupart du temps, les hôtes
accèdent à un serveur via le même nom d'hôte ou la même adresse IP.
</para>
</listitem>
<listitem>
<para>
Les utilisateurs semblent intéressés à tout insérer à l'intérieur de
blocs <command>TRY</command>, ce qui n'est pas aussi utile qu'on pourrait
le penser.
</para>
</listitem>
</itemizedlist>
<para>
Certaines évolutions ont résolu une partie de ces problèmes&nbsp;:
</para>
<itemizedlist>
<listitem>
<para>N&oelig;uds nommés, ensemble nommés</para>
<para>
Ceci est supporté par les commandes <xref
linkend="stmtdefine"/> et <xref linkend="stmtinclude"/> à partir de
&slony1; 1.1.
</para>
<para>
L'utilisation de <xref linkend="stmtinclude"/> pour créer des
<quote>fichiers préambule</quote> est une méthode essentielle
pour réduire les erreurs. Un fichier préambule est défini
<emphasis>une seule fois</emphasis> et vérifié <emphasis>une seule
fois</emphasis>. Ces fichiers peuvent être utilisés en tout confiance
par les autres scripts slonik.
</para>
</listitem>
<listitem>
<para>Boucles et opérateurs de contrôle</para>
<para>
Il est inutile de créer un analyseur complet, c'est trop complexe. Il
existe de nombreux langages de script que l'on peut utiliser pour
construire des scripts Slonik&nbsp;; il n'est pas intéressant d'en
imposer un nouveau.
</para>
</listitem>
</itemizedlist>
<para>
Il existe plusieurs façons de régler ces problèmes :
</para>
<itemizedlist>
<listitem>
<para>Inclure la génération du script slonik dans un script shell</para>
<para>
Les tests trouvés dans le répertoire <filename>src/ducttape</filename>
utilisent cette approche.
</para>
</listitem>
<listitem>
<para>
Les <link linkend="altperl">outils altperl</link> utilisent du code
Perl pour générer les scripts Slonik.
</para>
<para>
Vous définissez les configuration du cluster comme un ensemble d'objets
Perl&nbsp;; chaque script altperl utilise les objets Perl pour produire
le script slonik adéquate.
</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="slonikshell">
<title>Utiliser Slonik à partir d'un script shell</title>
<indexterm><primary>utiliser slonik à l'intérieur de scripts shell</primary></indexterm>
<para>
Comme on l'a vu précédemment, il existe de nombreux scripts de test pour
&slony1; dans le répertoire <filename>src/ducttape</filename> qui intègre
la génération de script Slonik à l'intérieur de code écrit en shell.
</para>
<para>
La plupart de ces scripts ne sont <emphasis>pas</emphasis> terriblement
sophistiqués. Typiquement, ils utilisent des structures comme celle-ci&nbsp;:
<programlisting><![CDATA[
DB1=slony_test1
DB2=slony_test2
slonik <<_EOF_
cluster name = T1;
node 1 admin conninfo = 'dbname=$DB1';
node 2 admin conninfo = 'dbname=$DB2';
try {
create set (id = 1, origin = 1, comment =
'Set 1 - pgbench tables');
set add table (set id = 1, origin = 1,
id = 1, fully qualified name = 'public.accounts',
comment = 'Table accounts');
set add table (set id = 1, origin = 1,
id = 2, fully qualified name = 'public.branches',
comment = 'Table branches');
set add table (set id = 1, origin = 1,
id = 3, fully qualified name = 'public.tellers',
comment = 'Table tellers');
set add table (set id = 1, origin = 1,
id = 4, fully qualified name = 'public.history',
comment = 'Table accounts');
}
on error {
exit 1;
}
_EOF_
]]></programlisting></para>
<para>
Une approche plus sophistiquée consiste à définir des composants communs,
notamment un <quote>préambule</quote> qui contient les commandes <xref
linkend="clustername"/> et <xref linkend="admconninfo"/> que l'on
retrouve dans chaque script Slonik&nbsp;:
<programlisting>
CLUSTER=T1
DB1=slony_test1
DB2=slony_test2
PREAMBULE="cluster name = $CLUSTER
node 1 admin conninfo = 'dbname=$DB1';
node 2 admin conninfo = 'dbname=$DB2';
"
</programlisting></para>
<para>
La variable <envar>PREAMBULE</envar> peut alors être réutilisée plusieurs
fois lorsque le script shell invoque plusieurs fois <command>slonik</command>.
Vous pouvez également utiliser <xref linkend="stmtinclude"/> et placer le
preambule dans un fichier que vous incluez.
</para>
<para>
Les variables shell fournissent une méthode simple pour assigner les noms
d'ensemble et de n&oelig;uds&nbsp;:
</para>
<programlisting><![CDATA[
origin=1
subscriber=2
mainset=1
slonik <<_EOF_
$PREAMBULE
try {
create set (id = $mainset, origin = $origin,
comment = 'Set $mainset - pgbench tables');
set add table (set id = $mainset, origin = $origin,
id = 1, fully qualified name = 'public.accounts',
comment = 'Table accounts');
set add table (set id = $mainset, origin = $origin,
id = 2, fully qualified name = 'public.branches',
comment = 'Table branches');
set add table (set id = $mainset, origin = $origin,
id = 3, fully qualified name = 'public.tellers',
comment = 'Table tellers');
set add table (set id = $mainset, origin = $origin,
id = 4, fully qualified name = 'public.history',
comment = 'Table accounts');
} on error {
exit 1;
}
_EOF_
]]></programlisting>
<para>
Le script peut être utilisé en suite pour boucler sur la liste des
tables&nbsp;:
</para>
<programlisting><![CDATA[
# Basic configuration
origin=1
subscriber=2
mainset=1
# List of tables to replicate
TABLES="accounts branches tellers history"
ADDTABLES=""
tnum=1
for table in `echo $TABLES`; do
ADDTABLES="$ADDTABLES
set add table ($set id = $mainset, origin = $origin,
id = $tnum, fully qualified name = 'public.$table',
comment = 'Table $tname');
"
let "tnum=tnum+1"
done
slonik <<_EOF_
$PREAMBULE
try {
create set (id = $mainset, origin = $origin,
comment = 'Set $mainset - pgbench tables');
$ADDTABLES
} on error {
exit 1;
}
_EOF_
]]></programlisting>
<para>
Ce n'est peut-être pas nécessaire si vous n'avez que quatre tables mais cette
méthode évite de recopier de longues listes de configuration à la main et
préviendra les erreurs sur les grands ensembles que vous rencontrerez dans
la <quote>vraie vie</quote>.
</para>
<para>
Vous pouvez créer des scripts encore plus sophistiqués si votre langage de
script possède&nbsp;:
</para>
<itemizedlist>
<listitem>
<para>
des structures de type <quote>Record</quote> qui permettent d'assigner
des objets en parallèle&nbsp;;
</para>
</listitem>
<listitem>
<para>
des fonctions, procédure ou des routines qui permettent d'implanter des
fonctionnalités une seule fois, puis de les appeler à divers endroits
de votre script.
</para>
</listitem>
<listitem>
<para>
Un mécanisme d'<quote>import de module</quote> afin de partager de
fonctionnalités commune entre plusieurs scripts.
</para>
</listitem>
</itemizedlist>
<para>
Si vous devez utiliser <ulink
url="http://www.gnu.org/software/bash/bash.html">Bash</ulink>, <ulink
url="http://www.zsh.org/">zsh</ulink> ou <ulink
url="http://www.kornshell.com/">Korn shell</ulink>, tous ces shells possèdent
des extensions qui gèrent correctement les structures de données
sophistiquées et les modules. Sur linux, Bash est très répandu&nbsp;; sur les
systèmes <trademark>UNIX</trademark> commerciaux, Korn est le standard alors
que sur BSD, les shells <quote>sophistiqués</quote> sont en option.
</para>
<para>
N'oubliez pas de regarder du côté des des autres langages de script, parmi
lesquels Perl est le plus évident, puisqu'il est largement répandu sur Linux,
<trademark>UNIX</trademark> et BSD.
</para>
</sect1>
<sect1 id="noslonik">
<title>Ne pas utiliser Slonik - les fonctions &slony1; de bas niveau</title>
<indexterm><primary>fonctions &slony1; de bas niveau</primary></indexterm>
<para>
Il est parfois nécessaire d'utiliser directement les procédures stockées
qui composent les différentes parties de &slony1;. Slonik n'est pas
<quote>magique</quote>&nbsp;; il est courant qu'une commande Slonik se
contente de décider le n&oelig;ud qui va recevoir la commande et lui
envoie une requête SQL qui contient une seule procédure stockée &slony1;.
</para>
<para>
Les développeurs de &slony1; espèrent que quelqu'un développera des outils
graphiques qui constitueront une alternative à Slonik&nbsp;; il serait alors
utile d'envoyer les ordres de configuration directement via les procédures
stockées. Si vous comptez vous lancer dans ce projet, il est conseillé
d'examiner comment les procédures stockées sont utilisées dans le fichier
<filename>slonik.c</filename> puisqu'il s'agit de l'utilisation la plus
correcte de ces fonctions.
</para>
<para>
Lorsque qu'on corrige des problèmes sur un cluster &slony1;
<quote>endommagé</quote>, il est parfois utile d'utiliser les procédures
stockées. C'est particulièrement efficace lorsque la configuration de la table
<xref linkend="table.sl-listen"/> est corrompue et que les événements ne se
propagent plus correctement. La méthode la <quote>plus simple</quote> pour
réparer cela est la suivante&nbsp;:
</para>
<para>
<command>SELECT _slonycluster.droplisten(li_origin,li_provider,li_receiver)
FROM _slonycluster.sl_listen;</command>
</para>
<para>
<command>SELECT _slonycluster.storelisten(pa_server, pa_server, pa_client)
FROM _slonycluster.sl_path;</command>
</para>
<para>
Le résultat de ces requêtes est la regénération et la
<emphasis>propagation</emphasis> des voies d'écoute. En lançant à la main la
fonction <function> _slonycluster.storelisten()</function>, on déclenche des
évènements <command>STORE_LISTEN</command> qui provoquent l'envoi des voies
d'écoute sur les autres n&oelig;uds du cluster.
</para>
<para>
En cas de problème <emphasis>local</emphasis> sur un n&oelig;ud, si vous ne
souhaitez pas propager les corrections (ce qui est assez rare, on cherche en
général à réparer <emphasis>l'ensemble</emphasis> du cluster), la requête
est la suivante&nbsp;:
</para>
<para><command>SELECT slonycluster.droplisten_int(li_origin,li_provider,li_receiver)
FROM _slonycluster.sl_listen;</command></para>
<para><command>SELECT _slonycluster.storelisten_int(pa_server, pa_server, pa_client)
FROM_slonycluster.sl_path;</command></para>
<para>
Si vous souhaitez ajouter le support de &slony1; dans d'autres outils
(<emphasis>par exemple&nbsp;:</emphasis> ajouter la réplication dans <ulink
url="http://www.pgadmin.org/"><productname>pgAdmin III</productname></ulink>),
vous devez savoir clairement quelles fonctions il faut appeler. Le
<quote>protocole</quote> normal est le suivant&nbsp;:
</para>
<itemizedlist>
<listitem>
<para>
La fonction <quote>principale</quote> (<emphasis>par exemple</emphasis>
celle sans le suffixe <command>_int</command>) est appelée sur un
n&oelig;ud <quote>adéquat</quote> du cluster &slony1;.
</para>
<para>
Dans la plupart des cas, la fonction peut être appelée sur n'importe
quel n&oelig;ud, et elle propage correctement les commandes aux autres
n&oelig;uds. C'est vrai pour <xref
linkend="function.storelisten-integer-integer-integer"/>, par exemple.
</para>
<para>
Dans d'autres cas, la fonction doit être lancée depuis un n&oelig;ud
particulier car c'est le seul endroit où les données sont valides.
Par exemple, <xref
linkend="function.subscribeset-integer-integer-integer-boolean"/> doit
être appelée sur le n&oelig;ud récepteur.
</para>
</listitem>
<listitem>
<para>
Si la fonction <quote>principale</quote> réussit, alors le changement de
configuration est effectué sur le n&oelig;ud local et un événement est
créé avec <xref linkend="function.createevent-name-text"/> pour que le
changement de configuration soit propagé à tous les autres n&oelig;uds
du cluster &slony1;.
</para>
</listitem>
<listitem>
<para>
Troisièmement, la version <command>_int</command> de la fonction doit
être lancée.
</para>
<para>
Dans certains cas, lorsque les fonctions sont idempotentes, le n&oelig;ud
sur lequel la fonction <quote>principale</quote> est exécutée peut traiter
la version <command>_int</command> très tôt.
</para>
<para>
Au moment où l'événement se propage, les n&oelig;uds lancent la version
<command>_int</command>, en espérant que tout se passe bien.
</para>
</listitem>
</itemizedlist>
</sect1>