Le planificateur classifie les opérations impliquées dans une requête
comme étant à parallélisation sûre,
parallélisation restreinte, ou
parallélisation non sûre. Une opération
à parallélisation sûre
est une opération n'entrant pas en conflit avec une
requête parallélisée. Une opération à parallélisation restreinte
ne peut pas être exécutée par un
worker parallélisé, mais peut l'être
par le leader pendant
l'exécution. De ce fait, les opérations à parallélisation
restreinte ne peuvent jamais survenir sous un nœud
Gather
ou Gather Merge
. Une
opération à parallélisation non sûre ne peut être exécutée dans une
requête parallélisée, y compris au niveau du
leader. Quand une requête contient quoi que
ce soit de non sûr à paralléliser, la parallélisation y est
complètement désactivée.
Les opérations suivantes sont toujours à parallélisation restreinte.
Parcours de CTE (Common Table Expressions).
Parcours de tables temporaires.
Parcours de tables externes, sauf si le wrapper de données distantes a
une API IsForeignScanParallelSafe
qui indique le
contraire.
Nœuds du plan pour lesquels un InitPlan
est
attaché.
Nœuds du plan qui référencent un SubPlan
corrélé.
Le planificateur ne peut pas déterminer automatiquement si une fonction ou
un agrégat définis par un utilisateur est à parallélisation sûre,
restreinte ou non sûre, car cela nécessiterait de pouvoir prédire
chaque opération réalisable par la fonction. En général, c'est équivalent au
problème de l'arrêt et de ce fait, impossible.
Même pour des fonctions simples où cela pourrait se faire, nous n'essayons
pas, car ce serait coûteux et sujet à erreurs. À la place, toutes les
fonctions définies par des utilisateurs sont supposées à parallélisation non sûre
sauf indication contraire. Lors de l'utilisation des
instructions CREATE FUNCTION et ALTER FUNCTION, un marquage est possible en spécifiant
PARALLEL SAFE
, PARALLEL RESTRICTED
ou PARALLEL UNSAFE
suivant ce qui est approprié. Lors
de l'utilisation de CREATE AGGREGATE, l'option
PARALLEL
peut être spécifiée comme
SAFE
, RESTRICTED
ou
UNSAFE
.
Les fonctions et agrégats doivent être marqués PARALLEL
UNSAFE
s'ils écrivent dans la base, accèdent à des séquences,
modifient l'état de la transaction même temporairement (par exemple, une
fonction PL/pgSQL qui définit un bloc EXCEPTION
pour
récupérer des erreurs), ou font des modifications persistantes sur les
paramètres. De façon similaire, les fonctions doivent être marquées
PARALLEL RESTRICTED
si elles accèdent à des tables
temporaires, à l'état de connexion du client, à des curseurs, à des
requêtes préparées ou à un quelconque état local du processus serveur que le système
ne peut pas synchroniser entre les différents
workers. Par exemple,
setseed
et random
sont à
parallélisation restreinte pour cette dernière raison.
En général, si une fonction est marquée comme étant sûre alors qu'elle ne
l'est pas, ou si elle est marquée restreinte alors que sa
parallélisation en fait n'est pas sûre, elle
peut être cause d'erreurs ou de réponses fausses lors de
l'utilisation dans une requête parallélisée. Les fonctions en langage C
peuvent en théorie avoir des comportements indéfinis en cas de mauvais
marquage, car le système n'a aucun moyen de se défendre contre du code C
arbitraire. Cela étant dit, dans la plupart des cas, le résultat ne sera pas
pire qu'avec toute autre fonction. En cas de doute, le mieux est probablement
de marquer les fonctions en tant que UNSAFE
.
Si une fonction exécutée avec un worker
parallèle acquiert des verrous non détenus par le
leader, par exemple en exécutant une
requête sur une table non référencée dans la requête, ces verrous seront
relâchés à la sortie du worker, et non pas
à la fin de la transaction. Si vous écrivez une fonction qui fait cela et
que cette différence de comportement a une importance pour vous, marquez ces
fonctions comme PARALLEL RESTRICTED
pour vous assurer
qu'elles ne s'exécutent qu'au sein du
leader.
Notez que le planificateur de requêtes ne cherche pas à différer
l'évaluation des fonctions ou agrégats à parallélisation restreinte
impliqués dans la requête pour obtenir un meilleur plan. Donc, par
exemple, si une clause WHERE
appliquée à une table
particulière est à parallélisation restreinte, le planificateur ne
tentera pas de placer le parcours de cette table dans une portion
parallélisée du plan. Dans certains cas, il serait possible
(voire efficace) d'inclure le parcours de cette table dans la
partie parallélisée de la requête et de différer l'évaluation de la
clause WHERE
afin qu'elle se déroule au-dessus du
nœud Gather
. Néanmoins, le planificateur ne le
fait pas.