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.