Remove XElite for now (#1349)

* Revert "Revert "Revert "Update to XElite (#1287)" (#1334)" (#1337)"

This reverts commit 196364e81e.

* Add changed sr.xlf
This commit is contained in:
Alex Ma
2021-12-22 11:04:45 -08:00
committed by GitHub
parent 5ca4054582
commit 18ca177767
15 changed files with 326 additions and 243 deletions

View File

@@ -30,7 +30,6 @@
<PackageReference Update="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" /> <PackageReference Update="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" />
<PackageReference Update="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.10.0" /> <PackageReference Update="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.10.0" />
<PackageReference Update="Microsoft.CodeAnalysis.Workspaces.Common" Version="3.10.0" /> <PackageReference Update="Microsoft.CodeAnalysis.Workspaces.Common" Version="3.10.0" />
<PackageReference Update="Microsoft.SqlServer.XEvent.XELite" Version="2021.6.21.10" />
<PackageReference Update="Moq" Version="4.8.2" /> <PackageReference Update="Moq" Version="4.8.2" />
<PackageReference Update="nunit" Version="3.12.0" /> <PackageReference Update="nunit" Version="3.12.0" />

View File

@@ -8756,16 +8756,6 @@ namespace Microsoft.SqlTools.ServiceLayer
return Keys.GetString(Keys.SessionAlreadyExists, sessionName); return Keys.GetString(Keys.SessionAlreadyExists, sessionName);
} }
public static string SessionMissingDetails(int id)
{
return Keys.GetString(Keys.SessionMissingDetails, id);
}
public static string StartProfilingFailed(String error)
{
return Keys.GetString(Keys.StartProfilingFailed, error);
}
public static string UnknownSizeUnit(string unit) public static string UnknownSizeUnit(string unit)
{ {
return Keys.GetString(Keys.UnknownSizeUnit, unit); return Keys.GetString(Keys.UnknownSizeUnit, unit);
@@ -9954,12 +9944,6 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string SessionAlreadyExists = "SessionAlreadyExists"; public const string SessionAlreadyExists = "SessionAlreadyExists";
public const string SessionMissingDetails = "SessionMissingDetails";
public const string StartProfilingFailed = "StartProfilingFailed";
public const string CategoryLocal = "CategoryLocal"; public const string CategoryLocal = "CategoryLocal";

View File

@@ -1571,16 +1571,6 @@
<value>An XEvent session named {0} already exists</value> <value>An XEvent session named {0} already exists</value>
<comment>. <comment>.
Parameters: 0 - sessionName (String) </comment> Parameters: 0 - sessionName (String) </comment>
</data>
<data name="SessionMissingDetails" xml:space="preserve">
<value>Unable to start streaming session {0} due to missing session details.</value>
<comment>.
Parameters: 0 - id (int) </comment>
</data>
<data name="StartProfilingFailed" xml:space="preserve">
<value>Failed to start profiler: {0}</value>
<comment>.
Parameters: 0 - error (String) </comment>
</data> </data>
<data name="CategoryLocal" xml:space="preserve"> <data name="CategoryLocal" xml:space="preserve">
<value>[Uncategorized (Local)]</value> <value>[Uncategorized (Local)]</value>

View File

@@ -759,8 +759,7 @@ PauseSessionFailed(String error) = Failed to pause session: {0}
StopSessionFailed(String error) = Failed to stop session: {0} StopSessionFailed(String error) = Failed to stop session: {0}
SessionNotFound = Cannot find requested XEvent session SessionNotFound = Cannot find requested XEvent session
SessionAlreadyExists(String sessionName) = An XEvent session named {0} already exists SessionAlreadyExists(String sessionName) = An XEvent session named {0} already exists
SessionMissingDetails(int id) = Unable to start streaming session {0} due to missing session details.
StartProfilingFailed(String error) = Failed to start profiler: {0}
;job categories ;job categories
CategoryLocal = [Uncategorized (Local)] CategoryLocal = [Uncategorized (Local)]

View File

@@ -5696,18 +5696,6 @@
<target state="new">Specifies whether the check constraint is Enabled</target> <target state="new">Specifies whether the check constraint is Enabled</target>
<note></note> <note></note>
</trans-unit> </trans-unit>
<trans-unit id="SessionMissingDetails">
<source>Unable to start streaming session {0} due to missing session details.</source>
<target state="new">Unable to start streaming session {0} due to missing session details.</target>
<note>.
Parameters: 0 - id (int) </note>
</trans-unit>
<trans-unit id="StartProfilingFailed">
<source>Failed to start profiler: {0}</source>
<target state="new">Failed to start profiler: {0}</target>
<note>.
Parameters: 0 - error (String) </note>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -26,7 +26,6 @@
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" /> <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" />
<PackageReference Include="Microsoft.SqlServer.DACFx" /> <PackageReference Include="Microsoft.SqlServer.DACFx" />
<PackageReference Include="Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider" /> <PackageReference Include="Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider" />
<PackageReference Include="Microsoft.SqlServer.XEvent.XELite" />
<PackageReference Include="System.Text.Encoding.CodePages" /> <PackageReference Include="System.Text.Encoding.CodePages" />
<PackageReference Include="Microsoft.SqlServer.Assessment" /> <PackageReference Include="Microsoft.SqlServer.Assessment" />
<PackageReference Include="Microsoft.SqlServer.Migration.Assessment" /> <PackageReference Include="Microsoft.SqlServer.Migration.Assessment" />

View File

@@ -22,7 +22,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
/// <summary> /// <summary>
/// Starts monitoring a profiler session /// Starts monitoring a profiler session
/// </summary> /// </summary>
void StartMonitoringSession(string viewerId, IXEventSession session); bool StartMonitoringSession(string viewerId, IXEventSession session);
/// <summary> /// <summary>
/// Stops monitoring a profiler session /// Stops monitoring a profiler session

View File

@@ -2,8 +2,6 @@
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
// //
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlServer.Management.XEvent;
namespace Microsoft.SqlTools.ServiceLayer.Profiler namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
@@ -17,16 +15,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
/// </summary> /// </summary>
int Id { get; } int Id { get; }
/// <summary>
/// Connection details associated with the session id.
/// </summary>
ConnectionDetails ConnectionDetails { get; set; }
/// <summary>
/// Session associated with the session id.
/// </summary>
Session Session { get; set; }
/// <summary> /// <summary>
/// Starts XEvent session /// Starts XEvent session
/// </summary> /// </summary>

View File

@@ -18,7 +18,6 @@ using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.XEvent; using Microsoft.SqlServer.Management.XEvent;
using Microsoft.SqlServer.Management.XEventDbScoped; using Microsoft.SqlServer.Management.XEventDbScoped;
using Microsoft.SqlServer.XEvent.XELite;
using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Protocol.Contracts; using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection;
@@ -121,42 +120,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
this.SessionMonitor.AddSessionListener(this); this.SessionMonitor.AddSessionListener(this);
} }
/// <summary>
/// Handle request to start a profiling session
/// </summary>
internal async Task HandleStartProfilingRequest(StartProfilingParams parameters, RequestContext<StartProfilingResult> requestContext)
{
await Task.Run(async () =>
{
try
{
Logger.Write(TraceEventType.Verbose, "HandleStartProfilingRequest started");
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo != null)
{
// create a new XEvent session and Profiler session
var xeSession = this.XEventSessionFactory.GetXEventSession(parameters.SessionName, connInfo);
monitor.StartMonitoringSession(parameters.OwnerUri, xeSession);
var result = new StartProfilingResult();
await requestContext.SendResult(result);
}
else
{
Logger.Write(TraceEventType.Error, "Connection Info could not be found for " + parameters.OwnerUri);
throw new Exception(SR.ProfilerConnectionNotFound);
}
}
catch (Exception e)
{
Logger.Write(TraceEventType.Error, "HandleStartProfilingRequest failed for uri " + parameters.OwnerUri);
await requestContext.SendError(new Exception (SR.StartProfilingFailed(e.Message), e));
}
});
}
/// <summary> /// <summary>
/// Handle request to start a profiling session /// Handle request to start a profiling session
/// </summary> /// </summary>
@@ -166,24 +129,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
try try
{ {
Logger.Write(TraceEventType.Verbose, "HandleCreateXEventSessionRequest started");
ConnectionInfo connInfo; ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection( ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri, parameters.OwnerUri,
out connInfo); out connInfo);
if (connInfo == null) if (connInfo == null)
{ {
Logger.Write(TraceEventType.Error, "Connection Info could not be found for " + parameters.OwnerUri);
throw new Exception(SR.ProfilerConnectionNotFound); throw new Exception(SR.ProfilerConnectionNotFound);
} }
else if (parameters.SessionName == null) else if (parameters.SessionName == null)
{ {
Logger.Write(TraceEventType.Error, "Session Name could not be found for " + parameters.OwnerUri);
throw new ArgumentNullException("SessionName"); throw new ArgumentNullException("SessionName");
} }
else if (parameters.Template == null) else if (parameters.Template == null)
{ {
Logger.Write(TraceEventType.Error, "Template could not be found for " + parameters.OwnerUri);
throw new ArgumentNullException("Template"); throw new ArgumentNullException("Template");
} }
else else
@@ -197,13 +156,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
xeSession = this.XEventSessionFactory.GetXEventSession(parameters.SessionName, connInfo); xeSession = this.XEventSessionFactory.GetXEventSession(parameters.SessionName, connInfo);
} }
catch { catch { }
Logger.Write(TraceEventType.Verbose, "Session with name '" + parameters.SessionName + "' was not found");
}
if (xeSession == null) if (xeSession == null)
{ {
Logger.Write(TraceEventType.Verbose, "Creating new XEventSession with SessionName " + parameters.SessionName);
// create a new XEvent session and Profiler session // create a new XEvent session and Profiler session
xeSession = this.XEventSessionFactory.CreateXEventSession(parameters.Template.CreateStatement, parameters.SessionName, connInfo); xeSession = this.XEventSessionFactory.CreateXEventSession(parameters.Template.CreateStatement, parameters.SessionName, connInfo);
} }
@@ -219,8 +175,42 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Write(TraceEventType.Error, "HandleCreateXEventSessionRequest failed for uri " + parameters.OwnerUri); await requestContext.SendError(new Exception(SR.CreateSessionFailed(e.Message)));
await requestContext.SendError(new Exception (SR.CreateSessionFailed(e.Message), e)); }
});
}
/// <summary>
/// Handle request to start a profiling session
/// </summary>
internal async Task HandleStartProfilingRequest(StartProfilingParams parameters, RequestContext<StartProfilingResult> requestContext)
{
await Task.Run(async () =>
{
try
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo != null)
{
// create a new XEvent session and Profiler session
var xeSession = this.XEventSessionFactory.GetXEventSession(parameters.SessionName, connInfo);
// start monitoring the profiler session
monitor.StartMonitoringSession(parameters.OwnerUri, xeSession);
var result = new StartProfilingResult();
await requestContext.SendResult(result);
}
else
{
throw new Exception(SR.ProfilerConnectionNotFound);
}
}
catch (Exception e)
{
await requestContext.SendError(new Exception(SR.StartSessionFailed(e.Message)));
} }
}); });
} }
@@ -234,7 +224,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
try try
{ {
Logger.Write(TraceEventType.Verbose, "HandleStopProfilingRequest started");
ProfilerSession session; ProfilerSession session;
monitor.StopMonitoringSession(parameters.OwnerUri, out session); monitor.StopMonitoringSession(parameters.OwnerUri, out session);
@@ -251,12 +240,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
await requestContext.SendResult(new StopProfilingResult { }); await requestContext.SendResult(new StopProfilingResult { });
break; break;
} }
catch (InvalidOperationException e) catch (InvalidOperationException)
{ {
remainingAttempts--; remainingAttempts--;
if (remainingAttempts == 0) if (remainingAttempts == 0)
{ {
Logger.Write(TraceEventType.Error, "Stop profiler session '" + session.XEventSession.Session.Name + "' failed after three retries, last exception was: " + e.Message);
throw; throw;
} }
Thread.Sleep(500); Thread.Sleep(500);
@@ -270,7 +258,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Write(TraceEventType.Error, "HandleStopProfilingRequest failed for uri " + parameters.OwnerUri);
await requestContext.SendError(new Exception(SR.StopSessionFailed(e.Message))); await requestContext.SendError(new Exception(SR.StopSessionFailed(e.Message)));
} }
}); });
@@ -285,14 +272,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
try try
{ {
Logger.Write(TraceEventType.Verbose, "HandlePauseProfilingRequest started");
monitor.PauseViewer(parameters.OwnerUri); monitor.PauseViewer(parameters.OwnerUri);
await requestContext.SendResult(new PauseProfilingResult { }); await requestContext.SendResult(new PauseProfilingResult { });
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Write(TraceEventType.Error, "HandlePauseProfilingRequest failed for uri " + parameters.OwnerUri);
await requestContext.SendError(new Exception(SR.PauseSessionFailed(e.Message))); await requestContext.SendError(new Exception(SR.PauseSessionFailed(e.Message)));
} }
}); });
@@ -307,7 +292,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
try try
{ {
Logger.Write(TraceEventType.Verbose, "HandleGetXEventSessionsRequest started");
var result = new GetXEventSessionsResult(); var result = new GetXEventSessionsResult();
ConnectionInfo connInfo; ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection( ConnectionServiceInstance.TryFindConnection(
@@ -315,7 +299,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
out connInfo); out connInfo);
if (connInfo == null) if (connInfo == null)
{ {
Logger.Write(TraceEventType.Error, "Connection Info could not be found for " + parameters.OwnerUri);
await requestContext.SendError(new Exception(SR.ProfilerConnectionNotFound)); await requestContext.SendError(new Exception(SR.ProfilerConnectionNotFound));
} }
else else
@@ -327,7 +310,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Write(TraceEventType.Error, "HandleGetXEventSessionsRequest failed for uri " + parameters.OwnerUri);
await requestContext.SendError(e); await requestContext.SendError(e);
} }
}); });
@@ -342,13 +324,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
try try
{ {
Logger.Write(TraceEventType.Verbose, "HandleDisconnectSessionRequest started");
ProfilerSession session; ProfilerSession session;
monitor.StopMonitoringSession(parameters.OwnerUri, out session); monitor.StopMonitoringSession(parameters.OwnerUri, out session);
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Write(TraceEventType.Error, "HandleDisconnectSessionRequest failed for uri " + parameters.OwnerUri);
await requestContext.SendError(e); await requestContext.SendError(e);
} }
}); });
@@ -394,15 +374,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
return store; return store;
} }
/// <summary>
/// Nulls out properties in ConnectionDetails that aren't compatible with XElite.
/// </summary>
private static void RemoveIncompatibleConnectionProperties(ConnectionDetails connDetails){
connDetails.ConnectRetryCount = null;
connDetails.ConnectRetryInterval = null;
connDetails.MultiSubnetFailover = null;
}
/// <summary> /// <summary>
/// Gets an XEvent session with the given name per the IXEventSessionFactory contract /// Gets an XEvent session with the given name per the IXEventSessionFactory contract
/// Also starts the session if it isn't currently running /// Also starts the session if it isn't currently running
@@ -412,7 +383,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
var sqlConnection = ConnectionService.OpenSqlConnection(connInfo); var sqlConnection = ConnectionService.OpenSqlConnection(connInfo);
SqlStoreConnection connection = new SqlStoreConnection(sqlConnection); SqlStoreConnection connection = new SqlStoreConnection(sqlConnection);
BaseXEStore store = CreateXEventStore(connInfo, connection); BaseXEStore store = CreateXEventStore(connInfo, connection);
RemoveIncompatibleConnectionProperties(connInfo.ConnectionDetails);
Session session = store.Sessions[sessionName]; Session session = store.Sessions[sessionName];
// start the session if it isn't already running // start the session if it isn't already running
@@ -429,7 +399,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
// create xevent session wrapper // create xevent session wrapper
return new XEventSession() return new XEventSession()
{ {
ConnectionDetails = connInfo.ConnectionDetails,
Session = session Session = session
}; };
} }

View File

@@ -17,13 +17,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
/// </summary> /// </summary>
public class ProfilerSession public class ProfilerSession
{ {
public bool IsStreaming { get; set; } private static readonly TimeSpan DefaultPollingDelay = TimeSpan.FromSeconds(1);
private object pollingLock = new object();
private bool isPolling = false;
private DateTime lastPollTime = DateTime.Now.Subtract(DefaultPollingDelay);
private TimeSpan pollingDelay = DefaultPollingDelay;
private ProfilerEvent lastSeenEvent = null; private ProfilerEvent lastSeenEvent = null;
private bool eventsLost = false; private bool eventsLost = false;
int lastSeenId = -1; int lastSeenId = -1;
public bool pollImmediatly = false;
/// <summary> /// <summary>
/// Connection to use for the session /// Connection to use for the session
/// </summary> /// </summary>
@@ -34,6 +39,57 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
/// </summary> /// </summary>
public IXEventSession XEventSession { get; set; } public IXEventSession XEventSession { get; set; }
/// <summary>
/// Try to set the session into polling mode if criteria is meet
/// </summary>
/// <returns>True if session set to polling mode, False otherwise</returns>
public bool TryEnterPolling()
{
lock (this.pollingLock)
{
if (pollImmediatly || (!this.isPolling && DateTime.Now.Subtract(this.lastPollTime) >= pollingDelay))
{
this.isPolling = true;
this.lastPollTime = DateTime.Now;
this.pollImmediatly = false;
return true;
}
else
{
return false;
}
}
}
/// <summary>
/// Is the session currently being polled
/// </summary>
public bool IsPolling
{
get
{
return this.isPolling;
}
set
{
lock (this.pollingLock)
{
this.isPolling = value;
}
}
}
/// <summary>
/// The delay between session polls
/// </summary>
public TimeSpan PollingDelay
{
get
{
return pollingDelay;
}
}
/// <summary> /// <summary>
/// Could events have been lost in the last poll /// Could events have been lost in the last poll
/// </summary> /// </summary>
@@ -50,7 +106,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
/// </summary> /// </summary>
private bool IsProfilerEvent(ProfilerEvent currentEvent) private bool IsProfilerEvent(ProfilerEvent currentEvent)
{ {
if (string.IsNullOrWhiteSpace(currentEvent.Name) || currentEvent.Values == null) if (string.IsNullOrWhiteSpace(currentEvent.Name) || currentEvent.Values == null)
{ {
return false; return false;
} }
@@ -89,7 +145,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
public void FilterOldEvents(List<ProfilerEvent> events) public void FilterOldEvents(List<ProfilerEvent> events)
{ {
this.eventsLost = false; this.eventsLost = false;
if (lastSeenId != -1) if (lastSeenId != -1)
{ {
// find the last event we've previously seen // find the last event we've previously seen
@@ -113,7 +169,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
events.RemoveRange(0, idx + 1); events.RemoveRange(0, idx + 1);
} }
else if (earliestSeenEventId > (lastSeenId + 1)) else if(earliestSeenEventId > (lastSeenId + 1))
{ {
// if there's a gap between the expected next event sequence // if there's a gap between the expected next event sequence
// and the furthest back event seen, we know we've lost events // and the furthest back event seen, we know we've lost events

View File

@@ -4,13 +4,19 @@
// //
using System; using System;
using System.Linq; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.SqlServer.XEvent.XELite; using System.Xml;
using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.XEvent;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts; using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Profiler namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
@@ -19,10 +25,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
/// </summary> /// </summary>
public class ProfilerSessionMonitor : IProfilerSessionMonitor public class ProfilerSessionMonitor : IProfilerSessionMonitor
{ {
private const int PollingLoopDelay = 1000;
private object sessionsLock = new object(); private object sessionsLock = new object();
private object listenersLock = new object(); private object listenersLock = new object();
private object pollingLock = new object();
private Task processorThread = null; private Task processorThread = null;
private struct Viewer private struct Viewer
@@ -46,9 +56,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
// XEvent Session Id's matched to their Profiler Sessions // XEvent Session Id's matched to their Profiler Sessions
private Dictionary<int, ProfilerSession> monitoredSessions = new Dictionary<int, ProfilerSession>(); private Dictionary<int, ProfilerSession> monitoredSessions = new Dictionary<int, ProfilerSession>();
// XEvent Session Id's matched to their stream cancellation tokens
private Dictionary<int, CancellationTokenSource> monitoredCancellationTokenSources = new Dictionary<int, CancellationTokenSource>();
// ViewerId -> Viewer objects // ViewerId -> Viewer objects
private Dictionary<string, Viewer> allViewers = new Dictionary<string, Viewer>(); private Dictionary<string, Viewer> allViewers = new Dictionary<string, Viewer>();
@@ -65,7 +72,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
} }
} }
public void StartMonitoringSession(string viewerId, IXEventSession session) /// <summary>
/// Start monitoring the provided session
/// </summary>
public bool StartMonitoringSession(string viewerId, IXEventSession session)
{ {
lock (this.sessionsLock) lock (this.sessionsLock)
{ {
@@ -105,10 +115,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
} }
else else
{ {
viewers = new List<string> { viewerId }; viewers = new List<string>{ viewerId };
sessionViewers.Add(session.Id, viewers); sessionViewers.Add(session.Id, viewers);
} }
} }
return true;
} }
/// <summary> /// <summary>
@@ -148,16 +160,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
lock (this.sessionsLock) lock (this.sessionsLock)
{ {
//cancel running XEventStream for session.
CancellationTokenSource targetToken;
if (monitoredCancellationTokenSources.Remove(sessionId, out targetToken))
{
targetToken.Cancel();
}
if (this.monitoredSessions.Remove(sessionId, out session)) if (this.monitoredSessions.Remove(sessionId, out session))
{ {
session.IsStreaming = false;
//remove all viewers for this session //remove all viewers for this session
List<string> viewerIds; List<string> viewerIds;
if (sessionViewers.Remove(sessionId, out viewerIds)) if (sessionViewers.Remove(sessionId, out viewerIds))
@@ -182,105 +186,113 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
} }
} }
public void PollSession(int sessionId)
{
lock (this.sessionsLock)
{
this.monitoredSessions[sessionId].pollImmediatly = true;
}
lock (this.pollingLock)
{
Monitor.Pulse(pollingLock);
}
}
/// <summary> /// <summary>
/// The core queue processing method, cycles through monitored sessions and creates a stream for them if not already. /// The core queue processing method
/// </summary> /// </summary>
/// <param name="state"></param>
private void ProcessSessions() private void ProcessSessions()
{ {
while (true) while (true)
{ {
lock (this.sessionsLock) lock (this.pollingLock)
{ {
foreach (var id in this.monitoredSessions.Keys) lock (this.sessionsLock)
{ {
ProfilerSession session; foreach (var session in this.monitoredSessions.Values)
this.monitoredSessions.TryGetValue(id, out session);
if (!session.IsStreaming)
{ {
List<string> viewers = this.sessionViewers[session.XEventSession.Id]; List<string> viewers = this.sessionViewers[session.XEventSession.Id];
if (viewers.Any(v => allViewers[v].active)){ if (viewers.Any(v => allViewers[v].active))
StartStream(id, session); {
ProcessSession(session);
} }
} }
} }
Monitor.Wait(this.pollingLock, PollingLoopDelay);
} }
} }
} }
/// <summary> /// <summary>
/// Helper function used to process the XEvent feed from a session's stream. /// Process a session for new XEvents if it meets the polling criteria
/// </summary> /// </summary>
private async Task HandleXEvent(IXEvent xEvent, ProfilerSession session) private void ProcessSession(ProfilerSession session)
{ {
ProfilerEvent profileEvent = new ProfilerEvent(xEvent.Name, xEvent.Timestamp.ToString()); if (session.TryEnterPolling())
foreach (var kvp in xEvent.Fields)
{ {
profileEvent.Values.Add(kvp.Key, kvp.Value.ToString()); Task.Factory.StartNew(() =>
}
foreach (var kvp in xEvent.Actions)
{
profileEvent.Values.Add(kvp.Key, kvp.Value.ToString());
}
var eventList = new List<ProfilerEvent>();
eventList.Add(profileEvent);
var eventsLost = session.EventsLost;
if (eventList.Count > 0 || eventsLost)
{
session.FilterOldEvents(eventList);
eventList = session.FilterProfilerEvents(eventList);
// notify all viewers of the event.
List<string> viewerIds = this.sessionViewers[session.XEventSession.Id];
foreach (string viewerId in viewerIds)
{ {
if (allViewers[viewerId].active) var events = PollSession(session);
bool eventsLost = session.EventsLost;
if (events.Count > 0 || eventsLost)
{ {
SendEventsToListeners(viewerId, eventList, eventsLost); // notify all viewers for the polled session
List<string> viewerIds = this.sessionViewers[session.XEventSession.Id];
foreach (string viewerId in viewerIds)
{
if (allViewers[viewerId].active)
{
SendEventsToListeners(viewerId, events, eventsLost);
}
}
} }
} });
} }
} }
/// <summary> private List<ProfilerEvent> PollSession(ProfilerSession session)
/// Function that creates a brand new stream from a session, this is called from ProcessSessions when a session doesn't have a stream running currently.
/// </summary>
private void StartStream(int id, ProfilerSession session)
{ {
if(session.XEventSession != null && session.XEventSession.Session != null && session.XEventSession.ConnectionDetails != null){ var events = new List<ProfilerEvent>();
CancellationTokenSource threadCancellationToken = new CancellationTokenSource(); try
var connectionString = ConnectionService.BuildConnectionString(session.XEventSession.ConnectionDetails); {
var eventStreamer = new XELiveEventStreamer(connectionString, session.XEventSession.Session.Name); if (session == null || session.XEventSession == null)
// Start streaming task here, will run until cancellation or error with the feed.
var task = eventStreamer.ReadEventStream(xEvent => HandleXEvent(xEvent, session), threadCancellationToken.Token);
task.ContinueWith(t =>
{ {
//If cancellation token is missing, that means stream was stopped by the client, do not notify in this case. return events;
CancellationTokenSource targetToken; }
if (monitoredCancellationTokenSources.TryGetValue(id, out targetToken))
{
StopSession(session.XEventSession.Id);
}
}, TaskContinuationOptions.OnlyOnFaulted);
this.monitoredCancellationTokenSources.Add(id, threadCancellationToken); var targetXml = session.XEventSession.GetTargetXml();
session.IsStreaming = true;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(targetXml);
var nodes = xmlDoc.DocumentElement.GetElementsByTagName("event");
foreach (XmlNode node in nodes)
{
var profilerEvent = ParseProfilerEvent(node);
if (profilerEvent != null)
{
events.Add(profilerEvent);
}
}
} }
else { catch (XEventException)
{
SendStoppedSessionInfoToListeners(session.XEventSession.Id);
ProfilerSession tempSession; ProfilerSession tempSession;
RemoveSession(id, out tempSession); RemoveSession(session.XEventSession.Id, out tempSession);
throw new Exception(SR.SessionMissingDetails(id)); }
catch (Exception ex)
{
Logger.Write(TraceEventType.Warning, "Failed to poll session. error: " + ex.Message);
}
finally
{
session.IsPolling = false;
} }
}
/// <summary> session.FilterOldEvents(events);
/// Helper function for notifying listeners and stopping session in case the session is stopped on the server. This is public for tests. return session.FilterProfilerEvents(events);
/// </summary>
public void StopSession(int Id){
SendStoppedSessionInfoToListeners(Id);
ProfilerSession tempSession;
RemoveSession(Id, out tempSession);
} }
/// <summary> /// <summary>
@@ -292,7 +304,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
foreach (var listener in this.listeners) foreach (var listener in this.listeners)
{ {
foreach (string viewerId in sessionViewers[sessionId]) foreach(string viewerId in sessionViewers[sessionId])
{ {
listener.SessionStopped(viewerId, sessionId); listener.SessionStopped(viewerId, sessionId);
} }
@@ -313,5 +325,31 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
} }
} }
} }
/// <summary>
/// Parse a single event node from XEvent XML
/// </summary>
private ProfilerEvent ParseProfilerEvent(XmlNode node)
{
var name = node.Attributes["name"];
var timestamp = node.Attributes["timestamp"];
var profilerEvent = new ProfilerEvent(name.InnerText, timestamp.InnerText);
foreach (XmlNode childNode in node.ChildNodes)
{
var childName = childNode.Attributes["name"];
XmlNode typeNode = childNode.SelectSingleNode("type");
var typeName = typeNode.Attributes["name"];
XmlNode valueNode = childNode.SelectSingleNode("value");
if (!profilerEvent.Values.ContainsKey(childName.InnerText))
{
profilerEvent.Values.Add(childName.InnerText, valueNode.InnerText);
}
}
return profilerEvent;
}
} }
} }

View File

@@ -5,7 +5,8 @@
using System.Linq; using System.Linq;
using Microsoft.SqlServer.Management.XEvent; using Microsoft.SqlServer.Management.XEvent;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Profiler namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
@@ -16,8 +17,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{ {
public Session Session { get; set; } public Session Session { get; set; }
public ConnectionDetails ConnectionDetails { get; set; }
public int Id public int Id
{ {
get { return Session.ID; } get { return Session.ID; }

View File

@@ -65,12 +65,17 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
// start profiling session // start profiling session
await profilerService.HandleStartProfilingRequest(requestParams, requestContext.Object); await profilerService.HandleStartProfilingRequest(requestParams, requestContext.Object);
profilerService.SessionMonitor.PollSession(1);
// simulate a short polling delay
Thread.Sleep(200);
profilerService.SessionMonitor.PollSession(1);
// wait for polling to finish, or for timeout // wait for polling to finish, or for timeout
System.Timers.Timer pollingTimer = new System.Timers.Timer(); System.Timers.Timer pollingTimer = new System.Timers.Timer();
pollingTimer.Interval = 10000; pollingTimer.Interval = 10000;
pollingTimer.Start(); pollingTimer.Start();
bool timeout = false; bool timeout = false;
pollingTimer.Elapsed += new System.Timers.ElapsedEventHandler((s_, e_) => { timeout = true; }); pollingTimer.Elapsed += new System.Timers.ElapsedEventHandler((s_, e_) => {timeout = true;});
while (sessionId == null && !timeout) while (sessionId == null && !timeout)
{ {
Thread.Sleep(250); Thread.Sleep(250);
@@ -120,8 +125,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo(); ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo); profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo);
profilerService.XEventSessionFactory = new TestXEventSessionFactory(); profilerService.XEventSessionFactory = new TestXEventSessionFactory();
mockSession.SetupProperty(p => p.ConnectionDetails, connectionInfo.ConnectionDetails);
mockSession.SetupProperty(p => p.Session, new Session(null, testUri));
var requestParams = new StopProfilingParams(); var requestParams = new StopProfilingParams();
requestParams.OwnerUri = testUri; requestParams.OwnerUri = testUri;
@@ -146,12 +149,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Test] [Test]
// TODO: Add more in-depth testing for pause effects on events received.
public async Task TestPauseProfilingRequest() public async Task TestPauseProfilingRequest()
{ {
bool success = false; bool success = false;
string testUri = "test_session"; string testUri = "test_session";
bool recievedEvents = false;
// capture pausing results // capture pausing results
var requestContext = new Mock<RequestContext<PauseProfilingResult>>(); var requestContext = new Mock<RequestContext<PauseProfilingResult>>();
requestContext.Setup(rc => rc.SendResult(It.IsAny<PauseProfilingResult>())) requestContext.Setup(rc => rc.SendResult(It.IsAny<PauseProfilingResult>()))
@@ -161,34 +164,75 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
return Task.FromResult(0); return Task.FromResult(0);
}); });
// capture Listener event notifications
var mockListener = new Mock<IProfilerSessionListener>();
mockListener.Setup(p => p.EventsAvailable(It.IsAny<string>(), It.IsAny<List<ProfilerEvent>>(), It.IsAny<bool>())).Callback(() =>
{
recievedEvents = true;
});
// setup profiler service // setup profiler service
var mockListener = new TestSessionListener();
var profilerService = new ProfilerService(); var profilerService = new ProfilerService();
profilerService.SessionMonitor.AddSessionListener(mockListener); profilerService.SessionMonitor.AddSessionListener(mockListener.Object);
profilerService.ConnectionServiceInstance = TestObjects.GetTestConnectionService(); profilerService.ConnectionServiceInstance = TestObjects.GetTestConnectionService();
ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo(); ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo); profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo);
var testSession = new TestXEventSession1();
testSession.ConnectionDetails = connectionInfo.ConnectionDetails;
testSession.Session = new Session(null, testUri);
var requestParams = new PauseProfilingParams(); var requestParams = new PauseProfilingParams();
requestParams.OwnerUri = testUri; requestParams.OwnerUri = testUri;
// begin monitoring session // begin monitoring session
profilerService.SessionMonitor.StartMonitoringSession(testUri, testSession); profilerService.SessionMonitor.StartMonitoringSession(testUri, new TestXEventSession1());
// poll the session
profilerService.SessionMonitor.PollSession(1);
Thread.Sleep(500);
profilerService.SessionMonitor.PollSession(1);
// wait for polling to finish, or for timeout
System.Timers.Timer pollingTimer = new System.Timers.Timer();
pollingTimer.Interval = 10000;
pollingTimer.Start();
bool timeout = false;
pollingTimer.Elapsed += new System.Timers.ElapsedEventHandler((s_, e_) => {timeout = true;});
while (!recievedEvents && !timeout)
{
Thread.Sleep(250);
}
pollingTimer.Stop();
// confirm that polling works
Assert.True(recievedEvents);
// pause viewer // pause viewer
await profilerService.HandlePauseProfilingRequest(requestParams, requestContext.Object); await profilerService.HandlePauseProfilingRequest(requestParams, requestContext.Object);
Assert.True(success); Assert.True(success);
recievedEvents = false;
success = false; success = false;
profilerService.SessionMonitor.PollSession(1);
// confirm that no events were sent to paused Listener
Assert.False(recievedEvents);
// unpause viewer // unpause viewer
await profilerService.HandlePauseProfilingRequest(requestParams, requestContext.Object); await profilerService.HandlePauseProfilingRequest(requestParams, requestContext.Object);
Assert.True(success); Assert.True(success);
profilerService.SessionMonitor.PollSession(1);
// wait for polling to finish, or for timeout
timeout = false;
pollingTimer.Start();
while (!recievedEvents && !timeout)
{
Thread.Sleep(250);
}
// check that events got sent to Listener
Assert.True(recievedEvents);
requestContext.VerifyAll(); requestContext.VerifyAll();
} }
@@ -219,14 +263,21 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
profilerService.ConnectionServiceInstance = TestObjects.GetTestConnectionService(); profilerService.ConnectionServiceInstance = TestObjects.GetTestConnectionService();
ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo(); ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo); profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo);
mockSession.SetupProperty(p => p.ConnectionDetails, connectionInfo.ConnectionDetails);
mockSession.SetupProperty(p => p.Session, new Session(null, testUri));
// start monitoring test session // start monitoring test session
profilerService.SessionMonitor.StartMonitoringSession(testUri, mockSession.Object); profilerService.SessionMonitor.StartMonitoringSession(testUri, mockSession.Object);
// Call stop session to simulate when a server has stopped a session on its side. // wait for polling to finish, or for timeout
profilerService.SessionMonitor.StopSession(0); System.Timers.Timer pollingTimer = new System.Timers.Timer();
pollingTimer.Interval = 10000;
pollingTimer.Start();
bool timeout = false;
pollingTimer.Elapsed += new System.Timers.ElapsedEventHandler((s_, e_) => {timeout = true;});
while (sessionStopped == false && !timeout)
{
Thread.Sleep(250);
}
pollingTimer.Stop();
// check that a stopped session notification was sent // check that a stopped session notification was sent
Assert.True(sessionStopped); Assert.True(sessionStopped);

View File

@@ -123,5 +123,41 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
profilerSession.FilterOldEvents(profilerEvents); profilerSession.FilterOldEvents(profilerEvents);
Assert.False(profilerSession.EventsLost); Assert.False(profilerSession.EventsLost);
} }
/// <summary>
/// Test the TryEnterPolling method
/// </summary>
[Test]
public void TestTryEnterPolling()
{
DateTime startTime = DateTime.Now;
// create new profiler session
var profilerSession = new ProfilerSession();
// enter the polling block
Assert.True(profilerSession.TryEnterPolling());
Assert.True(profilerSession.IsPolling);
// verify we can't enter again
Assert.False(profilerSession.TryEnterPolling());
// set polling to false to exit polling block
profilerSession.IsPolling = false;
bool outsideDelay = DateTime.Now.Subtract(startTime) >= profilerSession.PollingDelay;
// verify we can only enter again if we're outside polling delay interval
Assert.AreEqual(profilerSession.TryEnterPolling(), outsideDelay);
// reset IsPolling in case the delay has elasped on slow machine or while debugging
profilerSession.IsPolling = false;
// wait for the polling delay to elapse
Thread.Sleep(profilerSession.PollingDelay);
// verify we can enter the polling block again
Assert.True(profilerSession.TryEnterPolling());
}
} }
} }

View File

@@ -11,7 +11,6 @@ using System.Threading.Tasks;
using Microsoft.SqlServer.Management.XEvent; using Microsoft.SqlServer.Management.XEvent;
using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Profiler; using Microsoft.SqlTools.ServiceLayer.Profiler;
using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts; using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility; using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
@@ -207,10 +206,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
public int Id { get { return 51; } } public int Id { get { return 51; } }
public ConnectionDetails ConnectionDetails { get; set; }
public Session Session { get; set; }
public void Start(){} public void Start(){}
public void Stop(){} public void Stop(){}
@@ -296,10 +291,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
public int Id { get { return 1; } } public int Id { get { return 1; } }
public ConnectionDetails ConnectionDetails { get; set; }
public Session Session { get; set; }
public void Start(){} public void Start(){}
public void Stop(){} public void Stop(){}
@@ -391,10 +382,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
public int Id { get { return 2; } } public int Id { get { return 2; } }
public ConnectionDetails ConnectionDetails { get; set; }
public Session Session { get; set; }
public void Start(){} public void Start(){}
public void Stop(){} public void Stop(){}