Конвертер D5-DRX. Оптимизация утилиты миграции тел документов

7 0

Мы обнаружили недостаток утилиты выгрузки тел документов при миграции из Directum 5 в Directum RX в некоторых ситуациях и предложили решение для оптимизации процесса переноса.

Суть проблемы

Программа для переноса файлов из хранилищ Directum 5 в Directum RX располагается в папке RXStorageMigrator конвертера. В случае отсутствия тел документов в SQL-хранилище базовая версия утилиты переноса тел будет работает корректно, однако основной недостаток заключается в выгрузке тел в оперативную память при переносе тел документов, располагающихся в SQL-хранилище, что в свою очередь будет работать со сбоями при переносе объема данных выше вместительности оперативной памяти.

Ниже предложена модификация исходного кода данной утилиты для поочередной выгрузки в оперативную память и переноса тел документов в хранилища Directum RX.

Решение

Исходные коды хранятся в папке RXStorageMigrator_src конвертера. Код выгрузки всех тел документов в оперативную память располагается в файле StorageMigrtator.cs в методе GetMigratedFiles и вызывается внутри метода MigrateBodiesFromD5ToRXFS.

 

/// <summary>
/// Получить список файлов для миграции.
/// </summary>
/// <param name="connection">Соединение.</param>
/// <returns>Список файлов, подлежащих миграциия.</returns>
private List<IFSFile> GetMigratedFiles(SqlConnection connection)
{
  var result = new List<IFSFile>();
  var query = Constants.ConstLib.SelectMigratedDocsFromD5;
  var command = new SqlCommand(query, connection);
  command.CommandTimeout = 0;

  using (var reader = command.ExecuteReader())
  {
    while (reader.Read())
    {
      var file = new FSFile();
      file.Id = reader.GetInt32(0);
      file.Version = reader.GetInt32(1);
      file.SourceFileStorage = this.SourceFileStorages[reader.GetInt32(2)];

      //Guid bodyId;
      //var guidToParse = reader.GetString(3);
      //if (!Guid.TryParse(guidToParse, out bodyId))
      //{
      //  Program.logger.Error($"Body with id {guidToParse} is ignored.");
      //  continue;
      //}
      file.BodyId = reader.GetGuid(3);

      file.Body = reader.IsDBNull(4) ? null : (byte[])reader[4];
      file.Extension = reader.GetString(5);
      file.RXDocId = reader.GetInt32(6);
      file.TargetFileStorage = this.TargetFileStorages[reader.GetInt32(7)];

      file.MigrStatus = Constants.ConstLib.MigrStatusError;
      file.SourceFilePath = string.Empty;

      result.Add(file);
    }
  }
  return result;
}

Оптимизация заключается в переносе тела документа сразу после его получения внутри метода GetMigratedFiles с помощью вызова метода MigrateFile.

 

/// <summary>
/// Получить список файлов для миграции.
/// </summary>
/// <param name="connection">Соединение.</param>
/// <returns>Список файлов, подлежащих миграциия.</returns>
private void GetMigratedFiles(SqlConnection connection)
{
  // Пути, к которым не удалось получить доступ.
  var errorPath = new System.Collections.Concurrent.ConcurrentDictionary<int, string>();
  var query = Constants.ConstLib.SelectMigratedDocsFromD5;
  var command = new SqlCommand(query, connection);
  byte[] fileBody;
  command.CommandTimeout = 0;

  using (var reader = command.ExecuteReader())
  {
    while (reader.Read())
    {
      var file = new FSFile();
      file.Id = reader.GetInt32(0);
      file.Version = reader.GetInt32(1);
      file.SourceFileStorage = this.SourceFileStorages[reader.GetInt32(2)];
      file.BodyId = reader.GetGuid(3);

      fileBody = reader.IsDBNull(4) ? null : (byte[])reader[4];
      file.Extension = reader.GetString(5);
      file.RXDocId = reader.GetInt32(6);
      file.TargetFileStorage = this.TargetFileStorages[reader.GetInt32(7)];
      file.MigrStatus = Constants.ConstLib.MigrStatusError;
      file.SourceFilePath = string.Empty;

      MigrateFile(file, fileBody, errorPath);
    }
  }
}

public void MigrateFile(FSFile file, byte[] body, ConcurrentDictionary<int, string> errorPath)
{
    Console.WriteLine($"Перенос тела документа с RX Id = {file.RXDocId} и 5 Id = {file.BodyId}");

    if (file.SourceFileStorage.StorageType == Constants.ConstLib.SQLStorage)
    {
        MigrateBodyFromDBToFS(file, body);
        Console.WriteLine($"Завершено");
    }
    else
    {
        var message = string.Empty;
        var storagePath = file.SourceFileStorage.Path;
        if (!errorPath.ContainsKey(file.SourceFileStorage.Id))
        {
            try
            {
                file.SourceFilePath = Directory.EnumerateFiles(file.SourceFolderPath, "*.*")
                .FirstOrDefault(x => x.EndsWith($"({file.Id} v{file.Version}).{file.Extension}") && !x.Contains("~$"));

                if (string.IsNullOrWhiteSpace(file.SourceFilePath))
                {
                    var files = Directory.EnumerateFiles(file.SourceFolderPath, "*.*");
                    foreach (var item in files)
                    {
                        if (Path.GetFileNameWithoutExtension(item).EndsWith($"({file.Id} v{file.Version})"))
                        {
                            file.SourceFilePath = item;
                            break;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                try
                {
                    Directory.GetFiles(storagePath);
                }
                catch
                {
                    errorPath.TryAdd(file.SourceFileStorage.Id, e.Message);
                }
                message = $"Ошибка при получении доступа к папке {storagePath}: {e.Message}";
                Program.logger.Error(message);
                if (this.FixFileStorageError)
                    SetDataInTransferredTable(file, message);
                return;
            }
            if (string.IsNullOrWhiteSpace(file.SourceFilePath))
            {
                message = $"Файл с окончанием '({file.Id} v{file.Version}).{file.Extension}' в папке по пути {file.SourceFolderPath} не найден.";
                Program.logger.Error(message);
                SetDataInTransferredTable(file, message);
                return;
            }
            MigrateBodyFromFSToFS(file);
            Console.WriteLine($"Завершено");
        }
        else
        {
            if (this.FixFileStorageError)
                SetDataInTransferredTable(file, $"Ошибка при получении доступа к папке {storagePath}: {errorPath[file.SourceFileStorage.Id]}");
        }
    }
}

 

В данном коде также расширено логирование для мониторинга активности переноса тел документов. Он переносит файл сразу в момент обработки записи из результирующего запроса к БД Directum 5, работает в одном потоке и может быть модифицирован.

В методе MigrateBodiesFromD5ToRXFS необходимо убрать лишнюю обработку файлов. Итоговый код метода будет выглядеть так:

public void MigrateBodiesFromD5ToRXFS()
{
  try
  {
    using (var connection = new SqlConnection())
    {
      connection.ConnectionString = this.ConnectionStringD5;
      connection.Open();

      Console.WriteLine("Connection State: {0}", connection.State);

      var query = Constants.ConstLib.SelectCountMigratedDocsFromD5;
      var command = new SqlCommand(query, connection);
          
      command.CommandTimeout = 0;

      GetMigratedFiles(connection);
    }
  }
  catch (Exception ex)
  {
    Program.logger.Error(ex);
  }
}

 

Пока комментариев нет.

Авторизуйтесь, чтобы написать комментарий