/// <summary> /// Переиндексация /// </summary> /// <param name="validationResult">Результат валидации индекса</param> /// <param name="logFile">Имя лог-файла</param> /// <param name="indexName">Имя индекса</param> private void ReIndex(IndexValidationResult validationResult, String logFile, String indexName) { if (validationResult.State == IndexState.Corrupted && File.Exists(indexName)) // удаляем существующий индекс File.Delete(indexName); using (var source = OpenForReading(logFile)) using (var index = OpenForWriting(indexName)) { var recordId = String.Empty; var recordOffset = 0L; var recordLines = 0; var reader = new LineReader(source); using (var writer = new BinaryWriter(index, Encoding.Default)) { // записываем размер исходного лога writer.Seek(0, SeekOrigin.Begin); writer.Write(source.Length); switch (validationResult.State) { case IndexState.Corrupted: // оставляем место для количества записей в исходном логе writer.Write(validationResult.RecordsCount); break; case IndexState.Obsolete: // подсчет числа записей начнем с последней известной // записи в логе validationResult.RecordsCount--; // перемещаемся к началу последней записи в логе source.Seek(validationResult.LastRecordOffset, SeekOrigin.Begin); // перемещаемся к концу индексного файла минус один элемент индекса // (начинаем переиндексацию с последней известной записи!) writer.Seek(-(Int32)IndexElementSize, SeekOrigin.End); break; } // читаем исходный лог до конца while (!reader.Eof) { // запоминаем текущую позицию в логе var currentOffset = source.Position; // читаем очередную строку лога var rawEntry = EventRecordHelper.GetRawEntry(reader.ReadLine()); // проверяем ее на валидность по числу полей if (!EventRecordHelper.IsValidEntry(rawEntry)) // вероятнее всего, это - недозаписанное событие // в текущем логе, прерываем чтение break; // если сохраненный идентификатор записи не совпадает с // идентификатором считанной строки, эта строка является // первой в записи if (String.Compare(recordId, rawEntry[0]) != 0) { if (!String.IsNullOrEmpty(recordId)) { // сведения о предыдущей записи нужно сохранить // в индексном файле writer.Write(recordOffset); writer.Write(recordLines); // обнуляем счетчик строк в записи recordLines = 0; } // увеличиваем счетчик записей в логе validationResult.RecordsCount++; recordId = rawEntry[0]; recordOffset = currentOffset; } // увеличиваем число строк в записи recordLines++; // последняя запись if (reader.Eof) { // сохраняем сведения о последней записи writer.Write(recordOffset); writer.Write(recordLines); } } // сохраняем итоговое количество записей в индексе writer.Seek(sizeof(Int64), SeekOrigin.Begin); writer.Write(validationResult.RecordsCount); } } }
/// <summary> /// Переиндексация /// </summary> /// <param name="validationResult">Результат валидации индекса</param> /// <param name="logFile">Имя лог-файла</param> /// <param name="indexName">Имя индекса</param> private void ReIndex(IndexValidationResult validationResult, string logFile, string indexName) { if (validationResult.State == IndexState.Corrupted && File.Exists(indexName)) { // удаляем существующий индекс File.Delete(indexName); } using (var source = OpenForReading(logFile)) using (var index = OpenForWriting(indexName)) { var recordId = string.Empty; var recordOffset = 0L; var recordLines = 0; var reader = new LineReader(source); using (var writer = new BinaryWriter(index, Encoding.Default)) { // записываем размер исходного лога writer.Seek(0, SeekOrigin.Begin); writer.Write(source.Length); switch (validationResult.State) { case IndexState.Corrupted: // оставляем место для количества записей в исходном логе writer.Write(validationResult.RecordsCount); break; case IndexState.Obsolete: // подсчет числа записей начнем с последней известной // записи в логе validationResult.RecordsCount--; // перемещаемся к началу последней записи в логе source.Seek(validationResult.LastRecordOffset, SeekOrigin.Begin); // перемещаемся к концу индексного файла минус один элемент индекса // (начинаем переиндексацию с последней известной записи!) writer.Seek(-(int)IndexElementSize, SeekOrigin.End); break; } // читаем исходный лог до конца while (!reader.Eof) { // запоминаем текущую позицию в логе var currentOffset = source.Position; // читаем очередную строку лога var rawEntry = EventRecordHelper.GetRawEntry(reader.ReadLine()); // проверяем ее на валидность по числу полей if (!EventRecordHelper.IsValidEntry(rawEntry)) { // вероятнее всего, это - недозаписанное событие // в текущем логе, прерываем чтение break; } // если сохраненный идентификатор записи не совпадает с // идентификатором считанной строки, эта строка является // первой в записи if (string.Compare(recordId, rawEntry[0]) != 0) { if (!string.IsNullOrEmpty(recordId)) { // сведения о предыдущей записи нужно сохранить // в индексном файле writer.Write(recordOffset); writer.Write(recordLines); // обнуляем счетчик строк в записи recordLines = 0; } // увеличиваем счетчик записей в логе validationResult.RecordsCount++; recordId = rawEntry[0]; recordOffset = currentOffset; } // увеличиваем число строк в записи recordLines++; // последняя запись if (reader.Eof) { // сохраняем сведения о последней записи writer.Write(recordOffset); writer.Write(recordLines); } } // сохраняем итоговое количество записей в индексе writer.Seek(sizeof(Int64), SeekOrigin.Begin); writer.Write(validationResult.RecordsCount); } } }
/// <summary> /// Валидация индекса /// </summary> /// <param name="logFile">Имя исходного лог-файла</param> /// <param name="indexName">Имя индексного файла</param> /// <returns>Результат валидации индекса</returns> private IndexValidationResult ValidateIndex(String logFile, String indexName) { if (!File.Exists(indexName)) // индекс поврежден, если индексного файла не существует return new IndexValidationResult() { State = IndexState.Corrupted }; // открываем индекс на чтение using (var index = OpenForReading(indexName)) { // проверяем его структуру: // размер исходного лога | sizeof(Int64) // число записей | sizeof(Int64) // смещение/количество строк записи 1 | sizeof(Int64) + sizeof(Int32) // смещение/количество строк записи 2 // и т.д. // // т.о. размер индекса должен быть: // sizeof(Int64) * 2 + [число записей] * (sizeof(Int64) + sizeof(Int32)) // размер индекса не может быть меньше, чем минимально допустимый if (index.Length < IndexSize(0)) // индекс поврежден return new IndexValidationResult() { State = IndexState.Corrupted }; // считываем размер исходного лога и число записей using (var reader = new BinaryReader(index, Encoding.Default)) { var sourceLogSize = reader.ReadInt64(); var recordsCount = reader.ReadInt64(); // проверяем размер индекса по считанному числу записей if (index.Length != IndexSize(recordsCount)) // индекс поврежден return new IndexValidationResult() { State = IndexState.Corrupted }; // открываем для чтения исходный лог, нужно определить его размер using (var source = OpenForReading(logFile)) { if (sourceLogSize != source.Length) { // индекс устарел var validationResult = new IndexValidationResult(); validationResult.State = IndexState.Obsolete; validationResult.RecordsCount = recordsCount; // смещение последней записи в логе // с него будет начинаться частичная переиндексация index.Seek(-IndexElementSize, SeekOrigin.End); validationResult.LastRecordOffset = reader.ReadInt64(); return validationResult; } } } } // индекс валиден return new IndexValidationResult() { State = IndexState.Valid }; }
/// <summary> /// Валидация индекса /// </summary> /// <param name="logFile">Имя исходного лог-файла</param> /// <param name="indexName">Имя индексного файла</param> /// <returns>Результат валидации индекса</returns> private IndexValidationResult ValidateIndex(string logFile, string indexName) { if (!File.Exists(indexName)) { // индекс поврежден, если индексного файла не существует return new IndexValidationResult() { State = IndexState.Corrupted } } ; // открываем индекс на чтение using (var index = OpenForReading(indexName)) { // проверяем его структуру: // размер исходного лога | sizeof(Int64) // число записей | sizeof(Int64) // смещение/количество строк записи 1 | sizeof(Int64) + sizeof(Int32) // смещение/количество строк записи 2 // и т.д. // // т.о. размер индекса должен быть: // sizeof(Int64) * 2 + [число записей] * (sizeof(Int64) + sizeof(Int32)) // размер индекса не может быть меньше, чем минимально допустимый if (index.Length < IndexSize(0)) { // индекс поврежден return new IndexValidationResult() { State = IndexState.Corrupted } } ; // считываем размер исходного лога и число записей using (var reader = new BinaryReader(index, Encoding.Default)) { var sourceLogSize = reader.ReadInt64(); var recordsCount = reader.ReadInt64(); // проверяем размер индекса по считанному числу записей if (index.Length != IndexSize(recordsCount)) { // индекс поврежден return new IndexValidationResult() { State = IndexState.Corrupted } } ; // открываем для чтения исходный лог, нужно определить его размер using (var source = OpenForReading(logFile)) { if (sourceLogSize != source.Length) { // индекс устарел var validationResult = new IndexValidationResult(); validationResult.State = IndexState.Obsolete; validationResult.RecordsCount = recordsCount; // смещение последней записи в логе // с него будет начинаться частичная переиндексация index.Seek(-IndexElementSize, SeekOrigin.End); validationResult.LastRecordOffset = reader.ReadInt64(); return(validationResult); } } } } // индекс валиден return(new IndexValidationResult() { State = IndexState.Valid }); }