I’ve had so many questions about the same problem with the ring_buffer target in Extended Events lately that I figured I would write a blog post that explains all the information I teach about the ring_buffer target and the problems associated with it. Since the release of SQL Server 2012, and the new UI for Extended Events, I have never used the ring_buffer target once, in fact as the title of this post says I really hate the ring_buffer target. In this post I’ll go over the reasons I hate the ring_buffer target and hopefully convince you why you should use the file_target instead.
This is by far the most common problem I have to explain about the ring_buffer target by email. Generally the question is phrased along the lines of:
I got the code below from one of your articles on SQL Server central and it is not working. The problem I have is that when I run the code, it doesn’t show any deadlock graphs even though I know one just occurred in the application. It seems like I only see older deadlocks in the system_health session, but never the most recent one. I’ve turned on Trace 1222 and get the information that way, so why doesn’t this work.”
The reality of the situation is that the events are actually there, you just can’t see them because of a limitation of the sys.dm_xe_session_targets DMV. The target_data column of this DMV can only output roughly 4MB of XML data. The information about the 4MB formated XML limitation of the DMV was explained by Bob Ward on the CSS SQL Server Engineers blog post You may not see the data you expect in Extended Event Ring Buffer Targets…. back in 2009. To demonstrate the effect of this limitation, lets look at the number of events contained in the ring_buffer target for the system_health event session on a SQL Server 2012 SP1+CU7 server that I have permission to share the information from using the following query.
SELECT ring_buffer_event_count, event_node_count, ring_buffer_event_count - event_node_count AS events_not_in_xml FROM ( SELECT target_data.value('(RingBufferTarget/@eventCount)', 'int') AS ring_buffer_event_count, target_data.value('count(RingBufferTarget/event)', 'int') as event_node_count FROM ( SELECT CAST(target_data AS XML) AS target_data FROM sys.dm_xe_sessions as s INNER JOIN sys.dm_xe_session_targets AS st ON s.address = st.event_session_address WHERE s.name = N'system_health' AND st.target_name = N'ring_buffer' ) AS n ) AS t;
The ring_buffer_event_count comes from the eventCount attribute maintained in the root RingBufferTarget element of the XML document returned, and the event_node_count is just a count of the RingBufferTarget/event nodes contained by the XML document returned from the DMV. Here you can see that there are a total of 5000 events contained in the ring_buffer target, based on the new 2012 MAX_EVENTS_LIMIT option which is set at 5000 for the system_health session. Out of those, only 3574 are actually being output by the XML document from the DMV. That leaves us with 1426 events that are not available, but still exist in memory. The XML document outputs the events oldest first, so the events that we can’t see are actually the most recently collected events and are the ones that we’d probably want to actually have access to.
We can look at the memory usage for the target using the query below to compare the binary data size maintained within the target, versus the size of the formatted XML being output by the DMV.
SELECT target_data.value('(RingBufferTarget/@memoryUsed)', 'int') AS buffer_memory_used_bytes, ROUND(target_data.value('(RingBufferTarget/@memoryUsed)', 'int')/1024., 1) AS buffer_memory_used_kb, ROUND(target_data.value('(RingBufferTarget/@memoryUsed)', 'int')/1024/1024., 1) AS buffer_memory_used_MB, DATALENGTH(target_data) AS xml_length_bytes, ROUND(DATALENGTH(target_data)/1024., 1) AS xml_length_kb, ROUND(DATALENGTH(target_data)/1024./1024,1) AS xml_length_MB FROM ( SELECT CAST(target_data AS XML) AS target_data FROM sys.dm_xe_sessions as s INNER JOIN sys.dm_xe_session_targets AS st ON s.address = st.event_session_address WHERE s.name = N'system_health' AND st.target_name = N'ring_buffer') as tab(target_data)
Here we can see that the memory usage is roughly 1.7MB of binary data, but once serialized to formatted XML, the length of the document is roughly 4.7MB in size, which is larger than the published XML size, but only slightly. This is the problem with the nature of the data that can be generated by Extended Events, it can be very compact in binary format, but serializing the formatted XML creates a lot of added overhead for the events that have been collected. The system_health event session is specifically prone to having the maximum, 5000, number of events collected but the ring_buffer, but only a smaller subset of the events can actually be output by the DMV due to the sizes of the events that get collected by that event session. The events most likely to have a fairly large size are the sp_server_diagnostics_component_output and the xml_deadlock_report events, since the sizes of the XML being returned by both of those events depends on the specifics of the conditions for when they fired. I’ve seen sp_server_diagnostics_component_output events that are over 512KB in size on busy servers when problems occur, so that would significantly limit the available data that could be returned by the DMV for the ring_buffer target when one of those events is contained in the output XML.
No UI Support
This for me is probably the biggest reason that I don’t deal with the ring_buffer target anymore in my day to day work. The UI for Extended Events doesn’t support breaking down the information that is contained in the ring_buffer target at all. The only thing the UI does is show the XML that was output by the target_data column of the sys.dm_xe_session_targets DMV.
This means that to consume the data you have to either open the XML and scan down the events, or write XQuery to parse the XML into table form, which requires that you know the events, columns, and actions that were used in the event sessions definition to really access the data. If I am doing short term data collection and don’t want it persisted to a file in the file system on SQL Server 2012, I just opt for the Live View to stream the data back into the grid, where I don’t have to deal with the XML and can rapidly find the information I was interested. For anything long term, or even looking at information from the system_health event session, I use the file_target, which the UI can read and process the events from, eliminating the need to do any XQuery manually.
Watch the Memory Usage
This is a new one to me, but it is something to be incredibly careful about with the way the ring_buffer target gets configured on a production server. Two weeks ago, Andy Galbraith (Blog|Twitter) ran into an issue on a server where all connections were receiving 701 Insufficient System Memory errors. During his analysis Andy realized that the MEMORYCLERK_XE memory clerk in SQL Server was consuming close to 10GB of memory on a server with 16GB of total RAM with ‘max server memory’ configured to 11000MB. The problem was that the event session that was created with a MAX_EVENTS_LIMIT of 1,000,000 events, but with no MAX_MEMORY configuration on the target. Since the memory usage of the target is based on the events that have been captured this is, and no maximum was set, it can essentially use an unlimited amount of memory, leading to problems on the server if memory is limited like the example server Andy was troubleshooting.