ADF_11Mistakes_00

11 rzeczy których należy unikać przy pracy z Azure Data Factory

Azure Data Factory jest jednym z najczęściej wykorzystywanych serwisów związanych z danymi wewnątrz chmury Azure. Mimo swej popularności nadal istnieją elementy związane z ADF, które albo nie są w stu procentach zrozumiałe albo po prostu są wykorzystywane intuicyjnie na podstawie doświadczeń z innych narzędzi co nie zawsze skutkuje najlepszymi efektami. W ramach niniejszego artykułu postanowiłem opisać 11 rzeczy na które trzeba zwracać szczególną uwagę. Lista powstała na podstawie moich własnych doświadczeń związanych z audytowaniem różnych wdrożeń narzędzia – życzę udanej lektury!

Data Integration Unit

Pierwszą rzeczą o jakiej chciałbym wspomnieć jest  DIU. Jak zapewne wiecie Data Integration Unit jest abstrakcyjną jednostką determinującą “poziom wydajności” Copy Activity składającą się z przydzielonych zasobów CPU, pamięci i przepustowości sieci. Najprościej rzecz ujmując im więcej DIU tym z większą wydajnością powinna działać nasza aktywność – tak przynajmniej jest w teorii.

Kilka faktów na które trzeba zwracać uwagę:

  • DIU odnosi się tylko i wyłącznie do Azure Hosted Integration Runtime! Jeśli używamy Self-Hosted to wydajność jest determinowana przez m.in. maszynę na której ten IR jest postawiony.
  • Wydajność procesu nie zależy tylko od DIU ale też od tego jak szybko możemy pobierać dane z systemu źródłowego i z jaką prędkością może je przyjmować miejsce docelowe,
  • Domyślna wydajność ustawiona jest na AUTO co jest równe 4 DIU.

DIU to nie jedyna opcja wydajności możemy jeszcze dodatkowo ustawić ile równoległych połączeń może nasze Copy Activity nawiązać zarówno do źródła jak i do miejsca docelowego i dopiero kombinacja tych wszystkich ustawień daje nam określony poziom wydajności. Nie ma tutaj złotych rad jak powinniśmy to wszystko poustawiać bo to zależy od konkretnego przypadku ale zawsze należy posługiwać się zdrowym rozsądkiem. Widziałem już ładowania z ADF, które kompletnie zdusiły docelowego Azure SQL Database poprzez maksymalne równoległe wstawianie danych. Jedyne co moge polecić to lekturę kilku wskazówek, które znadują się w dokumetnacji: https://docs.microsoft.com/en-us/azure/data-factory/copy-activity-performance#copy-performance-optimization-features

Brak kontroli i esytmacji kosztów

Tworzenie kosztorysu w ADF nie jest zadaniem trywialnym i zależy od wielu czynników jak m.in:

  • użyte transformacje oraz ich ustawienia,
  • użyty Integration Runtime,
  • to czy ADF jest wewnątrz VNET,
  • regionu ADF.

Powyższe punkty to tylko część zmiennych wpływających na całkowity koszt. Z moich obserwacji wynika, że wielu deweloperów podchodzi do tego tematu na zasadzie “ustawień domyślnych” co nie do końca jest dobrym podejściem. Aby nieco rozjaśnić sytuację przedstawię tylko dwa wyliczenia co do kosztu:

  • Orkiestracja: 1$ za 1000 uruchomień orkiestrujących

W przypadku samych aktywności wewnątrz pipeline:

  • Data movement activities = $0.25 per DIU / hour
  • Pipeline activities: $0.005 / hour
  • External activties: $0.00025 / hour

Czyli Copy Activity kosztuje nas standardowo 0.25$ za DIU za godzinę. Weźmy taki przykład, że do skopiowane jest 40 tabel z systemu źródłowego gdzie do każdego kopiowania angażowane jest automatycznie 4DIU i pojedyncze przeładowanie trwa około 2,5 minuty. Ile za to zapłacimy? Pomijając wszelkie dodatkowe koszty związane z dodatkowymi aktywnościami to każde ładowanie jest zaokrąglane w górę do minuty mamy zatem 40 obiektów *  0.25$ * 4 DIU mamy zatem 40 * 1$ za godzinę wykonania. My wszystkie obiekty przeprocesowaliśmy w 3 minuty więc mamy 40 *3 czyli 120minut = 2h. 120 minut daje nam 2$. Stosunkowo mało jednakże jeśli mamy takie ładownanie powtórzyć 10 razy dziennie x 20 dni w miesiący mamy 400$ przy wszystkich powyższych założeniach.

Szczególna sytuacja jest wtedy gdy musimy wykonać mnóstwo odwołań do źródła np. do REST API z paginacją gdzie ilość requestów bez problemu może iść w tysiące. W tego typu przypadkach polecam zastanowić się nad wykonywaniem tego typu ładowań np. w Azure Functions, a orkiestrować cały proces w ADF.

Ładowanie plik po pliku

Częstym wzorcem ładowania jest użycie pętli FOREACH i COPY Activity wraz z Lookupem lub Get Metadata (sam taki proces opisałem tutaj). Czy w takim podejściu jest coś złego? Oczywiście, że nie ale są przypadki gdzie warto ładować pliki używając wildcard czyli * dzięki czemu pojedyncze COPY Activity jest w stanie zgarnąć wszystkie pliki w danej lokalizacji. Sam jestem zdania, że lepiej załadować więcej danych niż bezsensu wykonywać setki Copy activity co będzie w wielu przypadkach nie wydajne i patrząc na poprzedni punkt bez sensu nabije nam koszty.

Nadmierne używanie SQL Auth i Storage Access Key

To, że do poszczególnych usług jesteśmy w stanie uwierzytelnić się na kilka różnych sposobów to rzecz oczywista. W przypadku Azure SQL mamy tradycyjne SQL Auth oraz uwierzytelnienie oparte o Azure AAD. To drugie podejście powinno być traktowane jako domyślne i to na nim powinniśmy opierać nasze procesy. SQL Auth powinien być wykorzystywany tylko i wyłącznie w szczególnych przypadkach. Dlaczego? A no dlatego, że AAD zawsze będzie usługą pozwalającą zarządzać tożsamościami w sposób kompleksowy z mnóstwem opcji bezpieczeństwa jak MFA, monitoringiem itp., a SQL Auth całkowice to pomija co z całą pewnością w większości przypadków jest czymś niekorzystnym. Na szczęście od jakiegoś czasu możemy całkowicie wyłączyć SQL Auth więc jest nad tym pewna kontrola (link) 🙂

Podobnie rzecz ma się ze Storage Account gdzie nie wiem dlaczego, ale często widzę użycie Access Key. Po pierwsze taki klucz powinien być rotowany więc musimy zadbać o to aby ADF zawsze miał dostęp do aktualnej jego wartości (pomijam sytuacje gdzie nikt klucza nie rotuje bo to już skrajność), a po drugie taki klucz daje nam pełny dostęp do danych na takim storage co z całą pewnością nie jest tym czego byśmy oczekiwali. Pomijam już, że wiąże się to również z tym, że omijamy AAD podobnie jak w przypadku SQL Auth. Oczywiście istnieje sporo scenariuszy gdzie musimy użyć czegoś innego niż AAD ale nadal w wielu przypadkach wcale nie musimy, Podobnie jak wyżej tutaj również możemy wyłączyć uwierzytelnienie po kluczu (link).

Przechowywanie sekretów wewnątrz ADF

Sekrety są przechowywane w ADF w formie zaszyfrowanej ale mimo wszystko nie jest to najlepsze wyjście. Przez takie podejście musimy się martwić w jaki sposób sekrety przekazywać przy deploymencie np. z poziomu Azure DevOps itp. Czy jest to możliwe? Oczywiście, że tak ale bezpośrednia integracja z Azure Key Vault powstała właśnie po to aby takich problemów nie rozwiązywać na około. W moim odczuciu w 99% przypadków instancja ADF powinna iść w parze z instancją Azure Key Vault. Koniec kropka. Oba narzędzia bardzo fajnie się ze sobą integrują i taka konfiguracja nikomu nie powinna sprawić problemu – zachęcam do zapoznania się z artykułem który napisałem na ten temat link.

Domyślny timeout

Poszczególne zadania mają swój timeout jednakże wartość domyślna jest dosyć niepokojąca:

Czy napewno chcemy, źeby nasze aktywności miały timeout na poziomie 7 dni? Nie wydaje mi się. Warto zatem zmienić tę wartość na coś zdecydowanie sensowniejszego aby pewnego dnia się nie zdziwinić. Niestety właściwość ta nie może być parametryzowana, więc musimy ustawić ją ręcznie per zadanie.

Domyślne ustawienia zrównoleglania

Niektóre aktywności takie jak np. pętla Foreach posiadają opcje zrównoleglania. Sterują one tym jak wiele równoległych uruchomień może takowa pętla uruchomić w tym samym czasie. Domyślnie ustawienie to nie posiada żadnej wartości co oznacza, że ADF zrównolegli wykonanie maksymalnie. Czy to dobrze? W niektórych warunkach pewnie tak ale zdarza się, że jest to poważny problem. Wyobraźmy sobie sytuację gdzie pobieramy z bazy źródłowej kilkadziesiąt tabel – czy aby napewno chcemy pociągnąć je wszystkie na raz obciążając w ten sposób system źródłowy. Podobnie ma się sytuacja w drugą stronę gdzie zapis do miejsca docelowego może zajmować znaczną część czasu przez sam fakt, że procesów równolegle wstawiających dane jest zbyt dużo.

Ustawiania zmiennych wewnątrz pętli

Złota zasada: Nie ustawiamy zmiennych wewnątrz pętli (Set Variable) chyba że jest to pętla sekwencyjna lub dopisujemy wartość do zmiennej tablicowej (Append). Dlaczego? Zmienne mają zasięg globalny w ramach pipeline’a a nie pojedynczego pojedynczego wywołania pętli przez co przypisanie wartości do zmiennej wewnątrz pętli rzutuje na pozostałe “inkrementy” pętli.

BULK INSERT do Synapse

Troszeczkę z innej beczki bo jest to coś na pograniczu Synapse Dedicated SQL Pool oraz Data Factory. Jak wiecie SDSP jest technologią MPP składającą się z control node’a oraz worker nodeów. Standardowe podejście do ładowania bazy relacyjnej SQL Server mówiło o tym, że jednym z najszybszych sposobów na jej załadowanie jest użycie interfejsu BULK. Otóż w przypadku Synapse nie jest to już dobra praktyka, a wręcz odwrotnie. BULK Insert powoduje wrzucenie danych do Control Node który następnie rozdysponuje je między workery, co zatem mamy w alternatywie? Mechanizm Polybase oraz polecenie COPY, które wrzuca dane w optymalny sposób bezpośrednio do workerów. Z całą pewnością do zapamiętania:

 

Nadmierne używanie aktywności DELETE i COPY do zarządzania plikami na Data Lake

O tym, że plikami na Data Lake należy odpowiednio zarządzać wiemy nie od dziś. Zdarza się jednak, że owe zarządzanie odbywa się w nieodpowiednim miejscu. Osobiście widziałem implementacje gdzie pliki były przenoszone z jednego miejsca w drugie już dzień po tym jak się pojawiły lub były one usuwane po kilku dniach od pojawienia. Czy coś złego jest w takim podejściu? Oczywiście, że nie! Tylko przenoszenie pojedynczych plików czy chociażby ich kopiowanie z A do B w Data Factory jest często zbędnym kosztem. Pamiętajmy, że mamy coś takiego jak Lifecycle Management (link) czyli mechanizm który robi to automatycznie za nas. Zdecydowanie polecam wykorzystywać to co jest zamiast wymyślać koło od nowa!

Limity

Znajomość limitów danej technologii jest niezwykle istotna aby poprawnie zaplanować wdrożenie. Nie inaczej jest z ADF gdyż posiada on wiele różnych ograniczeń, które w szerszej perspektywie mogą wpłynąć na produkt końcowy. Zastanawiacie się jakie to ograniczenia? Oto kilka z nich:

  • max 40 aktywności w pojedynczym pipeline – dosyć istotne ograniczenia, łatwo go obejść implementując podejście parent – child czyli zestaw pipeline’ów wywołujących kolejne pipeline’y.
  • max 5000 obiektów w pojedynczej instancji ADF,
  • max 20 równoległych wykonań w pętli foreach,
  • 8,192 znaków w pojedynczym wyrażeniu.

To tylko kilka wybranych ograniczeń na które należy zwrócić uwagę. Część z nich da się zwiększyć poprzez support ale i tak warto planować rozwiązanie tak aby się w tych limitach zmieścić.

Czy to jedyne antywzorce związane z ADF? Oczywiście jest ich zdecydowanie więcej jednakże na ten moment postanowiłem wybrać te konkretne. Być może przygotuję kolejne zestawienie, na ten moment dziękuję i polecam polubić bloga na FACEBOOK.

Leave a Reply