Example #1
0
        public void Load(LogReader reader)
        {
            if (CurrentVersion != null)
            {
                return;
            }

            CurrentVersion = ReadVersionEdit(reader);
            Log.Debug($"Loading manifest");
            Print(Log, CurrentVersion);

            foreach (var level in CurrentVersion.Levels)             // Search all levels for file with matching index
            {
                foreach (FileMetadata tbl in level.Value)
                {
                    Table tableReader = tbl.Table;
                    if (tableReader == null)
                    {
                        tableReader = GetTable(tbl.FileNumber);
                        tableReader.Initialize();
                        tbl.Table = tableReader;
                    }
                }
            }

            if (!"leveldb.BytewiseComparator".Equals(CurrentVersion.Comparator, StringComparison.InvariantCultureIgnoreCase))
            {
                throw new Exception($"Found record, but contains invalid or unsupported comparator: {CurrentVersion.Comparator}");
            }
        }
Example #2
0
        public void Load(LogReader reader)
        {
            if (CurrentVersion != null)
            {
                return;
            }

            CurrentVersion = ReadVersionEdit(reader);
            Print(CurrentVersion);

            foreach (var level in CurrentVersion.NewFiles)             // Search all levels for file with matching index
            {
                foreach (FileMetadata tbl in level.Value)
                {
                    if (Log.IsDebugEnabled)
                    {
                        Log.Debug($"Found table file for key in level {level.Key} in file={tbl.FileNumber}");
                    }

                    Table tableReader = tbl.Table;
                    if (tableReader == null)
                    {
                        FileInfo f = new FileInfo(Path.Combine(_baseDirectory.FullName, $"{tbl.FileNumber:000000}.ldb"));
                        if (!f.Exists)
                        {
                            throw new Exception($"Could not find table {f.FullName}");
                        }
                        tableReader = new Table(f);
                        tbl.Table   = tableReader;
                    }
                }
            }
        }
Example #3
0
        internal void Load(LogReader reader)
        {
            _resultCache = new Dictionary <byte[], ResultCacheEntry>(new ByteArrayComparer());

            int entriesCount = 0;

            while (true)
            {
                ReadOnlySpan <byte> data = reader.ReadData();

                if (reader.Eof)
                {
                    if (Log.IsDebugEnabled)
                    {
                        Log.Debug($"Reached end of stream. No more records to read.");
                    }
                    break;
                }

                var entries = DecodeBatch(data);
                entriesCount += entries.Count;
                foreach (KeyValuePair <byte[], ResultCacheEntry> entry in entries.OrderBy(kvp => kvp.Value.Sequence))
                {
                    // This should overwrite older entries and only the latest operation should be saved
                    _resultCache[entry.Key] = entry.Value;
                    _estimatedSize         += (ulong)(entry.Key.Length + 8 + entry.Key.Length + 10 /* varlong * 2 */);
                }
            }
            Log.Debug($"Total count of entries read: {entriesCount}");
            Log.Debug($"Total count after filtering entries: {_resultCache.Count}");
        }
Example #4
0
        internal void Load(LogReader reader)
        {
            //_resultCache = new Dictionary<byte[], ResultCacheEntry>();
            _resultCache = new Dictionary <byte[], ResultCacheEntry>(new ByteArrayComparer());

            while (true)
            {
                Record record = reader.ReadRecord();

                if (record.LogRecordType == LogRecordType.Eof)
                {
                    if (Log.IsDebugEnabled)
                    {
                        Log.Debug($"Reached end of records: {record.ToString()}");
                    }
                    break;
                }

                if (record.LogRecordType != LogRecordType.Full)
                {
                    throw new Exception($"Invalid log file. Didn't find any records. Got record of type {record.LogRecordType}");
                }

                if (record.Length != (ulong)record.Data.Length)
                {
                    throw new Exception($"Invalid record state. Length not matching");
                }

                var entries = DecodeBatch(record.Data);
                foreach (var entry in entries)
                {
                    //_resultCache.TryAdd(entry.Key, entry.Value);
                    _resultCache[entry.Key] = entry.Value;
                }
            }

            _resultCache = _resultCache.OrderByDescending(kvp => kvp.Value.Sequence).ToDictionary(k => k.Key, k => k.Value);
        }
Example #5
0
        public static Version ReadVersionEdit(LogReader logReader)
        {
            var version = new Version();

            while (true)
            {
                ReadOnlySpan <byte> data = logReader.ReadData();

                if (logReader.Eof)
                {
                    break;
                }

                var reader = new SpanReader(data);

                //var versionEdit = new VersionEdit();

                while (!reader.Eof)
                {
                    var logTag = (LogTagType)reader.ReadVarLong();
                    switch (logTag)
                    {
                    case LogTagType.Comparator:
                    {
                        version.Comparator = reader.ReadLengthPrefixedString();
                        break;
                    }

                    case LogTagType.LogNumber:
                    {
                        version.LogNumber = reader.ReadVarLong();
                        break;
                    }

                    case LogTagType.NextFileNumber:
                    {
                        version.NextFileNumber = reader.ReadVarLong();
                        break;
                    }

                    case LogTagType.LastSequence:
                    {
                        version.LastSequenceNumber = reader.ReadVarLong();
                        break;
                    }

                    case LogTagType.CompactPointer:
                    {
                        int level       = (int)reader.ReadVarLong();
                        var internalKey = reader.ReadLengthPrefixedBytes();
                        version.CompactPointers[level] = internalKey.ToArray();
                        break;
                    }

                    case LogTagType.DeletedFile:
                    {
                        int   level      = (int)reader.ReadVarLong();
                        ulong fileNumber = reader.ReadVarLong();

                        if (!version.DeletedFiles.ContainsKey(level))
                        {
                            version.DeletedFiles[level] = new List <ulong>();
                        }
                        version.DeletedFiles[level].Add(fileNumber);
                        break;
                    }

                    case LogTagType.NewFile:
                    {
                        int   level                  = (int)reader.ReadVarLong();
                        ulong fileNumber             = reader.ReadVarLong();
                        ulong fileSize               = reader.ReadVarLong();
                        ReadOnlySpan <byte> smallest = reader.ReadLengthPrefixedBytes();
                        ReadOnlySpan <byte> largest  = reader.ReadLengthPrefixedBytes();

                        var fileMetadata = new FileMetadata();
                        fileMetadata.FileNumber  = fileNumber;
                        fileMetadata.FileSize    = fileSize;
                        fileMetadata.SmallestKey = smallest.ToArray();
                        fileMetadata.LargestKey  = largest.ToArray();
                        if (!version.Levels.ContainsKey(level))
                        {
                            version.Levels[level] = new List <FileMetadata>();
                        }
                        version.Levels[level].Add(fileMetadata);
                        break;
                    }

                    case LogTagType.PrevLogNumber:
                    {
                        version.PreviousLogNumber = reader.ReadVarLong();
                        break;
                    }

                    default:
                    {
                        throw new ArgumentOutOfRangeException($"Unknown tag={logTag}");
                    }
                    }
                }
            }

            // Clean files
            var deletedFiles = new List <ulong>();

            foreach (List <ulong> versionDeletedFile in version.DeletedFiles.Values)
            {
                deletedFiles.AddRange(versionDeletedFile);
            }

            foreach (KeyValuePair <int, List <FileMetadata> > levelKvp in version.Levels)
            {
                foreach (FileMetadata newFile in levelKvp.Value.ToArray())
                {
                    if (deletedFiles.Contains(newFile.FileNumber))
                    {
                        levelKvp.Value.Remove(newFile);
                    }
                }
            }

            version.Levels = version.Levels.OrderBy(kvp => kvp.Key).ToDictionary(pair => pair.Key, pair => pair.Value);
            version.Comparator ??= "leveldb.BytewiseComparator";

            return(version);
        }
Example #6
0
        public VersionEdit ReadVersionEdit(LogReader logReader)
        {
            string comparator         = null;
            ulong? logNumber          = null;
            ulong? previousLogNumber  = null;
            ulong? nextFileNumber     = null;
            ulong? lastSequenceNumber = null;

            VersionEdit finalVersion = new VersionEdit();

            while (true)
            {
                Record record = logReader.ReadRecord();

                if (record.LogRecordType != LogRecordType.Full)
                {
                    break;
                }

                var reader = new SpanReader(record.Data);

                VersionEdit versionEdit = new VersionEdit();

                while (!reader.Eof)
                {
                    LogTagType logTag = (LogTagType)reader.ReadVarLong();
                    switch (logTag)
                    {
                    case LogTagType.Comparator:
                    {
                        versionEdit.Comparator = reader.ReadLengthPrefixedString();
                        break;
                    }

                    case LogTagType.LogNumber:
                    {
                        versionEdit.LogNumber = reader.ReadVarLong();
                        break;
                    }

                    case LogTagType.NextFileNumber:
                    {
                        versionEdit.NextFileNumber = reader.ReadVarLong();
                        break;
                    }

                    case LogTagType.LastSequence:
                    {
                        versionEdit.LastSequenceNumber = reader.ReadVarLong();
                        break;
                    }

                    case LogTagType.CompactPointer:
                    {
                        int level       = (int)reader.ReadVarLong();
                        var internalKey = reader.ReadLengthPrefixedBytes();
                        versionEdit.CompactPointers[level] = internalKey.ToArray();
                        break;
                    }

                    case LogTagType.DeletedFile:
                    {
                        int   level      = (int)reader.ReadVarLong();
                        ulong fileNumber = reader.ReadVarLong();
                        if (!versionEdit.DeletedFiles.ContainsKey(level))
                        {
                            versionEdit.DeletedFiles[level] = new List <ulong>();
                        }
                        versionEdit.DeletedFiles[level].Add(fileNumber);
                        if (!finalVersion.DeletedFiles.ContainsKey(level))
                        {
                            finalVersion.DeletedFiles[level] = new List <ulong>();
                        }
                        finalVersion.DeletedFiles[level].Add(fileNumber);
                        break;
                    }

                    case LogTagType.NewFile:
                    {
                        int   level      = (int)reader.ReadVarLong();
                        ulong fileNumber = reader.ReadVarLong();
                        ulong fileSize   = reader.ReadVarLong();
                        var   smallest   = reader.ReadLengthPrefixedBytes();
                        var   largest    = reader.ReadLengthPrefixedBytes();

                        FileMetadata fileMetadata = new FileMetadata();
                        fileMetadata.FileNumber  = fileNumber;
                        fileMetadata.FileSize    = fileSize;
                        fileMetadata.SmallestKey = smallest.ToArray();
                        fileMetadata.LargestKey  = largest.ToArray();
                        if (!versionEdit.NewFiles.ContainsKey(level))
                        {
                            versionEdit.NewFiles[level] = new List <FileMetadata>();
                        }
                        versionEdit.NewFiles[level].Add(fileMetadata);
                        if (!finalVersion.NewFiles.ContainsKey(level))
                        {
                            finalVersion.NewFiles[level] = new List <FileMetadata>();
                        }
                        finalVersion.NewFiles[level].Add(fileMetadata);
                        break;
                    }

                    case LogTagType.PrevLogNumber:
                    {
                        versionEdit.PreviousLogNumber = reader.ReadVarLong();
                        break;
                    }

                    default:
                    {
                        throw new ArgumentOutOfRangeException($"Unknown tag={logTag}");
                    }
                    }
                }

                versionEdit.CompactPointers = versionEdit.CompactPointers.Count == 0 ? null : versionEdit.CompactPointers;
                versionEdit.DeletedFiles    = versionEdit.DeletedFiles.Count == 0 ? null : versionEdit.DeletedFiles;
                versionEdit.NewFiles        = versionEdit.NewFiles.Count == 0 ? null : versionEdit.NewFiles;

                comparator         = versionEdit.Comparator ?? comparator;
                logNumber          = versionEdit.LogNumber ?? logNumber;
                previousLogNumber  = versionEdit.PreviousLogNumber ?? previousLogNumber;
                nextFileNumber     = versionEdit.NextFileNumber ?? nextFileNumber;
                lastSequenceNumber = versionEdit.LastSequenceNumber ?? lastSequenceNumber;
            }

            // Clean files
            List <ulong> deletedFiles = new List <ulong>();

            foreach (var versionDeletedFile in finalVersion.DeletedFiles.Values)
            {
                deletedFiles.AddRange(versionDeletedFile);
            }

            foreach (var levelKvp in finalVersion.NewFiles)
            {
                foreach (var newFile in levelKvp.Value.ToArray())
                {
                    if (deletedFiles.Contains(newFile.FileNumber))
                    {
                        levelKvp.Value.Remove(newFile);
                    }
                }
            }
            finalVersion.NewFiles = finalVersion.NewFiles.OrderBy(kvp => kvp.Key).ToDictionary(pair => pair.Key, pair => pair.Value);

            finalVersion.Comparator         = comparator;
            finalVersion.LogNumber          = logNumber;
            finalVersion.PreviousLogNumber  = previousLogNumber;
            finalVersion.NextFileNumber     = nextFileNumber;
            finalVersion.LastSequenceNumber = lastSequenceNumber;

            return(finalVersion);
        }
        public void Open()
        {
            if (_dbLock == null)
            {
                throw new ObjectDisposedException("Database was closed and can not be reopened");
            }

            _dbLock.EnterWriteLock();

            try
            {
                if (_manifest != null)
                {
                    throw new InvalidOperationException("Already had manifest for database. Did you already open it?");
                }
                if (_memCache != null)
                {
                    throw new InvalidOperationException("Already had memory cache for database. Did you already open it?");
                }

                if (Directory.Name.EndsWith(".mcworld"))
                {
                    // Exported from MCPE. Unpack to temp

                    Log.Debug($"Opening directory: {Directory.Name}");

                    var originalFile = Directory;

                    string newDirPath = Path.Combine(Path.GetTempPath(), Directory.Name);
                    Directory = new DirectoryInfo(Path.Combine(newDirPath, "db"));
                    if (!Directory.Exists || originalFile.LastWriteTimeUtc > Directory.LastWriteTimeUtc)
                    {
                        ZipFile.ExtractToDirectory(originalFile.FullName, newDirPath, true);
                        Log.Warn($"Created new temp directory: {Directory.FullName}");
                    }
                    Directory.Refresh();
                    Log.Warn($"Extracted bedrock world and set new DB directory to: {Directory.FullName}");
                }

                // Verify that directory exists
                if (!Directory.Exists)
                {
                    Directory.Create();
                    Directory.Refresh();
                }
                if (!File.Exists(GetCurrentFileName()))
                {
                    if (!_createIfMissing)
                    {
                        var notFoundException = new DirectoryNotFoundException(Directory.FullName);
                        Log.Error(notFoundException);
                        throw notFoundException;
                    }

                    // Create new MANIFEST

                    var manifest = new Manifest(Directory);
                    manifest.CurrentVersion = new Version()
                    {
                        Comparator         = "leveldb.BytewiseComparator",
                        LogNumber          = 1,
                        PreviousLogNumber  = 0,
                        NextFileNumber     = 2,
                        LastSequenceNumber = 0
                    };
                    var manifestFileInfo = new FileInfo(GetManifestFileName(1));
                    if (manifestFileInfo.Exists)
                    {
                        throw new PanicException($"Trying to create database, but found existing MANIFEST file at {manifestFileInfo.FullName}. Aborting.");
                    }
                    using var writer = new LogWriter(manifestFileInfo);
                    manifest.Save(writer);
                    manifest.Close();

                    // Create new CURRENT text file and store manifest filename in it
                    using StreamWriter current = File.CreateText(GetCurrentFileName());
                    current.WriteLine(manifestFileInfo.Name);
                    current.Close();

                    // Done and created
                }

                Directory.Refresh();                 // If this has been manipulated on the way, this is really needed.

                // Read Manifest into memory

                string manifestFilename = GetManifestFileNameFromCurrent();
                Log.Debug($"Reading manifest from {manifestFilename}");
                using (var reader = new LogReader(new FileInfo(manifestFilename)))
                {
                    _manifest = new Manifest(Directory);
                    _manifest.Load(reader);
                }

                // Read current log
                var logFile = new FileInfo(GetLogFileName(_manifest.CurrentVersion.LogNumber));
                Log.Debug($"Reading log from {logFile.FullName}");
                using (var reader = new LogReader(logFile))
                {
                    _memCache = new MemCache();
                    _memCache.Load(reader);
                }

                // Append mode
                _log = new LogWriter(logFile);
            }
            finally
            {
                _dbLock.ExitWriteLock();
            }

            // We do this on startup. It will rotate the log files and create
            // level 0 tables. However, we want to use into reusing the logs.
            CompactMemCache();
            CleanOldFiles();

            _compactTimer = new Timer(state => DoCompaction(), null, 300, 300);
        }
Example #8
0
        public void Open()
        {
            if (_manifest != null)
            {
                throw new InvalidOperationException("Already had manifest for database. Did you already open it?");
            }
            if (_newMemCache != null)
            {
                throw new InvalidOperationException("Already had memory cache for database. Did you already open it?");
            }

            if (Directory.Name.EndsWith(".mcworld"))
            {
                // Exported from MCPE. Unpack to temp

                Log.Debug($"Opening directory: {Directory.Name}");

                var originalFile = Directory;

                string newDirPath = Path.Combine(Path.GetTempPath(), Directory.Name);
                Directory = new DirectoryInfo(Path.Combine(newDirPath, "db"));
                if (!Directory.Exists || originalFile.LastWriteTimeUtc > Directory.LastWriteTimeUtc)
                {
                    ZipFile.ExtractToDirectory(originalFile.FullName, newDirPath, true);
                    Log.Warn($"Created new temp directory: {Directory.FullName}");
                }

                Log.Warn($"Extracted bedrock world and set new DB directory to: {Directory.FullName}");
            }

            // Verify that directory exists
            if (!Directory.Exists)
            {
                if (!CreateIfMissing)
                {
                    throw new DirectoryNotFoundException(Directory.Name);
                }

                Directory.Create();

                // Create new MANIFEST

                VersionEdit newVersion = new VersionEdit();
                newVersion.NextFileNumber = 1;
                newVersion.Comparator     = "leveldb.BytewiseComparator";

                // Create new CURRENT text file and store manifest filename in it
                using (var manifestStream = File.CreateText($@"{Path.Combine(Directory.FullName, "CURRENT")}"))
                {
                    manifestStream.WriteLine($"MANIFEST-{newVersion.NextFileNumber++:000000}");
                    manifestStream.Close();
                }


                // Done
            }

            // Read Manifest into memory

            string manifestFilename;

            using (var currentStream = File.OpenText($@"{Path.Combine(Directory.FullName, "CURRENT")}"))
            {
                manifestFilename = currentStream.ReadLine();
                currentStream.Close();
            }

            Log.Debug($"Reading manifest from {Path.Combine(Directory.FullName, manifestFilename)}");
            using (var reader = new LogReader(new FileInfo($@"{Path.Combine(Directory.FullName, manifestFilename)}")))
            {
                _manifest = new Manifest(Directory);
                _manifest.Load(reader);
            }

            // Read current log
            //TODO: remove unit-test-stuff
            var      logFileName = Path.Combine(Directory.FullName, $"{_manifest.CurrentVersion.LogNumber + 1:000000}.log");
            FileInfo f           = new FileInfo(logFileName);

            if (!f.Exists)
            {
                f = new FileInfo(Path.Combine(Directory.FullName, $"{_manifest.CurrentVersion.LogNumber:000000}.log"));
            }

            using (var reader = new LogReader(f))
            {
                _newMemCache = new MemCache();
                _newMemCache.Load(reader);
            }
        }