BRS#
Zálohování a obnova databáze

Záloha

Třída MemoryDb umožňuje prostřednictvím svého správcovského rozhraní IAdmin (konkrétně pomocí metody MemoryDb.Backup) provést kompletní zálohu databáze. Prostřednictvím zámků (Zamykání položek databáze v operační paměti) je zajištěn výlučný přístup k celé databázi, takže záloha je pak odrazem platného konzistetního stavu dat v daném okamžiku. Zálohovány jsou kontajnery obsahující

  • města,
  • pomocné názvy (aliasy) měst,
  • plány autobusů a
  • autobusy, každý z nich včetně kontajneru jednotlivých sedadel.

Naopak indexy pro rychlé vyhledávání v jednotlivých kontajnerech součástí zálohy nejsou, protože je možné jejich vytvoření přímo z dat. Součástí zálohy jsou také další pomocné objekty jako počítadla zákazníků, měst, atd.

Záloha je směřována do výstupního proudu (System.IO.Stream). Volající tak má poměrně široké možnosti, jak zálohu provést. Přímo se nabízí použití proudu spjatého se souborem, dalšími možnostmi jsou proud spjatý s TCP soketem (záloha po síti na vzdálený počítač) nebo specializované proudy, které před vlastním zápisem do souborového systému provádějí komprimaci nebo šifrování dat.

Metoda pro provedení zálohy předpokládá, že výstupní proud je již inicializován a otevřen pro zápis. Před ukončením metody je proud synchronizován (tj. výstupní bufer je vyprázdněn), samotný proud však není uzavřen.

Obnovení ze zálohy

Je-li možné provést zálohu dat, musí existovat způsob, jak v případě potřeby data ze zálohy obnovit. Třída MemoryDb k tomu disponuje přetíženým konstruktorem, který má jediný parametr - vstupní proud System.IO.Stream. Pomocí dat čtených z tohoto proudu jsou obnoveny všechny uložené objekty (viz Záloha výše), indexy kontajnerů jsou následně vytvořeny přímo z obnovených dat. Výsledkem je tedy plně funkční databáze, která odpovídá stavu v okamžiku, kdy byla záloha provedena.

Otevření a inicializace vstupního proudu je odpovědností volajícího, musí být zajištěno před zavoláním konstruktoru. Předpokládá se otevření proudu. Konkrétní podoba proudu není určena stejně jako v případě zálohy - ve hře jsou souborový proud, přesměrovaný standardní vstup i další zmíněné možnosti jako soketový proud nebo proud zajišťují dekompresi a dešifrování uložených dat. Po dokončení obnovy kontruktor proud neuzavírá, to je ponecháno na odpovědnosti volajícího.

V průběhu načítání je kontrolována konzistence dat. Pokud dojde ke zjištění nesouladu, je zpracování proudu přerušeno a namísto toho je vytvořena prázdná databáze.

Formát dat

Pro možnost kontroly konzistence dat při obnově ze zálohy jsou data organizována do bloků, které jsou vždy uvozeny speciální hlavičkou. Takové bloky se mohou vzájemně zanořovat. Typy podporovaných bloků s vlastní hlavičkou jsou:

  • celá databáze (kromě úvodní hlavičky obsahuje i speciální závěrečnou hlavičku, která je zároveň poslední platnou položkou dat),
  • kontajner měst,
  • kontajner pomocných názvů (alisů) měst,
  • kontajner plánů autobusů,
  • kontajner autobusů,
  • město,
  • pomocný název města (alias),
  • plán autobusu,
  • autobus,
  • sedadlo v autobusu.

Každému druhu bloku odpovídá položka typu BkHeader.DatObject.

Přes značnou různorodost uchovávaných dat se ukazuje, že si vystačíme s několika elementárními druhy, z nichž lze vystavět i všechny složitější struktury. Základními prvky jsou:

  • logická hodnota uložená jako bool,
  • neznaménková celočíselná hodnota uložená podle své binární reprezentace v paměti,
  • další jednoduché typy (např. DateTime - lze reprezentovat jako long), struktury jednoduchých typů (např. BusInfo) jsou uloženy po jednotlivých položkách,
  • znakové řetězce jsou uloženy jako uint s jeho délkou následovaný polem znaků v UTF-8.

Kontajnery jsou ukládány tak, že nejprve je uložen počet položek jako uint, následují pak jednotlivé položky.

Hlavička bloku je složena ze dvou položek typu uint. První má pevnou hodnotu, druhá je určena druhem bloku. Pro usnadnění orientace při ladění a hledání chyb jsou vybrány hodnoty, které lze snadno identifikovat při otevření v nějakém hexadecimálním editoru (odpovídají sekvencím čitelných znaků).

Realizace

V předchozím oddíle (Formát dat) bylo vysvětleno, že veškerá data jsou pro účely zálohy a jejího následného obnovení zpracovávána jako položky několika málo základních typů - hlavička bloku, logická hodnota, neznaménková celočíselná hodnota, blok binárních dat a znakový řetězec.

Pro práci s těmito základními stavebními kameny jsou k dispozici dvě sesterské třídy - Backup pro ukládání dat do proudu a Restore pro načítání z proudu. Každá z nich implementuje metody pro práci se zmíněnými základními typy. Díky tomu pak lze snadno načíst např. znakový řetězec metodou Restore.String, který byl před tím uložen pomocí Backup.String.

Záloha

Za celé provedení zálohy je zodpovědná metoda MemoryDb.Backup. Po uložení hlavičky a vlastních jednoduchých položek (počítadlo zákazníků) postupně volá metody Cities.Backup pro zálohu kontajnerů měst, Buses.Backup pro zálohu kontajneru autobusů a Plans.Backup pro kontajner plánů autobusů. Aliasy měst zpracovává metoda sama.

Kontajner měst (třída Cities) v rámci metody pro zálohování zapíše vlastní hlavičku, vlastní položky a následně pro každé město volá City.Backup. Stejné je to s kontajnerem autobusů a plánů. Zpracování ve třídě Bus navíc znamená zavolání Seat.Backup pro každé sedadlo. Shrneme-li to, každá třída v databázi zapíše své vlastní položky a pro zápis položek ve vlastněném kontajneru volá metodu pro zálohu každé podpoložky.

Obnovení

Obnovení databáze ze zálohy probíhá v rámci přetíženého konstruktoru. Nejprve se načte a zkontroluje hlavička celé databáze a vlastní položky (počítadlo zákazníků). K tomu slouží příslušné metody třídy Restore. Následně se vytvářejí kontajnery měst, plánů a autobusů a to tak, že příslušné třídy mají také přetížený konstruktor s inicializací z proudu. Alisy měst se zpracovávají přímo.

V konstruktoru Buses (podobně pro Cities a Plans) se kontroluje správnost hlavičky a načítají se vlastní položky. Pro jednotlivé autobusy se pomocí přetíženého konstruktoru Bus vytvářejí instance autobusů, které se zařazují do kontajneru autobusů. V každém autobusu se pak ještě stejným způsobem inicializují ze vstupního proudu jednotlivá sedadla. Analogicky se zálohou se zpracování podřízených položek deleguje na příslušné třídy (tentokrát pomocí přetížených konstruktorů).

Zpracování chyb

Je samozřejmé, že v průběhu práce s proudy může dojít k chybám. Může se jednat o důsledek selhání hardware (chyba na disku, přerušení síťového spojení, ...) nebo o nekonzistenci dat (např. v důsledku poškození souboru uživatelem). Chyby formátu dat se kontrolují ve třídě Restore. V obou případech po zaregistrování chyby dojde k vyvolání výjimky. V případě zálohy je tato zachycena v chráněném bloku MemoryDb.Backup a selhání je oznámeno pomocí návratové hodnoty. V případě obnovení je chráněný blok v konstruktoru MemoryDb a výjimka má za následek, že veškeré dosud naplněné kontajnery jsou vyprázdněny a výsledkem volání konstruktoru je prázdná databáze.