diff --git a/Packages.props b/Packages.props
index a5d44818..9a9bf056 100644
--- a/Packages.props
+++ b/Packages.props
@@ -30,7 +30,6 @@
-
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
index f523424f..684f4cea 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
@@ -8761,16 +8761,6 @@ namespace Microsoft.SqlTools.ServiceLayer
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)
{
return Keys.GetString(Keys.UnknownSizeUnit, unit);
@@ -9962,12 +9952,6 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string SessionAlreadyExists = "SessionAlreadyExists";
- public const string SessionMissingDetails = "SessionMissingDetails";
-
-
- public const string StartProfilingFailed = "StartProfilingFailed";
-
-
public const string CategoryLocal = "CategoryLocal";
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
index 128499d6..74435378 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
@@ -1576,16 +1576,6 @@
An XEvent session named {0} already exists
.
Parameters: 0 - sessionName (String)
-
-
- Unable to start streaming session {0} due to missing session details.
- .
- Parameters: 0 - id (int)
-
-
- Failed to start profiler: {0}
- .
- Parameters: 0 - error (String)
[Uncategorized (Local)]
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
index a40d9dce..60d6183d 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
@@ -761,8 +761,7 @@ PauseSessionFailed(String error) = Failed to pause session: {0}
StopSessionFailed(String error) = Failed to stop session: {0}
SessionNotFound = Cannot find requested XEvent session
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
CategoryLocal = [Uncategorized (Local)]
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
index 6dbd66a1..e20d0667 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
+++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
@@ -5696,18 +5696,6 @@
Specifies whether the check constraint is Enabled
-
- Unable to start streaming session {0} due to missing session details.
- Unable to start streaming session {0} due to missing session details.
- .
- Parameters: 0 - id (int)
-
-
- Failed to start profiler: {0}
- Failed to start profiler: {0}
- .
- Parameters: 0 - error (String)
-
The underlying type "{0}" for sql variant column "{1}" could not be resolved.
The underlying type "{0}" for sql variant column "{1}" could not be resolved.
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj
index 50ddb5a6..95d1f2a5 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj
+++ b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj
@@ -26,7 +26,6 @@
-
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IProfilerSessionMonitor.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IProfilerSessionMonitor.cs
index 2b6aa292..4fd5a6d3 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IProfilerSessionMonitor.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IProfilerSessionMonitor.cs
@@ -22,7 +22,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
///
/// Starts monitoring a profiler session
///
- void StartMonitoringSession(string viewerId, IXEventSession session);
+ bool StartMonitoringSession(string viewerId, IXEventSession session);
///
/// Stops monitoring a profiler session
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSession.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSession.cs
index 61a262fa..4e91be3a 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSession.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/IXEventSession.cs
@@ -2,8 +2,6 @@
// Copyright (c) Microsoft. All rights reserved.
// 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
{
@@ -17,16 +15,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
///
int Id { get; }
- ///
- /// Connection details associated with the session id.
- ///
- ConnectionDetails ConnectionDetails { get; set; }
-
- ///
- /// Session associated with the session id.
- ///
- Session Session { get; set; }
-
///
/// Starts XEvent session
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs
index 59e7f89a..dc659fbc 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerService.cs
@@ -18,7 +18,6 @@ using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.XEvent;
using Microsoft.SqlServer.Management.XEventDbScoped;
-using Microsoft.SqlServer.XEvent.XELite;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection;
@@ -121,42 +120,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
this.SessionMonitor.AddSessionListener(this);
}
- ///
- /// Handle request to start a profiling session
- ///
- internal async Task HandleStartProfilingRequest(StartProfilingParams parameters, RequestContext 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));
- }
- });
- }
-
///
/// Handle request to start a profiling session
///
@@ -166,24 +129,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
try
{
- Logger.Write(TraceEventType.Verbose, "HandleCreateXEventSessionRequest started");
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo == null)
{
- Logger.Write(TraceEventType.Error, "Connection Info could not be found for " + parameters.OwnerUri);
throw new Exception(SR.ProfilerConnectionNotFound);
}
else if (parameters.SessionName == null)
{
- Logger.Write(TraceEventType.Error, "Session Name could not be found for " + parameters.OwnerUri);
throw new ArgumentNullException("SessionName");
}
else if (parameters.Template == null)
{
- Logger.Write(TraceEventType.Error, "Template could not be found for " + parameters.OwnerUri);
throw new ArgumentNullException("Template");
}
else
@@ -197,13 +156,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
xeSession = this.XEventSessionFactory.GetXEventSession(parameters.SessionName, connInfo);
}
- catch {
- Logger.Write(TraceEventType.Verbose, "Session with name '" + parameters.SessionName + "' was not found");
- }
+ catch { }
if (xeSession == null)
{
- Logger.Write(TraceEventType.Verbose, "Creating new XEventSession with SessionName " + parameters.SessionName);
// create a new XEvent session and Profiler session
xeSession = this.XEventSessionFactory.CreateXEventSession(parameters.Template.CreateStatement, parameters.SessionName, connInfo);
}
@@ -219,8 +175,42 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
catch (Exception e)
{
- Logger.Write(TraceEventType.Error, "HandleCreateXEventSessionRequest failed for uri " + parameters.OwnerUri);
- await requestContext.SendError(new Exception (SR.CreateSessionFailed(e.Message), e));
+ await requestContext.SendError(new Exception(SR.CreateSessionFailed(e.Message)));
+ }
+ });
+ }
+
+ ///
+ /// Handle request to start a profiling session
+ ///
+ internal async Task HandleStartProfilingRequest(StartProfilingParams parameters, RequestContext 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
{
- Logger.Write(TraceEventType.Verbose, "HandleStopProfilingRequest started");
ProfilerSession session;
monitor.StopMonitoringSession(parameters.OwnerUri, out session);
@@ -251,12 +240,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
await requestContext.SendResult(new StopProfilingResult { });
break;
}
- catch (InvalidOperationException e)
+ catch (InvalidOperationException)
{
remainingAttempts--;
if (remainingAttempts == 0)
{
- Logger.Write(TraceEventType.Error, "Stop profiler session '" + session.XEventSession.Session.Name + "' failed after three retries, last exception was: " + e.Message);
throw;
}
Thread.Sleep(500);
@@ -270,7 +258,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
catch (Exception e)
{
- Logger.Write(TraceEventType.Error, "HandleStopProfilingRequest failed for uri " + parameters.OwnerUri);
await requestContext.SendError(new Exception(SR.StopSessionFailed(e.Message)));
}
});
@@ -285,14 +272,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
try
{
- Logger.Write(TraceEventType.Verbose, "HandlePauseProfilingRequest started");
monitor.PauseViewer(parameters.OwnerUri);
await requestContext.SendResult(new PauseProfilingResult { });
}
catch (Exception e)
{
- Logger.Write(TraceEventType.Error, "HandlePauseProfilingRequest failed for uri " + parameters.OwnerUri);
await requestContext.SendError(new Exception(SR.PauseSessionFailed(e.Message)));
}
});
@@ -307,7 +292,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
try
{
- Logger.Write(TraceEventType.Verbose, "HandleGetXEventSessionsRequest started");
var result = new GetXEventSessionsResult();
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
@@ -315,7 +299,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
out connInfo);
if (connInfo == null)
{
- Logger.Write(TraceEventType.Error, "Connection Info could not be found for " + parameters.OwnerUri);
await requestContext.SendError(new Exception(SR.ProfilerConnectionNotFound));
}
else
@@ -327,7 +310,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
catch (Exception e)
{
- Logger.Write(TraceEventType.Error, "HandleGetXEventSessionsRequest failed for uri " + parameters.OwnerUri);
await requestContext.SendError(e);
}
});
@@ -342,13 +324,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
try
{
- Logger.Write(TraceEventType.Verbose, "HandleDisconnectSessionRequest started");
ProfilerSession session;
monitor.StopMonitoringSession(parameters.OwnerUri, out session);
}
catch (Exception e)
{
- Logger.Write(TraceEventType.Error, "HandleDisconnectSessionRequest failed for uri " + parameters.OwnerUri);
await requestContext.SendError(e);
}
});
@@ -394,15 +374,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
return store;
}
- ///
- /// Nulls out properties in ConnectionDetails that aren't compatible with XElite.
- ///
- private static void RemoveIncompatibleConnectionProperties(ConnectionDetails connDetails){
- connDetails.ConnectRetryCount = null;
- connDetails.ConnectRetryInterval = null;
- connDetails.MultiSubnetFailover = null;
- }
-
///
/// Gets an XEvent session with the given name per the IXEventSessionFactory contract
/// Also starts the session if it isn't currently running
@@ -412,7 +383,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
var sqlConnection = ConnectionService.OpenSqlConnection(connInfo);
SqlStoreConnection connection = new SqlStoreConnection(sqlConnection);
BaseXEStore store = CreateXEventStore(connInfo, connection);
- RemoveIncompatibleConnectionProperties(connInfo.ConnectionDetails);
Session session = store.Sessions[sessionName];
// start the session if it isn't already running
@@ -429,7 +399,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
// create xevent session wrapper
return new XEventSession()
{
- ConnectionDetails = connInfo.ConnectionDetails,
Session = session
};
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs
index bce45249..5835cd3b 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSession.cs
@@ -17,13 +17,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
///
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 bool eventsLost = false;
int lastSeenId = -1;
+ public bool pollImmediatly = false;
+
///
/// Connection to use for the session
///
@@ -34,6 +39,57 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
///
public IXEventSession XEventSession { get; set; }
+ ///
+ /// Try to set the session into polling mode if criteria is meet
+ ///
+ /// True if session set to polling mode, False otherwise
+ 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;
+ }
+ }
+ }
+
+ ///
+ /// Is the session currently being polled
+ ///
+ public bool IsPolling
+ {
+ get
+ {
+ return this.isPolling;
+ }
+ set
+ {
+ lock (this.pollingLock)
+ {
+ this.isPolling = value;
+ }
+ }
+ }
+
+ ///
+ /// The delay between session polls
+ ///
+ public TimeSpan PollingDelay
+ {
+ get
+ {
+ return pollingDelay;
+ }
+ }
+
///
/// Could events have been lost in the last poll
///
@@ -50,7 +106,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
///
private bool IsProfilerEvent(ProfilerEvent currentEvent)
{
- if (string.IsNullOrWhiteSpace(currentEvent.Name) || currentEvent.Values == null)
+ if (string.IsNullOrWhiteSpace(currentEvent.Name) || currentEvent.Values == null)
{
return false;
}
@@ -89,7 +145,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
public void FilterOldEvents(List events)
{
this.eventsLost = false;
-
+
if (lastSeenId != -1)
{
// find the last event we've previously seen
@@ -113,7 +169,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
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
// and the furthest back event seen, we know we've lost events
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSessionMonitor.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSessionMonitor.cs
index 3a3f7315..49a411b0 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSessionMonitor.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/ProfilerSessionMonitor.cs
@@ -4,13 +4,19 @@
//
using System;
-using System.Linq;
+using System.Collections.Concurrent;
using System.Collections.Generic;
+using Microsoft.Data.SqlClient;
+using System.Diagnostics;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.SqlServer.XEvent.XELite;
-using Microsoft.SqlTools.ServiceLayer.Connection;
+using System.Xml;
+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.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
@@ -19,10 +25,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
///
public class ProfilerSessionMonitor : IProfilerSessionMonitor
{
+ private const int PollingLoopDelay = 1000;
+
private object sessionsLock = new object();
private object listenersLock = new object();
+ private object pollingLock = new object();
+
private Task processorThread = null;
private struct Viewer
@@ -46,9 +56,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
// XEvent Session Id's matched to their Profiler Sessions
private Dictionary monitoredSessions = new Dictionary();
- // XEvent Session Id's matched to their stream cancellation tokens
- private Dictionary monitoredCancellationTokenSources = new Dictionary();
-
// ViewerId -> Viewer objects
private Dictionary allViewers = new Dictionary();
@@ -65,7 +72,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
}
- public void StartMonitoringSession(string viewerId, IXEventSession session)
+ ///
+ /// Start monitoring the provided session
+ ///
+ public bool StartMonitoringSession(string viewerId, IXEventSession session)
{
lock (this.sessionsLock)
{
@@ -105,10 +115,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
else
{
- viewers = new List { viewerId };
+ viewers = new List{ viewerId };
sessionViewers.Add(session.Id, viewers);
}
}
+
+ return true;
}
///
@@ -148,16 +160,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
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))
{
- session.IsStreaming = false;
-
//remove all viewers for this session
List 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);
+ }
+ }
+
///
- /// The core queue processing method, cycles through monitored sessions and creates a stream for them if not already.
+ /// The core queue processing method
///
+ ///
private void ProcessSessions()
{
while (true)
{
- lock (this.sessionsLock)
+ lock (this.pollingLock)
{
- foreach (var id in this.monitoredSessions.Keys)
+ lock (this.sessionsLock)
{
- ProfilerSession session;
- this.monitoredSessions.TryGetValue(id, out session);
- if (!session.IsStreaming)
+ foreach (var session in this.monitoredSessions.Values)
{
List viewers = this.sessionViewers[session.XEventSession.Id];
- if (viewers.Any(v => allViewers[v].active)){
- StartStream(id, session);
+ if (viewers.Any(v => allViewers[v].active))
+ {
+ ProcessSession(session);
}
}
}
+ Monitor.Wait(this.pollingLock, PollingLoopDelay);
}
}
}
///
- /// 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
///
- private async Task HandleXEvent(IXEvent xEvent, ProfilerSession session)
+ private void ProcessSession(ProfilerSession session)
{
- ProfilerEvent profileEvent = new ProfilerEvent(xEvent.Name, xEvent.Timestamp.ToString());
- foreach (var kvp in xEvent.Fields)
+ if (session.TryEnterPolling())
{
- profileEvent.Values.Add(kvp.Key, kvp.Value.ToString());
- }
- foreach (var kvp in xEvent.Actions)
- {
- profileEvent.Values.Add(kvp.Key, kvp.Value.ToString());
- }
- var eventList = new List();
- 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 viewerIds = this.sessionViewers[session.XEventSession.Id];
-
- foreach (string viewerId in viewerIds)
+ Task.Factory.StartNew(() =>
{
- 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 viewerIds = this.sessionViewers[session.XEventSession.Id];
+ foreach (string viewerId in viewerIds)
+ {
+ if (allViewers[viewerId].active)
+ {
+ SendEventsToListeners(viewerId, events, eventsLost);
+ }
+ }
}
- }
+ });
}
}
- ///
- /// 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.
- ///
- private void StartStream(int id, ProfilerSession session)
+ private List PollSession(ProfilerSession session)
{
- if(session.XEventSession != null && session.XEventSession.Session != null && session.XEventSession.ConnectionDetails != null){
- CancellationTokenSource threadCancellationToken = new CancellationTokenSource();
- var connectionString = ConnectionService.BuildConnectionString(session.XEventSession.ConnectionDetails);
- var eventStreamer = new XELiveEventStreamer(connectionString, session.XEventSession.Session.Name);
- // 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 =>
+ var events = new List();
+ try
+ {
+ if (session == null || session.XEventSession == null)
{
- //If cancellation token is missing, that means stream was stopped by the client, do not notify in this case.
- CancellationTokenSource targetToken;
- if (monitoredCancellationTokenSources.TryGetValue(id, out targetToken))
- {
- StopSession(session.XEventSession.Id);
- }
- }, TaskContinuationOptions.OnlyOnFaulted);
+ return events;
+ }
- this.monitoredCancellationTokenSources.Add(id, threadCancellationToken);
- session.IsStreaming = true;
+ var targetXml = session.XEventSession.GetTargetXml();
+
+ 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;
- RemoveSession(id, out tempSession);
- throw new Exception(SR.SessionMissingDetails(id));
+ RemoveSession(session.XEventSession.Id, out tempSession);
+ }
+ catch (Exception ex)
+ {
+ Logger.Write(TraceEventType.Warning, "Failed to poll session. error: " + ex.Message);
+ }
+ finally
+ {
+ session.IsPolling = false;
}
- }
- ///
- /// Helper function for notifying listeners and stopping session in case the session is stopped on the server. This is public for tests.
- ///
- public void StopSession(int Id){
- SendStoppedSessionInfoToListeners(Id);
- ProfilerSession tempSession;
- RemoveSession(Id, out tempSession);
+ session.FilterOldEvents(events);
+ return session.FilterProfilerEvents(events);
}
///
@@ -292,7 +304,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
foreach (var listener in this.listeners)
{
- foreach (string viewerId in sessionViewers[sessionId])
+ foreach(string viewerId in sessionViewers[sessionId])
{
listener.SessionStopped(viewerId, sessionId);
}
@@ -313,5 +325,31 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
}
}
}
+
+ ///
+ /// Parse a single event node from XEvent XML
+ ///
+ 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;
+ }
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Profiler/XEventSession.cs b/src/Microsoft.SqlTools.ServiceLayer/Profiler/XEventSession.cs
index 8c2ba38c..b1a3903c 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Profiler/XEventSession.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Profiler/XEventSession.cs
@@ -5,7 +5,8 @@
using System.Linq;
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
{
@@ -16,8 +17,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
{
public Session Session { get; set; }
- public ConnectionDetails ConnectionDetails { get; set; }
-
public int Id
{
get { return Session.ID; }
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerServiceTests.cs
index fffb1f9e..7efe8216 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerServiceTests.cs
@@ -65,12 +65,17 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
// start profiling session
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
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; });
+ pollingTimer.Elapsed += new System.Timers.ElapsedEventHandler((s_, e_) => {timeout = true;});
while (sessionId == null && !timeout)
{
Thread.Sleep(250);
@@ -120,8 +125,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo);
profilerService.XEventSessionFactory = new TestXEventSessionFactory();
- mockSession.SetupProperty(p => p.ConnectionDetails, connectionInfo.ConnectionDetails);
- mockSession.SetupProperty(p => p.Session, new Session(null, testUri));
var requestParams = new StopProfilingParams();
requestParams.OwnerUri = testUri;
@@ -146,12 +149,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
///
///
[Test]
- // TODO: Add more in-depth testing for pause effects on events received.
public async Task TestPauseProfilingRequest()
{
bool success = false;
string testUri = "test_session";
-
+ bool recievedEvents = false;
+
// capture pausing results
var requestContext = new Mock>();
requestContext.Setup(rc => rc.SendResult(It.IsAny()))
@@ -161,34 +164,75 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
return Task.FromResult(0);
});
+ // capture Listener event notifications
+ var mockListener = new Mock();
+ mockListener.Setup(p => p.EventsAvailable(It.IsAny(), It.IsAny>(), It.IsAny())).Callback(() =>
+ {
+ recievedEvents = true;
+ });
+
// setup profiler service
- var mockListener = new TestSessionListener();
var profilerService = new ProfilerService();
- profilerService.SessionMonitor.AddSessionListener(mockListener);
+ profilerService.SessionMonitor.AddSessionListener(mockListener.Object);
profilerService.ConnectionServiceInstance = TestObjects.GetTestConnectionService();
ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
profilerService.ConnectionServiceInstance.OwnerToConnectionMap.Add(testUri, connectionInfo);
- var testSession = new TestXEventSession1();
- testSession.ConnectionDetails = connectionInfo.ConnectionDetails;
- testSession.Session = new Session(null, testUri);
-
var requestParams = new PauseProfilingParams();
requestParams.OwnerUri = testUri;
// 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
await profilerService.HandlePauseProfilingRequest(requestParams, requestContext.Object);
Assert.True(success);
+ recievedEvents = false;
success = false;
+ profilerService.SessionMonitor.PollSession(1);
+
+ // confirm that no events were sent to paused Listener
+ Assert.False(recievedEvents);
+
// unpause viewer
await profilerService.HandlePauseProfilingRequest(requestParams, requestContext.Object);
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();
}
@@ -219,14 +263,21 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
profilerService.ConnectionServiceInstance = TestObjects.GetTestConnectionService();
ConnectionInfo connectionInfo = TestObjects.GetTestConnectionInfo();
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
profilerService.SessionMonitor.StartMonitoringSession(testUri, mockSession.Object);
- // Call stop session to simulate when a server has stopped a session on its side.
- profilerService.SessionMonitor.StopSession(0);
+ // 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 (sessionStopped == false && !timeout)
+ {
+ Thread.Sleep(250);
+ }
+ pollingTimer.Stop();
// check that a stopped session notification was sent
Assert.True(sessionStopped);
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerSessionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerSessionTests.cs
index 63d96268..85a00e83 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerSessionTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerSessionTests.cs
@@ -123,5 +123,41 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
profilerSession.FilterOldEvents(profilerEvents);
Assert.False(profilerSession.EventsLost);
}
+
+ ///
+ /// Test the TryEnterPolling method
+ ///
+ [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());
+ }
}
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerTestObjects.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerTestObjects.cs
index 198f6f77..94dcd171 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerTestObjects.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Profiler/ProfilerTestObjects.cs
@@ -11,7 +11,6 @@ using System.Threading.Tasks;
using Microsoft.SqlServer.Management.XEvent;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection;
-using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Profiler;
using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
@@ -207,10 +206,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
public int Id { get { return 51; } }
- public ConnectionDetails ConnectionDetails { get; set; }
-
- public Session Session { get; set; }
-
public void Start(){}
public void Stop(){}
@@ -296,10 +291,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
public int Id { get { return 1; } }
- public ConnectionDetails ConnectionDetails { get; set; }
-
- public Session Session { get; set; }
-
public void Start(){}
public void Stop(){}
@@ -391,10 +382,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
public int Id { get { return 2; } }
- public ConnectionDetails ConnectionDetails { get; set; }
-
- public Session Session { get; set; }
-
public void Start(){}
public void Stop(){}