Rezervační systém
|
Třída CMemoryDb umožňuje prostřednictvím svého správcovského rozhraní CAdmin (konkrétně pomocí metody CMemoryDb::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í
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 (jako objektu STL). 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 (std::ofstream
), 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 v binárním režimu. 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.
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 CMemoryDb k tomu disponuje přetíženým konstruktorem, který má jediný parametr - vstupní proud STL. 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 v binárním módu. Konkrétní podoba proudu není určena stejně jako v případě zálohy - ve hře jsou souborový proud std::ifstream
, přesměrovaný standardní vstup std::cin
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.
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:
Každému druhu bloku odpovídá položka výčtového typu CBkHeader::EObject.
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:
unsigned
s hodnotou 0
pro false
nebo 1
pro true
, time_t
), struktury jednoduchých typů (např. RBusInfo
) či blok binárních dat jsou uloženy jako binární reprezentace v paměti počítače (což může být samozřejmě závislé na konkrétním překladači a jeho nastavení), NUL
.Kontajnery jsou ukládány tak, že nejprve je uložen počet položek jako unsigned
, následují pak jednotlivé položky.
Hlavička bloku je složena ze dvou položek typu unsigned
. 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ů).
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 - CBackup pro ukládání dat do proudu a CRestore 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 CRestore::String, který byl před tím uložen pomocí CBackup::String.
Za celé provedení zálohy je zodpovědná metoda CMemoryDb::Backup. Po uložení hlavičky a vlastních jednoduchých položek (počítadlo zákazníků) postupně volá metody CCities::Backup pro zálohu kontajnerů měst, CBuses::Backup pro zálohu kontajneru autobusů a CPlans::Backup pro kontajner plánů autobusů. Aliasy měst zpracovává metoda sama.
Kontajner měst (třída CCities) 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á CCity::Backup. Stejné je to s kontajnerem autobusů a plánů. Zpracování ve třídě CBus navíc znamená zavolání CSeat::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í 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 CRestore. 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 CBuses (podobně pro CCities a CPlans) se kontroluje správnost hlavičky a načítají se vlastní položky. Pro jednotlivé autobusy se pomocí přetíženého konstruktoru CBus 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ů).
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 prvního typu jsou ošetřeny v knihovně STL nastavením příslušných chybových stavů, chyby formátu dat se kontrolují ve třídě CRestore. V obou případech po zaregistrování chyby dojde k vyvolání výjimky typu std::ios::failure
. V případě zálohy je tato zachycena v chráněném bloku CMemoryDb::Backup a selhání je oznámeno pomocí návratové hodnoty. V případě obnovení je chráněný blok v konstruktoru CMemoryDb 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.