Feat/result streaming (#721)

This changes adds the following two notifications from the results processing within a batch. These new notifications allows a consumer to stream results from a resultset instead of getting them all at once after the entire resultset has been fetched.

ResultsAvailable
This is issued after at least 1 row has been fetched for this resultset.

ResultsUpdated
This is issued periodically as more rows are available on this resultset. The final send of this notification when all rows have been fetched has the property 'Complete' set to true in the ResultSummary object.

Detailed Change Log:
* Initial completed implementation of QueryResults stream feature. 3 unittests still need fixing

* Fix for the 3 failing test. I will look into making MockBehavior strict again for the three tests later

* Making GetReader/GetWriter use filestream objects in FileShare.ReadWrite mode so the file can be concurrently read and written

* Changing resultsAvailable also to fire off on a timer instead of after 1st row

* adding a project for clr TableValuedFunction to produce result set with delays after each row. This is helpful in end to end testing.

* Fixing up some tests and simplifying implementation of result update timer

* Address review comments

* Some test fixes

* Disabled flaky test verification
This commit is contained in:
Arvind Ranasaria
2018-11-26 10:24:54 -08:00
committed by GitHub
parent 688e128c4c
commit 6dd9a4b5f1
46 changed files with 18070 additions and 7316 deletions

View File

@@ -27,7 +27,7 @@ namespace Microsoft.SqlTools.Hosting.v2
Keys.Culture = value;
}
}
public static string ServiceAlreadyRegistered
{
@@ -35,7 +35,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.ServiceAlreadyRegistered);
}
}
}
public static string MultipleServicesFound
{
@@ -43,7 +43,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.MultipleServicesFound);
}
}
}
public static string IncompatibleServiceForExtensionLoader
{
@@ -51,7 +51,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.IncompatibleServiceForExtensionLoader);
}
}
}
public static string ServiceProviderNotSet
{
@@ -59,7 +59,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.ServiceProviderNotSet);
}
}
}
public static string ServiceNotFound
{
@@ -67,7 +67,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.ServiceNotFound);
}
}
}
public static string ServiceNotOfExpectedType
{
@@ -75,7 +75,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.ServiceNotOfExpectedType);
}
}
}
public static string HostingUnexpectedEndOfStream
{
@@ -83,7 +83,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.HostingUnexpectedEndOfStream);
}
}
}
public static string HostingHeaderMissingColon
{
@@ -91,7 +91,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.HostingHeaderMissingColon);
}
}
}
public static string HostingHeaderMissingContentLengthHeader
{
@@ -99,7 +99,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.HostingHeaderMissingContentLengthHeader);
}
}
}
public static string HostingHeaderMissingContentLengthValue
{
@@ -107,7 +107,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.HostingHeaderMissingContentLengthValue);
}
}
}
public static string HostingJsonRpcHostAlreadyStarted
{
@@ -115,7 +115,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.HostingJsonRpcHostAlreadyStarted);
}
}
}
public static string HostingJsonRpcHostNotStarted
{
@@ -123,7 +123,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.HostingJsonRpcHostNotStarted);
}
}
}
public static string HostingJsonRpcVersionMissing
{
@@ -131,7 +131,7 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.HostingJsonRpcVersionMissing);
}
}
}
public static string HostingMessageMissingMethod
{
@@ -139,12 +139,12 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return Keys.GetString(Keys.HostingMessageMissingMethod);
}
}
}
public static string HostingMethodHandlerDoesNotExist(string messageType, string method)
{
return Keys.GetString(Keys.HostingMethodHandlerDoesNotExist, messageType, method);
}
}
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Keys
@@ -152,52 +152,52 @@ namespace Microsoft.SqlTools.Hosting.v2
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.Hosting.v2.Localization.SR", typeof(SR).GetTypeInfo().Assembly);
static CultureInfo _culture = null;
public const string ServiceAlreadyRegistered = "ServiceAlreadyRegistered";
public const string MultipleServicesFound = "MultipleServicesFound";
public const string IncompatibleServiceForExtensionLoader = "IncompatibleServiceForExtensionLoader";
public const string ServiceProviderNotSet = "ServiceProviderNotSet";
public const string ServiceNotFound = "ServiceNotFound";
public const string ServiceNotOfExpectedType = "ServiceNotOfExpectedType";
public const string HostingUnexpectedEndOfStream = "HostingUnexpectedEndOfStream";
public const string HostingHeaderMissingColon = "HostingHeaderMissingColon";
public const string HostingHeaderMissingContentLengthHeader = "HostingHeaderMissingContentLengthHeader";
public const string HostingHeaderMissingContentLengthValue = "HostingHeaderMissingContentLengthValue";
public const string HostingJsonRpcHostAlreadyStarted = "HostingJsonRpcHostAlreadyStarted";
public const string HostingJsonRpcHostNotStarted = "HostingJsonRpcHostNotStarted";
public const string HostingJsonRpcVersionMissing = "HostingJsonRpcVersionMissing";
public const string HostingMessageMissingMethod = "HostingMessageMissingMethod";
public const string HostingMethodHandlerDoesNotExist = "HostingMethodHandlerDoesNotExist";
public const string ServiceAlreadyRegistered = "ServiceAlreadyRegistered";
public const string MultipleServicesFound = "MultipleServicesFound";
public const string IncompatibleServiceForExtensionLoader = "IncompatibleServiceForExtensionLoader";
public const string ServiceProviderNotSet = "ServiceProviderNotSet";
public const string ServiceNotFound = "ServiceNotFound";
public const string ServiceNotOfExpectedType = "ServiceNotOfExpectedType";
public const string HostingUnexpectedEndOfStream = "HostingUnexpectedEndOfStream";
public const string HostingHeaderMissingColon = "HostingHeaderMissingColon";
public const string HostingHeaderMissingContentLengthHeader = "HostingHeaderMissingContentLengthHeader";
public const string HostingHeaderMissingContentLengthValue = "HostingHeaderMissingContentLengthValue";
public const string HostingJsonRpcHostAlreadyStarted = "HostingJsonRpcHostAlreadyStarted";
public const string HostingJsonRpcHostNotStarted = "HostingJsonRpcHostNotStarted";
public const string HostingJsonRpcVersionMissing = "HostingJsonRpcVersionMissing";
public const string HostingMessageMissingMethod = "HostingMessageMissingMethod";
public const string HostingMethodHandlerDoesNotExist = "HostingMethodHandlerDoesNotExist";
private Keys()
{ }
@@ -218,13 +218,13 @@ namespace Microsoft.SqlTools.Hosting.v2
{
return resourceManager.GetString(key, _culture);
}
public static string GetString(string key, object arg0, object arg1)
{
return string.Format(global::System.Globalization.CultureInfo.CurrentCulture, resourceManager.GetString(key, _culture), arg0, arg1);
}
}
}
}
}
}
}

View File

@@ -120,62 +120,62 @@
<data name="ServiceAlreadyRegistered" xml:space="preserve">
<value>Cannot register service for type {0}, one or more services already registered</value>
<comment></comment>
</data>
</data>
<data name="MultipleServicesFound" xml:space="preserve">
<value>Multiple services found for type {0}, expected only 1</value>
<comment></comment>
</data>
</data>
<data name="IncompatibleServiceForExtensionLoader" xml:space="preserve">
<value>Service of type {0} cannot be created by ExtensionLoader&lt;{1}&gt;</value>
<comment></comment>
</data>
</data>
<data name="ServiceProviderNotSet" xml:space="preserve">
<value>SetServiceProvider() was not called to establish the required service provider</value>
<comment></comment>
</data>
</data>
<data name="ServiceNotFound" xml:space="preserve">
<value>Service {0} was not found in the service provider</value>
<comment></comment>
</data>
</data>
<data name="ServiceNotOfExpectedType" xml:space="preserve">
<value>Service of Type {0} is not compatible with registered Type {1}</value>
<comment></comment>
</data>
</data>
<data name="HostingUnexpectedEndOfStream" xml:space="preserve">
<value>MessageReader's input stream ended unexpectedly, terminating</value>
<comment></comment>
</data>
</data>
<data name="HostingHeaderMissingColon" xml:space="preserve">
<value>Message header must separate key and value using ':'</value>
<comment></comment>
</data>
</data>
<data name="HostingHeaderMissingContentLengthHeader" xml:space="preserve">
<value>Fatal error: Content-Length header must be provided</value>
<comment></comment>
</data>
</data>
<data name="HostingHeaderMissingContentLengthValue" xml:space="preserve">
<value>Fatal error: Content-Length value is not an integer</value>
<comment></comment>
</data>
</data>
<data name="HostingJsonRpcHostAlreadyStarted" xml:space="preserve">
<value>JSON RPC host has already started</value>
<comment></comment>
</data>
</data>
<data name="HostingJsonRpcHostNotStarted" xml:space="preserve">
<value>JSON RPC host has not started</value>
<comment></comment>
</data>
</data>
<data name="HostingJsonRpcVersionMissing" xml:space="preserve">
<value>JSON RPC version parameter is missing or invalid</value>
<comment></comment>
</data>
</data>
<data name="HostingMessageMissingMethod" xml:space="preserve">
<value>JSON RPC message is missing required method parameter</value>
<comment></comment>
</data>
</data>
<data name="HostingMethodHandlerDoesNotExist" xml:space="preserve">
<value>{0} handler for method '{1}' does not exist.</value>
<comment>.
Parameters: 0 - messageType (string), 1 - method (string) </comment>
</data>
</root>
</data>
</root>

View File

@@ -286,7 +286,7 @@ namespace Microsoft.SqlTools.Hosting.Protocol
catch (Exception e)
{
// Log the error and send an error event to the client
string message = string.Format("Exception occurred while receiving input message: {0}", e.Message);
string message = $"Exception occurred while receiving input message: {e.Message}";
Logger.Write(TraceEventType.Error, message);
// TODO: Add event to output queue, and unit test it
@@ -296,8 +296,8 @@ namespace Microsoft.SqlTools.Hosting.Protocol
}
// Verbose logging
string logMessage = string.Format("Received message of type[{0}] and method[{1}]",
incomingMessage.MessageType, incomingMessage.Method);
string logMessage =
$"Received message with Id[{incomingMessage.Id}] of type[{incomingMessage.MessageType}] and method[{incomingMessage.Method}]";
Logger.Write(TraceEventType.Verbose, logMessage);
// Process the message
@@ -309,8 +309,8 @@ namespace Microsoft.SqlTools.Hosting.Protocol
{
// Method could not be handled, if the message was a request, send an error back to the client
// TODO: Localize
string mnfLogMessage = string.Format("Failed to find method handler for type[{0}] and method[{1}]",
incomingMessage.MessageType, incomingMessage.Method);
string mnfLogMessage =
$"Failed to find method handler for type[{incomingMessage.MessageType}] and method[{incomingMessage.Method}]";
Logger.Write(TraceEventType.Warning, mnfLogMessage);
if (incomingMessage.MessageType == MessageType.Request)
@@ -324,8 +324,8 @@ namespace Microsoft.SqlTools.Hosting.Protocol
catch (Exception e)
{
// General errors should be logged but not halt the processing loop
string geLogMessage = string.Format("Exception thrown when handling message of type[{0}] and method[{1}]: {2}",
incomingMessage.MessageType, incomingMessage.Method, e);
string geLogMessage =
$"Exception thrown when handling message of type[{incomingMessage.MessageType}] and method[{incomingMessage.Method}]: {e}";
Logger.Write(TraceEventType.Error, geLogMessage);
// TODO: Should we be returning a response for failing requests?
}