Nowe typy extentów w bazach OpenEdge

Extenty są podstawowym elementem obszaru przechowywania (storage area) bazy danych OpenEdge. O ile bazę można podzielić na elementy logiczne jak tabele, indexy itp. to extenty są elementami fizycznymi – po prostu plikami na dysku.
Rozmiar, typ i lokalizację extentów definiuje się za pomocą pliku [nazwa_bazy].st.
Są dwa podstawowe typy extentów:

  • Extent stały (fixed extent)Umożliwia kontrolę nad tym, ile miejsca na dysku przydzielić każdemu extentowi oraz ile extentów użyć. Przestrzeń w takich extentach jest sformatowana podczas tworzenia struktury bazy przez co wydajność zapisu do nich jest lepsza. Administrator musi jednak monitorować stopień wypełnienie danych.
  • Extent zmienny (variable extent)Może przyrastać dynamicznie, stanowiąc taki “wentyl bezpieczeństwa” gdyby extenty stałe się zapełniły. Ułatwia to utrzymanie systemu, ponieważ nie trzeba regularnie monitorować rozmiaru extentów, ale powoduje spadek wydajności, gdyż OpenEdge RDBMS musi w trakcie zapisu do extentu zmiennego stale przydzielać i formatować przestrzeń.

    Co ważne, domyślnie ten typ extentu rośnie, aż osiągnie maksymalny rozmiar pliku dozwolony przez system operacyjny albo zajmie cały dysk lub system plików!
    Dlatego opcjonalnie można podać określony maksymalny rozmiar. Nie oznacza to jednak, że przestrzeń dla tego extentu jest przydzielona i sformatowana z góry (jak w przypadku extentu stałego). Po prostu rośnie on tylko do określonego rozmiaru.

Tak wygląda definicja przykładowych extentów w pliku .st bazy.

# extent stały
d "Cust_Data":9,32;1 . f 640
# extent zmienny z maksymalnym rozmiarem
d "Cust_Data":9,32;1 . v 1280

Wiadomo, że administrator powinien pilnować żeby dane w obszarze nie przekroczyły 80% jego objetości. Gdy sie to stannie należy dodać extenty stałe i oczywiście ostatni zmienny. Istniejący wtedy extent stały zmieni sie na stały ale jego rozmiar może być zupełnie inny od pozostałych zmiennych, często jest on bardzo mały jak extent .d3 na poniższym rysunku.

Nie jest to błędem ale po pewnym czasie możemy mieć wiele takich małych extentów w których będzie bardzo mało danych.
W OpenEdge 12 możemy temu zaradzić ponieważ dodano nowe tokeny do tworzenia i zarządzania zmiennymi extentami.

  • e rozszerza istniejący extent o zmiennej długości.
  • ev rozszerza istniejący extent o zmiennej długości i ustala maksymalny rozmiar.
  • x tworzy nowy, wstępnie rozszerzony extent o zmiennej długości.
  • xv tworzy nowy, wstępnie rozszerzony extent o zmiennej długości i ustala maksymalny rozmiar.

Np. w obszarze Cust_Data mamy dwa extenty, jeden stały i jeden zmienny jak poniżej.

Chcę dodać nowy extent zmienny z preformatowanym rozmiarem 320 kB i maksymalnym rozmiarem 640 kB, a istniejący zmienny zrobić preformatowany do wartości 320 kB. Extent ten będzie zamieniony na extent stały o określonej przez mnie długości. Plik t_add.st wygląda tak:

d "Cust_Data":9,32;1   t_9.d2   e   320  
d "Cust_Data":9,32;1   t_9.d3   xv  320  640

Uruchamiam komendę prostrct add z opcją -validate i jeśli format jest poprawny to ponownie ale już bez opcji -validate.


W pliku struktury t.st widać dodany extent. Extent .d2 ma tę samą długośc co .d1.

d "Cust_Data":9,32;1 C:\WrkOpenEdge128\db\t_9.d1 f 320
d "Cust_Data":9,32;1 C:\WrkOpenEdge128\db\t_9.d2 f 320
d "Cust_Data":9,32;1 C:\WrkOpenEdge128\db\t_9.d3 xv 320 640


A gdy wylistujemy strukturę bazy poleceniem prostrct list [baza] > mylist.st mamy dodatkowe informacje:

...
   Area Name: Cust_Data, Type 6, Block Size 4096, Extents 3, Records/Block 32, Cluster Size 1
   Ext # 1, Type FIXED, Size 320 KByte, Name: ...\t_9.d1
   Ext # 2, Type FIXED, Size 320 KByte, Name: ...\t_9.d2
   Ext # 3, Type PRE-EXTENDED VARIABLE w/MAX SIZE, Size 320 KByte, Name: ...\t_9.d3
   ...

Obie wartości pre-extended i maksimum rozmiaru pliku możemy usunąć poleceniem:
prostrct add [baza] add.st gdzie plik add.st wygląda następująco:

d "Cust_Data":9,32;1   t_9.d3   ev   0  0

Pamiętajmy, że nie ma flagi v, więc nie można usunąć tylko tej drugiej wartości.

Memory Profiler 2

Wracamy do tematu związanego z użyciem Memory Profilera, dostępnego od wersji OE 12.8.9.
W poprzednim artykule pokazałem jak zainstalować i uruchomić to narzędzie.
Startujemy zatem MP, który jest instancją PASOE komendą: oemp startup

Teraz przygotujemy prosty plik konfiguracyjny mpconfig.cfg, zawierący informacje co ile ma odbyć się zrzut pamięci (cadence = 5 sek.) i do którego katalogu. Parametrów może być więcej ale na razie te wystarczą.
mpconfig.cfg

cadence 5
report-dir ./mpfiles

Można w ogóle pominąć te ustawienia i wtedy zrzut będzie odbywać się domyślnie co 10 sek. do aktualnego katalogu roboczego.
W AVM profilowanie pamięci włącza się za pomocą parametru -profileMemory.
Startuję sesję edytora prowin ./db/t -1 -profileMemory mpconfig.cfg
Jako przykład wezmę aplikację wykorzystywaną kiedyś podczas szkolenia z dynamicznych obiektów baz danych. Składa się z dwóch okienek wyswietlających dane dla dowolnych wybranych tabel, pól i zapytania. Uruchamiam aplikację i wybieram różne dane dla dynamicznych obiektów. Powtarzam cały proces jeszcze raz. Kończę sesję – w katalogu ./mpfiles wygenerowały się pliki.

Wchodzę do narzędzia MP podając w przeglądarce adres http://localhost:8880.
Widać od razu następujący komunikat:
Displays all the memory profiler recording files from the watched directories: C:/WrkOpenEdge128/oemp/upload, C:/WrkOpenEdge128/oemp/import
Kopiuję zatem do katalogu .oemp/upload te pliki .oem, których zawartośc chcę wyświetlić.

WYbrany plik najpierw trzeba zaimportować, klikam na przycisk Import, a następnie Review.

Otrzymuję wykres zmian pamięci aplikacji w funkcji czasu. Poniżej znajdują się dane dla wyswietlanej próbki.

Nazwa aplikacji _edit jest stąd, że uruchomiłem najpierw sesję edytora. Gdybym chciał rozróżnić nazwy trzeba uruchomić sesję bezpośrednio z parametrem -p [nazwa procedury.p].

Na zakończenie dodam, że zrzuty pamięci można realizować programowo wstawiając odpowiednie komendy w wybranych miejscach w kodzie. Tę metodę powinno się stosować kiedy podejrzewamy gdzie może być wyciek pamięci. Do tego tematu jeszcze powrócę.

Memory Profiler 1

W OpenEdge od wersji 12.8.9 mamy długo oczekiwane narzędzie Memory Profiler.

Umożliwia ono analizę wykorzystania pamięci przez aplikację w funkcji czasu a także w określonych interwałach, umożliwiając wizualizację wykorzystania pamięci przez kod aplikacji.
Wycieki pamięci występują, gdy aplikacja nie zwalnia niepotrzebnej już pamięci. W konsekwencji wykorzystanie jej przez aplikację rośnie z czasem, co może prowadzić do spadku wydajności, a ostatecznie do wyczerpania zasobów pamięci i awarii aplikacji lub systemu. Wycieki pamięci są szczególnie problematyczne w długo działających aplikacjach lub systemach wymagających wysokiej wydajności i niezawodności działania.
Memory Profiler zapewnia wizualizację wykorzystania pamięci i pomaga programistom ABL w skutecznym wykrywaniu i diagnozowaniu problemów z wykorzystaniem pamięci w ich aplikacjach ABL.

Korzyści płynące z używania narzędzia:

  • Identyfikacja wycieków pamięci — poprzez śledzenie nadmiernego zużycia pamięci i identyfikację wykorzystania pamięci w celu poprawy wydajności aplikacji.
  • Zwiększenie stabilności systemu i aplikacji — śledząc wykorzystanie pamięci, które nie jest zwalniane w działających aplikacjach lub w fazach testowania, zapobiegając w ten sposób potencjalnym awariom i niestabilności systemu w środowiskach produkcyjnych.
  • Debugowanie złożonych problemów — udostępniając statystyki i wizualizację, które upraszczają proces debugowania i przyspieszają rozwiązywanie problemów związanych z pamięcią.
  • Optymalizacja wydajności — identyfikując nieefektywne wykorzystanie pamięci.
  • Zapewnienie stosowania najlepszych praktyk — identyfikując obszary wymagające odpowiednich operacji czyszczenia i zachęcając do stosowania efektywnych praktyk kodowania.

    Z technicznego punktu widzenia to narzędzie jest specjalną instancją PASOE, którą możemy utworzyć korzystając z dostarczonych plików.
    Spakowane pliki te znajdują sie w katalogu [DLC]/servers/redist/oemp-[wersja oe].zip (np. oemp-12.8.9.zip).
    Plik zip kopiujemy do jakiegoś katalogu z pełnymi uprawnieniami i rozpakowujemy, przechodzimy do podkatalogu oemp\bin, a następnie w oknie komend proenv uruchamiamy komendę oemp install.
    Należy pamiętać żeby katalogiem, w którym umieścimy pliki nie byl podkatalog oemp w lokalizacji working directory ponieważ pojawi się wtedy poniższy błąd:

    Jak wybrałem podkatalog c:\oemp i wszystko poszło jak po maśle.

    Proces instalacji jest dość długi, u mnie trwał prawie 5 minut.

    Po zakończonej instalacji mamy nową instancję PASOE oemp, która odwołuje się do bazy danych /db/reportdb. Narzędzie startujemy poleceniem: oemp startup.
    Kiedy instancja jest już uruchomiona, wpisujemy następujący URL w przeglądarce: http://localhost:8880 i dostajemy główny widok Memory Profilera.

    Niedługo napiszę jak korzystać z OE Memory Profilera.

    Promon – analiza danych 3

    Dziś napiszę znowu o promonie odpowiadając przy okazji na kilka Waszych pytań, za które bardzo dziękuję.

    Pierwsze dotyczy strojenia parametru -spin (tylko dla licencji serwera Enterprise).
    Parametr „Spin Lock Retries” (–spin N) służy do określania, ile razy proces próbuje uzyskać zatrzask (latch) do zablokowanego zasobu w pamięci współdzielonej przed “pójściem na drzemkę”. Po wstrzymaniu procesu na określoną liczbę milisekund, proces uruchamia się ponownie i ponownie próbuje uzyskać zatrzask. Proces ten bardzo obciąża procesor dlatego uzywa się go wyłącznie w maszynach wieloprocesorowych, choć w starych systemach jednoprocesorowych zalecano użycie tego parametru z wartością 1, ze względu na to, że i tak był on szybszy niż mechanizm oparty na semaforach. Liczba nieudanych prób uzyskania zatrzasku określana jest w promonie Latch timeouts. Im jest ich więcej tym częściej dochodzi do konfliktów między zatrzaskami i tym gorsza jest wydajność bazy danych.

    Domyślne obliczenie brokera bazy danych to 6.000 x liczba procesorów zgłoszonych przez system operacyjny, co prawdopodobnie było odpowiednie, gdy na serwer przypadało 2 lub 4 rdzenie/procesory, ale teraz, w przypadku maszyn wielordzeniowych z 16 lub więcej rdzeniami, automatycznie obliczona liczba jest prawdopodobnie zbyt wysoka.
    Np. w moim laptopie procesor ma 4 rdzenie i 8 wątków i parametr ten ma domyślną wartość 48.000.
    Miejcie więc na względzie aby sprawdzić tę wartość w promonie:
    R&D -> 4. Administrative Functions 4. Adjust Latch Options


    W tym samym miejscu możemy zmienić tę wartość online (podopcja 1).

    Expert z White Star Software uważa, że wartości z przedziału 5.000 do 20.000 są wystarczające dla zdecydowanej większości baz danych, więc nie ma co szaleć. Należy przede wszystkim monitorować wykorzystanie CPU i w razie gdy osiąga ono duże wartości (90% lub więcej) zmniejszyć -spin online.

    Analizę timeoutów możemy podejrzeć na poniższym ekranie promona – ostatni wiersz.
    R&D -> 3. Other Displays -> 1. Performance Indicators -> Latch timeouts

    Drugim parametrem, o którym chcę tutaj napisać jest -lruskips dostępny także tylko dla serwera Enterprise i ma związek z zatrzaskami przy próbie dostępu do zasobu w puli buforów.
    Łańcuch LRU (Least Recently Used) składa się ze wskaźników do buforów w puli i przy każdym odczycie z tej puli (dla -lruskips = 0) potrzebne jest uzyskanie zatrzasku aby uaktualnić ten łańcuch. Jeśli ustawimy -lruskips = 100 (dobra wartość stosowana obecnie domyślnie) to próba uzyskania zatrzasku LRU będzie co 100-tny dostęp do bufora, co poprawia współbieżność i wydajność.
    Wartość parametru możemy dostosować także online w tym samym miejscu promona co dla -spin (patrz poniższy obrazek). Jeśli mamy dwie pule buforów (-B i -B2) to możemy określić osobno -lruskip i -lruskip2. Mechanizm ten nie jest wykorzystywany w prywatnej puli buforów.

    Jak dobrać długość clustra BI

    W poprzednim wpisie napisałem pod koniec jak z grubsza określić długośc clustra BI. Oczywiście wydłużanie tego clustra ma sens tylko dla licencji serwera bazy Enterprise. W Workgroup nie ma procesów asynchronicznych, a więc duża liczba buforów Dirty nie będzie “rozładowana” przed checkpointemi i cały system znacznie spowolni.
    A zatem, w bazie Enterprise chcemy aby odstęp między checkpointami był np. 4 razy większy niż obecnie, więc wydłużamy 4 razy cluster BI. Operacja jest bardzo prosta i odbywa się na zamkniętej bazie. Jednakże nie możemy kierować się tylko takim podejściem ponieważ szybkość dysków ma swoje granice.

    Do określenia maksymalnego rozmiaru clustra przydatny i powszechnie stosowany jest Furgal Storage Device Test lub po prostu Furgal Test zwany tak od nazwiska autora Mike’a Furgala. Polega on na wykonaniu polecenia BIGROW do pomiaru szybkości dysku poprzez pomiar czasu potrzebnego na utworzenie pliku BI o określonym rozmiarze.

    Po obcięciu pliku BI i wystartowaniu serwera bazy tworzone są domyślnie 4 clustry BI. Jednakże w trakcie normalnej pracy systemu mogą one być niewystarczające i plik BI urośnie do np. 6 clustrów.
    Operacja proutil z opcją bigrow służy do tego aby zwiększyć liczbę clustrów zanim uruchomimy bazę, poprzez wstępne sformatowanie określonej liczby clustrów. Pamiętajmy, że jeśli chcemy mieć liczbę clustrów 6 to podajemy jako parametr 2 (bo 4 są już i tak domyślne). Furgal Test wykazuje silną korelację między czasami wykonania bigrow a wydajnością systemu bazy danych.

    Warto dodać, że mechanizm rozszerzania pliku BI o dodatkowe sformatowane klastry został przeprojektowany i przyśpieszony (buforowany). Wcześniej tylko na Solarisie i AIX, obecnie (od OE 11.7.6 i OE 12.1) jest dostępny na wszystkich platformach w tym Linux i Windows.

    Mamy poniżej dwie komendy.
    proutil [baza] –C truncate bi –bi 16384
    time proutil [baza] –C bigrow 2

    Pierwsza ustawia większy niż domyślny rozmiar clustra bi na 16 MB, a druga mierzy czas sformatowania 6 clustrów (4 + 2).
    Plik BI urośnie zatem do 96 MB. Mamy zatem dwa parametry rozmiar BI i czas sformatowania. Dzielimy je przez siebie i otrzymujemy szybkość dysku w MB na sekundę.
    Nasz maksymalny rozmiar klastra BI powinien być nie większy niż 2-krotność szybkości dysku.
    Zróbmy ten test na testowej bazie na Windows – zamiast polecenia time możemy posłużyć się poniższym skryptem (timer.bat).

    @echo off
    echo %time% < nul
    cmd /c %1
    echo %time% < nul
    

    timer "proutil [baza] –C bigrow 2"

    Czas wykonania komendy to ok. 1 sekunda, czli prędkość wynosi w przybliżeniu 100 MB/sec. Maksymalny cluster zatem to 200 MB. Oczywiście taką komendę warto wykonać kilka razy i wziąć średni pomiar. No i nie ustawiamy od razu ten maksymalny rozmiar clustra ale zwiększamy go stopniowo, obserwując jak działa system (analiza checkpointów w promonie).

    W bazie wiedzy możemy znaleźć informację, że zgodnie z powszechnie akceptowanym od lat punktem odniesienia BIGROW w wielu różnych systemach, zapisanie pliku BI o rozmiarze 100 MB nie powinno zająć dłużej niż 10 sekund. W naszym teście zajęło to tylko 1 sekundę (dysk SSD).

    Promon – analiza danych 2

    Powracając do tematyki związanej z tym najbardziej znanym narzędziem dla administratorów baz, chciałbym napisać dwa słowa o ukrytej opcji, którą, jak się okazuje, nie wszyscy znają.
    Kiedyś była to opcja R&D, niewidoczna z głównego menu ale to było dawno temu.
    Drugie menu było bardziej ukryte. Trzeba było napisać devdbh, Enter i następnie 6.
    Obecnie w nowym promonie ta opcja jest widoczna właśnie na pozycji 6 w meny R&D chociaż możemy wejść do niej także “po staremu”.

    Zawiera zaawansowane opcje dogłębnego monitorowania aktywności i wydajności systemu baz danych OpenEdge.

    Jak widać lista jest całiem długa, ale są to informacje dla zaawansowanych administratorów, lub raczej dla techsupportu PSC. Może się zdarzyć że będziemy poproszeni o przesłanie konkretnych danych z tej listy.

    Powróćmy do danych, które możemy pobrać do własnej analizy. Myślę, że jednymi z najważniejszych są te związane z checkpointami.
    Uruchamiam własny program generujący transakcje w bazie testowej (kopia sports2000) i wchodzę do promona.
    W opcji R&D -> 1. Status Displays -> 9. BI Log sprawdzam parametry zapisu BI na dysku. Blok BI ma 8kB, a cluster tylko 512 kB (domyślna wartość). Zobaczmy jaki to będzie miało efekt w działaniu.

    Przechodzę do głównego menu, a następnie 3. Other Displays -> 4. Checkpoints.
    Popatrzmy na poniższą analizę. Nie wygląda to dobrze…

    Co oznaczają niektóre kolumny (wg dokumentacji):
    Dirty – liczba zmodyfikowanych buforów zaplanowanych do zapisania.
    CPT Q – Liczba buforów zapisanych z kolejki checkpoint przez procesy APW.
    Scan – Liczba buforów zapisanych przez procesy APW podczas cyklu skanowania.
    APW Q – Liczba buforów zapisanych przez kolejkę APW i zastąpionych w łańcuchu LRU przez procesy APW.
    DB Writes – Całkowita liczba buforów bazy danych zapisanych na końcu checkpointu.

    Administratorzy baz chcą aby odstęp między checkpointami (kolumna Len) był rzędu kilku minut, a tu mamy 2 sekundy. Ponadto podczas checkpointu jest nienaturalnie duża ilość zapisów na dysk – kolumna DB Writes (ten parametr w starszych wersjach jest określany jako Flushes). Tak liczne zapisy powodują spadek wydajności.
    Nie uruchomiłem jeszcze procesu APW, więc kolejka APW jest pusta, ale i tak widać od razu, że trzeba koniecznie wydłużyć cluster. Zwiększę go zatem z 0.5 MB do 16 MB. Rozmiar bloku BI zwiększę przy okazji z 8 KB do 16 KB.
    proutil [baza] -C truncate bi -bi 16384 -biblocksize 16

    Ponieważ rozmiar clustra został zwiększony 32 razy to dostęp między checkpointami zwiększy się z 2-3 sekund do ok. 70 sekund. Po odczekaniu zobaczmy jak teraz wygląda analiza checkpointów.

    Zobaczmy, że wartości Dirty nie przekraczają wartości DB Writes, jak poprzednio. Oznacza to, że jest dość czasu podczas checkpointu żeby zapisy zostały dokonane. Jednakże duża liczba tych zapisów może powodować spowolnienie systemu podczas checkpointu. Nie mamy tu na razie procesów asynchronicznych (tylko serwer Enterprise) i kolumna CPT Q pokazuje same zera.

    A teraz co się zmieni po uruchomieniu procesu APW (niebieska ramka). Wszystkie bufory do zapisu są umieszczone w kolejce checkpoint i zapisane przez procesy APW tak, że podczas checkpointu nie ma już żadnych zapisów i system nie będzie spowalniał

    Metoda wydłużenia czasu między checkpointami jest dość prosta i możemy pokusić się o dalsze zwiększenie clustra BI żeby czas wynosił tutaj 2-3 a nawet 5 minut. Pytanie tylko czy nasze dyski są dostatecznie szybkie aby temu podołać. Clustra nie można zwiększać bez końca bez żadnych konsekwencji.

    W następnym wpisie napiszę o prostym teście jak można taką graniczną wartość określić.

    Na koniec jeszcze mała uwaga dotycząca zbierania danych z promona poprzez skrypt. Większość parametrów znajduje się w tablicach VST, zawierających tylko jeden rekord (podawana wartość to różnica wartości z danego pola w czasie) ale czasem tych rekordów jest więcej, jak w przypadku checkpointów. Nie ma dla nich w promonie opcji automatycznego odświeżania. Najłatwiej jest oczywiście napisać program w ABL ale jeśli nie mamy takiej możliwości, to obliczamy po ilu minutach zapełnią się dane wszystkich checkpointów na ekranie (tutaj ok. 15 minut) i ustawiamy uruchomienie skryptu zapisującego tylko ten ekran (np. w cronie) co ten czas.

    Promon – analiza danych

    Administratorzy baz danych stają czasem przed problemem: jak analizować parametry generowane w narzędziu promon w funkcji czasu. Wszystkie te parametry pochodzą z tablic VST i można łatwo napisać odpowiedni program w języku ABL. Problem polega jednak na tym, że administratorzy często nie mają uprawnień do kompilacji.
    Sytuacja nie jest jednak beznadziejna i można sobie z nią poradzić bez większych problemów.

    Promon to narzędzie które można obsługiwać ręcznie, przeglądając określone statystyki lub generować zrzuty ekranowe w sposób automatyczny. Zrzuty takie można potem analizować na różne sposoby. Wystarczy stworzyć wejściowy plik tekstowy, zawierający zestaw znaków sterujących, odpowiadający dokładnie takim samym klawiszom które użylibyśmy przeglądali określone statystyki ręcznie.

    Zanim stworzymy taki plik, musicie wiedzieć, że dla wersji OE12 znajdują się w katalogu [DLC]\bin skrypty dla serwera bazy Enterprise: gather-script-enterprise.bat
    oraz Workgroup: gather-script-wrkgrp.bat.
    Wystarczy uruchomić taki skrypt na naszej bazie i powstanie plik wynikowy zawierający wiele ekranów z menu podstawowego oraz R&D. We wcześniejszych wersjach OE trzeba poszukać skryptu gather. Dokładniejszy opis jest w bazie wiedzy.
    Dla naszych potrzeb stwórzmy prostszy plik sterujący promon_in.txt:

    R&D  # Opcje R&D
    5    # Adjust Monitor Options
    1    # Display page length
    9999 # Enter 9999 for length
    3    # Monitor sampling interval
    30   # Ustaw na 30 sek.
    4    # Pause between displays
    30   # Ustaw na 30 sek.
    6    # Number of auto repeats 
    10   # Ustaw 10 odświeżeń
    t    # Powrót do głównego menu R&D 
    2    # Activity Displays
    1    # Summary
    A    # Tryb automatyczny

    Plik ten zawiera określone wybory menu i ustawienia oraz komentarz. Oczywiście dla wysterowania promona komentarz musimy usunąć. Plik więc będzie wyglądał następująco:

    R&D
    5
    1
    9999
    3
    30
    4
    30
    6
    10
    t
    2
    1
    A

    W tym prostym przykładzie dostaniemy zrzut 10 ekranów z ogólnej aktywności bazy. Każdy ekran wygeneruje się co 30 sekund.
    Uruchamiamy zatem promona na bazie testowej poleceniem:
    promon [baza] < promon_in.txt > promon_out.txt
    Można użyć opcjonalnie parametru -NL, szczególnie do analizy problemów z konfliktami aktywności w pamięci współdzielonej. Tutaj można go pominąć. Nas będzie interesował parametr BI Writes.
    Pominąłem jeden krok, uruchomiłem wcześniej program, który dokonuje losowo transakcji na tablicy Customer (kopia bazy sports2000). Krok ten zostawiam czytelnikom.

    Promon wygeneruje plik wynikowy promon_out.txt, zawierający zrzuty ekranowe. Z uwagi na jego obszerność wyświetlam tylko interesujący nas fragment. Zgodnie z ustawieniami plik zawiera 10 wygenerowanych takich ekranów.

    Event                  Total  Per Sec |Event                  Total  Per Sec
    
    Commits            17245004      99.4 |DB Reads                332       0.0
    Undos               1864747      10.8 |DB Writes            904278       5.2
    Record Reads       38455612     221.7 |BI Reads              61998       0.4
    Record Updates     15614825      90.0 |BI Writes           1380881       8.0
    Record Creates      1630171       9.4 |AI Writes                 0       0.0
    Record Deletes      1630180       9.4 |Checkpoints           20601       0.1
    Record Locks       82489712     475.5 |Flushed at chkpt     883672       5.1
    Record Waits              0       0.0 |Active trans              0
    

    Dla nas jest ważne aby wyekstrahować określone informacje, np. linie zawierające określone dane, jak badany parametr BI Writes.
    W systemie Windows możemy posłużyć się komendą findstr. Poniżej ściąga jak używać tej komendy:
    findstr [szukane słowo] promon_out.txt > wynik.txt
    findstr /C:”[szukana fraza]” promon_out.txt > wynik.txt

    W systemie Linux/Unix możemy posłużyć się komendą grep.
    Uruchamiamy tutaj następująca komendę.
    findstr /C:”BI Writes” promon_out.txt > bi.txt
    Zawartość pliku bi.txt wygląda następująco.

    Record Updates        10859     362.0 |BI Writes               938      31.3
    Record Updates        10879     362.6 |BI Writes              1005      33.5
    Record Updates        10476     349.2 |BI Writes               872      29.1
    Record Updates        10930     364.3 |BI Writes              1005      33.5
    Record Updates        10933     364.4 |BI Writes               938      31.3
    Record Updates        10850     350.0 |BI Writes               938      30.3
    Record Updates        10578     352.6 |BI Writes               939      31.3
    Record Updates        10847     361.6 |BI Writes               938      31.3
    Record Updates        10855     361.8 |BI Writes              1006      33.5
    Record Updates        10828     360.9 |BI Writes               938      31.3
    

    Juz jest nieźle, bo mamy tylko 10 potrzebnych linii. Teraz trzeba zaimportować te dane do arkusza jak Excell czy LibreOffice. Tu posłużyłem się LibreOffice, w którym można wygodnie ustalić miejsca podziału kolumn.

    Niepotrzebne kolumny można usunąć. Potrzebujemy jeszcze osi czasu, ale to prosta sprawa bo odczytujemy tylko czas dla pierwszego odczytu, dodajemy 30 sekund do następnego a pozostałe czasy wygenerują się automatycznie po przeciągnięciu kursorem myszy.

    Na koniec generujemy wykres w celu analizy. Tutaj jest akurat w Excellu.

    Jak widać cały proces nie jest trudny i gdy raz go przejdziemy nie będzie kłopotu aby powtarzać go dla innych danych.
    Następnym razem napiszę o jeszcze kilku analizach w promonie.

    Dynamic Data Masking II

    Kolejnym krokiem jest zdefiniowanie masek dla wybranych pól. Maski te zasłaniają nieautoryzowanym użytkownikom część lub całą zawartość pola. Mamy cztery rodzaje takich masek: domyślna, częściowa (partial), literal, null.

    Wszystkie ustawienia DDM w ABL (tak jak w poprzednim artykule) realizujemy poprzez metody DataAdminService. Zaczynamy od maski domyślnej, w ktorej mamy małe pole do menewru. Pola znakowe są oznaczane jako XXX (liczba X zależy od maskowanych danych), pola decimal 0.00 itd. Maska jest ustawiana poprzez prefiks “D:”. Zaczniemy od pola Balance.

    USING OpenEdge.DataAdmin.DataAdminService FROM PROPATH.
    
    DEFINE VARIABLE service AS DataAdminService NO-UNDO. 
    DEFINE VARIABLE lResult AS LOGICAL NO-UNDO.
    
    service = NEW DataAdminService(LDBNAME("DICTDB")).
    lResult = service:setDDMConfig("Customer","Balance","D:","#DDM_SEE_ContactInfo").
    

    Ustawmy taką samą maskę także na pola znakowe SalesRep i State.

    Żeby ustawienia maskowania były widoczne trzeba aktywować usługę DDM w bazie poleceniem:
    proutil sports2020 -C activateddm.

    Do testowania użyjemy prostej procedury findCust.p.

    // findCust.p
    FIND FIRST customer.
    DISPLAY customer EXCEPT comments WITH 1 COLUMN.
    PAUSE.
    

    Uruchamiamy dwie sesje klienta z bazą sports2020 logując się raz jako Admin i raz jako User (patrz poprzedni artykuł). Poniżej widać porównanie danych dla obu sesji – różnice zaznaczyłem na czerwono.

    Maska literal służy do maskowania danych gdy chcemy zamiast originalnej wartości podać własną. Należy pamiętać, że dla każdego rodzaju maski obowiązuje zasada zgodności typów, i tak tutaj dla pola znakowego City możemy podać własną maskę np. “UKRYTE“, dla pola PostalCode 00000 itd.
    Maska literal jest ustawiana poprzez prefiks “L:”.

    USING OpenEdge.DataAdmin.DataAdminService FROM PROPATH.
    
    DEFINE VARIABLE service AS DataAdminService NO-UNDO. 
    DEFINE VARIABLE lResult AS LOGICAL NO-UNDO.
    
    service = NEW DataAdminService(LDBNAME("DICTDB")).
    lResult = service:setDDMConfig("Customer","City","L:UKRYTE","#DDM_SEE_ContactInfo").
    lResult = service:setDDMConfig("Customer","PostalCode","L:00000","#DDM_SEE_ContactInfo").
    


    Pora przejść do najciekawszej maski, czyli częściowej, dającej najwięcej możliwości, ale stosowanej tylko dla pól znakowych.
    Składnia maski wygląda tak: P:prefix,char[:maxchars][,suffix]

    prefix – określa liczbę niezamaskowanych znaków, które mogą być wyświetlane na początku pola.
    char[:maxchars] – określa pojedynczy znak ASCII, który ma być użyty do ukrycia wartości kolumny, maxchars (wartość opcjonalna) określa maksymalną liczbę znaków do zamaskowania przy użyciu podanego znaku ASCII, reszta ciągu jest przycinana.
    [,suffix] – (wartość opcjonalna) określa liczbę znaków na końcu pola, które mogą być niezamaskowane.

    USING OpenEdge.DataAdmin.DataAdminService FROM PROPATH.
    
    DEFINE VARIABLE service AS DataAdminService NO-UNDO. 
    DEFINE VARIABLE lResult AS LOGICAL NO-UNDO.
    
    service = NEW DataAdminService(LDBNAME("DICTDB")).
    lResult = service:setDDMConfig("Customer","Phone","P:5,*:2,4","#DDM_SEE_ContactInfo").
    

    Testujemy pole Phone. Działa to w następujący sposób: pierwsze 5 znaków jest niezamaskowana, podobnie jak ostatnie 4 znaki, to co w środku jest zamaskowane maksymalnie 2 znakami “*“. Jeśli długość pola będzie miała tylko 10 znaków to będzie wyświetlana tylko jedna gwiazdka itd.

    Ostatnim rodzajem maski jest null, którą można stosować dla każego typu danych. Zamaskowane wartości są wyświetlane jako “?“. Maskę null ustawiamy za pomocą prefiksu N:.

    USING OpenEdge.DataAdmin.DataAdminService FROM PROPATH.
    
    DEFINE VARIABLE service AS DataAdminService NO-UNDO. 
    DEFINE VARIABLE lResult AS LOGICAL NO-UNDO.
    
    service = NEW DataAdminService(LDBNAME("DICTDB")).
    lResult = service:setDDMConfig("Customer","CreditLimit","N:","#DDM_SEE_ContactInfo").
    lResult = service:setDDMConfig("Customer","Terms","N:","#DDM_SEE_ContactInfo").
    


    Jeśli trzeba usunąć maskowanie z pola musimy posłużyć się metodą unsetDDMMask. Usuńmy maskę np. z pola State.

    USING OpenEdge.DataAdmin.DataAdminService FROM PROPATH.
    
    DEFINE VARIABLE service AS DataAdminService NO-UNDO. 
    DEFINE VARIABLE lResult AS LOGICAL NO-UNDO.
    
    service = NEW DataAdminService(LDBNAME("DICTDB")). 
    
    lResult = service:unsetDDMMask("Customer","State").
    


    Przy pomocy metod DataAdminService można manipulować rolami, tagami autoryzacji itp. Możemy podejrzeć np. tag i maskę dla danego pola.

    USING OpenEdge.DataAdmin.*. 
    VAR DataAdminService oDAS. 
    VAR CHARACTER cMask. 
    VAR CHARACTER cAuthTag. 
    
    oDAS = new DataAdminService (ldbname("DICTDB")). 
    oDAS:GetFieldDDMConfig ("Customer","Phone", OUTPUT cMask, OUTPUT cAuthTag).
    MESSAGE cMask SKIP cAuthTag
    VIEW-AS ALERT-BOX. 
    

    Poniżej widać dane dla pola Phone.

    OK, na tym na razie kończę. Do tematu powrócę jeśli będą od Was jakieś pytania.

    Dynamic Data Masking I

    Na początku chciałbym podać nieco wiadomości wprowadzających do Dynamic Data Masking (DDM). Najprościej mówiąc jest to zabezpieczenie danych w bazie na poziomie logicznym, ale definiowane przez administratora.
    Wyobrażmy sobie sytuację gdy deweloper tworzy aplikację, w której wyświetlane są dane wraźliwe jak np. numer telefonu, adres lub nawet pesel czy miesięczne zarobki. Z drugiej strony są osoby w firmie, które powinny te dane widzieć. Możemy to rozwiązać na poziomie pisania aplikacji lub zrobić to globalnie na poziomie bazy. Takie rozwiązanie zmusza każdego użytkownika do zalogowania się w bazie i, w zależności od uprawnień, daje wgląd do wybranych pól w tabelach.
    Administrator DDM (lub administrator zabezpieczeń) może skonfigurować maskę dla pól tabeli, która ukrywa poufne dane w zestawie wyników zapytania. Kontroluje on również uprawnienia dostępu użytkowników do przeglądania niezamaskowanych wartości określonych pól.
    Żeby korzystać z DDM trzeba mieć licencję na dodatek The OpenEdge Advanced Security (Progress OEAS) i serwer bazy danych Enterprise. Minimalna wersja to 12.8.4.

    OK, tworzymy testową bazę, kopię bazy sports2020 i włączamy funkcję DDM.
    proutil sports2020 -C enableddm
    Sprawdzamy czy funkcja ta jest dodana (na razie jeszcze nieaktywna).
    proutil sports2020 -C describe

    Pierwszym krokiem jest dodanie użytkowników do bazy. W dokumentacji online jest to zrealizowane przez program w obiektowym ABL. Ja zrobię to standardowo w narzędziu Data Administration -> Admin -> Security -> Edit User List.
    Dodaję użytkowników: Admin, User. Oczywiście można wykorzystać tutaj konta użytkowników zdefiniowanych nie w bazie ale w zewnętrznych systemach uwierzytelniania.

    Loguję się jako Admin i ustawiam go jako security adminisrtator Admin -> Security -> Security Administrators.

    Warto zablokować dostęp podczas runtime’u dla niezalogowanych użytkowników oraz ustawić sprawdzanie uprawnień kont także podczas runtime’u.Admin -> Database Options

    Aby określić uprawnienia użytkownika do dostępu do zamaskowanych danych, trzeba utworzyć w kodzie ABL rolę dla użytkowników zdefiniowanych w bazie (operacja mapowania użytkownika do roli).
    Poniższy przykładowy kod tworzy rolę myRole, którą wykorzystamy do zdefiniowania uprawnień przyznawanych użytkownikom do demaskowania danych:

    USING OpenEdge.DataAdmin.*.
    
    VAR DataAdminService service. 
    VAR IRole oRole.
    VAR LOGICAL lResult.
    
    service = NEW DataAdminService(LDBNAME("DICTDB")).
    
    oRole = service:NewRole("myRole"). 
    oRole:Description = "Role for DDM Admin".
    oRole:IsDDM = true.
    lResult = service:CreateRole(oRole). 
    
    DELETE OBJECT service.
    

    Następnym krokiem jest utwórzenie tagu autoryzacji i powiązania go z rolą utworzoną w poprzednim kroku. Tag autoryzacji ustanawia połączenie między zdefiniowanymi przez użytkownika rolami DDM a polami tabeli, do których ma zostać zastosowana maska. Nazwa tagu musi zaczynać się od #DDM_SEE_.

    Poniższy kod kojarzy tag autoryzacji #DDM_SEE_ContactInfo z rolą myRole:

    USING OpenEdge.DataAdmin.*.
    
    VAR DataAdminService service.
    VAR IAuthTag oTag.
    VAR LOGICAL lReturn.
    
    service = NEW DataAdminService (LDBNAME("DICTDB")).
    
         oTag = service:NewAuthTag("#DDM_SEE_ContactInfo"). 
         oTag:RoleName = service:GetRole("myRole"):Name.
         oTag:description = "To see contact info".
         lRETURN = service:CreateAuthTag(oTag).
    
    

    No i wreszcie przydzielamy zdefiniowaną rolę użytkownikowi Admin.

    USING OpenEdge.DataAdmin.*.
     
    VAR DataAdminService service.
    VAR IGrantedRole oRole.
    VAR IUser oUser.
    VAR LOGICAL lResult = FALSE.
     
    service = NEW DataAdminService(LDBNAME("DICTDB")).
    oRole = service:NewGrantedRole().
    oRole:Role = service:GetRole("myRole").
    oUser = service:GetUser("Admin").
     
    IF VALID-OBJECT(oRole:Role) AND VALID-OBJECT(oUser) THEN DO:
      oRole:Grantee = oUser:Name.
     
       IF oUser:Domain:Name NE "" THEN
          oRole:Grantee = oRole:Grantee  + "@" + oUser:Domain:Name.
     
        oRole:CanGrant = false. // Cannot grant to others.
        // Granting Role to Admin
       lResult = service:CreateGrantedRole(oRole).
       
    END.
    

    Następnym krokiem jest zdefiniowanie maski dla wybranych pól i przetestowanie przykładów, ale tym zajmiemy się w następnym artykule. Teraz chciałbym pokazać gdzie można szukać informacji o zefiniowanych rolach i tagach.
    Elementy te zostały dodane do metaschematu bazy, a jeśli tak, to są w określonych nowych tablicach VST.
    Po pierwsze możemy zrzucić dane do plików poprzez opcję w Data Administration Admin -> Dump Data and Definitions -> Security Permissions.
    Poniżej widać pierwsze linie z dwóch plików, w których są zdefiniowane przez nas dane.

    _sec-auth-tag.d

    "myRole" "#DDM_SEE_ContactInfo" "Can see contact info"
    ...
    

    _sec-granted-role.d

    "f82629bb-b26d-6b83-d314-c5d870ea3fee" "Admin" "myRole" no "Admin" ""
    ...
    

    Informacja w plikach pochodzi z poniższych ukrytych tablic VST.

    Wykorzystując je możmy sami wygenerować potrzebne informacje jak w poniższym przykładzie.

    FIND FIRST _sec-granted-role NO-LOCK.
       DISPLAY  _grantee _role-name  WITH SIDE-LABELS.
    
    FIND FIRST _sec-auth-tag NO-LOCK.
       DISPLAY  _sec-auth-tag._role-name _auth-tag SKIP 
                _description WITH SIDE-LABELS.
    


    OK, następnym razem weźmiemy na warsztat tworzenie różnych masek i testowanie programów.

    Bezpieczeństwo PASOE Manager

    O bezpieczeństwie PASOE pisałem już niejednokrotnie i jest to związane z różnymi aspektami zabezpieczeń. Tym razem będzie krótko o Tomcat Manager i OpenEdge Manager, o czym zresztą obiecałem w poprzednim artykule.

    Służą one do zarządzania aplikacjami webowymi i wielosesyjnymi agentami i przy braku odpowiedniego zabezpieczenia mogą stać się potencjalnym celem ataków hakerskich.

    Progress zdecydowanie zaleca podjęcie następujących działań w celu zapewnienia bezpieczeństwa instancji PASOE i wdrożonych aplikacji internetowych (ten proces jest dobrze opisany w dokumentacji):

    • Ograniczenie dostępu do adresów URL dla zarządzania zdalnego poprzez Tomcat Manager i OpenEdge Manager.
    • Zmiana domyślnego hasła dla serwera Tomcat (tomcat/tomcat).

    Pierwsze z nich polega na edycji tekstowego pliku: CATALINA_BASE/webapps/[manager]/META-INF/context.xml, a konkretnie elementu: <Valve className=”org.apache.catalina.valves.RemoteAddrValve…”

    Poniżej widać fragment pliku context.xml dla wersji OE 12.8 – element Valve nie był wzięty w komentarz, a więc dostęp zdalny był możliwy tylko dla adresu localhost (127.0.0.1), co widać poniżej (we wcześniejszych wersjach element był zakomentowany i domyślny dostęp był dla wszystkich adresów).

    Dostęp do tego samego managera wpisując domyślny adres routera jest już zabroniony.

    Drugim zabezpieczeniem jest zmiana domyślnego hasła tomcata (i ew. nazwy użytkownika, tomcat:tomcat) w plikach instancji PASOE xml oraz w OpenEdge Managaer/Explorer.

    Zaczynamy od zrobienia kopii plików instancji ../conf/server.xml oraz ../conf/tomcat-users.xml.

    W pliku server.xml wyłączamy domyślną bazę z danymi użytkowników UserDatabase, tzw. Realm.

    <!-- feature:begin:UserDatabase:off
            <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                resourceName="UserDatabase" />
     feature:end:UserDatabase:off -->

    Dodajemy nową bazę, która używa klasy CredentialHandler class, np.:

    <!-- feature:begin:UserDatabase-pbkdf2:on -->
            <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                resourceName="UserDatabase" >
              <CredentialHandler className="org.apache.catalina.realm.SecretKeyCredentialHandler"
                   algorithm="PBKDF2WithHmacSHA512"
                   iterations="10000"
                   saltLength="16"
                   keyLength="256" />
            </Realm>
    <!--     feature:end:UserDatabase-pbkdf2:on -->

    PBKDF jest zaawansowanym i bardzo silnym algorytmem szyfrowania haseł, zgodnym z FIPS. Im więcej podamy iteracji tym lepiej ale i proces szyfrowania jest wolniejszy. Zapisujemy plik server.xml.

    Teraz trzeba wygenerować nowe hasło. W proenv, przechodzimy do katalogu: DLC/servers/pasoe/bin. Przykładowa instrukcja, która uwzględnia parametry szyfrowania z pliku server.xml wygląda następująco:
    digest -a PBKDF2WithHmacSHA512 -i 10000 -s 16 -k 256 -h “org.apache.catalina.realm.SecretKeyCredentialHandler” mycat
    Mycat to oczywiście moje nowe hasło w miejsce tomcat.

    Komenda zwraca zaszyfrowaną wartość hasła, którą oczywiście musimy skopiować i umieścić w pliku CATALINA_BASE/conf/tomcat-users.xml.
    Warto przy okazji zrobić przegląd kont użytkowników i zostawić tylko niezbędne. Nazwę admina też można zmienić. Ja zmieniam teraz tylko hasło.

    Startuję moją instancję testową i widzę, że OE Manager nie jest dostępny. Tak samo będzie jeśli będę chciał dostać się do managera: localhost:8833.

    Widoki analizy sesji i żądań są wyłączone.

    Podobnie metryki dla aplikacji.

    Wchodzę do konfiguracji i w polach Tomcat manager password oraz OpenEdge manager password wpisuję moje nowe hasło jako zwykły tekst: mycat.
    Trzeba chwilę odczekać aż zmiany będą widoczne, a następnie nacisnąć przycisk Test Connection.

    Połączenie powinno się teraz udać.

    A widoki są dostępne

    Nowe hasła są zapisane w pliku %DLC%\properties\pasmgr.properties

    Nowe hasło musimy podać także jeśli chcemy wejść z poziomu managera tomcata i przeglądać dane np. przy użyciu Swaggera.

    1 2 3 5