/// <summary> /// Initialize LiteEngine using initial engine settings /// </summary> public LiteEngine(EngineSettings settings) { _settings = settings ?? throw new ArgumentNullException(nameof(settings)); // clear checkpoint if database is readonly if (_settings.ReadOnly) { _settings.Checkpoint = 0; } LOG($"start initializing{(_settings.ReadOnly ? " (readonly)" : "")}", "ENGINE"); try { // initialize locker service (no dependency) _locker = new LockService(settings.Timeout, settings.ReadOnly); // initialize disk service (will create database if needed) _disk = new DiskService(settings); // read page with no cache ref (has a own PageBuffer) - do not Release() support var buffer = _disk.ReadFull(FileOrigin.Data).First(); // if first byte are 1 this datafile are encrypted but has do defined password to open if (buffer[0] == 1) { throw new LiteException(0, "This data file is encrypted and needs a password to open"); } _header = new HeaderPage(buffer); // initialize wal-index service _walIndex = new WalIndexService(_disk, _locker); // if exists log file, restore wal index references (can update full _header instance) if (_disk.GetLength(FileOrigin.Log) > 0) { _walIndex.RestoreIndex(ref _header); } // initialize sort temp disk _sortDisk = new SortDisk(settings.CreateTempFactory(), CONTAINER_SORT_SIZE, settings.UtcDate); // initialize transaction monitor as last service _monitor = new TransactionMonitor(_header, _locker, _disk, _walIndex, _settings); // register system collections this.InitializeSystemCollections(); LOG("initialization completed", "ENGINE"); } catch (Exception ex) { LOG(ex.Message, "ERROR"); // explicit dispose (but do not run shutdown operation) this.Dispose(true); throw; } }
/// <summary> /// Do checkpoint operation to copy log pages into data file. Return how many transactions was commited inside data file /// Checkpoint requires exclusive lock database /// </summary> public int Checkpoint() { // no log file or no confirmed transaction, just exit if (_disk.GetLength(FileOrigin.Log) == 0 || _confirmTransactions.Count == 0) { return(0); } var mustExit = _locker.EnterExclusive(); try { return(this.CheckpointInternal()); } finally { if (mustExit) { _locker.ExitExclusive(); } } }
/// <summary> /// Do checkpoint operation to copy log pages into data file. Return how many transactions was commited inside data file /// If soft = true, just try enter in exclusive mode - if not possible, just exit /// </summary> public void Checkpoint(bool soft) { // get original log length var logLength = _disk.GetLength(FileOrigin.Log); // no log file or no confirmed transaction, just exit if (logLength == 0 || _confirmTransactions.Count == 0) { return; } // for safe, lock all database (read/write) before run checkpoint operation // future versions can be smarter and avoid lock (be more like SQLite checkpoint) if (soft) { // shutdown mode only try enter in exclusive mode... if not possible, exit without checkpoint if (_locker.TryEnterExclusive() == false) { return; } } else { _locker.EnterReserved(true); } LOG($"checkpoint", "WAL"); // wait all pages write on disk _disk.Queue.Wait(); ENSURE(_disk.Queue.Length == 0, "no pages on queue when checkpoint"); // getting all "good" pages from log file to be copied into data file IEnumerable <PageBuffer> source() { foreach (var buffer in _disk.ReadFull(FileOrigin.Log)) { // read direct from buffer to avoid create BasePage structure var transactionID = buffer.ReadUInt32(BasePage.P_TRANSACTION_ID); // only confied paged can be write on data disk if (_confirmTransactions.Contains(transactionID)) { var pageID = buffer.ReadUInt32(BasePage.P_PAGE_ID); // clear isConfirmed/transactionID buffer.Write(uint.MaxValue, BasePage.P_TRANSACTION_ID); buffer.Write(false, BasePage.P_IS_CONFIRMED); buffer.Position = BasePage.GetPagePosition(pageID); yield return(buffer); } } } // write all log pages into data file (sync) _disk.Write(source(), FileOrigin.Data); // reset _confirmTransactions.Clear(); _index.Clear(); _currentReadVersion = 0; // clear cache _disk.Cache.Clear(); // clear log file (sync) _disk.SetLength(0, FileOrigin.Log); // remove exclusive lock _locker.ExitReserved(true); }