Exemplo n.º 1
0
        /// <summary>
        /// Get a page from cache or from disk (get from cache or from disk)
        /// </summary>
        public T GetPage <T>(uint pageID)
            where T : BasePage
        {
            lock (_disk)
            {
                var page = _cache.GetPage(pageID);

                // is not on cache? load from disk
                if (page == null)
                {
                    var buffer = _disk.ReadPage(pageID);

                    // if datafile are encrypted, decrypt buffer (header are not encrypted)
                    if (_crypto != null && pageID > 0)
                    {
                        buffer = _crypto.Decrypt(buffer);
                    }

                    page = BasePage.ReadPage(buffer);

                    _cache.AddPage(page);
                }

                return((T)page);
            }
        }
Exemplo n.º 2
0
        private void AvoidDirtyRead()
        {
            // if disk are exclusive don't need check dirty read
            if (_disk.IsExclusive)
            {
                return;
            }

            _log.Write(Logger.CACHE, "checking disk to avoid dirty read");

            // empty cache? just exit
            if (_cache.CleanUsed == 0)
            {
                return;
            }

            // get ChangeID from cache
            var header   = _cache.GetPage(0) as HeaderPage;
            var changeID = header == null ? 0 : header.ChangeID;

            // and get header from disk
            var disk = BasePage.ReadPage(_disk.ReadPage(0)) as HeaderPage;

            // if header change, clear cache and add new header to cache
            if (disk.ChangeID != changeID)
            {
                _log.Write(Logger.CACHE, "file changed from another process, cleaning all cache pages");

                _cache.ClearPages();
                _cache.AddPage(disk);
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Reduce disk size re-arranging unused spaces. Can change password. If temporary disk was not provided, use MemoryStream temp disk
        /// </summary>
        public long Shrink(string password = null, IDiskService temp = null)
        {
            var originalSize = _disk.FileLength;

            // if temp disk are not passed, use memory stream disk
            temp = temp ?? new StreamDiskService(new MemoryStream());

            using (_locker.Reserved())
                using (_locker.Exclusive())
                    using (var engine = new LiteEngine(temp, password))
                    {
                        // read all collection
                        foreach (var collectionName in this.GetCollectionNames())
                        {
                            // first create all user indexes (exclude _id index)
                            foreach (var index in this.GetIndexes(collectionName).Where(x => x.Field != "_id"))
                            {
                                engine.EnsureIndex(collectionName, index.Field, index.Unique);
                            }

                            // now copy documents
                            var docs = this.Find(collectionName, Query.All());

                            engine.InsertBulk(collectionName, docs);
                        }

                        // copy user version
                        engine.UserVersion = this.UserVersion;

                        // set current disk size to exact new disk usage
                        _disk.SetLength(temp.FileLength);

                        // read new header page to start copy
                        var header = BasePage.ReadPage(temp.ReadPage(0)) as HeaderPage;

                        // copy (as is) all pages from temp disk to original disk
                        for (uint i = 0; i <= header.LastPageID; i++)
                        {
                            var page = temp.ReadPage(i);

                            _disk.WritePage(i, page);
                        }

                        // create/destroy crypto class
                        _crypto = password == null ? null : new AesEncryption(password, header.Salt);

                        // initialize all services again (crypto can be changed)
                        this.InitializeServices();

                        // return how many bytes are reduced
                        return(originalSize - temp.FileLength);
                    }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Reduce disk size re-arranging unused spaces. Can change password. If temporary disk was not provided, use MemoryStream temp disk
        /// </summary>
        public long Shrink(string password = null, IDiskService temp = null)
        {
            var originalSize = _disk.FileLength;

            // if temp disk are not passed, use memory stream disk
            temp = temp ?? new StreamDiskService(new MemoryStream());

            using(_locker.Write())
            using (var engine = new LiteEngine(temp, password))
            {
                // read all collection
                foreach (var collectionName in this.GetCollectionNames())
                {
                    // first create all user indexes (exclude _id index)
                    foreach (var index in this.GetIndexes(collectionName).Where(x => x.Field != "_id"))
                    {
                        engine.EnsureIndex(collectionName, index.Field, index.Unique);
                    }

                    // copy all docs
                    engine.Insert(collectionName, this.Find(collectionName, Query.All()));
                }

                // copy user version
                engine.UserVersion = this.UserVersion;

                // set current disk size to exact new disk usage
                _disk.SetLength(temp.FileLength);

                // read new header page to start copy
                var header = BasePage.ReadPage(temp.ReadPage(0)) as HeaderPage;

                // copy (as is) all pages from temp disk to original disk
                for (uint i = 0; i <= header.LastPageID; i++)
                {
                    var page = temp.ReadPage(i);

                    _disk.WritePage(i, page);
                }

                // create/destroy crypto class
                _crypto = password == null ? null : new AesEncryption(password, header.Salt);

                // initialize all services again (crypto can be changed)
                this.InitializeServices();
            }

            // return how many bytes are reduced
            return originalSize - temp.FileLength;
        }
Exemplo n.º 5
0
        /// <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);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Get a page from cache or from disk (and put on cache)
        /// </summary>
        public T GetPage <T>(uint pageID)
            where T : BasePage
        {
            // lock concurrency access (read access are not in a lock transaction)
            lock (_cache)
            {
                var page = _cache.GetOrDefault(pageID);

                // is not on cache? load from disk
                if (page == null)
                {
                    var buffer = _disk.ReadPage(pageID);

                    // if datafile are encrypted, decrypt buffer (header are not encrypted)
                    if (_crypto != null && pageID > 0)
                    {
                        buffer = _crypto.Decrypt(buffer);
                    }

                    page = BasePage.ReadPage(buffer);
                    _cache.Add(pageID, page);
                }

                return((T)page);
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Get a page from cache or from disk (and put on cache)
        /// </summary>
        public T GetPage <T>(uint pageID, bool setDirty = false)
            where T : BasePage
        {
            var page = _cache.GetPage(pageID);

            // is not on cache? load from disk
            if (page == null)
            {
                var buffer = _disk.ReadPage(pageID);
                page = BasePage.ReadPage(buffer);
                _cache.AddPage(page);
            }

#if DEBUG
            // if page is empty, convert to T
            if (page.PageType == PageType.Empty && typeof(T) != typeof(BasePage))
            {
                throw new Exception("Pager.GetPage<T>() never shuld happend");
            }
#endif
            // set page as dirty if passing by param
            if (setDirty)
            {
                this.SetDirty((T)page);
            }

            return((T)page);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Initialize LiteEngine using custom disk service implementation and full engine options
        /// </summary>
        public LiteEngine(IDiskService disk, string password = null, TimeSpan?timeout = null, int cacheSize = 5000, Logger log = null, bool utcDate = false)
        {
            if (disk == null)
            {
                throw new ArgumentNullException("disk");
            }

            _timeout    = timeout ?? TimeSpan.FromMinutes(1);
            _cacheSize  = cacheSize;
            _disk       = disk;
            _log        = log ?? new Logger();
            _bsonReader = new BsonReader(utcDate);

            try
            {
                // initialize datafile (create) and set log instance
                _disk.Initialize(_log, password);

                // lock disk (read mode) before read header
                var position = _disk.Lock(LockState.Read, _timeout);

                var buffer = _disk.ReadPage(0);

                _disk.Unlock(LockState.Read, position);

                // create header instance from array bytes
                var header = BasePage.ReadPage(buffer) as HeaderPage;

                // hash password with sha1 or keep as empty byte[20]
                var sha1 = password == null ? new byte[20] : AesEncryption.HashSHA1(password);

                // compare header password with user password even if not passed password (datafile can have password)
                if (sha1.BinaryCompareTo(header.Password) != 0)
                {
                    throw LiteException.DatabaseWrongPassword();
                }

                // initialize AES encryptor
                if (password != null)
                {
                    _crypto = new AesEncryption(password, header.Salt);
                }

                // initialize all services
                this.InitializeServices();

                // if header are marked with recovery, do it now
                if (header.Recovery)
                {
                    _trans.Recovery();
                }
            }
            catch (Exception)
            {
                // explicit dispose
                this.Dispose();
                throw;
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Get a page from cache or from disk (and put on cache)
        /// </summary>
        public T GetPage <T>(uint pageID)
            where T : BasePage
        {
            // lock concurrency access (read access are not in a lock transaction)
            lock (_cache)
            {
                var buffer = _disk.ReadPage(pageID);

                var page = BasePage.ReadPage(buffer);

                return((T)page);
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Initialize LiteEngine using custom disk service implementation and full engine options
        /// </summary>
        public LiteEngine(IDiskService disk, string password = null, TimeSpan?timeout = null, int cacheSize = 5000, Logger log = null)
        {
            if (disk == null)
            {
                throw new ArgumentNullException("disk");
            }

            _timeout   = timeout ?? TimeSpan.FromMinutes(1);
            _cacheSize = cacheSize;
            _disk      = disk;
            _log       = log ?? new Logger();

            try
            {
                // initialize datafile (create) and set log instance
                _disk.Initialize(_log, password);

                // read header page
                var header = BasePage.ReadPage(_disk.ReadPage(0)) as HeaderPage;

                // hash password with sha1 or keep as empty byte[20]
                var sha1 = password == null ? new byte[20] : AesEncryption.HashSHA1(password);

                // compare header password with user password even if not passed password (datafile can have password)
                if (sha1.BinaryCompareTo(header.Password) != 0)
                {
                    throw LiteException.DatabaseWrongPassword();
                }

                // initialize AES encryptor
                if (password != null)
                {
                    _crypto = new AesEncryption(password, header.Salt);
                }

                // initialize all services
                this.InitializeServices();

                // try recovery if has journal file
                _trans.Recovery();
            }
            catch (Exception)
            {
                // explicit dispose
                this.Dispose();
                throw;
            }
        }
Exemplo n.º 11
0
        /// <summary>
        /// Test if cache still valid (if datafile was changed by another process reset cache)
        /// Returns true if file was changed
        /// [Thread Safe]
        /// </summary>
        private bool DetectDatabaseChanges()
        {
            // if disk are exclusive don't need check dirty read
            if (_disk.IsExclusive)
            {
                return(false);
            }

            // empty cache? just exit
            if (_cache.CleanUsed == 0)
            {
                return(false);
            }

            _log.Write(Logger.CACHE, "checking disk to detect database changes from another process");

            // get ChangeID from cache
            var header   = _cache.GetPage(0) as HeaderPage;
            var changeID = header == null ? 0 : header.ChangeID;

            // and get header from disk
            var disk = BasePage.ReadPage(_disk.ReadPage(0)) as HeaderPage;

            // if disk header are in recovery mode, throw exception to datafile re-open and recovery pages
            if (disk.Recovery)
            {
                _log.Write(Logger.ERROR, "datafile in recovery mode, need re-open database");

                throw LiteException.NeedRecover();
            }

            // if header change, clear cache and add new header to cache
            if (disk.ChangeID != changeID)
            {
                _log.Write(Logger.CACHE, "file changed from another process, cleaning all cache pages");

                _cache.ClearPages();
                _cache.AddPage(disk);
                return(true);
            }

            return(false);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Initialize LiteEngine using custom disk service implementation and full engine options
        /// </summary>
        public LiteEngine(IDiskService disk, string password = null, TimeSpan? timeout = null, bool autocommit = true, int cacheSize = 5000, Logger log = null)
        {
            _timeout = timeout ?? TimeSpan.FromMinutes(1);
            _cacheSize = cacheSize;
            _autocommit = autocommit;
            _disk = disk;
            _log = log ?? new Logger();
            _locker = new Locker(_timeout);

            // initialize datafile (create) and set log instance
            _disk.Initialize(_log, password);

            // read header page
            var header = BasePage.ReadPage(_disk.ReadPage(0)) as HeaderPage;

            // hash password with sha1 or keep as empty byte[20]
            var sha1 = password == null ? new byte[20] : AesEncryption.HashSHA1(password);

            // compare header password with user password even if not passed password (datafile can have password)
            if (sha1.BinaryCompareTo(header.Password) != 0)
            {
                // explicit dispose
                _disk.Dispose();
                throw LiteException.DatabaseWrongPassword();
            }

            // initialize AES encryptor
            if (password != null)
            {
                _crypto = new AesEncryption(password, header.Salt);
            }

            // initialize all services
            this.InitializeServices();

            if (_disk.IsJournalEnabled)
            {
                // try recovery if has journal file
                _trans.Recovery();
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Get a page from cache or from disk (and put on cache)
        /// </summary>
        public T GetPage <T>(uint pageID, bool setDirty = false)
            where T : BasePage
        {
            var page = _cache.GetPage(pageID);

            // is not on cache? load from disk
            if (page == null)
            {
                var buffer = _disk.ReadPage(pageID);
                page = BasePage.ReadPage(buffer);
                _cache.AddPage(page);
            }

            // set page as dirty if passing by param
            if (setDirty)
            {
                this.SetDirty((T)page);
            }

            return((T)page);
        }