Трассировка и «устройство не готово» - PullRequest
0 голосов
/ 12 марта 2012

Я написал очень простое приложение, которое принимает партии строк журнала телеметрии в XML-файлах и транскрибирует их в таблицу на SQL Server.Каждый из этих блоков входящих данных я называю «главой».

По соображениям производительности каждая глава заключена в транзакцию, так что вместо ста неявных транзакций существует одна транзакция для 100 новых строк.,

К сожалению, есть дубликаты, и из-за общего транзакционного контекста они вынимают всю главу.Это редко, но не достаточно редко, чтобы игнорировать.Поэтому я добавил цикл, флаг повтора и перехват (SqlException), который поддерживает список пропусков.Когда строка вставляется при вставке, я откатываю транзакцию, создаю новую, добавляю номер строки в список пропуска, устанавливаю флаг повторных попыток и запускаю цикл.Все строки обрабатываются повторно, за исключением строк, которые пропущены в списке.Если второй ряд barfs, то происходит то же самое, за исключением того, что в скип-листе есть два элемента.Это финальная игра, которая дает мне карри.Когда глава обрабатывается от начала до конца без каких-либо исключений, цикл завершается, и затем я проверяю, пуст ли список пропусков.Когда он не пуст, я пытаюсь использовать Trace.TraceWarning (), чтобы написать запись в журнале событий, детализирующую ошибочные строки и блок XML для более поздней экспертизы.

Это на этомуказать, что дела идут на югЭтот оператор Trace вызывает исключение Win32, в котором утверждается, что «устройство не готово».

Кто-нибудь видел что-нибудь подобное?

За ночь я заметил, что иногда работает без жалоб,Я вставил Thread.Sleep () непосредственно перед Trace.TraceWarning (), поэтому будет интересно посмотреть, решит ли это это.Вероятно, мне следует добавить, что я заметил, что сообщение успешно зарегистрировано в прослушивателе трассировки Visual Studio, и именно это заставляет меня думать, что это связано со скоростью или временем.

Через день и становится ясно, что спящий поток не делаетразница.Я переписал весь бизнес так, что StringBuilder накапливает состояние, и после завершения цикла выполняется единственный оператор трассировки.Win32Exception больше не существует, даже если несколько строк отклонены, что требует более двух проходов.Что именно вызывает это конкретное исключение, остается для меня неясным.Я надеюсь, что кто-то может пролить свет на этот темный угол, но мне больше нечего добавить, так как для каждой главы идеально подходит одна запись журнала;«Обходной путь» будет улучшением кода даже без Win32Exception.

Говорил слишком рано.Злая Win32Exception возвращается.Вот код в его нынешнем виде:

public void Write(string xml)
{
  StringBuilder sb = new StringBuilder();
  //Trace.TraceInformation(xml);
  SqlConnection cxn = new SqlConnection(Properties.Settings.Default.DatabaseBob);
  cxn.Open();
  SqlTransaction txn = null;
  SqlCommand cmd = new SqlCommand("INSERT INTO LOG(MAC, snip more fields ) VALUES(@MAC, snip more params)", cxn);
  var P = cmd.Parameters;
  P.Add(new SqlParameter("@MAC", SqlDbType.BigInt));

  /*snip define more SQL params*/

  XmlDocument doc = new XmlDocument();
  bool barfed = false;
  List<int> skipList = new List<int>();
  _stopwatch.Start();
  int i = 0;
  doc.LoadXml(xml);
  var root = doc.FirstChild.NextSibling;
  P["@MAC"].Value = ulong.Parse(root.Attributes["MAC"].Value, System.Globalization.NumberStyles.AllowHexSpecifier);
  P["@D2"].Value = P["@D3"].Value = 0; //COM2 
  do
    try
    {
      cmd.Transaction = txn = cxn.BeginTransaction();
      i = 0;
      barfed = false;
      for (var n = root.FirstChild; n != null; n = n.NextSibling)
      {
        foreach (XmlAttribute attr in n.Attributes)
        {
          var p = P["@" + attr.Name];
          switch (p.SqlDbType)
          {
            case SqlDbType.DateTime:
              p.Value = DateTime.Parse(attr.Value).ToUniversalTime();
              break;
            case SqlDbType.Float:
              p.Value = float.Parse(attr.Value);
              break;
            case SqlDbType.Bit:
              p.Value = Convert.ToBoolean(int.Parse(attr.Value));
              break;
          }
        }
        i++;
        if (!skipList.Contains(i))
          if ((DateTime)P["@GPSTIME"].Value > DateTime.UtcNow.AddMinutes(1))
          {
            sb.AppendFormat("Node {0} is being skipped because it has a future date.\r\n", i);
            skipList.Add(i);
          }
          else
            cmd.ExecuteNonQuery();
      }
      txn.Commit();
      sb.AppendFormat("{0} logs per {1}ms", i, _stopwatch.ElapsedMilliseconds);
    }
    catch (SqlException)
    {
      sb.AppendFormat("Node {0} is being skipped because it is a duplicate.\r\n", i);
      txn.Rollback();
      skipList.Add(i);
      barfed = true;
    }
    catch (Exception ex)
    {
      sb.AppendLine(ex.Message);
      txn.Rollback();
    }
  while (barfed);
  _stopwatch.Reset();
  if (skipList.Count == 0)
    Trace.TraceInformation(sb.ToString());
  else
  {
    sb.AppendLine();
    sb.Append(xml);
    Trace.TraceWarning(sb.ToString());
  }
}

И это трассировка стека из исключения:

at System.Diagnostics.EventLogInternal.InternalWriteEvent(UInt32 eventID, UInt16 category, EventLogEntryType type, String[] strings, Byte[] rawData, String currentMachineName)
at System.Diagnostics.EventLogInternal.WriteEvent(EventInstance instance, Byte[] data, Object[] values)
at System.Diagnostics.EventLog.WriteEvent(EventInstance instance, Object[] values)
at System.Diagnostics.EventLogTraceListener.TraceEvent(TraceEventCache eventCache, String source, TraceEventType severity, Int32 id, String message)
at System.Diagnostics.TraceInternal.TraceEvent(TraceEventType eventType, Int32 id, String format, Object[] args)
at System.Diagnostics.Trace.TraceWarning(String message)
at Bob.ChapterWriter.Write(String xml) in I:\Project Bob\LogWriter\ChapterWriter.cs:line 179
at SyncInvokeWrite(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)

Метод Write вызывается WCF в ответ на поступающее сообщение MSMQ.Содержимое сообщения - это строка XML.Удаление прослушивателя журнала событий устраняет проблему (пока).

Для тех, кто проверяет трассировку стека, строка 179 находится в конце вставленного кода.Вот оно снова:

Trace.TraceWarning(sb.ToString()); 

Мне приходит в голову мысль: интересно, не потому ли, что XML делает сообщение слишком большим для журнала событий?У меня уже есть настройка, которая ограничивает размер файла главы.Я попробую уменьшить его и посмотрю, что произойдет.

1 Ответ

1 голос
/ 17 марта 2012

Да, настоящая проблема была в слишком большом тексте сообщения.Максимальный размер записи журнала событий составляет 64 КБ, включая все заголовки Microsoft и еще много чего.Я знал это, но мне не приходило в голову, что это может быть проблемой, потому что у меня был ограничен 32K XML.Причина, по которой это было слишком много, заключается в следующем: когда вы помещаете XML в текст, который будет встроен в XML, он экранируется, что увеличивает размер каждого недопустимого символа в четыре раза.Все мои угловые скобки и двойные кавычки были в четыре раза больше.И их много.

Выводы из этого:

  • Не помещайте большие куски XML в сообщения журнала событий.
  • Есливы получаете Win32Exception из трассировки в журнал событий, ваши сообщения, вероятно, слишком велики.
  • Один из способов обойти это ограничение - использовать вместо этого прослушиватель трассировки текстовых файлов.
...