Frak na miarę wyroczni (część 1)

Współpraca Tuxedo i Oracle przy pomocy XA

Marcin Jackowski

Transakcje

Pojęcie transakcji jest dla dziedziny baz danych jednym
z podstawowych i nieodzownych przy obsłudze wielu współbieżnych odwołań.
Wewnętrzne mechanizmy pojedynczego systemu zarządzania bazą danych (oparte na
różnych odmianach blokowania i kopiowana danych) zapewniają sekwencjom poleceń
wydawanych z różnych sesji spełnienie czterech zasad powszechnie znanych jako
ACID:

  • niepodzielności (atomicity) –
    transakcja wykonuje się
    w całości lub wcale, w razie awarii przywraca się stan wyjściowy;
  • spójności (consistency) – stan zasobu
    po wykonaniu nie może naruszać więzów integralności, choć w trakcie
    dopuszcza się niespójności;
  • odrębności (isolation) – transakcje
    nie wpływają na siebie, nawet jeśli odwołują się do tych samych zasobów;
  • trwałości (durability) – rezultaty
    zostają zapisane i dostatecznie zabezpieczone przed utratą.

O transakcjach rozproszonych mówimy wówczas, gdy wymaganiom
tym ma sprostać więcej uczestników posiadających własne autonomiczne
zasoby, najczęściej umieszczone na różnych komputerach. Można powiedzieć,
że ponad nie wiedzącymi o sobie transakcjami lokalnymi, działa koordynująca
je nadbudowa, dzięki której może z nich powstać jedna metatransakcja. Składowe
takiej globalnej transakcji określa się często mianem jej odgałęzień (ang.
branch), zaś systemy tego typu skrótem DTP (Distributed Transaction
Processing
).

Model DTP X/Open
i protokół XA

Z oczywistych przyczyn działanie mechanizmów transakcji
rozproszonych powinno podlegać regułom możliwie prostym i niezależnym od
liczby i typów uczestniczących zasobów. Tylko wtedy system DTP ma szanse być
rzeczywiście otwarty, skalowalny i podatny na zmiany tak jakościowe, jak i ilościowe.
Można to osiągnąć drogą centralizacji i jednolitych zasad kierowania
procesem. Od każdego uczestnika wymaga się więc zdolności do współdziałania
przy inicjowaniu, wycofywaniu i zatwierdzaniu transakcji przez nadrzędnego
koordynatora. Rolę takiego nadzorcy pełni zazwyczaj monitor transakcyjny (TM
transaction monitor, transaction manager), np. Tuxedo, a
ustandaryzowany interfejs XA pozwala na jego komunikację z każdym zarządcą
zasobów (ang. resource manager – RM) na tych samych zasadach, niezależnie
od tego, ile zasobów obejmuje transakcja i czy są nimi Oracle, Informix czy
system kolejkowy w rodzaju MQSeries.

Diagram widoczny na Rys. 1 ilustruje model DTP X/Open, na
razie bez wnikania w szczegóły jego składników, które będziemy odsłaniać
stopniowo. Widać na nim, że model opiera się na oddzieleniu właściwej pracy
aplikacji z zasobem jako takim (np. poleceń DML) od komend wyznaczających
granice transakcji oraz od podłączania i odłączania zasobu określanych skrótem
TX. Pierwszą obsługuje zarządca zasobu we właściwy sobie sposób (w
przypadku Oracle np. OCI lub Pro*C), na drugie zaś reaguje monitor transakcji.
XA jest uzupełnieniem tego trójkąta pomiędzy RM i TM i – jak wiele
dedykowanych protokołów – działa
w sposób zupełnie niewidoczny dla programisty. Producent zasobu musi, rzecz
jasna, przystosować go do współpracy z XA, a w szczególności dostarczyć
odpowiednie biblioteki, których zastosowanie omówimy w dalszej części artykułu.


Rys. 1.
Ogólna ilustracja modelu DTP X/Open

Interfejs XA jako standard organizujący przetwarzanie
transakcyjne dla systemów unixowych, obowiązuje od 15 lat, po skodyfikowaniu
przez organizację X/Open tych zasad, na których dużo wcześniej oparto działanie
Tuxedo. W przeciwieństwie do niektórych innych monitorów transakcji, odwołania
do bazy danych odbywają się w serwerach Tuxedo identycznie jak bez udziału
monitora, co jest bez porównania bardziej wygodne i elastyczne niż pełne pośrednictwo.

Zatwierdzanie dwufazowe

Jeśli w jednym z odgałęzień transakcji rozproszonej wystąpią
przeszkody uniemożliwiające zatwierdzenie, to (niezależnie od ich natury)
trzeba odwrócić skutki we wszystkich pozostałych zasobach. Zarządca nie może
więc rozesłać pojedynczego polecenia commit do wszystkich uczestników
i z tą chwilą uznać sprawy za zakończoną. Nie tylko każdy uczestnik, ale i
sam mechanizm komunikacji musi posiadać zabezpieczania przed awariami. W Tuxedo
rolę tę pełni protokół zatwierdzania w dwóch fazach (w skrócie 2PC od
ang. two-phase commit). W pierwszej nadzorca wysyła do wszystkich odgałęzień
polecenie przygotowania do zatwierdzenia (ang. prepare to commit lub krótko
prepare) i oczekuje na sygnały o jego wykonaniu. Dany zasób odpowiada
pozytywnie, jeśli we własnym zakresie dokona zabezpieczenia wyników
transakcji przed ich utratą (nie jest to jeszcze finalne zatwierdzenie, ale np.
zapisanie danych na dysku, w REDOLOGu itp.). Jeśli
z któregoś odgałęzienia nie przyjdzie w określonym czasie potwierdzenie
wykonania I fazy lub przyjdzie odpowiedź odmowna, to zarządca zleca pozostałym
wycofanie. Kiedy uda mu się zebrać potwierdzenia od wszystkich, to następuje
II faza – właściwego utrwalania transakcji. Interfejs XA obejmuje
kilkanaście funkcji, z których część służy właśnie powyższej
interakcji między zarządcą a zasobami.

Oczywiście w czasie II fazy 2PC też mogą się zdarzyć
awarie zasobów, jednak każdy z nich jest odpowiednio przygotowany do ich
naprawienia (np. baza danych dysponuje kopiami niezbędnymi do przywrócenia
takiego stanu, jakiego wymaga spójność transakcji, a także mechanizmami
poawaryjnego odtwarzania). Sceptycy mogą powiedzieć, że wystarczy poważniejszy
defekt w rodzaju zniszczenia nośnika, aby zniweczyć rezultaty takich działań.
Jest to prawda, żaden protokół nie uodporni systemu na nieoczekiwany koniec
świata. Choć znalezienie optymalnego punktu w wymiarach kosztów zabezpieczeń
i następstw awarii jest zawsze kwestią pewnego kompromisu opartego na
prawdopodobieństwie, to jednak nie przekreśla to realnej skuteczności
zabezpieczeń.

Zastosowania i możliwości Tuxedo

Wspomniana już najistotniejsza rola koordynatora transakcji
rozproszonych nie wyczerpuje zastosowań i własności Tuxedo. Nie wyczerpie ich
także ten krótki tekst,
w którym głównego bohatera pokażemy z założenia dość wybiórczo, raczej
na zasadzie zachęcenia czytelnika do dalszych poszukiwań i eksperymentów.
Najistotniejsze cechy Tuxedo trzeba jednak wymienić przynajmniej hasłowo,
zaliczają się do nich:

  1. Różnorodność oferowanych modeli działania aplikacji,
    wśród nich
    a. zleceniowy synchroniczny (proces wywołujący serwis
    wstrzymuje działanie do czasu otrzymania wyniku);
    b. zleceniowy asynchroniczny (proces wywołujący serwis
    nie zaprzestaje przetwarzania i ma możliwość sprawdzania, czy nadeszła
    odpowiedź od serwisu);
    c. konwersacyjny (proces inicjatora nawiązuje połączenie
    z serwerem, następnie oba porozumiewają się przechodząc na przemian w
    tryb nadawania i nasłuchu);
    d. powiadomienia i zdarzenia (mechanizmy wysyłania i
    odbierania wiadomości zarówno do wskazanych, jak i do wszystkich aktualnie
    podłączonych klientów);
    e. kolejki (mechanizmy wstawiania i pobierania komunikatów
    z kolejek; kolejki LIFO i FIFO działają w sposób trwały i uczestnicząc
    w transakcji podlegają w pełni jej zasadom).
  2. Możliwość kierowania wywołań serwisów zależnie od
    danych (ang. data-dependent routing), np. do instancji serwera na
    maszynie właściwego oddziału banku, zależnie od podanego numeru.
  3. Dostosowywanie liczby uruchomionych procesów do
    rzeczywistego zapotrzebowania na ich usługi (serwer może mieć przypisaną
    minimalną i maksymalną liczbę instancji, a system uruchamia i zamyka je zależnie
    od aktualnej liczby oczekujących wywołań).
  4. Równomierne dzielenie obciążenia między serwery (load
    ballancing
    ) i odciążenie niektórych zasobów na zasadzie dzielenia połączenia
    z nimi między wielu odbiorców.
  5. Odporność na awarie (fault tollerance) dzięki
    takim środkom jak definiowanie alternatywnych serwerów i dróg sieciowych zastępujących
    te, które uległy awarii, automatyczne startowanie nowych instancji z uwzględnieniem
    parametrów czasowych itp.
  6. Możliwość tworzenia aplikacji rozproszonych
    wielomaszynowych i stowarzyszonych (tzw. domen czyli autonomicznych aplikacji,
    które współpracują w pewnym zakresie).
  7. Kilka metod kontroli dostępu do aplikacji i poszczególnych
    serwisów o różnym stopniu restrykcyjności (hasło aplikacji, autoryzacja
    kont, listy dostępowe ACL).

Z punktu widzenia programisty można traktować Tuxedo jako
nakładkę na system operacyjny rozszerzającą jego możliwości o obsługę
rozproszonych transakcji i inne wspomniane wyżej cechy. Program korzystający z
nich ma do dyspozycji kilkadziesiąt funkcji interfejsu API Tuxedo określanego
skrótem ATMI od Application-to-Transaction Manager Interface. Nazwy większości
z nich rozpoczynają się ciągiem tp.

W systemie typu UNIX, Tuxedo realizuje swoje zadania przy
pomocy mechanizmów komunikacji międzyprocesowej IPC (segmentów pamięci
dzielonej, semaforów
i kolejek). Można je podglądać poleceniem ipcs, a w ostateczności
likwidować komendą ipcrm, co niekiedy bywa konieczne w sytuacjach
poawaryjnych. Przenosząc Tuxedo na platformę Windows jego autorzy musieli
stworzyć takie mechanizmy razem z poleceniami ipcs i ipcrm.

Reprezentatywnym przykładem zastosowania Tuxedo może być
System NCTS wspierający obieg dokumentów celnych w obrębie Unii Europejskiej.
Podobnie zbudowane aplikacje działają w państwach członkowskich i komunikują
się wzajemnie jako domeny Tuxedo.

Geneza Tuxedo

Przybliżając czytelnikom Tuxedo nie można też pominąć
paru zdań tła historycznego.

Tuxedo wywodzi się z Laboratoriów Bell’a, gdzie w
1983 roku rozpoczęto prace nad rozszerzeniem systemu UNIX o mechanizmy
transakcyjne (projekt znany jako TUX),
a także nad realizacją unixowej bazy danych (DUX). Początkowo firma AT&T
wykorzystywała ich wyniki tylko do celów wewnętrznych. W 1989 r. wchodzące w
jej skład Unix System Laboratories przejęły i scaliły osiągnięcia tych
kilku nurtów projektowych w jeden produkt komercyjny – Tuxedo (tłumaczone
jako Transactions on UNIX Extended for Distributed Operations). W 1993 roku
firma Novell przejęła USL razem z prawami do monitora, a w 1996 sprzedała je
firmie BEA, która udoskonala go do dzisiaj.

W ciągu 20 lat produkt rozwijał się razem z całą
dziedziną IT i obecnie zasługuje na miano dojrzałej i stabilnej technologii
brokera usług (ang. service request broker). Systemy z udziałem Tuxedo
najczęściej zalicza się do trójwarstwowych, a sam monitor tworzy dodatkową
warstwę względem klasycznych serwera i klienta. Przy dużej złożoności i
rozproszeniu części serwerowej, jedną z ról warstwy pośredniej jest
skuteczna separacja pozostałych dwóch warstw poprzez ukrycie tych cech przed
klientem. Ma on do dyspozycji dobrze określony zestaw usług i korzysta
z nich niezależnie od tego, gdzie fizycznie odbywa się obsługa zleceń, jaki
ma przebieg i w jaki sposób powstało oprogramowanie reagujące na wywołania.
Monitory transakcji odegrały dużą rolę w przygotowaniu technologicznego
gruntu dla idei integracji aplikacji.

Jak się rodzi transakcja

Omówiliśmy mechanizm kończenia transakcji, a przecież
najpierw musi dojść do jej rozpoczęcia. Dla Tuxedo dzieje się to najczęściej
z chwilą wywołania funkcji tpbegin – może się to odbyć równie
dobrze w kodzie klienta, co serwisu uaktywnionego dowolnym z możliwych trybów.
Monitor nadaje wówczas transakcji globalny wewnętrzny identyfikator –
tzw. GTRID. Na użytek komunikacji z zasobami poprzez XA, w podobnej roli występuje
symbol XID, pod którym kryje się GTRID rozszerzony o identyfikator odgałęzienia.
Tuxedo wykonuje odpowiednie konwersje między nimi i przekazuje XID do
wszystkich zasobów
i odgałęzień powstających bezpośrednio, czy pośrednio na skutek wywołania
inicjującego transakcję. Jest to niezbędne do rozpoznania przez RM, z którą
transakcją związać otrzymane polecenia.

Jeśli kod następujący po funkcji tpbegin odwołuje
się do innych zasobów, czy wywołuje inne serwisy, to „zaraża je”
wszystkie transakcją – zachodzące zmiany „idą na jej
konto”. Monitor prowadzi ewidencję zarażonych uczestników, dzięki której
wie, do kogo skierować polecenia zarówno zatwierdzenia, jak i wycofania
transakcji.

Funkcja tpbegin ma bardzo istotny parametr –
limit czasu (w sekundach). Jeśli transakcja nie zakończy się przed jego upływem,
to monitor wycofa ją z własnej inicjatywy. Zabezpiecza to system przed
nadmiernym przeciąganiem procesów i blokowaniem zasobów przez zbyt długi
czas.

Z oczywistych przyczyn proces nie może toczyć się równocześnie
w kontekście więcej niż jednej transakcji, a wywołanie tpbegin w
procesie „zainfekowanym” kończy się błędem. Niekiedy ten sam kod
może znaleźć zastosowanie zarówno jako fragment większej transakcyjnej całości,
jak
i jako samodzielna usługa z własną transakcją. Czy zmusza nas to do
zdublowania go i utrzymywania dwóch wersji? Na szczęście nie, dzięki funkcji
tpgetlev program może sprawdzić, czy jest objęty transakcją, czy nie,
i rozpocząć własną tylko w drugim przypadku. Z kolei dwie inne funkcje ATMI
tpsuspend i tpresume – jak nietrudno wywnioskować z
ich nazw, pozwalają zawiesić uczestnictwo procesu w transakcji i wznowić je.
O ich przydatności
w kontekście współpracy Tuxedo i Oracle wspomnimy dalej.

Inicjowanie transakcji może też następować automatycznie,
bez jawnego użycia tpbegin. Wystarczy w tym celu odpowiedni wpis dla
serwisu w pliku konfiguracyjnym.

FML – otwartość i elastyczność przekazywania danych

Tuxedo z założenia ma integrować środowiska różnych
systemów operacyjnych. Trudno byłoby uznać to zadanie za spełnione, gdyby twórcy
rozproszonej, heterogenicznej aplikacji musieli za każdym razem troszczyć się
o formaty danych łączonych podsystemów i konwersje między nimi. Swą
elastyczność w tym względzie Tuxedo w dużym stopniu zawdzięcza bogatemu
zestawowi środków do przekazywania danych, do których zaliczają się tzw.
bufory typowane (typed buffers). Bufor jest obszarem pamięci
o określonym typie i strukturze, z możliwością określania podtypów.
Buforowe typy danych mają zdefiniowane odpowiedniki i funkcje dostępowe po
stronie języków programowania, dzięki czemu o ich konwersje może troszczyć
się Tuxedo.

Wśród typów buforów warto wymienić XML oraz FML (skrót
od Field Manipulation Language), który poznamy bliżej posługując się
językiem C. Bufor FML jest dynamicznie tworzonym i samoopisującym się zbiorem
pól
o zdefiniowanych wcześniej nazwach i typach. Pole może w nim występować
wielokrotnie – podlega indeksowaniu jak w tablicy. Manipulacje na polach
ze strony programisty praktycznie sprowadzają się do zapisu i odczytu ze
wskazaniem nazwy pola i indeksu. Tuxedo troszczy się o ew. konwersje i
transmisję buforów między uczestnikami przetwarzania.

Dodatkowym wbudowanym w FML wsparciem dla programisty jest możliwość
definiowania struktur języka C (tzw. views) i prostego przekazywania
danych między nimi a buforem FML w obu kierunkach pojedynczym wywołaniem
odpowiedniej funkcji. Zastosujemy te mechanizmy w przykładowej aplikacji, co
pozwoli zademonstrować je bardziej namacalnie.

A jak to wygląda w praktyce?

W dalszej części artykułu zaprezentujemy prostą namiastkę
aplikacji, która mimo ilościowego ograniczenia brakiem rozproszenia na wiele
maszyn czy zasobów, pozwoli jednak jakościowo zademonstrować zawartą w
tytule współpracę Tuxedo i Oracle przy pomocy XA i wniknąć trochę głębiej
w tajniki ich współdziałania.

c.d.n.