Rozproszone systemy plików ze względu na swoją budowę i działanie obejmują bardzo szerokie spektrum zagadnień związanych z ogólnie rozumianymi systemami rozproszonymi. Problemy i kwestie, które zostały poruszone na poprzednich wykładach w dużej mierze dotyczą również systemów rozproszonych, jak choćby nazewnictwo, problem spójności, mechanizmy synchronizacji.
Na początku wykładu zajmiemy się omówieniem ogólnej charakterystyki rozproszonych systemów plików. Następnie przedstawimy m.in.. modele plików, modele dostępów do pliku, mechanizmy pamięci podręcznej. Opiszemy także kwestię zwielokrotniania plików, serwery pełnostanowe i bezstanowe oraz problem niezawodności. Na koniec przedstawimy wybrane rozwiązania stosowane w rzeczywistych systemach plików (NFS, Coda).
Stosowanie systemu plików uwalnia użytkowników, programistów od wielu czynności związanych z zarządzaniem danymi. W przypadku scentralizowanych systemów plików można powiedzieć w skrócie, że użytkownicy zostają zwolnieni z bezpośredniego manipulowania urządzeniami przechowującymi dane. Natomiast rozproszone systemy plików pozwalają użytkownikom na wygodne wykonywanie operacji plikowych nie tylko na lokalnym komputerze, ale także w środowisku rozproszonym, gdzie dane przechowywane są na wielu rozproszonych urządzeniach.
Aby pokrótce zaprezentować możliwości rozproszonych systemów plików, wymienimy kilka ich wybranych cech. Umożliwiają np. zdalne współdzielenie informacji. Oznacza to, że plik, który został utworzony na jednym komputerze może być dostępny z innego komputera w sieci. Rozproszony system plików powinien być również być możliwie przeźroczysty dla użytkownika. Np. przeźroczystość lokalizacji sprawia, że użytkownik nie musi znać fizycznego miejsca, w którym znajduje się plik, tym powinien zająć się system plików. Kolejną cechą rozproszonych systemów plików, która staje się w dzisiejszych czasach coraz bardziej pożądana, jest umożliwienie użytkownikom przemieszczania się między różnymi miejscami w sieci oraz tymczasowe odłączanie się od niej. Innymi słowy użytkownik nie powinien być zmuszony do pracy na jednym ściśle określonym komputerze, ale powinien mieć możliwość przełączenia się pomiędzy różnymi komputami, które być może są w innym geograficznie odległym miejscu. Z mobilnością użytkownika wiąże się również pojęcie dostępności (ang. availability ). W środowisku rozproszonym jeden z serwerów, który przechowuje dane użytkownika może ulec awarii. W tym celu rozproszony system plików przechowuje wiele kopii danych na różnych węzłach systemu, czyli tzw. repliki plików. W momencie awarii system może połączyć użytkownika z innym serwerem, który zawiera kopie jego danych. W idealnym przypadku klient rozproszonego systemu plików nie musi być świadomy awarii i może dalej działać na swoich danych.
Rozproszone systemy plików pozwalają także na przechowywanie i organizacje danych, które nie byłyby możliwe do przechowania na pojedynczych stanowiskach.
Wśród nich znajduje się m.in. usługa odpowiedzialna za bezpośrednie zarządzanie danymi na urządzeniach przechowujących dane (ang. storage service ). W skład tej usługi wchodzą np. zadania związane z alokacją i zarządzaniem przestrzenią dyskową, w której są przechowywane pliki. Usługa ta tworzy pewną logiczną warstwę zarządzania danymi. Architektura tej usługi jest bardzo podobna dla scentralizowanych i rozproszonych systemów plików.
Kolejną usługą we wspomnianej grupie usług jest właściwa usługa plikowa (ang. true file service ). Dostarcza ona odpowiednich operacji do zarządzania poszczególnymi plikami danych i ich zawartością. Pojawiają się tu operacje tworzenia i kasowania plików, operacje czytania i pisania do plików itp. Z usługą tą powiązanych jest wiele dodatkowych kwestii takich jak: mechanizm dostępu do plików, semantyka współdzielenia plików, mechanizmy pamięci podręcznej, zwielokrotnianie plików, sterowanie współbieżnością, mechanizm zachowania spójności danych, protokoły aktualizacji danych, mechanizm kontroli dostępu i wiele innych. Wyodrębnienie tej usługi od poprzedniej daje możliwość odseparowania warstwy zarządzania danymi od warstwy nośników danych.
Trzecią i ostatnią prezentowaną tu usługą jest usługa nazewnicza (ang. name service ). Zajmuje się ona odwzorowaniem nazw plików na specjalne identyfikatory. Identyfikatory są rodzajem uchwytów do poszczególnych plików i ułatwiają zarządzanie plikami przez rozproszony system plików. Z usługa nazewniczą związana jest usługa katalogowa (ang, direktory service ), która wprowadza hierarchię do struktury plików i pozwala nią zarządzać.
Jedną z takich właściwości jest przeźroczystość (ang. transparency ). W systemach rozproszonych można wyróżnić wiele typów przeźroczystości.
Przeźroczystość struktury pozwala na ukrycie przed klientami faktu korzystania z wielu serwerów. Klient nie musi znać liczby serwerów, na której przechowuje swoje dane. Najlepiej gdyby widział rozproszony system plików jako jeden scentralizowany system.
Przeźroczystość dostępu sprawia, że zdalny i lokalny dostęp do plików wygląda identycznie. W idealnym wypadku zdalne i lokalne operacje na plikach powinny być takie same.
Przeźroczystość nazewnictwa wprowadza niezależność nazw plików od ich lokalizacji. Na podstawie nazwy użytkownik nie jest w stanie określić miejsca fizycznego przechowania pliku. Zmiana miejsca przechowywania pliku nie wpływa tym samym na nazwę pliku.
Przeźroczystość zwielokrotniania.ukrywa fakt istnienia wielu kopii tego samego piku na różnych urządzeniach.
Kolejną, wspomnianą już wcześniej właściwością, która staję się coraz bardziej pożądana (m.in. jako skutek rozwoju sieci bezprzewodowych), jest mobilność (ang. mobility ) użytkowników. Mobilność w idealnym przypadku daje użytkownikowi dostęp do jego danych bez względu na to gdzie się znajduje. W praktyce osiągnięcie pełnej mobilności jest często niemożliwe. Pojawia się tu m.in. problem operacji wykonywanych przez klienta w odłączeniu od serwera plików.
Istotnym czynnikiem, który określa m.in. przydatność rozproszonego systemu plików jest czas wykonania żądań klienta. Na wydajność działania systemu plików składa się wiele czynników. Są to np. czas dostępu do informacji na nośniku fizycznym, czas przetwarzania żądania przez procesor oraz czas komunikacji. Dobry rozproszony system plików powinien mieć wydajność podobną do scentralizowanego systemu.
Z punktu widzenia użytkownika system plików musi być prosty w obsłudze. Semantyka systemu powinna być możliwie prosta. Ma to swoje odzwierciedlenie w małej liczbie łatwych do zrozumienia operacji. Interfejs rozproszonego systemu plików znowu powinien być maksymalnie podobny do tego w scentralizowanych systemach.
Wysoka dostępność (ang. high availibilty ) charakteryzuje miedzy innymi odporność systemu na awarię. System, który uległ częściowej awarii powinien być nadal dostępny i świadczyć usługi. Oczywiście nie jest wykluczone, że będą one w okrojonym zakresie. Jednym ze środków utrzymywania wysokiej dostępności jest użycie zwielokrotniania.
Dobry rozproszony system plików musi być niezawodny . Prawdopodobieństwo utraty danych powinno być zminimalizowane do granic praktycznych możliwości. Tworzenie kopii zapasowych, przywracanie systemu do stanu sprzed awarii to zadania, które powinien wykonywać automatycznie system plików.
Ponieważ w rozproszonym systemie plików, operacje na plikach mogą być wykonywane współbieżnie przez wielu użytkowników, często istnieje potrzeba zagwarantowania odpowiedniego poziomu spójności danych . Stosowane są to np. mechanizmy transakcyjne, protokoły spójności nastawione na dane oraz na klienta.
Bardzo istotnym elementem systemu plików jest bezpieczeństwo. Wraz z rosnącymi rozmiarami systemu, rośnie problem bezpieczeństwa systemu. Bezpieczny system musi zapewnić użytkownikom zachowanie ich prywatności. Informacje przechowywane w plikach powinny być odpowiednio chronione przed niepożądanym dostępem z zewnątrz. Również prawa dostępu do pliku muszą być należycie zabezpieczone.
Heterogeniczność jest na ogół cechą bardzo pożądaną w środowiskach rozproszonych. Im system większy i rozleglejszy tym przeważnie jest on bardziej zróżnicowany. Heterogeniczność jest cechą, która wiąże się poniekąd z poprzednimi cechami tj. mobilnością, skalowalnością itp. Użytkownik, który może się przemieszczać powinien mieć swobodę wyboru platformy systemowej, a nawet sprzętowej, na której chce pracować. Pożądana jest sytuacja, gdy użytkownik ma dostęp do swoich danych na różnych komputerach niezależnie od np. systemu operacyjnego. Heterogeniczność oznacza również zdolność do podłączania do systemu wielu różnych nośników danych. Z pewnością zwiększa to użyteczność takiego rozproszonego systemu.
Najprostszy model plików zakłada, że struktura danych jest nieznana tzn. serwer plików nie ma możliwości interpretacji danych. Z punktu widzenia systemu operacyjnego nie jest interesujące co zawiera dany plik. Tym zajmują się aplikacje, które będą go używać. Rzadziej używanym modelem pliku jest plik o znanej strukturze. Dla tego rodzaju plików zakłada się, że ciąg danych jest uszeregowany. Taki poszeregowany ciąg danych tworzą rekordy, które w przypadku różnych plików mogą być różnych rozmiarów. W wypadku strukturalizowanych plików rekord jest najmniejszą jednostką danych, do której można uzyskać dostęp. Operacje odczytu i zapisu wykonywane są wtedy na zbiorach rekordów.
W dalszej kolejności strukturalizowane pliki dzieli się na dwa typy. Pliki, których rekordy posiadają indeksy i pliki, których rekordy takich indeksów nie posiadają. W tym momencie wkraczamy jednak w zagadnienia związane z bazami danych i nie będziemy ich tutaj dalej omawiać. Należy jednak pamiętać, że oba zagadnienia są ze sobą ściśle związane.
Wróćmy z powrotem do modeli plików, których struktura jest nieznana. Taki model używany jest przez większość dzisiejszych systemów operacyjnych. Jest to spowodowane między innymi tym, że zarządzanie takimi plikami i ich zawartością jest zdecydowanie prostsze nie tylko dla systemu plików, ale także dla różnych aplikacji.
Istotną zaletą używania modeli plików niemodyfikowalnych jest łatwość utrzymania spójności w sytuacji, gdy dane są współdzielone. Łatwiej wtedy utrzymać integralność replik plików w rozproszonym środowisku, ponieważ system zostaje zwolniony z problemu związanego z utrzymaniem spójności wielu różnych kopii. Wersjonowanie wprowadza jednakże kilka problemów, takich jak zwiększone zużycie przestrzeni dyskowych.
Na początku zajmiemy się zdalnym dostępem do plików. Zasadniczo rozróżniamy dwie metody zdalnego dostępu do plików. Pierwsza metoda, to model bazujący na zdalnej usłudze. W tym przypadku wszystkie operacje, których żąda klient wykonywane są na serwerze plików tzn. klient wysyła pewne żądanie wykonania operacji na plikach do serwera, serwer wykonuje żądanie klienta, a następnie ewentualnie wysyła wyniki z powrotem do klienta. Można powiedzieć, że model ten bazuje na komunikatach przesyłanych przez sieć w celu dostępu do plików i odesłania wyników przez serwer. W niektórych przypadkach nakład spowodowany przez komunikację może być znaczący.
W przypadku projektowania protokołów bazujących na dostępie do plików w postaci zdalnych usług należy zwrócić uwagę na ilość przesłanych komunikatów oraz nakład przy generowaniu wiadomości.
Kolejnym podejściem do zdalnego dostępu do plików jest użycie pamięci podręcznej. W przypadku poprzedniego modelu zdalnego dostępu do plików z każdym żądaniem związany był ruch w sieci. Podejście stosujące pamięć podręczną stara się zminimalizować ten ruch poprzez wykonywanie niektórych dostępów do plików lokalnie. Zasada działania takiego modelu jest następująca. Gdy klient żąda jakichś danych z pliku, a nie ma ich lokalnie stara się je uzyskać od serwera. Dopiero na lokalnych danych klient wykonuje operacje dostępu. Z użyciem pamięci podręcznej wiąże się m.in. polityka usuwania zbędnych kopii. Używana jest tu np. metoda LRU (ang. least recently used ), która może określać kiedy i które kopie danych mają być usunięte.
Zastosowanie pamięci podręcznej redukuje ruch w sieci. Problemem jest natomiast zachowanie spójności kopii podręcznej z oryginałem.
Pierwszy model bazuje na przesyłaniu całych plików (ang. file-level transfer model ). Gdy tylko potrzebne są dane z pewnego pliku, przesyłany jest cały plik. Plik może być przesyłany od serwera do klienta, jak również odwrotnie. Takie podejście do przesyłania ma kilka zasadniczych zalet. Liczba komunikatów przy przesyłaniu całego pliku jest mniejsza niż przy przesyłaniu tego samego pliku w kawałkach w odpowiedzi na kilka żądań. Przesłanie całości pliku naraz do klienta ogranicza liczbę dostępów do serwera, co zwiększa potencjalnie skalowalność systemu plików i odporność na awarię serwera plików lub sieci. Minusem tego podejścia jest przesyłanie zbędnych danych. Czasami klient potrzebuje tylko części danych z pliku. W gorszym przypadku klient może po prostu nie mieć miejsca na przechowanie całego pliku.
Zamiast pliku w całości można przesyłać tylko jego fragmenty – bloki (ang. block-level transfer model ). Jako bloki pliku należy tu rozumieć fragmenty pliku, których rozmiar jest zazwyczaj stały. Zaletą tego modelu jest na pewno zmniejszenie wymagań co do przestrzeni dyskowej klienta ze względu na rozmiar bloków. Wadą jest zwiększony ruch sieciowy, szczególnie w przypadku przesyłania plików złożonych z wielu małych bloków.
Jeszcze mniejszą jednostką przesyłania danych plików są bajty (ang. byte-level transfer model ). Główną zaletą tego modelu jest jego elastyczność. Niestety pojawia się tu problem z zarządzaniem pamięcią podręczną.
Powyższe trzy modele charakteryzują się tym, że mogą być używane do plików o znanej strukturze i do plików bez struktury. Gdy weźmiemy pod rozwagę tylko pliki o znanej strukturze, automatycznie nasuwa się pomysł, aby tam gdzie jest to możliwe, użyć rekordów jako jednostek transferu danych (ang. record-level transfer model ).
Jedno z rozwiązań tego problemu bazuje na semantyce systemu UNIX. Użycie tej semantyki wymaga całkowitego uporządkowania operacji w czasie. W ten sposób każda operacja odczytu widzi wszystkie operacje zapisu, które nastąpiły przed nią. W wypadku gdy plik jest współdzielony przez kilku użytkowników, modyfikacja jednego użytkownika będzie natychmiast widoczna przez operacje odczytu innego użytkownika. Semantyka ta wydaję się dosyć naturalna, z tego też powodu często jest implementowana w systemach plików, szczególnie tych nierozproszonych. Implementacja takiej semantyki współdzielenia w systemach rozproszonych nie jest jednak prosta. Jednym ze sposobów osiągnięcia takiej semantyki jest użycie jednego wybranego serwera do koordynacji operacji zapisów i odczytów na plikach. W rozwiązaniu tym zakłada się również brak pamięci podręcznej po stronie klientów. Podejście takie nie gwarantuje jednakże zachowania takiej semantyki. Problemem może być np. kolejności wykonania żądań z powodu opóźnień komunikacyjnych. Co więcej wydajność, niezawodność i skalowalność takiego rozwiązania pozostawiają wiele do życzenia.
Wspomniana semantyka systemu UNIX w praktyce jest znacznie rozluźniona dla systemów rozproszonych, a dla aplikacji, które mimo wszystko wymagają tej semantyki proponuje się specjalne mechanizmy, niezależne od systemu plików np. blokady.
Semantyka sesji jest kolejnym modelem współdzielenia plików. Użycie plików przy zastosowaniu semantyki sesji wygląda następująco: otwarcie pliku, wykonywanie operacji zapisu lub odczytu, zamknięcie pliku po skończeniu wszystkich operacji. Ciąg operacji pomiędzy operacjami zamknięcia i otwarcia pliku określany jest mianem sesji. Sesja charakteryzuje się tym, że wszystkie operacje wykonywane przez danego klienta w ramach jednej sesji są widoczne tylko dla niego. Dopiero po zakończeniu sesji zmiany dokonane na pliku są widoczne przez procesy innych klientów. Ponieważ ten sam plik może być modyfikowany równocześnie przez kilku klientów, pojawia się pytanie jak wygląda plik wynikowy po takich operacjach. W praktyce najczęściej zakłada się, że wynik takich równoczesnych sesji działających na tym samym pliku daje w wyniku plik, który jest wynikiem działania jednej z nich. Innymi słowy nie wiadomo, która z aktywnych sesji utworzy zawartość pliku, na którym operują. Semantyka pliku ze względu na swój charakter i wymagania używana jest najczęściej stosowana z polityką przesyłania całego pliku.
Kolejna semantyka określa pliki jako niezmienne (ang. immutable ). Każda aktualizacja pliku oznacza utworzenie kolejnej pełnej wersji pliku. Semantyka ta zezwala na współdzieleni plików tylko w trybie do odczytu. Znika tu problem, kiedy modyfikacje danego pliku maja być widoczne dla innych użytkowników pliku.
Ostatnią prezentowaną semantyką współdzielenia plików jest semantyka transakcji. Transakcja podobnie jak sesja obejmuje ciąg operacji zapisu i odczytu zawartych pomiędzy specjalnymi instrukcjami początek_transkacji i koniec_transakcji . Różnica w stosunku do semantyki sesji polega na tym, że transakcja może obejmować operacje na wielu plikach. Wykonanie kilku współbieżnych transakcji na danym pliku daje nam gwarancję, że zawartość tego pliku będzie taka, jak gdyby transakcje te były wykonane sekwencyjnie w pewnym porządku.
W celu uniknięcia dostępu do dysku serwera określone pliki można przechowywać w pamięci głównej serwera. Klient, który żąda pliku oszczędza w ten sposób na operacjach dyskowych po stronie serwera. Mechanizm ten jest stosunkowo prosty do implementacji, a ponadto może on być całkowicie przeźroczysty dla klienta. Kopia pliku w pamięci podręcznej jest fizycznie blisko oryginału, dlatego zapewnienie spójności obu nie powinno Stanowic większego problemu. Zaletą tego podejścia jest także wsparcie semantyki systemu UNIX dla współdzielenia plików. Należy jednak zauważyć, iż zastosowanie pamięci podręcznej na serwerze nie zmniejsza wcale kosztów komunikacji i jest praktycznie obojętne, jeżeli chodzi o skalowalność i niezawodność systemu rozproszonego.
Pamięć podręczna może być zastosowana również po stronie klienta. Mamy tu do wyboru dysk lub pamięć główną. Użycie dysku eliminuje ruch w sieci, ale pozostaje kwestia operacji dyskowych. Jedną z głównych zalet przechowywania podręcznej kopii pliku na dysku klienta jest niezawodność. Pamięć podręczna tego rodzaju może być również zazwyczaj bardzo duża, czego rezultatem jest m.in. trafność odwołań do danych w takiej pamięci. Gdy pamięć główna klienta jest za mała, użycie takiej pamięci podręcznej okazuje się wręcz niezbędne. W przypadku tego podejście istotną cechą jest ograniczenie komunikacji pomiędzy serwerem plików a klientem co procentuje w postaci skalowalności systemu rozproszonego. Niedogodnościami pamięci podręcznej po stronie klienta jest konieczność kontaktowania się z serwerem dla każdej operacji dostępu do pliku mimo, że dane są dostępne lokalnie.
Przechowywanie kopii podręcznej w pamięci głównej klienta ma dwie zasadnicze cechy: zmniejsza konieczność komunikacji klienta z serwerem oraz ogranicza liczbę operacji dyskowych. Użycie takiej pamięci podręcznej wpływa znacząco na skalowalność systemu, a także na jego niezawodność.
Podsumowując zastosowanie dysku po stronie klienta, jako miejsca pamięci podręcznej zwiększa znacząco niezawodność systemu, jednakże to pamięć główna klienta gwarantuje szybszy dostęp do danych.
Podejście polegające na natychmiastowym przepisywaniu (ang. write-through ) zmodyfikowanych danych do serwera jest jednym ze schematów propagowania modyfikacji. Takie rozwiązanie oferuje zalety w postaci wysokiej niezawodności oraz dogodności dla zastosowania semantyki systemu UNIX. Pierwsza zaleta wynika z tego, że dane są przesyłane natychmiast do serwera i ewentualna awaria klienta nie powinna spowodować utraty większej ilości aktualnych danych. Wadą takiego podejścia jest natomiast konieczność komunikowania się klienta z serwerem podczas każdej operacji zapisu. Efektem tego jest znacznie obniżona wydajność. Z tego względu rozwiązanie to zalecane jest szczególnie dla systemów, gdzie liczba operacji odczytu znacznie przewyższa liczbę operacji zapisu.
Można wyróżnić kilka odmian schematów z opóźnionym zapisem w zależności od tego kiedy aktualizowane są dane na serwerze. Pierwsza metoda zakłada istnienie pewnej polityki, która decyduje o tym kiedy dane z kopii podręcznej są przesyłane do serwera. Zastosowanie odpowiedniej polityki może znacznie poprawić efektywność systemu. Minusem takiego rozwiązania może być zbyt długie przechowywanie aktualnych danych tylko w pamięci podręcznej, co wiąże się z kolejnymi problemami np. niezawodnością.
Kolejna metoda zakłada okresowe przesyłanie danych od klienta do serwera. Co pewien okres czasu wszystkie modyfikacje, które nastąpiły od momentu ostatniej synchronizacji pamięci podręcznej klienta z serwerem, są przesyłane do serwera.
W trzeciej metodzie opóźnionego zapisu dane z pamięci podręcznej przesyłane są do serwera, gdy odpowiadający im plik zostanie zamknięty. Taki schemat przesyłania nadaje się szczególnie dla plików, które otwierane są na długie okresy i są często modyfikowane. W przypadku plików, które otwierane są na krótko i są rzadko modyfikowane, zmniejszenie narzutu komunikacyjnego może okazać się niewielkie.
Stosowanie opóźnionego przepisywania zmian z pamięci podręcznej na serwer ma kilka ważnych cech. Operacje zapisu wykonywane są stosunkowo szybko, ponieważ zmiany dokonywane są tylko na kopii podręcznej. Zmodyfikowane dane mogą być skasowane zanim trafią na serwer. W ten sposób redukowane są zbędne komunikaty. Poza tym przesyłanie kilku modyfikacji pliku jednocześnie jest bardziej wydajne niż przesyłanie ich jedna za drugą. Opóźnione zapisywanie nie jest pozbawione jednak wad. Pojawiają się tu problemy z niezawodnością (np. awaria klienta i utrata zawartości pamięci podręcznej). Poza tym semantyka współdzielenia nie jest tu ściśle określona i zależy od czasu propagacji danych.
Klient może na przykład sprawdzać aktualność pamięci podręcznej przy każdej operacji dostępu do danych. Rozwiązanie takie nie jest jednak do końca pożądane ze względu na to, że podważa ono sens używania pamięci podręcznej.
Walidacja inicjowana przez klienta może być również przeprowadzana co pewien stały okres czasu.
Jeszcze inne podejście zakłada sprawdzanie pamięci podręcznej w momencie otwierania odpowiedniego pliku. Schemat ten często używany jest przy okazji semantyki sesji z propagacją zmian przy zamknięciu pliku.
To czy kopie danych pliku w pamięci podręcznej różnią się od głównej kopii pliku, określa się np. za pomocą różnic w datach modyfikacji. Innym sposobem porównania kopii plików jest użycie znaczników czasu, wektorów wersji, sum kontrolnych itp.
W tym podejściu klient powiadamia serwer, kiedy otwiera plik do czytania lub pisania. Serwer pamięta tryby w jakich są używane poszczególne pliki w taki sposób, że może śledzić możliwości powstania ewentualnych niespójności danych. Np. gdy serwer zauważy, że jeden plik został otwarty w trybie do zapisu, to wie że nie może otworzyć jednocześnie tego samego pliku drugi raz. Natomiast jeżeli plik został otwarty w trybie do odczytu, nic nie stoi na przeszkodzie, aby inni użytkownicy czytali go w tym samym czasie. Gdy użytkownik kończy korzystać z pliku, wysyła do serwera odpowiednie powiadomienie, żeby ten aktualizował swoje informacje.
Walidacja inicjowana przez serwer mimo swojej efektywności ma też kilka wad. Komplikuje kod klienta i serwera, co jest skutkiem odejścia od tradycyjnego modelu klient-serwer i żądanie-odpowiedź. Serwer musi być w tym przypadku pełnostanowy. Co więcej można powiedzieć, że w pewien sposób nadal używana jest walidacja pamięci podręcznej inicjowanej przez klienta, ponieważ sprawdzanie pamięci podręcznej przebiega podczas otwarcia pliku.
W porównaniu z wcześniej prezentowanym rozwiązaniem, w przypadku systemu AFS serwer nie musi być informowany o operacjach otwarcia pliku, a jedynie w momencie zakończenia sesji, które zawierały operacje zapisu.
Poza wcześniej wymienioną zwiększoną dostępnością, zwielokrotnianie oferuje szereg innych korzyści. Utworzenie wielu kopii tych samych danych pozwala na zwiększenie niezawodności systemu plików. Jeżeli chodzi o korzyści komunikacyjne, zwielokrotnianie może poprawić czasy reakcji na żądania klientów, zmniejsza ruch w sieci, poprawia przepustowość systemu. Zwielokrotnianie plików daje również możliwość rozłożenia obciążenia związanego z żądaniami klientów na wiele serwerów. Jeżeli dwa lub więcej serwerów posiadają identyczne dane, żądania dostępu do tych danych można rozdzielić pomiędzy serwery, tak aby jeden serwer nie był nadmiernie obciążony. Wpływa to także na poprawę skalowalności takiego systemu.
Zwielokrotnianie po stronie klienta umożliwia również klientom przeprowadzanie operacji w odłączeniu od serwera, co jest dużą zaletą w przypadku, gdy używane są urządzenia przenośne.
Kopie oryginału używane przy zwielokrotnianiu (repliki) są zazwyczaj powiązane z serwerem, natomiast kopie w pamięci podręcznej umiejscowione są zazwyczaj u klienta. Przyjmuje się ponadto że repliki danych są bardziej trwałe, dostępne, bezpieczne i dokładne od danych przechowywanych w pamięci podręcznej. Zazwyczaj kopie podręczne są zależne od replik i na nich bazują np. swoje aktualizacje. Pamięć podręczna stosowana jest również tam gdzie wymagana jest lokalność przy dostępie do danych. Repliki są stosowane z kolei w celu poprawienia dostępności i wydajności.
Sterowanie replikacją określa ile powinno być kopii danych i na jakich serwerach. Zwykle sterowanie zwielokrotnianiem odbywa się w pełni automatycznie, ale nie jest tak zawsze. Są sytuacje, w których użytkownik powinien być świadomy niektórych aspektów sterowania. Pozwala to na zwiększenie elastyczności systemu i daje większą swobodę użytkownikowi.
Wyróżnia się dwa odmienne podejścia do replikacji. Pierwsze podejście określa replikację jako zupełnie jawną dla użytkownika. Określa on np. gdzie mają być umieszczone repliki danego pliku, ile ma być tych replik, może kasować wybrane repliki. Drugie podejście reprezentuje zupełnie odmienny styl zwielokrotniania i ukrywa przed użytkownikiem cały proces replikacji.
Repliki tylko do odczytu (ang. read-only replication ) stosowane są tam gdzie tworzone są repliki plików tylko do odczytu. Ponieważ pliki, które mogą być modyfikowane nie są poddawane zwielokrotnianiu, to problem aktualizacji replik znika całkowicie. Takie podejście nadaje się szczególnie w systemach i aplikacjach, gdzie dane są bardzo często czytane, ale rzadko modyfikowane.
Systemy plików, które stosują inne mniej restrykcyjne od poprzedniego podejście do replikacji, pozwalają na odczyt danych z dowolnej repliki pliku, ale operacja zapisu musi być jednocześnie wykonana na wszystkich replikach pliku (ang. read-any – write-all protocol ). Do implementacji takiego podejścia używane są m.in. blokady zakładane na plikach.
Kolejne podejście eliminuje zasadniczą wadę poprzedniego protokołu. Mianowicie, przedstawiony wcześniej protokół aktualizacji replik wymagał od systemu, aby zapis był wykonany na wszystkich replikach. Pojawiał się jednak problem, co robić jeżeli jedna lub więcej replik nie jest dostępnych w danej chwili. Z tego powodu rozluźniono jeszcze bardziej wymagania co do liczby aktualizowanych replik. W podejściu, które aktualizuje tylko dostępne repliki (ang. available-copies protocol ) nie wymaga się, aby operacja zapisu musiała być wykonana jednocześnie na wszystkich replikach. Serwer, który uległ awarii, zanim zrealizuje jakiekolwiek żądanie klienta, odzyskuje dane kopiując je np. z innego serwera mającego aktualne dane.
Jeszcze inne podejście do aktualizacji wielu replik polega na zastosowaniu podstawowej kopii danych (ang. primary-copy protocol ). Każdy plik posiada swoją podstawową kopię danych. Wszelkie operacje odczytu mogą być wykonywane przez klientów na kopii podstawowej lub dowolnej innej replice pliku. Natomiast operacje zapisu muszą być wykonywane tylko na tej jednej kopii podstawowej. Każdy serwer, który posiada replikę danego pliku aktualizuje swoje dane poprzez synchronizację z serwerem przechowującym podstawową replikę tego pliku. Może to zrobić czekając aż serwer z kopią podstawowa powiadomi go zaszłych modyfikacjach lub sam może żądać informacji o aktualizacjach.
Ponieważ operacje zapisu nie muszą być wykonywane na wszystkich replikach zaistniała potrzeba rozróżniania wersji replik. Robi się to np. poprzez przypisanie każdej replice numeru wersji, który jest zwiększany wraz z kolejnymi jej modyfikacjami.
Operacja odczytu dla pliku przebiega w tym protokole następująco: 1) Zdobądź odpowiednia liczbę r replik, które będą stanowić kworum do odczytu, 2) Wybierz spośród wybranych kopii tę o najwyższym numerze wersji, 3) Wykonaj operację odczytu na tej replice.
Operacja zapisu do pliku wykonuje się w następujących krokach: 1) Zdobądź odpowiednią liczbę w replik (kworum do zapisu), 2) Wybierz replikę z największym numerem wersji, 3) Zwiększ numer wersji, 4) Zapisz nową wartość do wszystkich replik wchodzących w skład kworum oraz przypisz im nowy numer wersji.
Jeżeli r=1 , a w=n (gdzie n jest liczbą wszystkich replik), mamy do czynienia ze specjalnym przypadkiem protokołu, który został zaprezentowany wcześniej jako protokół, który czyta z dowolnej repliki a pisze do wszystkich (ang. read-any – write-all ).
Gdy r=n , a w=1 mamy do czynienia z protokołem, który czyta do czytania wymaga wszystkich replik, natomiast do zapisu tylko jednej (ang. read-all – write-any ). Protokół ten może być wykorzystany szczególnie tam, gdzie liczba operacji zapisu jest znacznie większa od liczby operacji odczytu.
Trzecia odmiana protokołu bazującego na kworum (ang. majority-consensus protocol ) zakłada, że liczba r i w są równe lub prawie równe. Protokołu tego używa się tam, gdzie liczba operacji zapisu i odczytu jest podobna.
Czasami zakłada się również, że niektóre repliki z pewnych względów (np. bezpieczeństwo, wydajność) są ważniejsze od innych. Mamy wtedy do czynienia z podejściem, w którym używa się wag replik do określenia kworum (ang. consensus with weighted voting ). Liczby r i w określają odpowiednio liczbę (sumę wag) potrzebną do wykonania operacji odczytu i zapisu. Warunek, który muszą spełnić liczby r i w zmienia się na warunek (r+w>v ), gdzie v oznacza sumę wszystkich wag przypisanych do replik danego pliku.
W środowiskach rozproszonych często będziemy mieli do czynienia z mniejszymi lub większymi awariami. Ich występowanie może okazać się fatalne w skutkach, nie tylko dla spójności danych, ale i całego systemu.
W celu osiągnięcia jak najwyższej niezawodności systemu plików ważne są szczególnie takie cechy plików jak dostępność danych, odporność na wszelkiego rodzaju awarie nośników danych, możliwość odzyskiwania danych (np. po cofnięciu operacji przez klienta). Dostępność opisuje w tym wypadku m.in. czas przez jaki dany plik jest niedostępny.
Istotny wpływ na niezawodność ma architektura serwera plików, a dokładniej to czy serwer przechowuje historię interakcji z klientem, czy też nie. W zależności od tego wyróżniamy serwery pełnostanowe i bezstanowe. Oba typy serwerów zostaną przedstawione w dalszej części wykładu.
Jako przykład operacji, która wymaga zapamiętania stanu przez serwer posłużymy się operacją odczytu. Niech klient wyśle żądanie otwarcia pliku do serwera, a następnie operację odczytu pierwszych n bajtów pliku. Serwer w odpowiedzi na żądanie wykona operację otwarcia pliku i odczyta żądane dane, które następnie prześle do klienta. Klient zażąda odczytania kolejnych bajtów z otwartego pliku. W tym momencie serwer, który pamięta miejsce w pliku gdzie zakończył poprzednio czytać dane, może kontynuować odczyt następnych bajtów.
Istotną zaletą serwera bezstanowego jest jego odporność na ewentualne awarie. W przypadku awarii serwera pełnostanowego często tracone są wszystkie informacje o stanie i potrzebne są mechanizmy do przywracania stanu sprzed awarii. Ponieważ serwer bezstanowy nie przechowuje informacji, jak serwer pełnostanowy problem odzyskiwania stanu praktycznie znika.
Serwery bezstanowe mają jednak swoje słabości. Wykonanie operacji przez takie serwery trwa z reguły dłużej niż robią to serwery pełnostanowe. W przypadku powtarzania pewnego żądania trzeba uważać na to czy operacja, którą ma wykonać serwer bezstanowy jest idempotentna tzn. czy daje ten sam wynik mimo tego, że jest powtarzana wiele razy.
Pierwszą przesłanką dla, których stosuje się transakcje w rozproszonych systemach plików jest mechanizm odzyskiwania danych, przywracania stanu. Istotną cecha transakcji jest tu atomowość. Przypuśćmy np., że któryś z serwerów plików uległ awarii podczas, gdy jakiś klient wykonywał na nim operacje. Atomowość zapewni nam, że stan po awarii będzie taki jak przed awarią. Natomiast gdy przyjrzymy się co by było gdyby nie było atomowości zobaczymy, że dane po takiej awarii mogłyby być zupełnie niespójne.
Transakcje ułatwiają również współbieżne współdzielenie plików, które zachowuje spójność modyfikowanych danych. Wykonywanie wielu operacji jednocześnie przez wielu klientów na tych samych danych często prowadzi do niepożądanej zawartości pliku wynikowego. Transakcje pozwalają rozwiązać ten problem szeregując odpowiednio operacje odczytu i zapisu. W ten sposób można przewidzieć wynik grupy operacji.
Problematyka transakcji została poruszona w odrębnym wykładzie i nie będziemy się dalej nią tutaj zajmować.
Spójrzmy teraz na główne cechy systemu NFS. NFS umożliwia wyeksportowanie części struktury katalogowej wraz z zawartością, czyli plikami na inny zdalny komputer. Na tym zdalnym komputerze wyeksportowana część struktury katalogowej jest zaszywana w lokalnej strukturze plików w taki sposób, aby dostęp do danych niczym nie różnił się od operacji na lokalnych plikach. Sam proces importowania i przyłączania zdalnej struktury katalogów określany jest jako montowanie. W systemie tym role klienta i serwera może praktycznie pełnić każdy komputer, który posiada odpowiednie oprogramowanie.
NFS jest jednym z najczęściej używanych systemów plików tego typu. Charakteryzuje się przeźroczystością na poziomie położenia, wędrówki, dostępu. W najnowszej wersji systemu NFS widać dążenie twórców do zwiększenia skalowalności systemu. Przejawem tego jest m.in. przygotowanie pewnych prostych mechanizmów do zastosowania replikacji, która nie była w ogóle wspierana w poprzednich wersjach tego systemu.
NFS oferuje przeźroczysty dostęp do plików, a klienci tego systemu nie są w zasadzie świadomi gdzie znajduje się plik, na którym operują. NFS należy do systemów, które bazują na modelu zdalnego dostępu. Aby zrealizować taki model dostępu posłużono się warstwą wirtualnego systemu plików, który pośredniczy pomiędzy warstwą wywołań systemowych, a rzeczywistym systemem plików, zdalnym lub lokalnym. Jeżeli okazuje się, że klient żąda dostępu do lokalnego systemu plików żądanie przekazywane jest do lokalnego systemu plików. W przeciwnym razie, gdy żądanie odwołuje się do pliku na zdalnym komputerze, informacja o tym przekazywana jest do warstwy klient NFS, która komunikuje się ze zdalnym serwerem NFS. Serwer NFS poprzez warstwę wirtualnego systemu plików stara się zrealizować żądanie i odsyła ewentualny wynik do klienta.
Wśród operacji znajdziemy oczywiście operacje do odczytu (read ) i zapisu (write ) danych z i do pliku. Są także operacje do zarządzania atrybutami plików. Istotną operacją jest operacja lookup , która służy do poszukiwania uchwytu plikowego (identyfikatora) na podstawie nazwy pliku. Operacja readdir stosowana jest z kolei do czytania zawartości katalogów. W wyniku jej działania klient uzyskuje nazwy plików wraz z odpowiadającymi im identyfikatorami. Poza tym mamy także operacje do zmiany nazwy plików, usuwania plików itp.
Pewna nowością w wersji 4. systemu NFS jest wprowadzenie operacji otwarcia (ang. open ) i zamknięcia (ang. close ) pliku. Wcześniejsze wersje systemu NFS pozwalały na użycie bezstanowego serwera plików. W wersji 4. założono jednak, że serwer przechowuje stan pomiędzy operacjami.
Bardzo przydatną cechą systemu jest możliwość wykonywania operacji na danych, gdy klient jest czasowo odłączony od sieci lub odpowiednie serwery nie są dostępne.
Interfejs operacji plikowych w Coda podobny jest do systemu UNIX.
System Coda używa optymistycznej strategii zwielokrotniania. Klienci mogą jednocześnie modyfikować ten sam plik. W ten sposób powstaje wiele wersji pliku, które przesyłane są do odpowiednich grup AVSG . Kluczowe jest tu zagadnienie wykrywania niespójności i usuwanie ich. W tym celu Coda używa wektorów wersji Coda (ang. Coda version vector ), CVV . Taki wektor wersji posiada każdy plik. Poszczególne pozycje wektora odpowiadają liczbie modyfikacji pliku wykonanych przez serwery z grupy AVSG.
Rozpatrzmy teraz przykład przedstawiony na diagramie. System składa się z trzech serwerów, z których każdy niech przechowuje replikę pewnego pliku f . Na początku działania systemu wektory wersji dla pliku f , na każdym serwerze są identyczne i wynoszą [1, 1, 1]. W skutek pierwszych dwóch, pomyślnie przeprowadzonych modyfikacji wektory wersji pliku f zwiększyły swoją wartość na wszystkich serwerach do wartości [3, 3, 3]. W tym momencie pojawiła się awaria, która spowodowała odcięcie trzeciego serwera od dwóch pozostałych. Kolejne modyfikacje na pliku f spowodowały powstanie dwóch różnych wersji tego samego pliku oznaczonych odpowiednio wektorami wersji [4, 4, 3] na serwerach 1 i 2 oraz [3, 3, 4] na serwerze 3. Kiedy nastąpi awaria zostanie usunięta, wektory wersji kopii pliku f zostaną porównane i nastąpi faza scalania. Scalanie w zależności od możliwości może być przeprowadzone automatycznie lub przy asyście użytkownika.