2012-03-12 3 views
0

아주 간단한 응용 프로그램을 작성했습니다. 원격 파일 로깅 행의 일괄 처리를 XML 파일로 받아 들여 SQL Server의 테이블로 옮깁니다. 들어오는 데이터 블록의 각 하나는 "챕터"라고 부릅니다.추적 및 '장치가 준비되지 않았습니다.'

성능상의 이유로 각 장은 100 개의 암시 적 트랜잭션 대신 100 개의 새로운 행에 대한 단일 트랜잭션이 있도록 트랜잭션으로 래핑됩니다.

불행히도 중복되는 부분이 있으며 공유 된 트랜잭션 컨텍스트로 인해 전체 장이 제거됩니다. 이것은 흔하지는 않지만 무시할만큼 희귀하지는 않습니다. 그래서 루프, 재시도 플래그 및 건너 뛰기 목록을 유지하는 catch (SqlException)를 추가했습니다. 행에 barfs가 삽입되면 트랜잭션을 롤백하고 새 트랜잭션을 만들고 건너 뛰기 목록에 행 번호를 추가하고 재시도 플래그를 설정 한 다음 루프를 반복합니다. 건너 뛸 목록에있는 행을 제외하고 모든 행이 다시 처리됩니다. 두 번째 행이 barfs이면 건너 뛰기 목록에 두 개의 항목이 있다는 점을 제외하면 똑같은 일이 발생합니다.

이 전체 배열이 작동합니다. 저에게 카레를주는 마지막 게임입니다. 챕터가 더 이상 예외없이 끝에서 끝까지 처리되면 루프가 종료 된 다음 건너 뛰기 목록이 비어 있는지 확인합니다. 이 비어 있지 않으면이 비어 있습니다. Trace.TraceWarning()을 사용하여 실패한 행에 대해 자세히 설명하는 이벤트 로그 항목을 작성하고 이후 포렌식에 대한 XML 블록을 작성합니다.

이 시점에서 상황은 계속됩니다. 이 Trace 문은 'device not ready'라는 Win32 예외가있는 barfs입니다.

이런 사람이 있습니까?

밤새 나는 으로 보았습니다. 불만없이 작동합니다. Trace.TraceWarning() 바로 앞에 Thread.Sleep()을 삽입 했으므로이 클래스가 정렬되는지 여부를 확인하는 것이 좋습니다. 아마도 메시지가 Visual Studio의 추적 수신기에 성공적으로 기록되었다는 사실을 알리면 속도 또는 타이밍과 관련된 것으로 생각됩니다.

다음날과 그 스레드를자는 것이 아무런 차이가 없다는 것이 분명합니다. 나는 전체 비즈니스를 다시 작성하여 StringBuilder가 상태를 축적하고 루프가 종료 된 후에 단일 trace 문이 발생합니다. 두 개 이상의 패스가 필요한 여러 행이 거부 된 경우에도 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 메소드가 도착 MSMQ를 메시지에 대한 응답으로 WCF에 의해 호출됩니다. 메시지의 내용은 XML 문자열입니다. 이벤트 로그 리스너를 제거하면 문제가 사라집니다 (지금까지).

스택 추적을 검사하는 경우 179 행은 붙여 넣은 코드의 끝에 있습니다. 여기에 다시입니다 :

Trace.TraceWarning(sb.ToString()); 

생각이 나에게 오는 다음 XML 이벤트 로그에 대한 메시지가 너무 큰 수 있기 때문에 궁금하다? 이미 챕터 파일의 크기를 제한하는 설정이 있습니다.그것을 줄이려고 시도하고 무슨 일이 일어나는 지 봅니다.

+0

예외의 스택 추적을 게시하십시오. –

+0

"ReportEvent"라는 네이티브 Windows API 함수가 실패했습니다. 컴퓨터의 안정성, 특히 디스크의 안정성에 대해 조금은 걱정할 필요가 있습니다. 확실히 다른 디스크에서 시도하십시오. 내가 상상할 수있는 유일한 다른 점은 이벤트 로그에 너무 많은 이벤트가 넘쳐 흐르고 있다는 것입니다. –

+0

SSD이므로 자기 플래터 디스크가 아닙니다. 현재 120 초당 하나의 로그 항목. 어쨌든 문제는 실제로 사후에 대비해 XML을 포함하여 과도하게 큰 메시지 텍스트였습니다. 챕터 크기를 제한하는 설정이 있었기 때문에 16K로 줄였습니다. 이제 모든 것이 늠름한 것 같습니다. –

답변

1

그래, 진짜 문제는 크기가 큰 메시지 텍스트입니다. 이벤트 로그 항목의 최대 크기는 모든 Microsoft 헤더와 기타 등 64K입니다. 나는 XML을 32K로 제한했기 때문에 이것이 문제가 될 수 있다는 것을 알지 못했다. 너무 많은 이유는 XML에 삽입 할 텍스트에 XML을 넣으면 이스케이프 처리가되어 모든 불법 문자의 크기를 4 배로 늘립니다. 모든 꺽쇠 괄호와 큰 따옴표는 크기가 4 배가되었습니다. 그리고 그들 중 많은 수가 있습니다. 이에서

테이크가는 요리는 다음과 같습니다

  • 이벤트 로그 메시지에 XML의 큰 덩어리를 넣지 마십시오.
  • 추적에서 이벤트 로그로 Win32Exception이 발생하면 메시지가 너무 클 수 있습니다.
  • 이 제한을 피하는 한 가지 방법은 대신 텍스트 파일 추적 수신기를 사용하는 것입니다.
관련 문제