/// <summary> /// Encapsulate all operations in a single write transaction /// </summary> private T Transaction <T>(string collection, bool addIfNotExists, Func <CollectionPage, T> action) { // always starts write operation locking database using (_locker.Write()) { try { var col = this.GetCollectionPage(collection, addIfNotExists); var result = action(col); _trans.PersistDirtyPages(); return(result); } catch (Exception ex) { _log.Write(Logger.ERROR, ex.Message); // if an error occurs during an operation, rollback must be called to avoid datafile inconsistent _cache.DiscardDirtyPages(); throw; } } }
/// <summary> /// Get journal pages and override all into datafile /// </summary> public void Recovery() { _log.Write(Logger.RECOVERY, "initializing recovery mode"); using (_locker.Write()) { // double check in header need recovery (could be already recover from another thread) var header = BasePage.ReadPage(_disk.ReadPage(0)) as HeaderPage; if (header.Recovery == false) { return; } // read all journal pages foreach (var buffer in _disk.ReadJournal(header.LastPageID)) { // read pageID (first 4 bytes) var pageID = BitConverter.ToUInt32(buffer, 0); _log.Write(Logger.RECOVERY, "recover page #{0:0000}", pageID); // write in stream (encrypt if datafile is encrypted) _disk.WritePage(pageID, _crypto == null || pageID == 0 ? buffer : _crypto.Encrypt(buffer)); } // shrink datafile _disk.ClearJournal(header.LastPageID); } }