我编写了一个非常简单的应用程序,它接受XML文件中的批量遥测日志行,并将它们转录到SQL Server上的表中。这些输入数据块中的每一个都称为“章节”。
出于性能原因,每个章节都包含在一个事务中,因此,对于100个新行,只有一个事务,而不是一百个隐式事务。
不幸的是,存在重复,并且由于共享的事务上下文,它们取出了整章。这种情况并不常见,但并不罕见。所以我添加了一个循环,一个重试标志和一个维护跳过列表的catch(SqlException)。当插入行barfs时,我回滚事务,创建一个新事务,将行号添加到跳过列表,设置重试标志并让它循环。除了跳过列出的行之外,所有行都被重新处理。如果是第二行barfs,除了skip-list有两个项目之外,会发生同样的事情。
这整个安排都有效。这是给我咖喱的最后一场比赛。当章节被端到端地处理而没有任何例外时,循环退出然后我检查跳过列表是否为空。当不是为空时,我尝试使用Trace.TraceWarning()编写一个事件日志条目,详细说明失败的行和XML块以供以后取证。
正是在这一点上事情向南发展。此跟踪语句barfs具有声称“设备未就绪”的Win32异常。
有没有人见过这样的东西?
一夜之间,我注意到偶尔会无怨无悔。我已经在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)
WCF在响应MSMQ消息到达时调用Write方法。消息的内容是XML字符串。删除事件日志侦听器会使问题消失(到目前为止)。
对于那些检查堆栈跟踪的人,第179行位于粘贴代码的末尾。这又是:
Trace.TraceWarning(sb.ToString());
我想到了一个想法:我想知道是因为XML使消息对事件日志来说太大了吗?我已经有一个限制章节文件大小的设置。我会尝试减少它,看看会发生什么。
答案 0 :(得分:1)
是的,真正的问题是超大的消息文本。事件日志条目的最大大小为64K,包括所有Microsoft标头和诸如此类的内容。我知道,但我没有想到这可能是问题因为我将XML限制在32K。它太多的原因是:当你将XML放入将嵌入XML的文本中时,它会被转义,这会使每个非法字符的大小增加四倍。我的所有尖括号和双引号的大小都是四倍。而且有很多。
这样做的结果是: