Contrôler l’état global dans des UDFs scalaires Scala¶
Lors de la conception d’une UDF et d’un gestionnaire qui nécessite l’accès à un état partagé, vous devrez tenir compte de la façon dont Snowflake exécute des UDFs pour traiter les lignes.
La plupart des gestionnaires devraient suivre ces lignes directrices :
Si vous devez initialiser un état partagé qui ne change pas d’une ligne à l’autre, initialisez-le en dehors de la fonction du gestionnaire, comme dans un constructeur.
Rédigez votre méthode de handler de façon à ce qu’elle soit sécurisée.
Évitez de stocker et de partager l’état dynamique entre les lignes.
Si votre UDF ne peut pas suivre ces directives, ou si vous souhaitez mieux comprendre les raisons de ces directives, veuillez lire les sous-sections suivantes.
Comprendre la parallélisation¶
Pour améliorer les performances, Snowflake effectue la parallélisation à la fois sur et dans des JVMs.
Parallélisme à travers des JVMs¶
Snowflake effectue la parallélisation entre les processus Worker dans un entrepôt. Chaque Worker exécute un (ou plusieurs) JVMs. Cela signifie qu’il n’y a pas d’état partagé global. Au maximum, l’état ne peut être partagé qu’au sein d’un seul JVM.
Parallélisme au sein des JVMs¶
Chaque JVM peut exécuter plusieurs threads qui peuvent appeler la méthode du handler de la même instance en parallèle. Cela signifie que chaque méthode du handler doit être à l’abri des fils.
Si une IMMUTABLE est SQL et qu’une instruction UDF appelle la même UDF plusieurs fois avec les mêmes arguments pour la même ligne, alors l’UDF renvoie la même valeur pour chaque appel pour cette ligne.
Par exemple, la commande suivante renvoie deux fois la même valeur si l’UDF est IMMUTABLE :
SELECT my_scala_udf(42), my_scala_udf(42) FROM table1;Si vous souhaitez que plusieurs appels renvoient des valeurs indépendantes même s’ils reçoivent les mêmes arguments, et si vous ne souhaitez pas déclarer la fonction VOLATILE, liez plusieurs UDFs distinctes à la même méthode de traitement.
Pour ce faire, vous pouvez suivre ces étapes.
Créez un fichier JAR nommé
@udf_libs/rand.jar
avec le code suivant :class MyClass { var x: Double = 0.0 // Constructor def this() = { x = Math.random() } // Handler def myHandler(): Double = x }Créez des UDFs Scala comme indiqué ci-dessous.
Ces UDFs portent des noms différents, mais utilisent le même fichier JAR et le même handler dans ce fichier JAR.
CREATE FUNCTION my_scala_udf_1() RETURNS DOUBLE LANGUAGE SCALA IMPORTS = ('@udf_libs/rand.jar') HANDLER = 'MyClass.myHandler'; CREATE FUNCTION my_scala_udf_2() RETURNS DOUBLE LANGUAGE SCALA IMPORTS = ('@udf_libs/rand.jar') HANDLER = 'MyClass.myHandler';Utilisez le code suivant pour appeler les deux UDFs.
Les UDFs pointent vers le même fichier JAR et le même handler. Ces appels créent deux instances de la même classe. Chaque instance renvoie une valeur indépendante, de sorte que l’exemple ci-dessous renvoie deux valeurs indépendantes, plutôt que de renvoyer deux fois la même valeur :
SELECT my_scala_udf_1(), my_scala_udf_2() FROM table1;
Stockage des informations d’état JVM¶
Une raison d’éviter de s’appuyer sur un état partagé dynamique est que les lignes ne sont pas nécessairement traitées dans un ordre prévisible. Chaque fois qu’une instruction SQL est exécutée, Snowflake peut faire varier le nombre de lots, l’ordre dans lequel les lots sont traités et l’ordre des lignes dans un lot. Si une UDF scalaire est conçue de telle sorte qu’une ligne affecte la valeur de retour d’une ligne suivante, alors l” UDF peut renvoyer des résultats différents chaque fois que l” UDF est exécutée.