AzureDataFactory_VariablesInALoop_00

Używanie zmiennych w pętli w Azure Data Factory – dlaczego nie warto

W ostatnim czasie miałem możliwość powiedzenia kilku słów na m.in. na temat użycia zmiennych w Azure Data Factory podczas kolejnego spotkania Data Community. Patrząc na odzew po sesji, który otrzymałem wiele osób było zaskoczonych faktem, że zmienne wewnątrz pętli wykonywanej równolegle zachowują się właśnie w taki sposób. Z tego też powodu postanowiłem napisać kilka słów na ten temat – zapraszam.

O tym, że konstrukcje takie jak pętle są podstawowym i niezbędnym elementem programowania raczej przedstawiać nie muszę. Nie inaczej jest w przypadku ADF gdzie pełnią one standardową rolę związaną z przechowywaniem wartości pobranych lub wyliczonych w określonym miejscu przepływu. Schemat działania Data Factory jest raczej znany i w wielu miejscach sprowadza się do zagnieżdżania określonych wywołań itp. Z tym podejściem wiąże się jedna pułapka związana właśnie ze zmiennymi ponieważ mają pętlę w swoim pipeline i używając wewnątrz zmiennych narażamy się na nieprawidłowe rezultaty. Tutaj może pojawić się pytanie dlaczego, a odpowiedź jest bardzo prosta – ponieważ zmienne zawsze mają scope/zasięg całego pipeline’a, a nie pojedynczej iteracji w pętli.

Sprawdźmy to na przykładzie – na poniższym obrazku widzimy logiczne wykonanie pętli. Będziemy iterować po tablicy zawierającej trzy owoce tj. Apples, Bananas, Oranges. Wewnątrz pętli odczytujemy wartość aktualnie przetwarzanego owoca i zapisujemy do zmiennej w “Set Variable” następnie tą zmienną przekazujemy do kolejnego pipeline’a:

 

Charakterystyka pętli ForEach w ADF jest taka, że poszczególne wywołania mogą być sekwencyjne czyli odpalane jeden po drugim, ale jest też możliwosć odpalenia ich równolegle co też zostało ukazane na powyższym obrazku. Co zatem tutaj jest nie tak? A no to, że jak wspomniałem wcześniej wartości zmiennych mają zasięg całego pipeline, a nie pojedynczego wywołania pętli. Dlatego też powyższa grafika mimo, że intuicyjnie wydaje się prawidłowa to w rzeczywistości działa to całkowicie inaczej.

Prawidłowa grafika przedstawiająca wykonanie wygląda następująco:

Poszczególne iteracje są oczywiście wywoływane równolegle ale “klocki” wewnątrz bardzo często nie są wywoływane idealnie w tym samym momencie. Ze względu na fakt, iż zmienna jest jedna to każda iteracja przypisuje wartość tej samej zmiennej i tak na początku zmienna ma wartość “Apples” dopóki kolejna iteracja nie nadpisze to wartością “Oranges” i jeszcze kolejna wartością “Bananas”. Ostatecznie do wywołań kolejnego pipeline’a przekazana w każdej iteracji będzie wartość “Bananas” bo akurat na moment wywołania tego elementu zmienna miała taką wartość. Ciekawe prawda? Przejdźmy do portalu żeby zobaczyć czy ADF rzeczywiście w taki sposób się zachowuje.

Mamy zatem dwie zmiennej __varListOfFruits zawiera listę owoców po której będziemy iterować oraz __varResultFruit to zmienna do której będziemy przypisywać owoc z aktualnej iteracji – zmienna ta będzie też przekazywana do kolejnego pipeline’a:

Następnie mamy pętle która iteruje po __varListOfFruits:

Zwróćcie proszę uwagę na przełącznik Sequential który domyślnie jest wyłączony, a który ma wpływ na nasz wynik. Jaki ma wpływ? A no taki, że po znaznaczeniu tej opcji po prostu nic nie będzie zrównoleglane. Wewnątrz pętli mamy trzy taski:

Set variable przypisuje aktualną wartość pętli do zmiennej __varResultFruit:

Wait ma za zadanie wstrzymać na kilka sekund przetwarzanie żebyśmy mogli zasymulować rzeczywiste wykonanie:

Zmienna, a raczej jej wartość jest przekazywana do następnego pipeline’a:

Po uruchomieniu pipeline’a w trybie Debug dostajemy następujące wykonanie:

To co powinno zwrócić naszą uwagę to fakt, że poszczególne wywołania wykonały się idealnie jeden po drugim – wskazówkę co się stało może nam dać notyfikacja od ADF:

Co to oznacza? A no to, że Debug sam w sobie uruchamia wszystko sekwencyjnie więc pozornie wszystko może działać zgodnie z oczekiwaniami. Ciekawie się zrobić gdy uruchomimy przepływ zwyczajnie poprzez trigger:

Widzimy, że sekwencja kroków jest zaburzona i poszczególne elementy są uruchamiane równolegle. Jak zobaczymy jakie wartości zmiennej są przekazywane do wywoływanego pipeline’u to troszeczkę się zdziwimy bo widzimy, że trzykrotnie przekazana została wartość “Apples” co nie do końca było tym czegobyśmy oczekiwali:

Mamy zatem z tego wszystkiego kilka punktów do zapamiętania:

  • Zmienne mają zasięg całego pipeline, a nie iteracji pętli,
  • zmienne nie powinny być używane wewnątrz pętli wykonywanych równolegle,
  • zmienne są w porządku gdy używamy ich poza pętlami lub w pętlach równoległych,
  • Uruchamianie w trybie Debug zawsze działa sekwencyjnie,
  • Przekazywanie wartości do np. innego pipeline możemy wykonać bezpośrednio bez udziału zmiennych.

Leave a Reply