From 727b4603926934561d4ad86e8718aff4376d1854 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 8 Mar 2024 16:34:43 -0800 Subject: [PATCH 01/47] SNOW-817091: Add async execution --- Snowflake.Data/Client/SnowflakeDbCommand.cs | 190 ++++++++++- .../Client/SnowflakeDbConnection.cs | 17 +- Snowflake.Data/Core/RestParams.cs | 2 + Snowflake.Data/Core/RestRequest.cs | 7 +- Snowflake.Data/Core/RestResponse.cs | 31 ++ Snowflake.Data/Core/SFBindUploader.cs | 4 +- Snowflake.Data/Core/SFResultSet.cs | 6 +- Snowflake.Data/Core/SFStatement.cs | 321 +++++++++++++++--- Snowflake.Data/Core/Session/SFSession.cs | 48 +++ 9 files changed, 556 insertions(+), 70 deletions(-) diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index ca415bacc..9b861dc5f 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -11,13 +11,14 @@ using System.Threading.Tasks; using Newtonsoft.Json; using Snowflake.Data.Log; +using System.Text.RegularExpressions; namespace Snowflake.Data.Client { [System.ComponentModel.DesignerCategory("Code")] public class SnowflakeDbCommand : DbCommand { - private DbConnection connection; + private SnowflakeDbConnection connection; private SFStatement sfStatement; @@ -25,6 +26,12 @@ public class SnowflakeDbCommand : DbCommand private SFLogger logger = SFLoggerFactory.GetLogger(); + // Async max retry and retry pattern + private const int AsyncNoDataMaxRetry = 24; + private readonly int[] _asyncRetryPattern = { 1, 1, 2, 3, 4, 8, 10 }; + + private static readonly Regex UuidRegex = new Regex("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); + public SnowflakeDbCommand() { logger.Debug("Constructing SnowflakeDbCommand class"); @@ -274,6 +281,179 @@ protected override async Task ExecuteDbDataReaderAsync(CommandBeha } } + /// + /// Execute a query in async mode. + /// + /// The query id. + public string ExecuteInAsyncMode() + { + logger.Debug($"ExecuteInAsyncMode"); + SFBaseResultSet resultSet = ExecuteInternal(asyncExec: true); + return resultSet.queryId; + } + + /// + /// Executes an asynchronous query in async mode. + /// + /// + /// The query id. + public async Task ExecuteAsyncInAsyncMode(CancellationToken cancellationToken) + { + logger.Debug($"ExecuteAsyncInAsyncMode"); + var resultSet = await ExecuteInternalAsync(cancellationToken, asyncExec: true).ConfigureAwait(false); + return resultSet.queryId; + } + + /// + /// Gets the query status based on query ID. + /// + /// + /// The query status. + public QueryStatus GetQueryStatus(string queryId) + { + logger.Debug($"GetQueryStatus"); + + if (UuidRegex.IsMatch(queryId)) + { + var sfStatement = new SFStatement(connection.SfSession); + return sfStatement.GetQueryStatus(queryId); + } + else + { + var errorMessage = $"The given query id {queryId} is not valid uuid"; + logger.Error(errorMessage); + throw new Exception(errorMessage); + } + + } + + /// + /// Gets the query status based on query ID. + /// + /// + /// + /// The query status. + public async Task GetQueryStatusAsync(string queryId, CancellationToken cancellationToken) + { + logger.Debug($"GetQueryStatusAsync"); + + // Check if queryId is valid uuid + if (UuidRegex.IsMatch(queryId)) + { + var sfStatement = new SFStatement(connection.SfSession); + return await sfStatement.GetQueryStatusAsync(queryId, cancellationToken).ConfigureAwait(false); + } + else + { + var errorMessage = $"The given query id {queryId} is not valid uuid"; + logger.Error(errorMessage); + throw new Exception(errorMessage); + } + } + + /// + /// Gets the query results based on query ID. + /// + /// + /// The query results. + public DbDataReader GetResultsFromQueryId(string queryId) + { + logger.Debug($"GetResultsFromQueryId"); + + int retryPatternPos = 0; + int noDataCounter = 0; + + QueryStatus status; + while (true) + { + status = GetQueryStatus(queryId); + + if (!QueryStatuses.IsStillRunning(status)) + { + break; + } + + // Timeout based on query status retry rules + Thread.Sleep(TimeSpan.FromSeconds(_asyncRetryPattern[retryPatternPos])); + + // If no data, increment the no data counter + if (status == QueryStatus.NO_DATA) + { + noDataCounter++; + + // Check if retry for no data is exceeded + if (noDataCounter > AsyncNoDataMaxRetry) + { + var errorMessage = "Max retry for no data is reached"; + logger.Error(errorMessage); + throw new Exception(errorMessage); + } + } + + if (retryPatternPos < _asyncRetryPattern.Length - 1) + { + retryPatternPos++; + } + } + + connection.SfSession.AsyncQueries.Remove(queryId); + SFBaseResultSet resultSet = sfStatement.GetResultWithId(queryId); + + return new SnowflakeDbDataReader(this, resultSet); + } + + /// + /// Gets the query results based on query ID. + /// + /// + /// + /// The query results. + public async Task GetResultsFromQueryIdAsync(string queryId, CancellationToken cancellationToken) + { + logger.Debug($"GetResultsFromQueryIdAsync"); + + int retryPatternPos = 0; + int noDataCounter = 0; + + QueryStatus status; + while (true) + { + status = GetQueryStatus(queryId); + + if (!QueryStatuses.IsStillRunning(status)) + { + break; + } + + // Timeout based on query status retry rules + await Task.Delay(TimeSpan.FromSeconds(_asyncRetryPattern[retryPatternPos]), cancellationToken).ConfigureAwait(false); + + // If no data, increment the no data counter + if (status == QueryStatus.NO_DATA) + { + noDataCounter++; + + // Check if retry for no data is exceeded + if (noDataCounter > AsyncNoDataMaxRetry) + { + var errorMessage = "Max retry for no data is reached"; + logger.Error(errorMessage); + throw new Exception(errorMessage); + } + } + + if (retryPatternPos < _asyncRetryPattern.Length - 1) + { + retryPatternPos++; + } + } + + connection.SfSession.AsyncQueries.Remove(queryId); + SFBaseResultSet resultSet = await sfStatement.GetResultWithIdAsync(queryId, cancellationToken).ConfigureAwait(false); + + return new SnowflakeDbDataReader(this, resultSet); + } + private static Dictionary convertToBindList(List parameters) { if (parameters == null || parameters.Count == 0) @@ -354,18 +534,18 @@ private void SetStatement() this.sfStatement = new SFStatement(session); } - private SFBaseResultSet ExecuteInternal(bool describeOnly = false) + private SFBaseResultSet ExecuteInternal(bool describeOnly = false, bool asyncExec = false) { CheckIfCommandTextIsSet(); SetStatement(); - return sfStatement.Execute(CommandTimeout, CommandText, convertToBindList(parameterCollection.parameterList), describeOnly); + return sfStatement.Execute(CommandTimeout, CommandText, convertToBindList(parameterCollection.parameterList), describeOnly, asyncExec); } - private Task ExecuteInternalAsync(CancellationToken cancellationToken, bool describeOnly = false) + private Task ExecuteInternalAsync(CancellationToken cancellationToken, bool describeOnly = false, bool asyncExec = false) { CheckIfCommandTextIsSet(); SetStatement(); - return sfStatement.ExecuteAsync(CommandTimeout, CommandText, convertToBindList(parameterCollection.parameterList), describeOnly, cancellationToken); + return sfStatement.ExecuteAsync(CommandTimeout, CommandText, convertToBindList(parameterCollection.parameterList), describeOnly, asyncExec, cancellationToken); } private void CheckIfCommandTextIsSet() diff --git a/Snowflake.Data/Client/SnowflakeDbConnection.cs b/Snowflake.Data/Client/SnowflakeDbConnection.cs index b773a0150..5a7e7fbee 100755 --- a/Snowflake.Data/Client/SnowflakeDbConnection.cs +++ b/Snowflake.Data/Client/SnowflakeDbConnection.cs @@ -153,7 +153,10 @@ public override void Close() { var transactionRollbackStatus = SnowflakeDbConnectionPool.GetPooling() ? TerminateTransactionForDirtyConnectionReturningToPool() : TransactionRollbackStatus.Undefined; - if (CanReuseSession(transactionRollbackStatus) && SnowflakeDbConnectionPool.AddSession(SfSession)) + if (CanReuseSession(transactionRollbackStatus) && + SfSession.StillRunningAsyncQueries() && + SnowflakeDbConnectionPool.AddSession(SfSession) + ) { logger.Debug($"Session pooled: {SfSession.sessionId}"); } @@ -176,7 +179,7 @@ public override async Task CloseAsync() } #endif - public virtual Task CloseAsync(CancellationToken cancellationToken) + public virtual async Task CloseAsync(CancellationToken cancellationToken) { logger.Debug("Close Connection."); TaskCompletionSource taskCompletionSource = new TaskCompletionSource(); @@ -191,7 +194,9 @@ public virtual Task CloseAsync(CancellationToken cancellationToken) { var transactionRollbackStatus = SnowflakeDbConnectionPool.GetPooling() ? TerminateTransactionForDirtyConnectionReturningToPool() : TransactionRollbackStatus.Undefined; - if (CanReuseSession(transactionRollbackStatus) && SnowflakeDbConnectionPool.AddSession(SfSession)) + if (CanReuseSession(transactionRollbackStatus) && + await SfSession.StillRunningAsyncQueriesAsync(cancellationToken).ConfigureAwait(false) && + SnowflakeDbConnectionPool.AddSession(SfSession)) { logger.Debug($"Session pooled: {SfSession.sessionId}"); _connectionState = ConnectionState.Closed; @@ -199,7 +204,7 @@ public virtual Task CloseAsync(CancellationToken cancellationToken) } else { - SfSession.CloseAsync(cancellationToken).ContinueWith( + await SfSession.CloseAsync(cancellationToken).ContinueWith( previousTask => { if (previousTask.IsFaulted) @@ -220,7 +225,7 @@ public virtual Task CloseAsync(CancellationToken cancellationToken) _connectionState = ConnectionState.Closed; taskCompletionSource.SetResult(null); } - }, cancellationToken); + }, cancellationToken).ConfigureAwait(false); } } else @@ -229,7 +234,7 @@ public virtual Task CloseAsync(CancellationToken cancellationToken) taskCompletionSource.SetResult(null); } } - return taskCompletionSource.Task; + await taskCompletionSource.Task; } protected virtual bool CanReuseSession(TransactionRollbackStatus transactionRollbackStatus) diff --git a/Snowflake.Data/Core/RestParams.cs b/Snowflake.Data/Core/RestParams.cs index 9dd4de8c8..72b910847 100644 --- a/Snowflake.Data/Core/RestParams.cs +++ b/Snowflake.Data/Core/RestParams.cs @@ -39,6 +39,8 @@ internal static class RestPath internal const string SF_QUERY_PATH = "/queries/v1/query-request"; + internal const string SF_MONITOR_QUERY_PATH = "/monitoring/queries/"; + internal const string SF_SESSION_HEARTBEAT_PATH = SF_SESSION_PATH + "/heartbeat"; internal const string SF_CONSOLE_LOGIN = "/console/login"; diff --git a/Snowflake.Data/Core/RestRequest.cs b/Snowflake.Data/Core/RestRequest.cs index bc4ec8d21..112743f77 100644 --- a/Snowflake.Data/Core/RestRequest.cs +++ b/Snowflake.Data/Core/RestRequest.cs @@ -129,6 +129,8 @@ internal SFRestRequest() : base() internal bool _isLogin { get; set; } + internal bool _isStatusRequest { get; set; } + public override string ToString() { return String.Format("SFRestRequest {{url: {0}, request body: {1} }}", Url.ToString(), @@ -154,7 +156,7 @@ HttpRequestMessage IRestRequest.ToRequestMessage(HttpMethod method) // add quote otherwise it would be reported as error format string osInfo = "(" + SFEnvironment.ClientEnv.osVersion + ")"; - if (isPutGet) + if (isPutGet || _isStatusRequest) { message.Headers.Accept.Add(applicationJson); } @@ -313,6 +315,9 @@ class QueryRequest [JsonProperty(PropertyName = "queryContextDTO", NullValueHandling = NullValueHandling.Ignore)] internal RequestQueryContext QueryContextDTO { get; set; } + + [JsonProperty(PropertyName = "asyncExec")] + internal bool asyncExec { get; set; } } // The query context in query response diff --git a/Snowflake.Data/Core/RestResponse.cs b/Snowflake.Data/Core/RestResponse.cs index 97f3f9772..75f1698ea 100755 --- a/Snowflake.Data/Core/RestResponse.cs +++ b/Snowflake.Data/Core/RestResponse.cs @@ -423,6 +423,37 @@ internal class PutGetEncryptionMaterial internal long smkId { get; set; } } + internal class QueryStatusResponse : BaseRestResponse + { + + [JsonProperty(PropertyName = "data")] + internal QueryStatusData data { get; set; } + } + + internal class QueryStatusData + { + [JsonProperty(PropertyName = "queries", NullValueHandling = NullValueHandling.Ignore)] + internal List queries { get; set; } + } + + internal class QueryStatusDataQueries + { + [JsonProperty(PropertyName = "id", NullValueHandling = NullValueHandling.Ignore)] + internal string id { get; set; } + + [JsonProperty(PropertyName = "status", NullValueHandling = NullValueHandling.Ignore)] + internal string status { get; set; } + + [JsonProperty(PropertyName = "state", NullValueHandling = NullValueHandling.Ignore)] + internal string state { get; set; } + + [JsonProperty(PropertyName = "errorCode", NullValueHandling = NullValueHandling.Ignore)] + internal string errorCode { get; set; } + + [JsonProperty(PropertyName = "errorMessage", NullValueHandling = NullValueHandling.Ignore)] + internal string errorMessage { get; set; } + } + // Retrieved from: https://stackoverflow.com/a/18997172 internal class SingleOrArrayConverter : JsonConverter { diff --git a/Snowflake.Data/Core/SFBindUploader.cs b/Snowflake.Data/Core/SFBindUploader.cs index 68af7405b..a1b3f161d 100644 --- a/Snowflake.Data/Core/SFBindUploader.cs +++ b/Snowflake.Data/Core/SFBindUploader.cs @@ -290,7 +290,7 @@ private void CreateStage() try { SFStatement statement = new SFStatement(session); - SFBaseResultSet resultSet = statement.Execute(0, CREATE_STAGE_STMT, null, false); + SFBaseResultSet resultSet = statement.Execute(0, CREATE_STAGE_STMT, null, false, false); session.SetArrayBindStage(STAGE_NAME); } catch (Exception e) @@ -314,7 +314,7 @@ internal async Task CreateStageAsync(CancellationToken cancellationToken) try { SFStatement statement = new SFStatement(session); - var resultSet = await statement.ExecuteAsync(0, CREATE_STAGE_STMT, null, false, cancellationToken).ConfigureAwait(false); + var resultSet = await statement.ExecuteAsync(0, CREATE_STAGE_STMT, null, false, false, cancellationToken).ConfigureAwait(false); session.SetArrayBindStage(STAGE_NAME); } catch (Exception e) diff --git a/Snowflake.Data/Core/SFResultSet.cs b/Snowflake.Data/Core/SFResultSet.cs index 55b069806..03e1794c9 100755 --- a/Snowflake.Data/Core/SFResultSet.cs +++ b/Snowflake.Data/Core/SFResultSet.cs @@ -28,7 +28,7 @@ public SFResultSet(QueryExecResponseData responseData, SFStatement sfStatement, { try { - columnCount = responseData.rowType.Count; + columnCount = responseData.rowType?.Count ?? 0; this.sfStatement = sfStatement; UpdateSessionStatus(responseData); @@ -40,10 +40,10 @@ public SFResultSet(QueryExecResponseData responseData, SFStatement sfStatement, _chunkDownloader = ChunkDownloaderFactory.GetDownloader(responseData, this, cancellationToken); } - _currentChunk = new SFResultChunk(responseData.rowSet); + _currentChunk = responseData.rowSet != null ? new SFResultChunk(responseData.rowSet) : null; responseData.rowSet = null; - sfResultSetMetaData = new SFResultSetMetaData(responseData, this.sfStatement.SfSession); + sfResultSetMetaData = responseData.rowType != null ? new SFResultSetMetaData(responseData, this.sfStatement.SfSession) : null; isClosed = false; diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index a8ada8af2..56c53b70a 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -17,6 +17,61 @@ namespace Snowflake.Data.Core { + /// + /// The status types of the query. + /// + public enum QueryStatus + { + RUNNING, + ABORTING, + SUCCESS, + FAILED_WITH_ERROR, + ABORTED, + QUEUED, + FAILED_WITH_INCIDENT, + DISCONNECTED, + RESUMING_WAREHOUSE, + // purposeful typo.Is present in QueryDTO.java + QUEUED_REPARING_WAREHOUSE, + RESTARTED, + BLOCKED, + NO_DATA, + } + + internal static class QueryStatuses + { + internal static bool IsStillRunning(QueryStatus status) + { + switch (status) + { + case QueryStatus.RUNNING: + case QueryStatus.RESUMING_WAREHOUSE: + case QueryStatus.QUEUED: + case QueryStatus.QUEUED_REPARING_WAREHOUSE: + case QueryStatus.NO_DATA: + return true; + default: + return false; + } + } + + internal static bool IsAnError(QueryStatus status) + { + switch (status) + { + case QueryStatus.ABORTING: + case QueryStatus.FAILED_WITH_ERROR: + case QueryStatus.ABORTED: + case QueryStatus.FAILED_WITH_INCIDENT: + case QueryStatus.DISCONNECTED: + case QueryStatus.BLOCKED: + return true; + default: + return false; + } + } + } + class SFStatement { static private SFLogger logger = SFLoggerFactory.GetLogger(); @@ -90,7 +145,7 @@ private void ClearQueryRequestId() _requestId = null; } - private SFRestRequest BuildQueryRequest(string sql, Dictionary bindings, bool describeOnly) + private SFRestRequest BuildQueryRequest(string sql, Dictionary bindings, bool describeOnly, bool asyncExec) { AssignQueryRequestId(); @@ -120,8 +175,9 @@ private SFRestRequest BuildQueryRequest(string sql, Dictionary private bool SessionExpired(BaseRestResponse r) => r.code == SFSession.SF_SESSION_EXPIRED_CODE; - internal async Task ExecuteAsync(int timeout, string sql, Dictionary bindings, bool describeOnly, + internal async Task ExecuteAsync(int timeout, string sql, Dictionary bindings, bool describeOnly, bool asyncExec, CancellationToken cancellationToken) { // Trim the sql query and check if this is a PUT/GET command string trimmedSql = TrimSql(sql); - if (IsPutOrGetCommand(trimmedSql)) { + if (IsPutOrGetCommand(trimmedSql)) + { throw new NotImplementedException("Get and Put are not supported in async calls. Use Execute() instead of ExecuteAsync()."); } @@ -287,8 +344,8 @@ internal async Task ExecuteAsync(int timeout, string sql, Dicti logger.Warn("Exception encountered trying to upload binds to stage. Attaching binds in payload instead. {0}", e); } } - - var queryRequest = BuildQueryRequest(sql, bindings, describeOnly); + + var queryRequest = BuildQueryRequest(sql, bindings, describeOnly, asyncExec); try { QueryExecResponse response = null; @@ -309,19 +366,26 @@ internal async Task ExecuteAsync(int timeout, string sql, Dicti var lastResultUrl = response.data?.getResultUrl; - while (RequestInProgress(response) || SessionExpired(response)) + if (asyncExec) { - var req = BuildResultRequest(lastResultUrl); - response = await _restRequester.GetAsync(req, cancellationToken).ConfigureAwait(false); - - if (SessionExpired(response)) - { - logger.Info("Ping pong request failed with session expired, trying to renew the session."); - await SfSession.renewSessionAsync(cancellationToken).ConfigureAwait(false); - } - else + SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.RUNNING); + } + else + { + while (RequestInProgress(response) || SessionExpired(response)) { - lastResultUrl = response.data?.getResultUrl; + var req = BuildResultRequest(lastResultUrl); + response = await _restRequester.GetAsync(req, cancellationToken).ConfigureAwait(false); + + if (SessionExpired(response)) + { + logger.Info("Ping pong request failed with session expired, trying to renew the session."); + await SfSession.renewSessionAsync(cancellationToken).ConfigureAwait(false); + } + else + { + lastResultUrl = response.data?.getResultUrl; + } } } @@ -338,8 +402,8 @@ internal async Task ExecuteAsync(int timeout, string sql, Dicti ClearQueryRequestId(); } } - - internal SFBaseResultSet Execute(int timeout, string sql, Dictionary bindings, bool describeOnly) + + internal SFBaseResultSet Execute(int timeout, string sql, Dictionary bindings, bool describeOnly, bool asyncExec) { // Trim the sql query and check if this is a PUT/GET command string trimmedSql = TrimSql(sql); @@ -350,7 +414,7 @@ internal SFBaseResultSet Execute(int timeout, string sql, Dictionary bindings, bool describeOnly) + private SFBaseResultSet ExecuteSqlOtherThanPutGet(int timeout, string sql, Dictionary bindings, bool describeOnly, bool asyncExec) { try { @@ -432,12 +496,13 @@ private SFBaseResultSet ExecuteSqlOtherThanPutGet(int timeout, string sql, Dicti } } - QueryExecResponse response = - ExecuteHelper( - timeout, - sql, - bindings, - describeOnly); + QueryExecResponse response = + ExecuteHelper( + timeout, + sql, + bindings, + describeOnly, + asyncExec); return BuildResultSet(response, CancellationToken.None); } @@ -531,12 +596,13 @@ internal T ExecuteHelper( int timeout, string sql, Dictionary bindings, - bool describeOnly) + bool describeOnly, + bool asyncExec = false) where T : BaseQueryExecResponse where U : IQueryExecResponseData { registerQueryCancellationCallback(timeout, CancellationToken.None); - var queryRequest = BuildQueryRequest(sql, bindings, describeOnly); + var queryRequest = BuildQueryRequest(sql, bindings, describeOnly, asyncExec); try { T response = null; @@ -558,20 +624,28 @@ internal T ExecuteHelper( if (typeof(T) == typeof(QueryExecResponse)) { QueryExecResponse queryResponse = (QueryExecResponse)(object)response; - var lastResultUrl = queryResponse.data?.getResultUrl; - while (RequestInProgress(response) || SessionExpired(response)) + if (asyncExec) { - var req = BuildResultRequest(lastResultUrl); - response = _restRequester.Get(req); + SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.RUNNING); + } + else + { + var lastResultUrl = queryResponse.data?.getResultUrl; - if (SessionExpired(response)) + while (RequestInProgress(response) || SessionExpired(response)) { - logger.Info("Ping pong request failed with session expired, trying to renew the session."); - SfSession.renewSession(); - } - else - { - lastResultUrl = queryResponse.data?.getResultUrl; + var req = BuildResultRequest(lastResultUrl); + response = _restRequester.Get(req); + + if (SessionExpired(response)) + { + logger.Info("Ping pong request failed with session expired, trying to renew the session."); + SfSession.renewSession(); + } + else + { + lastResultUrl = queryResponse.data?.getResultUrl; + } } } } @@ -612,13 +686,14 @@ internal async Task ExecuteAsyncHelper( string sql, Dictionary bindings, bool describeOnly, - CancellationToken cancellationToken + CancellationToken cancellationToken, + bool asyncExec = false ) where T : BaseQueryExecResponse where U : IQueryExecResponseData { registerQueryCancellationCallback(timeout, CancellationToken.None); - var queryRequest = BuildQueryRequest(sql, bindings, describeOnly); + var queryRequest = BuildQueryRequest(sql, bindings, describeOnly, asyncExec); try { T response = null; @@ -640,20 +715,28 @@ CancellationToken cancellationToken if (typeof(T) == typeof(QueryExecResponse)) { QueryExecResponse queryResponse = (QueryExecResponse)(object)response; - var lastResultUrl = queryResponse.data?.getResultUrl; - while (RequestInProgress(response) || SessionExpired(response)) + if (asyncExec) { - var req = BuildResultRequest(lastResultUrl); - response = await _restRequester.GetAsync(req, cancellationToken).ConfigureAwait(false); + SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.RUNNING); + } + else + { + var lastResultUrl = queryResponse.data?.getResultUrl; - if (SessionExpired(response)) + while (RequestInProgress(response) || SessionExpired(response)) { - logger.Info("Ping pong request failed with session expired, trying to renew the session."); - await SfSession.renewSessionAsync(cancellationToken).ConfigureAwait(false); - } - else - { - lastResultUrl = queryResponse.data?.getResultUrl; + var req = BuildResultRequest(lastResultUrl); + response = await _restRequester.GetAsync(req, cancellationToken).ConfigureAwait(false); + + if (SessionExpired(response)) + { + logger.Info("Ping pong request failed with session expired, trying to renew the session."); + await SfSession.renewSessionAsync(cancellationToken).ConfigureAwait(false); + } + else + { + lastResultUrl = queryResponse.data?.getResultUrl; + } } } } @@ -680,6 +763,138 @@ CancellationToken cancellationToken } } + /// + /// Creates a request to get the query status based on query ID. + /// + /// + /// The request to get the query status. + private SFRestRequest BuildQueryStatusRequest(string queryId) + { + var queryUri = SfSession.BuildUri(RestPath.SF_MONITOR_QUERY_PATH + queryId); + + return new SFRestRequest + { + Url = queryUri, + authorizationToken = string.Format(SF_AUTHORIZATION_SNOWFLAKE_FMT, SfSession.sessionToken), + serviceName = SfSession.ParameterMap.ContainsKey(SFSessionParameter.SERVICE_NAME) + ? (String)SfSession.ParameterMap[SFSessionParameter.SERVICE_NAME] : null, + HttpTimeout = Timeout.InfiniteTimeSpan, + RestTimeout = Timeout.InfiniteTimeSpan, + sid = SfSession.sessionId, + _isStatusRequest = true + }; + } + + /// + /// Gets the query status based on query ID. + /// + /// + /// The query status. + internal QueryStatus GetQueryStatus(string queryId) + { + var queryRequest = BuildQueryStatusRequest(queryId); + + try + { + QueryStatusResponse response = null; + bool receivedFirstQueryResponse = false; + while (!receivedFirstQueryResponse) + { + response = _restRequester.Get(queryRequest); + if (SessionExpired(response)) + { + SfSession.renewSession(); + queryRequest.authorizationToken = string.Format(SF_AUTHORIZATION_SNOWFLAKE_FMT, SfSession.sessionToken); + } + else + { + receivedFirstQueryResponse = true; + } + } + + if (!response.success) + { + throw new SnowflakeDbException( + response.data.queries[0].state, + response.code, + response.message, + queryId); + } + + var status = response.data.queries.Count != 0 ? response.data.queries[0].status : QueryStatus.NO_DATA.ToString(); + + QueryStatus queryStatus; + Enum.TryParse(status, out queryStatus); + + SfSession.AsyncQueries[queryId] = queryStatus; + return queryStatus; + } + catch + { + logger.Error("Query execution failed."); + throw; + } + finally + { + ClearQueryRequestId(); + } + } + + /// + /// Gets the query status based on query ID. + /// + /// + /// The query status. + internal async Task GetQueryStatusAsync(string queryId, CancellationToken cancellationToken) + { + var queryRequest = BuildQueryStatusRequest(queryId); + + try + { + QueryStatusResponse response = null; + bool receivedFirstQueryResponse = false; + while (!receivedFirstQueryResponse) + { + response = await _restRequester.GetAsync(queryRequest, cancellationToken).ConfigureAwait(false); + if (SessionExpired(response)) + { + SfSession.renewSession(); + queryRequest.authorizationToken = string.Format(SF_AUTHORIZATION_SNOWFLAKE_FMT, SfSession.sessionToken); + } + else + { + receivedFirstQueryResponse = true; + } + } + + if (!response.success) + { + throw new SnowflakeDbException( + response.data.queries[0].state, + response.code, + response.message, + queryId); + } + + var status = response.data.queries.Count != 0 ? response.data.queries[0].status : QueryStatus.NO_DATA.ToString(); + + QueryStatus queryStatus; + Enum.TryParse(status, out queryStatus); + + SfSession.AsyncQueries[queryId] = queryStatus; + return queryStatus; + } + catch + { + logger.Error("Query execution failed."); + throw; + } + finally + { + ClearQueryRequestId(); + } + } + /// /// Trim the query by removing spaces and comments at the beginning. /// diff --git a/Snowflake.Data/Core/Session/SFSession.cs b/Snowflake.Data/Core/Session/SFSession.cs index e39370f19..fc318008d 100755 --- a/Snowflake.Data/Core/Session/SFSession.cs +++ b/Snowflake.Data/Core/Session/SFSession.cs @@ -577,6 +577,54 @@ internal bool IsExpired(long timeoutInSeconds, long utcTimeInSeconds) { return _startTime + timeoutInSeconds <= utcTimeInSeconds; } + + /// + /// Contains the ID and status of queries executed in async mode + /// + internal Dictionary AsyncQueries = new Dictionary(); + + /// + /// Checks if the session contains async queries that are still executing + /// + /// True if session can be closed, false otherwise + internal bool StillRunningAsyncQueries() + { + foreach (var query in AsyncQueries.ToList()) + { + var sfStatement = new SFStatement(this); + var status = sfStatement.GetQueryStatus(query.Key); + if (QueryStatuses.IsStillRunning(status)) + { + return true; + } + + AsyncQueries.Remove(query.Key); + } + + return false; + } + + /// + /// Checks if the session contains async queries that are still executing + /// + /// + /// True if session can be closed, false otherwise + internal async Task StillRunningAsyncQueriesAsync(CancellationToken cancellationToken) + { + foreach (var query in AsyncQueries.ToList()) + { + var sfStatement = new SFStatement(this); + var status = await sfStatement.GetQueryStatusAsync(query.Key, cancellationToken).ConfigureAwait(false); + if (QueryStatuses.IsStillRunning(status)) + { + return true; + } + + AsyncQueries.Remove(query.Key); + } + + return false; + } } } From 812cba066abfa9d6ae0a944709fb71d36cbcd57d Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 8 Mar 2024 16:35:02 -0800 Subject: [PATCH 02/47] SNOW-817091: Add tests --- .../IntegrationTests/SFConnectionPoolIT.cs | 2 +- .../IntegrationTests/SFDbCommandIT.cs | 651 ++++++++++++++++++ .../UnitTests/SFStatementTest.cs | 52 +- 3 files changed, 697 insertions(+), 8 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs index 2a403521e..4f5020538 100644 --- a/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFConnectionPoolIT.cs @@ -333,7 +333,7 @@ void ThreadProcess2(string connstr) Thread.Sleep(5000); SFStatement statement = new SFStatement(conn1.SfSession); - SFBaseResultSet resultSet = statement.Execute(0, "select 1", null, false); + SFBaseResultSet resultSet = statement.Execute(0, "select 1", null, false, false); Assert.AreEqual(true, resultSet.Next()); Assert.AreEqual("1", resultSet.GetString(0)); SnowflakeDbConnectionPool.ClearAllPools(); diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 6e92c0aac..59b39a764 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -175,6 +175,375 @@ public void TestExecuteAsyncWithMaxRetryReached() Assert.GreaterOrEqual(stopwatch.ElapsedMilliseconds, 30 * 1000); } } + + [Test] + public void TestAsyncExecQueryAsync() + { + string queryId; + var expectedWaitTime = 5; + Task task; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; + + // Act + var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); + queryTask.Wait(); + queryId = queryTask.Result; + + var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); + statusTask.Wait(); + var queryStatus = statusTask.Result; + + // Assert + Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); + + // Act + var readerTask = cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None); + readerTask.Wait(); + DbDataReader reader = readerTask.Result; + + statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); + statusTask.Wait(); + queryStatus = statusTask.Result; + + // Assert + Assert.IsTrue(reader.Read()); + Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); + Assert.AreEqual(QueryStatus.SUCCESS, queryStatus); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + } + + [Test] + public void TestMixedSyncAndAsyncQueryAsync() + { + string queryId; + var expectedWaitTime = 5; + Task task; + + // Start the async exec query + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; + + // Act + var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); + queryTask.Wait(); + queryId = queryTask.Result; + + var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); + statusTask.Wait(); + var queryStatus = statusTask.Result; + + // Assert + Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + + // Execute a normal query + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"select 1;"; + + // Act + var row = cmd.ExecuteScalar(); + + // Assert + Assert.AreEqual(1, row); + } + + conn.Close(); + } + + // Get results of the async exec query + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Act + var readerTask = cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None); + readerTask.Wait(); + DbDataReader reader = readerTask.Result; + + var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); + statusTask.Wait(); + var queryStatus = statusTask.Result; + + // Assert + Assert.IsTrue(reader.Read()); + Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); + Assert.AreEqual(QueryStatus.SUCCESS, queryStatus); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + } + + [Test] + public void TestStillRunningAsyncQueriesAsync() + { + string queryIdOne, queryIdTwo; + Task task; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({3}, \'SECONDS\');"; + + // Act + var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); + queryTask.Wait(); + queryIdOne = queryTask.Result; + + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({10}, \'SECONDS\');"; + + // Act + queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); + queryTask.Wait(); + queryIdTwo = queryTask.Result; + var stillRunningTask = conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None); + stillRunningTask.Wait(); + var statusTask = cmd.GetQueryStatusAsync(queryIdOne, CancellationToken.None); + statusTask.Wait(); + + // Assert + Assert.IsTrue(QueryStatuses.IsStillRunning(statusTask.Result)); + Assert.IsTrue(stillRunningTask.Result); + + // Act + var readerTask = cmd.GetResultsFromQueryIdAsync(queryIdOne, CancellationToken.None); + readerTask.Wait(); + statusTask = cmd.GetQueryStatusAsync(queryIdOne, CancellationToken.None); + statusTask.Wait(); + stillRunningTask = conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None); + stillRunningTask.Wait(); + + // Assert + Assert.IsFalse(QueryStatuses.IsStillRunning(statusTask.Result)); + Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdTwo))); + Assert.IsTrue(stillRunningTask.Result); + + // Act + readerTask = cmd.GetResultsFromQueryIdAsync(queryIdTwo, CancellationToken.None); + readerTask.Wait(); + statusTask = cmd.GetQueryStatusAsync(queryIdTwo, CancellationToken.None); + statusTask.Wait(); + stillRunningTask = conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None); + stillRunningTask.Wait(); + + // Assert + Assert.IsFalse(QueryStatuses.IsStillRunning(statusTask.Result)); + Assert.IsFalse(stillRunningTask.Result); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + } + + [Test] + public void TestFailedAsyncExecQueryThrowsErrorAsync() + { + string queryId; + Task task; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"SELECT * FROM FAKE_TABLE;"; + + // Act + var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); + queryTask.Wait(); + queryId = queryTask.Result; + + var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); + statusTask.Wait(); + var queryStatus = statusTask.Result; + + while (QueryStatuses.IsStillRunning(queryStatus)) + { + Thread.Sleep(1000); + statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); + statusTask.Wait(); + queryStatus = statusTask.Result; + } + + // Assert + Assert.AreEqual(QueryStatus.FAILED_WITH_ERROR, queryStatus); + + // Act + var readerTask = cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None); + var thrown = Assert.Throws(() => readerTask.Wait()); + + // Assert + Assert.IsTrue(thrown.InnerException.Message.Contains("'FAKE_TABLE' does not exist")); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + } + + [Test] + public void TestGetStatusOfInvalidQueryIdAsync() + { + string fakeQueryId = "fakeQueryId"; + Task task; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + var statusTask = cmd.GetQueryStatusAsync(fakeQueryId, CancellationToken.None); + + // Act + var thrown = Assert.Throws(() => statusTask.Wait()); + + // Assert + Assert.IsTrue(thrown.InnerException.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + } + + [Test] + public void TestGetResultsOfInvalidQueryIdAsync() + { + string fakeQueryId = "fakeQueryId"; + Task task; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + var readerTask = cmd.GetResultsFromQueryIdAsync(fakeQueryId, CancellationToken.None); + + // Act + var thrown = Assert.Throws(() => readerTask.Wait()); + + // Assert + Assert.IsTrue(thrown.InnerException.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + } + + [Test, NonParallelizable] + public void TestGetStatusOfUnknownQueryIdAsync() + { + string unknownQueryId = "ba321edc-1abc-123e-987f-1234a56b789c"; + Task task; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Act + var statusTask = cmd.GetQueryStatusAsync(unknownQueryId, CancellationToken.None); + statusTask.Wait(); + + // Assert + Assert.AreEqual(QueryStatus.NO_DATA, statusTask.Result); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + } + + [Test] + public void TestGetResultsOfUnknownQueryIdAsync() + { + string unknownQueryId = "ab123fed-1abc-987f-987f-1234a56b789c"; + Task task; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + var readerTask = cmd.GetResultsFromQueryIdAsync(unknownQueryId, CancellationToken.None); + + // Act + var thrown = Assert.Throws(() => readerTask.Wait()); + + // Assert + Assert.IsTrue(thrown.Message.Contains($"Max retry for no data is reached")); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + } } [TestFixture] @@ -1040,5 +1409,287 @@ public void TestGetQueryId() conn.Close(); } } + + [Test] + public void TestAsyncExecQuery() + { + string queryId; + var expectedWaitTime = 5; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; + + // Act + queryId = cmd.ExecuteInAsyncMode(); + + // Assert + Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))); + + // Act + DbDataReader reader = cmd.GetResultsFromQueryId(queryId); + + // Assert + Assert.IsTrue(reader.Read()); + Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); + Assert.AreEqual(QueryStatus.SUCCESS, cmd.GetQueryStatus(queryId)); + } + + conn.Close(); + } + } + + [Test] + public void TestMixedSyncAndAsyncQuery() + { + string queryId; + var expectedWaitTime = 5; + + // Start the async exec query + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; + + // Act + queryId = cmd.ExecuteInAsyncMode(); + + // Assert + Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))); + } + + conn.Close(); + } + + // Execute a normal query + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"select 1;"; + + // Act + var row = cmd.ExecuteScalar(); + + // Assert + Assert.AreEqual(1, row); + } + + conn.Close(); + } + + // Get results of the async exec query + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Act + DbDataReader reader = cmd.GetResultsFromQueryId(queryId); + + // Assert + Assert.IsTrue(reader.Read()); + Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); + Assert.AreEqual(QueryStatus.SUCCESS, cmd.GetQueryStatus(queryId)); + } + + conn.Close(); + } + } + + + [Test] + public void TestStillRunningAsyncQueries() + { + string queryIdOne, queryIdTwo; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({3}, \'SECONDS\');"; + + // Act + queryIdOne = cmd.ExecuteInAsyncMode(); + + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({10}, \'SECONDS\');"; + + // Act + queryIdTwo = cmd.ExecuteInAsyncMode(); + + // Assert + Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdOne))); + Assert.IsTrue(conn.SfSession.StillRunningAsyncQueries()); + + // Act + cmd.GetResultsFromQueryId(queryIdOne); + + // Assert + Assert.IsFalse(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdOne))); + Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdTwo))); + Assert.IsTrue(conn.SfSession.StillRunningAsyncQueries()); + + // Act + cmd.GetResultsFromQueryId(queryIdTwo); + + // Assert + Assert.IsFalse(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdTwo))); + Assert.AreEqual(QueryStatus.SUCCESS, cmd.GetQueryStatus(queryIdTwo)); + Assert.IsFalse(conn.SfSession.StillRunningAsyncQueries()); + } + + conn.Close(); + } + } + + [Test] + public void TestFailedAsyncExecQueryThrowsError() + { + string queryId; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"SELECT * FROM FAKE_TABLE;"; + + // Act + queryId = cmd.ExecuteInAsyncMode(); + while (QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))) + { + Thread.Sleep(1000); + } + + // Assert + Assert.AreEqual(QueryStatus.FAILED_WITH_ERROR, cmd.GetQueryStatus(queryId)); + + // Act + var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(queryId)); + + // Assert + Assert.IsTrue(thrown.Message.Contains("'FAKE_TABLE' does not exist")); + } + + conn.Close(); + } + } + + [Test] + public void TestGetStatusOfInvalidQueryId() + { + string fakeQueryId = "fakeQueryId"; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Act + var thrown = Assert.Throws(() => cmd.GetQueryStatus(fakeQueryId)); + + // Assert + Assert.IsTrue(thrown.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); + } + + conn.Close(); + } + } + + [Test] + public void TestGetResultsOfInvalidQueryId() + { + string fakeQueryId = "fakeQueryId"; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Act + var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(fakeQueryId)); + + // Assert + Assert.IsTrue(thrown.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); + } + + conn.Close(); + } + } + + [Test, NonParallelizable] + public void TestGetStatusOfUnknownQueryId() + { + string unknownQueryId = "ab123cde-1cba-789a-987f-1234a56b789c"; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Act + var queryStatus = cmd.GetQueryStatus(unknownQueryId); + + // Assert + Assert.AreEqual(QueryStatus.NO_DATA, queryStatus); + } + + conn.Close(); + } + } + + [Test] + public void TestGetResultsOfUnknownQueryId() + { + string unknownQueryId = "ba987def-1abc-987f-987f-1234a56b789c"; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Act + var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(unknownQueryId)); + + // Assert + Assert.IsTrue(thrown.Message.Contains($"Max retry for no data is reached")); + } + + conn.Close(); + } + } } } diff --git a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs index 330b19f96..e1a8b3042 100755 --- a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs @@ -21,7 +21,7 @@ public void TestSessionRenew() SFSession sfSession = new SFSession("account=test;user=test;password=test", null, restRequester); sfSession.Open(); SFStatement statement = new SFStatement(sfSession); - SFBaseResultSet resultSet = statement.Execute(0, "select 1", null, false); + SFBaseResultSet resultSet = statement.Execute(0, "select 1", null, false, false); Assert.AreEqual(true, resultSet.Next()); Assert.AreEqual("1", resultSet.GetString(0)); Assert.AreEqual("new_session_token", sfSession.sessionToken); @@ -37,7 +37,7 @@ public void TestSessionRenewDuringQueryExec() SFSession sfSession = new SFSession("account=test;user=test;password=test", null, restRequester); sfSession.Open(); SFStatement statement = new SFStatement(sfSession); - SFBaseResultSet resultSet = statement.Execute(0, "select 1", null, false); + SFBaseResultSet resultSet = statement.Execute(0, "select 1", null, false, false); Assert.AreEqual(true, resultSet.Next()); Assert.AreEqual("1", resultSet.GetString(0)); } @@ -57,7 +57,7 @@ public void TestServiceName() for (int i = 0; i < 5; i++) { SFStatement statement = new SFStatement(sfSession); - SFBaseResultSet resultSet = statement.Execute(0, "SELECT 1", null, false); + SFBaseResultSet resultSet = statement.Execute(0, "SELECT 1", null, false, false); expectServiceName += "a"; Assert.AreEqual(expectServiceName, sfSession.ParameterMap[SFSessionParameter.SERVICE_NAME]); } @@ -73,7 +73,7 @@ public void TestTrimSqlBlockComment() SFSession sfSession = new SFSession("account=test;user=test;password=test", null, restRequester); sfSession.Open(); SFStatement statement = new SFStatement(sfSession); - SFBaseResultSet resultSet = statement.Execute(0, "/*comment*/select 1/*comment*/", null, false); + SFBaseResultSet resultSet = statement.Execute(0, "/*comment*/select 1/*comment*/", null, false, false); Assert.AreEqual(true, resultSet.Next()); Assert.AreEqual("1", resultSet.GetString(0)); } @@ -88,7 +88,7 @@ public void TestTrimSqlBlockCommentMultiline() SFSession sfSession = new SFSession("account=test;user=test;password=test", null, restRequester); sfSession.Open(); SFStatement statement = new SFStatement(sfSession); - SFBaseResultSet resultSet = statement.Execute(0, "/*comment\r\ncomment*/select 1/*comment\r\ncomment*/", null, false); + SFBaseResultSet resultSet = statement.Execute(0, "/*comment\r\ncomment*/select 1/*comment\r\ncomment*/", null, false, false); Assert.AreEqual(true, resultSet.Next()); Assert.AreEqual("1", resultSet.GetString(0)); } @@ -103,7 +103,7 @@ public void TestTrimSqlLineComment() SFSession sfSession = new SFSession("account=test;user=test;password=test", null, restRequester); sfSession.Open(); SFStatement statement = new SFStatement(sfSession); - SFBaseResultSet resultSet = statement.Execute(0, "--comment\r\nselect 1\r\n--comment", null, false); + SFBaseResultSet resultSet = statement.Execute(0, "--comment\r\nselect 1\r\n--comment", null, false, false); Assert.AreEqual(true, resultSet.Next()); Assert.AreEqual("1", resultSet.GetString(0)); } @@ -118,9 +118,47 @@ public void TestTrimSqlLineCommentWithClosingNewline() SFSession sfSession = new SFSession("account=test;user=test;password=test", null, restRequester); sfSession.Open(); SFStatement statement = new SFStatement(sfSession); - SFBaseResultSet resultSet = statement.Execute(0, "--comment\r\nselect 1\r\n--comment\r\n", null, false); + SFBaseResultSet resultSet = statement.Execute(0, "--comment\r\nselect 1\r\n--comment\r\n", null, false, false); Assert.AreEqual(true, resultSet.Next()); Assert.AreEqual("1", resultSet.GetString(0)); } + + [Test] + [TestCase(QueryStatus.RUNNING, true)] + [TestCase(QueryStatus.RESUMING_WAREHOUSE, true)] + [TestCase(QueryStatus.QUEUED, true)] + [TestCase(QueryStatus.QUEUED_REPARING_WAREHOUSE, true)] + [TestCase(QueryStatus.NO_DATA, true)] + [TestCase(QueryStatus.ABORTING, false)] + [TestCase(QueryStatus.SUCCESS, false)] + [TestCase(QueryStatus.FAILED_WITH_ERROR, false)] + [TestCase(QueryStatus.ABORTED, false)] + [TestCase(QueryStatus.FAILED_WITH_INCIDENT, false)] + [TestCase(QueryStatus.DISCONNECTED, false)] + [TestCase(QueryStatus.RESTARTED, false)] + [TestCase(QueryStatus.BLOCKED, false)] + public void TestIsStillRunning(QueryStatus status, bool expectedResult) + { + Assert.AreEqual(expectedResult, QueryStatuses.IsStillRunning(status)); + } + + [Test] + [TestCase(QueryStatus.ABORTING, true)] + [TestCase(QueryStatus.FAILED_WITH_ERROR, true)] + [TestCase(QueryStatus.ABORTED, true)] + [TestCase(QueryStatus.FAILED_WITH_INCIDENT, true)] + [TestCase(QueryStatus.DISCONNECTED, true)] + [TestCase(QueryStatus.BLOCKED, true)] + [TestCase(QueryStatus.RUNNING, false)] + [TestCase(QueryStatus.RESUMING_WAREHOUSE, false)] + [TestCase(QueryStatus.QUEUED, false)] + [TestCase(QueryStatus.QUEUED_REPARING_WAREHOUSE, false)] + [TestCase(QueryStatus.NO_DATA, false)] + [TestCase(QueryStatus.SUCCESS, false)] + [TestCase(QueryStatus.RESTARTED, false)] + public void TestIsAnError(QueryStatus status, bool expectedResult) + { + Assert.AreEqual(expectedResult, QueryStatuses.IsAnError(status)); + } } } From 3810211d26c8a838e562ae6c5d1e688e34f93d85 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 8 Mar 2024 17:35:28 -0800 Subject: [PATCH 03/47] SNOW-817091: Fix session closing issue --- Snowflake.Data/Client/SnowflakeDbConnection.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Snowflake.Data/Client/SnowflakeDbConnection.cs b/Snowflake.Data/Client/SnowflakeDbConnection.cs index 5a7e7fbee..a578d143d 100755 --- a/Snowflake.Data/Client/SnowflakeDbConnection.cs +++ b/Snowflake.Data/Client/SnowflakeDbConnection.cs @@ -153,8 +153,8 @@ public override void Close() { var transactionRollbackStatus = SnowflakeDbConnectionPool.GetPooling() ? TerminateTransactionForDirtyConnectionReturningToPool() : TransactionRollbackStatus.Undefined; - if (CanReuseSession(transactionRollbackStatus) && - SfSession.StillRunningAsyncQueries() && + if ((CanReuseSession(transactionRollbackStatus) || + SfSession.StillRunningAsyncQueries()) && SnowflakeDbConnectionPool.AddSession(SfSession) ) { @@ -194,8 +194,8 @@ public virtual async Task CloseAsync(CancellationToken cancellationToken) { var transactionRollbackStatus = SnowflakeDbConnectionPool.GetPooling() ? TerminateTransactionForDirtyConnectionReturningToPool() : TransactionRollbackStatus.Undefined; - if (CanReuseSession(transactionRollbackStatus) && - await SfSession.StillRunningAsyncQueriesAsync(cancellationToken).ConfigureAwait(false) && + if ((CanReuseSession(transactionRollbackStatus) || + await SfSession.StillRunningAsyncQueriesAsync(cancellationToken).ConfigureAwait(false)) && SnowflakeDbConnectionPool.AddSession(SfSession)) { logger.Debug($"Session pooled: {SfSession.sessionId}"); From 03d7b6444f71b6b5568c23e0d7e0b26056e94162 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 8 Mar 2024 18:08:54 -0800 Subject: [PATCH 04/47] SNOW-817091: Mark nonparallelizable --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 59b39a764..c55f3c3cd 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -516,7 +516,7 @@ public void TestGetStatusOfUnknownQueryIdAsync() } } - [Test] + [Test, NonParallelizable] public void TestGetResultsOfUnknownQueryIdAsync() { string unknownQueryId = "ab123fed-1abc-987f-987f-1234a56b789c"; @@ -1669,7 +1669,7 @@ public void TestGetStatusOfUnknownQueryId() } } - [Test] + [Test, NonParallelizable] public void TestGetResultsOfUnknownQueryId() { string unknownQueryId = "ba987def-1abc-987f-987f-1234a56b789c"; From 97904a967ab9ba63bab900fe04b17c322d6f653e Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 8 Mar 2024 18:48:00 -0800 Subject: [PATCH 05/47] Revert last commit --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index c55f3c3cd..59b39a764 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -516,7 +516,7 @@ public void TestGetStatusOfUnknownQueryIdAsync() } } - [Test, NonParallelizable] + [Test] public void TestGetResultsOfUnknownQueryIdAsync() { string unknownQueryId = "ab123fed-1abc-987f-987f-1234a56b789c"; @@ -1669,7 +1669,7 @@ public void TestGetStatusOfUnknownQueryId() } } - [Test, NonParallelizable] + [Test] public void TestGetResultsOfUnknownQueryId() { string unknownQueryId = "ba987def-1abc-987f-987f-1234a56b789c"; From eae1e7d471afec556e90f3a5a7a4d39fe298093d Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 8 Mar 2024 19:11:56 -0800 Subject: [PATCH 06/47] SNOW-817091: Add check for NET Framework --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 59b39a764..7c115971b 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -537,7 +537,11 @@ public void TestGetResultsOfUnknownQueryIdAsync() var thrown = Assert.Throws(() => readerTask.Wait()); // Assert +#if NETFRAMEWORK + Assert.IsTrue(thrown.InnerException.Message.Contains($"Max retry for no data is reached")); +#else Assert.IsTrue(thrown.Message.Contains($"Max retry for no data is reached")); +#endif } task = conn.CloseAsync(CancellationToken.None); From e52aded949d1881e6a6760a60bd398a603e5e9a5 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 15 Mar 2024 14:15:13 -0700 Subject: [PATCH 07/47] Remove empty line --- Snowflake.Data/Client/SnowflakeDbCommand.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index 9b861dc5f..8a54ba5b8 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -324,7 +324,6 @@ public QueryStatus GetQueryStatus(string queryId) logger.Error(errorMessage); throw new Exception(errorMessage); } - } /// From c064688a3124e4397db981275282a64c8dc54a74 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 15 Mar 2024 14:34:31 -0700 Subject: [PATCH 08/47] Include uppercase letters in uuid regex --- Snowflake.Data/Client/SnowflakeDbCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index 8a54ba5b8..ef6ce3240 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -30,7 +30,7 @@ public class SnowflakeDbCommand : DbCommand private const int AsyncNoDataMaxRetry = 24; private readonly int[] _asyncRetryPattern = { 1, 1, 2, 3, 4, 8, 10 }; - private static readonly Regex UuidRegex = new Regex("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); + private static readonly Regex UuidRegex = new Regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); public SnowflakeDbCommand() { From b0827ff2ed7f82e278c7346a2cf53e1cca20d334 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 15 Mar 2024 17:15:01 -0700 Subject: [PATCH 09/47] Add short description for async mode --- Snowflake.Data/Client/SnowflakeDbCommand.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index ef6ce3240..bbdf13da1 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -283,6 +283,7 @@ protected override async Task ExecuteDbDataReaderAsync(CommandBeha /// /// Execute a query in async mode. + /// Async mode means the server will respond immediately with the query ID and execute the query asynchronously /// /// The query id. public string ExecuteInAsyncMode() @@ -294,6 +295,7 @@ public string ExecuteInAsyncMode() /// /// Executes an asynchronous query in async mode. + /// Async mode means the server will respond immediately with the query ID and execute the query asynchronously /// /// /// The query id. From d9288ab5881dd0b774aa94514e843be556c1d406 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 15 Mar 2024 20:50:12 -0700 Subject: [PATCH 10/47] Add check for PUT/GET in async exec mode --- .../IntegrationTests/SFDbCommandIT.cs | 25 +++++++++++++++++++ Snowflake.Data/Core/SFStatement.cs | 4 +++ 2 files changed, 29 insertions(+) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 7c115971b..be79a14ad 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -1604,6 +1604,31 @@ public void TestFailedAsyncExecQueryThrowsError() } } + [Test] + public void TestAsyncExecQueryPutGetThrowsNotImplemented() + { + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + cmd.CommandText = $"PUT file://non_existent_file.csv @~;"; + //cmd.CommandText = "GET @~ file://C:\\tmp\\;"; + + // Act + var thrown = Assert.Throws(() => cmd.ExecuteInAsyncMode()); + + // Assert + Assert.IsTrue(thrown.Message.Contains("Get and Put are not supported in async execution mode")); + } + + conn.Close(); + } + } + [Test] public void TestGetStatusOfInvalidQueryId() { diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index 56c53b70a..7e5aaa394 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -411,6 +411,10 @@ internal SFBaseResultSet Execute(int timeout, string sql, Dictionary Date: Mon, 18 Mar 2024 10:31:48 -0700 Subject: [PATCH 11/47] Add GET command to test --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index be79a14ad..203f1d807 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -1616,13 +1616,21 @@ public void TestAsyncExecQueryPutGetThrowsNotImplemented() { // Arrange cmd.CommandText = $"PUT file://non_existent_file.csv @~;"; - //cmd.CommandText = "GET @~ file://C:\\tmp\\;"; // Act var thrown = Assert.Throws(() => cmd.ExecuteInAsyncMode()); // Assert Assert.IsTrue(thrown.Message.Contains("Get and Put are not supported in async execution mode")); + + // Arrange + cmd.CommandText = "GET @~ file://C:\\tmp\\;"; + + // Act + thrown = Assert.Throws(() => cmd.ExecuteInAsyncMode()); + + // Assert + Assert.IsTrue(thrown.Message.Contains("Get and Put are not supported in async execution mode")); } conn.Close(); From 95cd9382f5b380894eab83e59b21a69b00eddb37 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 18 Mar 2024 11:18:30 -0700 Subject: [PATCH 12/47] Add check for cancellation while getting results --- .../IntegrationTests/SFDbCommandIT.cs | 44 +++++++++++++++++++ Snowflake.Data/Client/SnowflakeDbCommand.cs | 6 +++ 2 files changed, 50 insertions(+) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 203f1d807..d9aec3fa5 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -312,6 +312,50 @@ public void TestMixedSyncAndAsyncQueryAsync() } } + [Test] + public void TestAsyncExecCancelWhileGettingResultsAsync() + { + string queryId; + Task task; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + task = conn.OpenAsync(CancellationToken.None); + task.Wait(); + + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) + { + // Arrange + CancellationTokenSource cancelToken = new CancellationTokenSource(); + cmd.CommandText = $"CALL SYSTEM$WAIT(60, \'SECONDS\');"; + + // Act + var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); + queryTask.Wait(); + queryId = queryTask.Result; + + var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); + statusTask.Wait(); + var queryStatus = statusTask.Result; + + // Assert + Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); + + // Act + var readerTask = cmd.GetResultsFromQueryIdAsync(queryId, cancelToken.Token); + cancelToken.Cancel(); + var thrown = Assert.Throws(() => readerTask.Wait()); + + // Assert + Assert.IsTrue(thrown.InnerException.Message.Contains("A task was canceled")); + } + + task = conn.CloseAsync(CancellationToken.None); + task.Wait(); + } + } + [Test] public void TestStillRunningAsyncQueriesAsync() { diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index bbdf13da1..21eb44736 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -419,6 +419,12 @@ public async Task GetResultsFromQueryIdAsync(string queryId, Cance QueryStatus status; while (true) { + if (cancellationToken.IsCancellationRequested) + { + logger.Debug("Cancellation requested for getting results from query id"); + cancellationToken.ThrowIfCancellationRequested(); + } + status = GetQueryStatus(queryId); if (!QueryStatuses.IsStillRunning(status)) From 20518fe5382d4fc5201a40d12b8fb0cfe720c0c8 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 18 Mar 2024 11:49:11 -0700 Subject: [PATCH 13/47] Replace GetQueryStatus with GetQueryStatusAsync --- Snowflake.Data/Client/SnowflakeDbCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index 21eb44736..c63d3fd69 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -425,7 +425,7 @@ public async Task GetResultsFromQueryIdAsync(string queryId, Cance cancellationToken.ThrowIfCancellationRequested(); } - status = GetQueryStatus(queryId); + status = await GetQueryStatusAsync(queryId, cancellationToken); if (!QueryStatuses.IsStillRunning(status)) { From 6ce4a8a9dd515bf0e72653d95ca7d2f8c3c10dfe Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 18 Mar 2024 16:45:29 -0700 Subject: [PATCH 14/47] Refactor async exec retry into one function --- .../IntegrationTests/SFDbCommandIT.cs | 8 +- Snowflake.Data/Client/SnowflakeDbCommand.cs | 77 +++++++------------ 2 files changed, 32 insertions(+), 53 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index d9aec3fa5..589f0d9eb 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -1717,10 +1717,10 @@ public void TestGetResultsOfInvalidQueryId() using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { // Act - var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(fakeQueryId)); + var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(fakeQueryId)); // Assert - Assert.IsTrue(thrown.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); + Assert.IsTrue(thrown.InnerException.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); } conn.Close(); @@ -1763,10 +1763,10 @@ public void TestGetResultsOfUnknownQueryId() using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { // Act - var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(unknownQueryId)); + var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(unknownQueryId)); // Assert - Assert.IsTrue(thrown.Message.Contains($"Max retry for no data is reached")); + Assert.IsTrue(thrown.InnerException.Message.Contains($"Max retry for no data is reached")); } conn.Close(); diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index c63d3fd69..4fe6cd462 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -353,29 +353,35 @@ public async Task GetQueryStatusAsync(string queryId, CancellationT } /// - /// Gets the query results based on query ID. + /// Checks query status until it is done executing. /// /// - /// The query results. - public DbDataReader GetResultsFromQueryId(string queryId) + /// + /// + internal async Task RetryUntilQueryResultIsAvailable(string queryId, CancellationToken cancellationToken, bool isAsync) { - logger.Debug($"GetResultsFromQueryId"); - int retryPatternPos = 0; int noDataCounter = 0; QueryStatus status; while (true) { - status = GetQueryStatus(queryId); + status = isAsync ? await GetQueryStatusAsync(queryId, cancellationToken) : GetQueryStatus(queryId); if (!QueryStatuses.IsStillRunning(status)) { - break; + return; } // Timeout based on query status retry rules - Thread.Sleep(TimeSpan.FromSeconds(_asyncRetryPattern[retryPatternPos])); + if (isAsync) + { + await Task.Delay(TimeSpan.FromSeconds(_asyncRetryPattern[retryPatternPos]), cancellationToken).ConfigureAwait(false); + } + else + { + Thread.Sleep(TimeSpan.FromSeconds(_asyncRetryPattern[retryPatternPos])); + } // If no data, increment the no data counter if (status == QueryStatus.NO_DATA) @@ -396,6 +402,19 @@ public DbDataReader GetResultsFromQueryId(string queryId) retryPatternPos++; } } + } + + /// + /// Gets the query results based on query ID. + /// + /// + /// The query results. + public DbDataReader GetResultsFromQueryId(string queryId) + { + logger.Debug($"GetResultsFromQueryId"); + + Task task = RetryUntilQueryResultIsAvailable(queryId, CancellationToken.None, false); + task.Wait(); connection.SfSession.AsyncQueries.Remove(queryId); SFBaseResultSet resultSet = sfStatement.GetResultWithId(queryId); @@ -413,47 +432,7 @@ public async Task GetResultsFromQueryIdAsync(string queryId, Cance { logger.Debug($"GetResultsFromQueryIdAsync"); - int retryPatternPos = 0; - int noDataCounter = 0; - - QueryStatus status; - while (true) - { - if (cancellationToken.IsCancellationRequested) - { - logger.Debug("Cancellation requested for getting results from query id"); - cancellationToken.ThrowIfCancellationRequested(); - } - - status = await GetQueryStatusAsync(queryId, cancellationToken); - - if (!QueryStatuses.IsStillRunning(status)) - { - break; - } - - // Timeout based on query status retry rules - await Task.Delay(TimeSpan.FromSeconds(_asyncRetryPattern[retryPatternPos]), cancellationToken).ConfigureAwait(false); - - // If no data, increment the no data counter - if (status == QueryStatus.NO_DATA) - { - noDataCounter++; - - // Check if retry for no data is exceeded - if (noDataCounter > AsyncNoDataMaxRetry) - { - var errorMessage = "Max retry for no data is reached"; - logger.Error(errorMessage); - throw new Exception(errorMessage); - } - } - - if (retryPatternPos < _asyncRetryPattern.Length - 1) - { - retryPatternPos++; - } - } + await RetryUntilQueryResultIsAvailable(queryId, cancellationToken, true); connection.SfSession.AsyncQueries.Remove(queryId); SFBaseResultSet resultSet = await sfStatement.GetResultWithIdAsync(queryId, cancellationToken).ConfigureAwait(false); From e4beae6a4a2b4fba393ccbf792f6429c7f9f6962 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 18 Mar 2024 17:43:43 -0700 Subject: [PATCH 15/47] Fix indent --- Snowflake.Data/Core/SFStatement.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index 7e5aaa394..982f9f230 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -500,13 +500,13 @@ private SFBaseResultSet ExecuteSqlOtherThanPutGet(int timeout, string sql, Dicti } } - QueryExecResponse response = - ExecuteHelper( - timeout, - sql, - bindings, - describeOnly, - asyncExec); + QueryExecResponse response = + ExecuteHelper( + timeout, + sql, + bindings, + describeOnly, + asyncExec); return BuildResultSet(response, CancellationToken.None); } From 60335ec27651046fe2e0a51f47efb2770c7d341c Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 18 Mar 2024 18:21:13 -0700 Subject: [PATCH 16/47] Replace task with await --- .../IntegrationTests/SFDbCommandIT.cs | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 589f0d9eb..beb93d6b8 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -177,17 +177,15 @@ public void TestExecuteAsyncWithMaxRetryReached() } [Test] - public void TestAsyncExecQueryAsync() + public async Task TestAsyncExecQueryAsync() { string queryId; var expectedWaitTime = 5; - Task task; using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { @@ -195,25 +193,15 @@ public void TestAsyncExecQueryAsync() cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; // Act - var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); - queryTask.Wait(); - queryId = queryTask.Result; - - var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); - statusTask.Wait(); - var queryStatus = statusTask.Result; + queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); + var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); // Act - var readerTask = cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None); - readerTask.Wait(); - DbDataReader reader = readerTask.Result; - - statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); - statusTask.Wait(); - queryStatus = statusTask.Result; + DbDataReader reader = await cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None).ConfigureAwait(false); + queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert Assert.IsTrue(reader.Read()); @@ -221,8 +209,7 @@ public void TestAsyncExecQueryAsync() Assert.AreEqual(QueryStatus.SUCCESS, queryStatus); } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } From 78b027202d56ab908cdc8775c443632e3266eb70 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 18 Mar 2024 20:46:38 -0700 Subject: [PATCH 17/47] Replace task with await --- .../IntegrationTests/SFDbCommandIT.cs | 213 ++++++------------ 1 file changed, 69 insertions(+), 144 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index beb93d6b8..4520e40a5 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -214,18 +214,16 @@ public async Task TestAsyncExecQueryAsync() } [Test] - public void TestMixedSyncAndAsyncQueryAsync() + public async Task TestMixedSyncAndAsyncQueryAsync() { string queryId; var expectedWaitTime = 5; - Task task; // Start the async exec query using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { @@ -233,20 +231,14 @@ public void TestMixedSyncAndAsyncQueryAsync() cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; // Act - var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); - queryTask.Wait(); - queryId = queryTask.Result; - - var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); - statusTask.Wait(); - var queryStatus = statusTask.Result; + queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); + var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } // Execute a normal query @@ -274,19 +266,13 @@ public void TestMixedSyncAndAsyncQueryAsync() using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { // Act - var readerTask = cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None); - readerTask.Wait(); - DbDataReader reader = readerTask.Result; - - var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); - statusTask.Wait(); - var queryStatus = statusTask.Result; + var reader = await cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None).ConfigureAwait(false); + var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert Assert.IsTrue(reader.Read()); @@ -294,22 +280,17 @@ public void TestMixedSyncAndAsyncQueryAsync() Assert.AreEqual(QueryStatus.SUCCESS, queryStatus); } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } [Test] - public void TestAsyncExecCancelWhileGettingResultsAsync() + public async Task TestAsyncExecCancelWhileGettingResultsAsync() { - string queryId; - Task task; - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { @@ -318,42 +299,35 @@ public void TestAsyncExecCancelWhileGettingResultsAsync() cmd.CommandText = $"CALL SYSTEM$WAIT(60, \'SECONDS\');"; // Act - var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); - queryTask.Wait(); - queryId = queryTask.Result; - - var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); - statusTask.Wait(); - var queryStatus = statusTask.Result; + var queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); + var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); // Act - var readerTask = cmd.GetResultsFromQueryIdAsync(queryId, cancelToken.Token); cancelToken.Cancel(); - var thrown = Assert.Throws(() => readerTask.Wait()); + var thrown = Assert.ThrowsAsync(async () => + await cmd.GetResultsFromQueryIdAsync(queryId, cancelToken.Token).ConfigureAwait(false)); + // Assert - Assert.IsTrue(thrown.InnerException.Message.Contains("A task was canceled")); + Console.WriteLine(thrown.Message); + Assert.IsTrue(thrown.Message.Contains("The operation was canceled")); } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } [Test] - public void TestStillRunningAsyncQueriesAsync() + public async Task TestStillRunningAsyncQueriesAsync() { - string queryIdOne, queryIdTwo; - Task task; using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { @@ -361,68 +335,51 @@ public void TestStillRunningAsyncQueriesAsync() cmd.CommandText = $"CALL SYSTEM$WAIT({3}, \'SECONDS\');"; // Act - var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); - queryTask.Wait(); - queryIdOne = queryTask.Result; + var queryIdOne = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); // Arrange cmd.CommandText = $"CALL SYSTEM$WAIT({10}, \'SECONDS\');"; // Act - queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); - queryTask.Wait(); - queryIdTwo = queryTask.Result; - var stillRunningTask = conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None); - stillRunningTask.Wait(); - var statusTask = cmd.GetQueryStatusAsync(queryIdOne, CancellationToken.None); - statusTask.Wait(); + var queryIdTwo = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); + var stillRunning = await conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None).ConfigureAwait(false); + var queryStatus = await cmd.GetQueryStatusAsync(queryIdOne, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(statusTask.Result)); - Assert.IsTrue(stillRunningTask.Result); + Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); + Assert.IsTrue(stillRunning); // Act - var readerTask = cmd.GetResultsFromQueryIdAsync(queryIdOne, CancellationToken.None); - readerTask.Wait(); - statusTask = cmd.GetQueryStatusAsync(queryIdOne, CancellationToken.None); - statusTask.Wait(); - stillRunningTask = conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None); - stillRunningTask.Wait(); + await cmd.GetResultsFromQueryIdAsync(queryIdOne, CancellationToken.None).ConfigureAwait(false); + queryStatus = await cmd.GetQueryStatusAsync(queryIdOne, CancellationToken.None).ConfigureAwait(false); + stillRunning = await conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None).ConfigureAwait(false); // Assert - Assert.IsFalse(QueryStatuses.IsStillRunning(statusTask.Result)); + Assert.IsFalse(QueryStatuses.IsStillRunning(queryStatus)); Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdTwo))); - Assert.IsTrue(stillRunningTask.Result); + Assert.IsTrue(stillRunning); // Act - readerTask = cmd.GetResultsFromQueryIdAsync(queryIdTwo, CancellationToken.None); - readerTask.Wait(); - statusTask = cmd.GetQueryStatusAsync(queryIdTwo, CancellationToken.None); - statusTask.Wait(); - stillRunningTask = conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None); - stillRunningTask.Wait(); + await cmd.GetResultsFromQueryIdAsync(queryIdTwo, CancellationToken.None).ConfigureAwait(false); + queryStatus = await cmd.GetQueryStatusAsync(queryIdTwo, CancellationToken.None).ConfigureAwait(false); + stillRunning = await conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None).ConfigureAwait(false); // Assert - Assert.IsFalse(QueryStatuses.IsStillRunning(statusTask.Result)); - Assert.IsFalse(stillRunningTask.Result); + Assert.IsFalse(QueryStatuses.IsStillRunning(queryStatus)); + Assert.IsFalse(stillRunning); } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } [Test] - public void TestFailedAsyncExecQueryThrowsErrorAsync() + public async Task TestFailedAsyncExecQueryThrowsErrorAsync() { - string queryId; - Task task; - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { @@ -430,153 +387,121 @@ public void TestFailedAsyncExecQueryThrowsErrorAsync() cmd.CommandText = $"SELECT * FROM FAKE_TABLE;"; // Act - var queryTask = cmd.ExecuteAsyncInAsyncMode(CancellationToken.None); - queryTask.Wait(); - queryId = queryTask.Result; - - var statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); - statusTask.Wait(); - var queryStatus = statusTask.Result; - + var queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); + var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); while (QueryStatuses.IsStillRunning(queryStatus)) { Thread.Sleep(1000); - statusTask = cmd.GetQueryStatusAsync(queryId, CancellationToken.None); - statusTask.Wait(); - queryStatus = statusTask.Result; + queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); } // Assert Assert.AreEqual(QueryStatus.FAILED_WITH_ERROR, queryStatus); // Act - var readerTask = cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None); - var thrown = Assert.Throws(() => readerTask.Wait()); + var thrown = Assert.ThrowsAsync(async () => + await cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None).ConfigureAwait(false)); // Assert - Assert.IsTrue(thrown.InnerException.Message.Contains("'FAKE_TABLE' does not exist")); + Assert.IsTrue(thrown.Message.Contains("'FAKE_TABLE' does not exist")); } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } [Test] - public void TestGetStatusOfInvalidQueryIdAsync() + public async Task TestGetStatusOfInvalidQueryIdAsync() { string fakeQueryId = "fakeQueryId"; - Task task; using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { - // Arrange - var statusTask = cmd.GetQueryStatusAsync(fakeQueryId, CancellationToken.None); - // Act - var thrown = Assert.Throws(() => statusTask.Wait()); + var thrown = Assert.ThrowsAsync(async () => + await cmd.GetQueryStatusAsync(fakeQueryId, CancellationToken.None).ConfigureAwait(false)); // Assert - Assert.IsTrue(thrown.InnerException.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); + Assert.IsTrue(thrown.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } [Test] - public void TestGetResultsOfInvalidQueryIdAsync() + public async Task TestGetResultsOfInvalidQueryIdAsync() { string fakeQueryId = "fakeQueryId"; - Task task; using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { - // Arrange - var readerTask = cmd.GetResultsFromQueryIdAsync(fakeQueryId, CancellationToken.None); - // Act - var thrown = Assert.Throws(() => readerTask.Wait()); + var thrown = Assert.ThrowsAsync(async () => + await cmd.GetResultsFromQueryIdAsync(fakeQueryId, CancellationToken.None).ConfigureAwait(false)); // Assert - Assert.IsTrue(thrown.InnerException.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); + Assert.IsTrue(thrown.Message.Contains($"The given query id {fakeQueryId} is not valid uuid")); } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } [Test, NonParallelizable] - public void TestGetStatusOfUnknownQueryIdAsync() + public async Task TestGetStatusOfUnknownQueryIdAsync() { string unknownQueryId = "ba321edc-1abc-123e-987f-1234a56b789c"; - Task task; using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { // Act - var statusTask = cmd.GetQueryStatusAsync(unknownQueryId, CancellationToken.None); - statusTask.Wait(); + var queryStatus = await cmd.GetQueryStatusAsync(unknownQueryId, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.AreEqual(QueryStatus.NO_DATA, statusTask.Result); + Assert.AreEqual(QueryStatus.NO_DATA, queryStatus); } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } [Test] - public void TestGetResultsOfUnknownQueryIdAsync() + public async Task TestGetResultsOfUnknownQueryIdAsync() { string unknownQueryId = "ab123fed-1abc-987f-987f-1234a56b789c"; - Task task; using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) { conn.ConnectionString = ConnectionString; - task = conn.OpenAsync(CancellationToken.None); - task.Wait(); + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { - // Arrange - var readerTask = cmd.GetResultsFromQueryIdAsync(unknownQueryId, CancellationToken.None); - // Act - var thrown = Assert.Throws(() => readerTask.Wait()); + var thrown = Assert.ThrowsAsync(async () => + await cmd.GetResultsFromQueryIdAsync(unknownQueryId, CancellationToken.None).ConfigureAwait(false)); // Assert -#if NETFRAMEWORK - Assert.IsTrue(thrown.InnerException.Message.Contains($"Max retry for no data is reached")); -#else Assert.IsTrue(thrown.Message.Contains($"Max retry for no data is reached")); -#endif } - task = conn.CloseAsync(CancellationToken.None); - task.Wait(); + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } } From bc20b8a978d16b31d1736cf54f31963cafbc6819 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 18 Mar 2024 22:53:21 -0700 Subject: [PATCH 18/47] Fix async test for .NET Framework --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 4520e40a5..a43fb006d 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -310,10 +310,12 @@ public async Task TestAsyncExecCancelWhileGettingResultsAsync() var thrown = Assert.ThrowsAsync(async () => await cmd.GetResultsFromQueryIdAsync(queryId, cancelToken.Token).ConfigureAwait(false)); - // Assert - Console.WriteLine(thrown.Message); +#if NETFRAMEWORK + Assert.IsTrue(thrown.Message.Contains("A task was canceled")); +#else Assert.IsTrue(thrown.Message.Contains("The operation was canceled")); +#endif } await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); From ad8d40fd0595523b9d465ec70443bd138103d00b Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 18 Mar 2024 23:00:42 -0700 Subject: [PATCH 19/47] Fix async test for .NET Framework --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index a43fb006d..56aad561d 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -307,11 +307,16 @@ public async Task TestAsyncExecCancelWhileGettingResultsAsync() // Act cancelToken.Cancel(); +#if NET471 + var thrown = Assert.ThrowsAsync(async () => + await cmd.GetResultsFromQueryIdAsync(queryId, cancelToken.Token).ConfigureAwait(false)); +#else var thrown = Assert.ThrowsAsync(async () => await cmd.GetResultsFromQueryIdAsync(queryId, cancelToken.Token).ConfigureAwait(false)); +#endif // Assert -#if NETFRAMEWORK +#if NET472 Assert.IsTrue(thrown.Message.Contains("A task was canceled")); #else Assert.IsTrue(thrown.Message.Contains("The operation was canceled")); From c94a51d5e68478c425fe705cfefee74e39d6fddc Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 18 Mar 2024 23:35:23 -0700 Subject: [PATCH 20/47] Use PascalCase for enum values --- .../IntegrationTests/SFDbCommandIT.cs | 18 ++--- .../UnitTests/SFStatementTest.cs | 79 ++++++++++++------- Snowflake.Data/Client/SnowflakeDbCommand.cs | 2 +- Snowflake.Data/Core/SFStatement.cs | 62 +++++++-------- 4 files changed, 92 insertions(+), 69 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 56aad561d..9cb4c07e3 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -206,7 +206,7 @@ public async Task TestAsyncExecQueryAsync() // Assert Assert.IsTrue(reader.Read()); Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); - Assert.AreEqual(QueryStatus.SUCCESS, queryStatus); + Assert.AreEqual(QueryStatus.Success, queryStatus); } await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); @@ -277,7 +277,7 @@ public async Task TestMixedSyncAndAsyncQueryAsync() // Assert Assert.IsTrue(reader.Read()); Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); - Assert.AreEqual(QueryStatus.SUCCESS, queryStatus); + Assert.AreEqual(QueryStatus.Success, queryStatus); } await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); @@ -403,7 +403,7 @@ public async Task TestFailedAsyncExecQueryThrowsErrorAsync() } // Assert - Assert.AreEqual(QueryStatus.FAILED_WITH_ERROR, queryStatus); + Assert.AreEqual(QueryStatus.FailedWithError, queryStatus); // Act var thrown = Assert.ThrowsAsync(async () => @@ -481,7 +481,7 @@ public async Task TestGetStatusOfUnknownQueryIdAsync() var queryStatus = await cmd.GetQueryStatusAsync(unknownQueryId, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.AreEqual(QueryStatus.NO_DATA, queryStatus); + Assert.AreEqual(QueryStatus.NoData, queryStatus); } await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); @@ -1405,7 +1405,7 @@ public void TestAsyncExecQuery() // Assert Assert.IsTrue(reader.Read()); Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); - Assert.AreEqual(QueryStatus.SUCCESS, cmd.GetQueryStatus(queryId)); + Assert.AreEqual(QueryStatus.Success, cmd.GetQueryStatus(queryId)); } conn.Close(); @@ -1474,7 +1474,7 @@ public void TestMixedSyncAndAsyncQuery() // Assert Assert.IsTrue(reader.Read()); Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); - Assert.AreEqual(QueryStatus.SUCCESS, cmd.GetQueryStatus(queryId)); + Assert.AreEqual(QueryStatus.Success, cmd.GetQueryStatus(queryId)); } conn.Close(); @@ -1523,7 +1523,7 @@ public void TestStillRunningAsyncQueries() // Assert Assert.IsFalse(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdTwo))); - Assert.AreEqual(QueryStatus.SUCCESS, cmd.GetQueryStatus(queryIdTwo)); + Assert.AreEqual(QueryStatus.Success, cmd.GetQueryStatus(queryIdTwo)); Assert.IsFalse(conn.SfSession.StillRunningAsyncQueries()); } @@ -1554,7 +1554,7 @@ public void TestFailedAsyncExecQueryThrowsError() } // Assert - Assert.AreEqual(QueryStatus.FAILED_WITH_ERROR, cmd.GetQueryStatus(queryId)); + Assert.AreEqual(QueryStatus.FailedWithError, cmd.GetQueryStatus(queryId)); // Act var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(queryId)); @@ -1662,7 +1662,7 @@ public void TestGetStatusOfUnknownQueryId() var queryStatus = cmd.GetQueryStatus(unknownQueryId); // Assert - Assert.AreEqual(QueryStatus.NO_DATA, queryStatus); + Assert.AreEqual(QueryStatus.NoData, queryStatus); } conn.Close(); diff --git a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs index e1a8b3042..472731eb2 100755 --- a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs @@ -6,6 +6,25 @@ namespace Snowflake.Data.Tests.UnitTests { using Snowflake.Data.Core; using NUnit.Framework; + using System; + + public enum ServerQueryStatus + { + RUNNING, + ABORTING, + SUCCESS, + FAILED_WITH_ERROR, + ABORTED, + QUEUED, + FAILED_WITH_INCIDENT, + DISCONNECTED, + RESUMING_WAREHOUSE, + // purposeful typo.Is present in QueryDTO.java + QUEUED_REPARING_WAREHOUSE, + RESTARTED, + BLOCKED, + NO_DATA, + } /** * Mock rest request test @@ -124,41 +143,45 @@ public void TestTrimSqlLineCommentWithClosingNewline() } [Test] - [TestCase(QueryStatus.RUNNING, true)] - [TestCase(QueryStatus.RESUMING_WAREHOUSE, true)] - [TestCase(QueryStatus.QUEUED, true)] - [TestCase(QueryStatus.QUEUED_REPARING_WAREHOUSE, true)] - [TestCase(QueryStatus.NO_DATA, true)] - [TestCase(QueryStatus.ABORTING, false)] - [TestCase(QueryStatus.SUCCESS, false)] - [TestCase(QueryStatus.FAILED_WITH_ERROR, false)] - [TestCase(QueryStatus.ABORTED, false)] - [TestCase(QueryStatus.FAILED_WITH_INCIDENT, false)] - [TestCase(QueryStatus.DISCONNECTED, false)] - [TestCase(QueryStatus.RESTARTED, false)] - [TestCase(QueryStatus.BLOCKED, false)] + [TestCase(ServerQueryStatus.RUNNING, true)] + [TestCase(ServerQueryStatus.RESUMING_WAREHOUSE, true)] + [TestCase(ServerQueryStatus.QUEUED, true)] + [TestCase(ServerQueryStatus.QUEUED_REPARING_WAREHOUSE, true)] + [TestCase(ServerQueryStatus.NO_DATA, true)] + [TestCase(ServerQueryStatus.ABORTING, false)] + [TestCase(ServerQueryStatus.SUCCESS, false)] + [TestCase(ServerQueryStatus.FAILED_WITH_ERROR, false)] + [TestCase(ServerQueryStatus.ABORTED, false)] + [TestCase(ServerQueryStatus.FAILED_WITH_INCIDENT, false)] + [TestCase(ServerQueryStatus.DISCONNECTED, false)] + [TestCase(ServerQueryStatus.RESTARTED, false)] + [TestCase(ServerQueryStatus.BLOCKED, false)] public void TestIsStillRunning(QueryStatus status, bool expectedResult) { - Assert.AreEqual(expectedResult, QueryStatuses.IsStillRunning(status)); + QueryStatus queryStatus; + Enum.TryParse(status.ToString(), true, out queryStatus); + Assert.AreEqual(expectedResult, QueryStatuses.IsStillRunning(queryStatus)); } [Test] - [TestCase(QueryStatus.ABORTING, true)] - [TestCase(QueryStatus.FAILED_WITH_ERROR, true)] - [TestCase(QueryStatus.ABORTED, true)] - [TestCase(QueryStatus.FAILED_WITH_INCIDENT, true)] - [TestCase(QueryStatus.DISCONNECTED, true)] - [TestCase(QueryStatus.BLOCKED, true)] - [TestCase(QueryStatus.RUNNING, false)] - [TestCase(QueryStatus.RESUMING_WAREHOUSE, false)] - [TestCase(QueryStatus.QUEUED, false)] - [TestCase(QueryStatus.QUEUED_REPARING_WAREHOUSE, false)] - [TestCase(QueryStatus.NO_DATA, false)] - [TestCase(QueryStatus.SUCCESS, false)] - [TestCase(QueryStatus.RESTARTED, false)] + [TestCase(ServerQueryStatus.ABORTING, true)] + [TestCase(ServerQueryStatus.FAILED_WITH_ERROR, true)] + [TestCase(ServerQueryStatus.ABORTED, true)] + [TestCase(ServerQueryStatus.FAILED_WITH_INCIDENT, true)] + [TestCase(ServerQueryStatus.DISCONNECTED, true)] + [TestCase(ServerQueryStatus.BLOCKED, true)] + [TestCase(ServerQueryStatus.RUNNING, false)] + [TestCase(ServerQueryStatus.RESUMING_WAREHOUSE, false)] + [TestCase(ServerQueryStatus.QUEUED, false)] + [TestCase(ServerQueryStatus.QUEUED_REPARING_WAREHOUSE, false)] + [TestCase(ServerQueryStatus.NO_DATA, false)] + [TestCase(ServerQueryStatus.SUCCESS, false)] + [TestCase(ServerQueryStatus.RESTARTED, false)] public void TestIsAnError(QueryStatus status, bool expectedResult) { - Assert.AreEqual(expectedResult, QueryStatuses.IsAnError(status)); + QueryStatus queryStatus; + Enum.TryParse(status.ToString(), true, out queryStatus); + Assert.AreEqual(expectedResult, QueryStatuses.IsAnError(queryStatus)); } } } diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index 4fe6cd462..b6f552fa9 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -384,7 +384,7 @@ internal async Task RetryUntilQueryResultIsAvailable(string queryId, Cancellatio } // If no data, increment the no data counter - if (status == QueryStatus.NO_DATA) + if (status == QueryStatus.NoData) { noDataCounter++; diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index 982f9f230..f12a2e3ae 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -22,20 +22,20 @@ namespace Snowflake.Data.Core /// public enum QueryStatus { - RUNNING, - ABORTING, - SUCCESS, - FAILED_WITH_ERROR, - ABORTED, - QUEUED, - FAILED_WITH_INCIDENT, - DISCONNECTED, - RESUMING_WAREHOUSE, + Running, + Aborting, + Success, + FailedWithError, + Aborted, + Queued, + FailedWithIncident, + Disconnected, + ResumingWarehouse, // purposeful typo.Is present in QueryDTO.java - QUEUED_REPARING_WAREHOUSE, - RESTARTED, - BLOCKED, - NO_DATA, + QueuedReparingWarehouse, + Restarted, + Blocked, + NoData, } internal static class QueryStatuses @@ -44,11 +44,11 @@ internal static bool IsStillRunning(QueryStatus status) { switch (status) { - case QueryStatus.RUNNING: - case QueryStatus.RESUMING_WAREHOUSE: - case QueryStatus.QUEUED: - case QueryStatus.QUEUED_REPARING_WAREHOUSE: - case QueryStatus.NO_DATA: + case QueryStatus.Running: + case QueryStatus.ResumingWarehouse: + case QueryStatus.Queued: + case QueryStatus.QueuedReparingWarehouse: + case QueryStatus.NoData: return true; default: return false; @@ -59,12 +59,12 @@ internal static bool IsAnError(QueryStatus status) { switch (status) { - case QueryStatus.ABORTING: - case QueryStatus.FAILED_WITH_ERROR: - case QueryStatus.ABORTED: - case QueryStatus.FAILED_WITH_INCIDENT: - case QueryStatus.DISCONNECTED: - case QueryStatus.BLOCKED: + case QueryStatus.Aborting: + case QueryStatus.FailedWithError: + case QueryStatus.Aborted: + case QueryStatus.FailedWithIncident: + case QueryStatus.Disconnected: + case QueryStatus.Blocked: return true; default: return false; @@ -368,7 +368,7 @@ internal async Task ExecuteAsync(int timeout, string sql, Dicti if (asyncExec) { - SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.RUNNING); + SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.Running); } else { @@ -630,7 +630,7 @@ internal T ExecuteHelper( QueryExecResponse queryResponse = (QueryExecResponse)(object)response; if (asyncExec) { - SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.RUNNING); + SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.Running); } else { @@ -721,7 +721,7 @@ internal async Task ExecuteAsyncHelper( QueryExecResponse queryResponse = (QueryExecResponse)(object)response; if (asyncExec) { - SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.RUNNING); + SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.Running); } else { @@ -825,10 +825,10 @@ internal QueryStatus GetQueryStatus(string queryId) queryId); } - var status = response.data.queries.Count != 0 ? response.data.queries[0].status : QueryStatus.NO_DATA.ToString(); + var status = response.data.queries.Count != 0 ? response.data.queries[0].status.Replace("_", string.Empty) : QueryStatus.NoData.ToString(); QueryStatus queryStatus; - Enum.TryParse(status, out queryStatus); + Enum.TryParse(status, true, out queryStatus); SfSession.AsyncQueries[queryId] = queryStatus; return queryStatus; @@ -880,10 +880,10 @@ internal async Task GetQueryStatusAsync(string queryId, Cancellatio queryId); } - var status = response.data.queries.Count != 0 ? response.data.queries[0].status : QueryStatus.NO_DATA.ToString(); + var status = response.data.queries.Count != 0 ? response.data.queries[0].status.Replace("_", string.Empty) : QueryStatus.NoData.ToString(); QueryStatus queryStatus; - Enum.TryParse(status, out queryStatus); + Enum.TryParse(status, true, out queryStatus); SfSession.AsyncQueries[queryId] = queryStatus; return queryStatus; From c899f2618377cecffdd70e215748f8c240f7d344 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Tue, 19 Mar 2024 08:38:01 -0700 Subject: [PATCH 21/47] Remove comment about QueryDTO --- .../UnitTests/SFStatementTest.cs | 57 ++++++++++--------- Snowflake.Data/Core/SFStatement.cs | 2 +- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs index 472731eb2..366bb498e 100755 --- a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs @@ -8,7 +8,8 @@ namespace Snowflake.Data.Tests.UnitTests using NUnit.Framework; using System; - public enum ServerQueryStatus + // Based on the values that the server sends back + public enum TestQueryStatus { RUNNING, ABORTING, @@ -19,7 +20,7 @@ public enum ServerQueryStatus FAILED_WITH_INCIDENT, DISCONNECTED, RESUMING_WAREHOUSE, - // purposeful typo.Is present in QueryDTO.java + // purposeful typo QUEUED_REPARING_WAREHOUSE, RESTARTED, BLOCKED, @@ -143,19 +144,19 @@ public void TestTrimSqlLineCommentWithClosingNewline() } [Test] - [TestCase(ServerQueryStatus.RUNNING, true)] - [TestCase(ServerQueryStatus.RESUMING_WAREHOUSE, true)] - [TestCase(ServerQueryStatus.QUEUED, true)] - [TestCase(ServerQueryStatus.QUEUED_REPARING_WAREHOUSE, true)] - [TestCase(ServerQueryStatus.NO_DATA, true)] - [TestCase(ServerQueryStatus.ABORTING, false)] - [TestCase(ServerQueryStatus.SUCCESS, false)] - [TestCase(ServerQueryStatus.FAILED_WITH_ERROR, false)] - [TestCase(ServerQueryStatus.ABORTED, false)] - [TestCase(ServerQueryStatus.FAILED_WITH_INCIDENT, false)] - [TestCase(ServerQueryStatus.DISCONNECTED, false)] - [TestCase(ServerQueryStatus.RESTARTED, false)] - [TestCase(ServerQueryStatus.BLOCKED, false)] + [TestCase(TestQueryStatus.RUNNING, true)] + [TestCase(TestQueryStatus.RESUMING_WAREHOUSE, true)] + [TestCase(TestQueryStatus.QUEUED, true)] + [TestCase(TestQueryStatus.QUEUED_REPARING_WAREHOUSE, true)] + [TestCase(TestQueryStatus.NO_DATA, true)] + [TestCase(TestQueryStatus.ABORTING, false)] + [TestCase(TestQueryStatus.SUCCESS, false)] + [TestCase(TestQueryStatus.FAILED_WITH_ERROR, false)] + [TestCase(TestQueryStatus.ABORTED, false)] + [TestCase(TestQueryStatus.FAILED_WITH_INCIDENT, false)] + [TestCase(TestQueryStatus.DISCONNECTED, false)] + [TestCase(TestQueryStatus.RESTARTED, false)] + [TestCase(TestQueryStatus.BLOCKED, false)] public void TestIsStillRunning(QueryStatus status, bool expectedResult) { QueryStatus queryStatus; @@ -164,19 +165,19 @@ public void TestIsStillRunning(QueryStatus status, bool expectedResult) } [Test] - [TestCase(ServerQueryStatus.ABORTING, true)] - [TestCase(ServerQueryStatus.FAILED_WITH_ERROR, true)] - [TestCase(ServerQueryStatus.ABORTED, true)] - [TestCase(ServerQueryStatus.FAILED_WITH_INCIDENT, true)] - [TestCase(ServerQueryStatus.DISCONNECTED, true)] - [TestCase(ServerQueryStatus.BLOCKED, true)] - [TestCase(ServerQueryStatus.RUNNING, false)] - [TestCase(ServerQueryStatus.RESUMING_WAREHOUSE, false)] - [TestCase(ServerQueryStatus.QUEUED, false)] - [TestCase(ServerQueryStatus.QUEUED_REPARING_WAREHOUSE, false)] - [TestCase(ServerQueryStatus.NO_DATA, false)] - [TestCase(ServerQueryStatus.SUCCESS, false)] - [TestCase(ServerQueryStatus.RESTARTED, false)] + [TestCase(TestQueryStatus.ABORTING, true)] + [TestCase(TestQueryStatus.FAILED_WITH_ERROR, true)] + [TestCase(TestQueryStatus.ABORTED, true)] + [TestCase(TestQueryStatus.FAILED_WITH_INCIDENT, true)] + [TestCase(TestQueryStatus.DISCONNECTED, true)] + [TestCase(TestQueryStatus.BLOCKED, true)] + [TestCase(TestQueryStatus.RUNNING, false)] + [TestCase(TestQueryStatus.RESUMING_WAREHOUSE, false)] + [TestCase(TestQueryStatus.QUEUED, false)] + [TestCase(TestQueryStatus.QUEUED_REPARING_WAREHOUSE, false)] + [TestCase(TestQueryStatus.NO_DATA, false)] + [TestCase(TestQueryStatus.SUCCESS, false)] + [TestCase(TestQueryStatus.RESTARTED, false)] public void TestIsAnError(QueryStatus status, bool expectedResult) { QueryStatus queryStatus; diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index f12a2e3ae..fcd7abb70 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -31,7 +31,7 @@ public enum QueryStatus FailedWithIncident, Disconnected, ResumingWarehouse, - // purposeful typo.Is present in QueryDTO.java + // purposeful typo QueuedReparingWarehouse, Restarted, Blocked, From d874805c72bc441f846a0cfb089ad7637b4b6234 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Tue, 19 Mar 2024 13:06:40 -0700 Subject: [PATCH 22/47] Remove AsyncQueries property and related functions --- .../IntegrationTests/SFDbCommandIT.cs | 103 ------------------ Snowflake.Data/Client/SnowflakeDbCommand.cs | 2 - .../Client/SnowflakeDbConnection.cs | 9 +- Snowflake.Data/Core/SFStatement.cs | 20 +--- Snowflake.Data/Core/Session/SFSession.cs | 48 -------- 5 files changed, 5 insertions(+), 177 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 9cb4c07e3..aa2b3023e 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -327,59 +327,6 @@ public async Task TestAsyncExecCancelWhileGettingResultsAsync() } } - [Test] - public async Task TestStillRunningAsyncQueriesAsync() - { - - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) - { - conn.ConnectionString = ConnectionString; - await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); - - using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) - { - // Arrange - cmd.CommandText = $"CALL SYSTEM$WAIT({3}, \'SECONDS\');"; - - // Act - var queryIdOne = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); - - // Arrange - cmd.CommandText = $"CALL SYSTEM$WAIT({10}, \'SECONDS\');"; - - // Act - var queryIdTwo = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); - var stillRunning = await conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None).ConfigureAwait(false); - var queryStatus = await cmd.GetQueryStatusAsync(queryIdOne, CancellationToken.None).ConfigureAwait(false); - - // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); - Assert.IsTrue(stillRunning); - - // Act - await cmd.GetResultsFromQueryIdAsync(queryIdOne, CancellationToken.None).ConfigureAwait(false); - queryStatus = await cmd.GetQueryStatusAsync(queryIdOne, CancellationToken.None).ConfigureAwait(false); - stillRunning = await conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None).ConfigureAwait(false); - - // Assert - Assert.IsFalse(QueryStatuses.IsStillRunning(queryStatus)); - Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdTwo))); - Assert.IsTrue(stillRunning); - - // Act - await cmd.GetResultsFromQueryIdAsync(queryIdTwo, CancellationToken.None).ConfigureAwait(false); - queryStatus = await cmd.GetQueryStatusAsync(queryIdTwo, CancellationToken.None).ConfigureAwait(false); - stillRunning = await conn.SfSession.StillRunningAsyncQueriesAsync(CancellationToken.None).ConfigureAwait(false); - - // Assert - Assert.IsFalse(QueryStatuses.IsStillRunning(queryStatus)); - Assert.IsFalse(stillRunning); - } - - await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); - } - } - [Test] public async Task TestFailedAsyncExecQueryThrowsErrorAsync() { @@ -1481,56 +1428,6 @@ public void TestMixedSyncAndAsyncQuery() } } - - [Test] - public void TestStillRunningAsyncQueries() - { - string queryIdOne, queryIdTwo; - - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) - { - conn.ConnectionString = ConnectionString; - conn.Open(); - - using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) - { - // Arrange - cmd.CommandText = $"CALL SYSTEM$WAIT({3}, \'SECONDS\');"; - - // Act - queryIdOne = cmd.ExecuteInAsyncMode(); - - // Arrange - cmd.CommandText = $"CALL SYSTEM$WAIT({10}, \'SECONDS\');"; - - // Act - queryIdTwo = cmd.ExecuteInAsyncMode(); - - // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdOne))); - Assert.IsTrue(conn.SfSession.StillRunningAsyncQueries()); - - // Act - cmd.GetResultsFromQueryId(queryIdOne); - - // Assert - Assert.IsFalse(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdOne))); - Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdTwo))); - Assert.IsTrue(conn.SfSession.StillRunningAsyncQueries()); - - // Act - cmd.GetResultsFromQueryId(queryIdTwo); - - // Assert - Assert.IsFalse(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryIdTwo))); - Assert.AreEqual(QueryStatus.Success, cmd.GetQueryStatus(queryIdTwo)); - Assert.IsFalse(conn.SfSession.StillRunningAsyncQueries()); - } - - conn.Close(); - } - } - [Test] public void TestFailedAsyncExecQueryThrowsError() { diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index b6f552fa9..5eaee9c62 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -416,7 +416,6 @@ public DbDataReader GetResultsFromQueryId(string queryId) Task task = RetryUntilQueryResultIsAvailable(queryId, CancellationToken.None, false); task.Wait(); - connection.SfSession.AsyncQueries.Remove(queryId); SFBaseResultSet resultSet = sfStatement.GetResultWithId(queryId); return new SnowflakeDbDataReader(this, resultSet); @@ -434,7 +433,6 @@ public async Task GetResultsFromQueryIdAsync(string queryId, Cance await RetryUntilQueryResultIsAvailable(queryId, cancellationToken, true); - connection.SfSession.AsyncQueries.Remove(queryId); SFBaseResultSet resultSet = await sfStatement.GetResultWithIdAsync(queryId, cancellationToken).ConfigureAwait(false); return new SnowflakeDbDataReader(this, resultSet); diff --git a/Snowflake.Data/Client/SnowflakeDbConnection.cs b/Snowflake.Data/Client/SnowflakeDbConnection.cs index a578d143d..218e89337 100755 --- a/Snowflake.Data/Client/SnowflakeDbConnection.cs +++ b/Snowflake.Data/Client/SnowflakeDbConnection.cs @@ -153,10 +153,7 @@ public override void Close() { var transactionRollbackStatus = SnowflakeDbConnectionPool.GetPooling() ? TerminateTransactionForDirtyConnectionReturningToPool() : TransactionRollbackStatus.Undefined; - if ((CanReuseSession(transactionRollbackStatus) || - SfSession.StillRunningAsyncQueries()) && - SnowflakeDbConnectionPool.AddSession(SfSession) - ) + if (CanReuseSession(transactionRollbackStatus) && SnowflakeDbConnectionPool.AddSession(SfSession)) { logger.Debug($"Session pooled: {SfSession.sessionId}"); } @@ -194,9 +191,7 @@ public virtual async Task CloseAsync(CancellationToken cancellationToken) { var transactionRollbackStatus = SnowflakeDbConnectionPool.GetPooling() ? TerminateTransactionForDirtyConnectionReturningToPool() : TransactionRollbackStatus.Undefined; - if ((CanReuseSession(transactionRollbackStatus) || - await SfSession.StillRunningAsyncQueriesAsync(cancellationToken).ConfigureAwait(false)) && - SnowflakeDbConnectionPool.AddSession(SfSession)) + if (CanReuseSession(transactionRollbackStatus) && SnowflakeDbConnectionPool.AddSession(SfSession)) { logger.Debug($"Session pooled: {SfSession.sessionId}"); _connectionState = ConnectionState.Closed; diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index fcd7abb70..dd0358c9f 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -366,11 +366,7 @@ internal async Task ExecuteAsync(int timeout, string sql, Dicti var lastResultUrl = response.data?.getResultUrl; - if (asyncExec) - { - SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.Running); - } - else + if (!asyncExec) { while (RequestInProgress(response) || SessionExpired(response)) { @@ -628,11 +624,7 @@ internal T ExecuteHelper( if (typeof(T) == typeof(QueryExecResponse)) { QueryExecResponse queryResponse = (QueryExecResponse)(object)response; - if (asyncExec) - { - SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.Running); - } - else + if (!asyncExec) { var lastResultUrl = queryResponse.data?.getResultUrl; @@ -719,11 +711,7 @@ internal async Task ExecuteAsyncHelper( if (typeof(T) == typeof(QueryExecResponse)) { QueryExecResponse queryResponse = (QueryExecResponse)(object)response; - if (asyncExec) - { - SfSession.AsyncQueries.Add(response.data.queryId, QueryStatus.Running); - } - else + if (!asyncExec) { var lastResultUrl = queryResponse.data?.getResultUrl; @@ -830,7 +818,6 @@ internal QueryStatus GetQueryStatus(string queryId) QueryStatus queryStatus; Enum.TryParse(status, true, out queryStatus); - SfSession.AsyncQueries[queryId] = queryStatus; return queryStatus; } catch @@ -885,7 +872,6 @@ internal async Task GetQueryStatusAsync(string queryId, Cancellatio QueryStatus queryStatus; Enum.TryParse(status, true, out queryStatus); - SfSession.AsyncQueries[queryId] = queryStatus; return queryStatus; } catch diff --git a/Snowflake.Data/Core/Session/SFSession.cs b/Snowflake.Data/Core/Session/SFSession.cs index fc318008d..e39370f19 100755 --- a/Snowflake.Data/Core/Session/SFSession.cs +++ b/Snowflake.Data/Core/Session/SFSession.cs @@ -577,54 +577,6 @@ internal bool IsExpired(long timeoutInSeconds, long utcTimeInSeconds) { return _startTime + timeoutInSeconds <= utcTimeInSeconds; } - - /// - /// Contains the ID and status of queries executed in async mode - /// - internal Dictionary AsyncQueries = new Dictionary(); - - /// - /// Checks if the session contains async queries that are still executing - /// - /// True if session can be closed, false otherwise - internal bool StillRunningAsyncQueries() - { - foreach (var query in AsyncQueries.ToList()) - { - var sfStatement = new SFStatement(this); - var status = sfStatement.GetQueryStatus(query.Key); - if (QueryStatuses.IsStillRunning(status)) - { - return true; - } - - AsyncQueries.Remove(query.Key); - } - - return false; - } - - /// - /// Checks if the session contains async queries that are still executing - /// - /// - /// True if session can be closed, false otherwise - internal async Task StillRunningAsyncQueriesAsync(CancellationToken cancellationToken) - { - foreach (var query in AsyncQueries.ToList()) - { - var sfStatement = new SFStatement(this); - var status = await sfStatement.GetQueryStatusAsync(query.Key, cancellationToken).ConfigureAwait(false); - if (QueryStatuses.IsStillRunning(status)) - { - return true; - } - - AsyncQueries.Remove(query.Key); - } - - return false; - } } } From 4a5cb1dc19298f29f44e4ecc62a012063eaa3a75 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Wed, 20 Mar 2024 11:42:37 -0700 Subject: [PATCH 23/47] Add missing indent --- Snowflake.Data/Core/SFStatement.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index dd0358c9f..bdfcf2a12 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -497,12 +497,12 @@ private SFBaseResultSet ExecuteSqlOtherThanPutGet(int timeout, string sql, Dicti } QueryExecResponse response = - ExecuteHelper( - timeout, - sql, - bindings, - describeOnly, - asyncExec); + ExecuteHelper( + timeout, + sql, + bindings, + describeOnly, + asyncExec); return BuildResultSet(response, CancellationToken.None); } From 0c3fbb5dee48384cc3bb158d45c0aaa06a4e4d49 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Wed, 20 Mar 2024 11:58:44 -0700 Subject: [PATCH 24/47] Break while loop after a number of status retries --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index aa2b3023e..7cc616dbc 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -338,15 +338,17 @@ public async Task TestFailedAsyncExecQueryThrowsErrorAsync() using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { // Arrange + var statusRetryCount = 0; cmd.CommandText = $"SELECT * FROM FAKE_TABLE;"; // Act var queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); - while (QueryStatuses.IsStillRunning(queryStatus)) + while (statusRetryCount < 5 && QueryStatuses.IsStillRunning(queryStatus)) { Thread.Sleep(1000); queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); + statusRetryCount++; } // Assert @@ -1441,13 +1443,15 @@ public void TestFailedAsyncExecQueryThrowsError() using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { // Arrange + var statusRetryCount = 0; cmd.CommandText = $"SELECT * FROM FAKE_TABLE;"; // Act queryId = cmd.ExecuteInAsyncMode(); - while (QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))) + while (statusRetryCount < 5 && QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))) { Thread.Sleep(1000); + statusRetryCount++; } // Assert From b9a58a36526904e28ae306c5f5b767705a5e2afd Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Wed, 20 Mar 2024 12:54:32 -0700 Subject: [PATCH 25/47] Rename test --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 7cc616dbc..b289a68a5 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -214,7 +214,7 @@ public async Task TestAsyncExecQueryAsync() } [Test] - public async Task TestMixedSyncAndAsyncQueryAsync() + public async Task TestExecuteNormalQueryWhileAsyncExecQueryIsRunningAsync() { string queryId; var expectedWaitTime = 5; @@ -1362,7 +1362,7 @@ public void TestAsyncExecQuery() } [Test] - public void TestMixedSyncAndAsyncQuery() + public void TestExecuteNormalQueryWhileAsyncExecQueryIsRunning() { string queryId; var expectedWaitTime = 5; From 912f4a250eb7600ce2e04f0dd235d7770afbce9a Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Wed, 20 Mar 2024 16:54:33 -0700 Subject: [PATCH 26/47] Use different sessions for the test --- .../IntegrationTests/SFDbCommandIT.cs | 156 ++++++++---------- 1 file changed, 66 insertions(+), 90 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index b289a68a5..f09f73a2c 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -219,68 +219,56 @@ public async Task TestExecuteNormalQueryWhileAsyncExecQueryIsRunningAsync() string queryId; var expectedWaitTime = 5; - // Start the async exec query - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + SnowflakeDbConnection[] connections = new SnowflakeDbConnection[3]; + for (int i = 0; i < connections.Length; i++) { - conn.ConnectionString = ConnectionString; - await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); - - using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) - { - // Arrange - cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; + connections[i] = new SnowflakeDbConnection(ConnectionString); + await connections[i].OpenAsync(CancellationToken.None).ConfigureAwait(false); + } - // Act - queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); - var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); + // Start the async exec query + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)connections[0].CreateCommand()) + { + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; - // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); - } + // Act + queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); + var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); - await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); + // Assert + Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); } // Execute a normal query - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)connections[1].CreateCommand()) { - conn.ConnectionString = ConnectionString; - conn.Open(); - - using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) - { - // Arrange - cmd.CommandText = $"select 1;"; - - // Act - var row = cmd.ExecuteScalar(); + // Arrange + cmd.CommandText = $"select 1;"; - // Assert - Assert.AreEqual(1, row); - } + // Act + var row = cmd.ExecuteScalar(); - conn.Close(); + // Assert + Assert.AreEqual(1, row); } // Get results of the async exec query - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)connections[2].CreateCommand()) { - conn.ConnectionString = ConnectionString; - await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); - - using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) - { - // Act - var reader = await cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None).ConfigureAwait(false); - var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); + // Act + var reader = await cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None).ConfigureAwait(false); + var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); - // Assert - Assert.IsTrue(reader.Read()); - Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); - Assert.AreEqual(QueryStatus.Success, queryStatus); - } + // Assert + Assert.IsTrue(reader.Read()); + Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); + Assert.AreEqual(QueryStatus.Success, queryStatus); + } - await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); + for (int i = 0; i < connections.Length; i++) + { + await connections[i].CloseAsync(CancellationToken.None).ConfigureAwait(false); } } @@ -1367,66 +1355,54 @@ public void TestExecuteNormalQueryWhileAsyncExecQueryIsRunning() string queryId; var expectedWaitTime = 5; - // Start the async exec query - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + SnowflakeDbConnection[] connections = new SnowflakeDbConnection[3]; + for (int i = 0; i < connections.Length; i++) { - conn.ConnectionString = ConnectionString; - conn.Open(); - - using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) - { - // Arrange - cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; + connections[i] = new SnowflakeDbConnection(ConnectionString); + connections[i].Open(); + } - // Act - queryId = cmd.ExecuteInAsyncMode(); + // Start the async exec query + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)connections[0].CreateCommand()) + { + // Arrange + cmd.CommandText = $"CALL SYSTEM$WAIT({expectedWaitTime}, \'SECONDS\');"; - // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))); - } + // Act + queryId = cmd.ExecuteInAsyncMode(); - conn.Close(); + // Assert + Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))); } // Execute a normal query - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)connections[1].CreateCommand()) { - conn.ConnectionString = ConnectionString; - conn.Open(); + // Arrange + cmd.CommandText = $"select 1;"; - using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) - { - // Arrange - cmd.CommandText = $"select 1;"; + // Act + var row = cmd.ExecuteScalar(); - // Act - var row = cmd.ExecuteScalar(); - - // Assert - Assert.AreEqual(1, row); - } - - conn.Close(); + // Assert + Assert.AreEqual(1, row); } // Get results of the async exec query - using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)connections[2].CreateCommand()) { - conn.ConnectionString = ConnectionString; - conn.Open(); + // Act + DbDataReader reader = cmd.GetResultsFromQueryId(queryId); - using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) - { - // Act - DbDataReader reader = cmd.GetResultsFromQueryId(queryId); - - // Assert - Assert.IsTrue(reader.Read()); - Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); - Assert.AreEqual(QueryStatus.Success, cmd.GetQueryStatus(queryId)); - } + // Assert + Assert.IsTrue(reader.Read()); + Assert.AreEqual($"waited {expectedWaitTime} seconds", reader.GetString(0)); + Assert.AreEqual(QueryStatus.Success, cmd.GetQueryStatus(queryId)); + } - conn.Close(); + for (int i = 0; i < connections.Length; i++) + { + connections[i].Close(); } } From a622537f4d9c8154d30356ccaa1935d2be4ab702 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Thu, 21 Mar 2024 08:35:06 -0700 Subject: [PATCH 27/47] Modify test tag --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index f09f73a2c..ea0bc3046 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -213,7 +213,7 @@ public async Task TestAsyncExecQueryAsync() } } - [Test] + [Test, NonParallelizable] public async Task TestExecuteNormalQueryWhileAsyncExecQueryIsRunningAsync() { string queryId; @@ -1349,7 +1349,7 @@ public void TestAsyncExecQuery() } } - [Test] + [Test, NonParallelizable] public void TestExecuteNormalQueryWhileAsyncExecQueryIsRunning() { string queryId; From 21915dc3fdd5340349482035c7a12c89826da9f0 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Thu, 21 Mar 2024 10:28:48 -0700 Subject: [PATCH 28/47] Assign enum with string values --- .../UnitTests/SFStatementTest.cs | 79 +++++++------------ Snowflake.Data/Core/SFStatement.cs | 41 +++++++--- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs index 366bb498e..b1b77a811 100755 --- a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs @@ -8,25 +8,6 @@ namespace Snowflake.Data.Tests.UnitTests using NUnit.Framework; using System; - // Based on the values that the server sends back - public enum TestQueryStatus - { - RUNNING, - ABORTING, - SUCCESS, - FAILED_WITH_ERROR, - ABORTED, - QUEUED, - FAILED_WITH_INCIDENT, - DISCONNECTED, - RESUMING_WAREHOUSE, - // purposeful typo - QUEUED_REPARING_WAREHOUSE, - RESTARTED, - BLOCKED, - NO_DATA, - } - /** * Mock rest request test */ @@ -144,45 +125,41 @@ public void TestTrimSqlLineCommentWithClosingNewline() } [Test] - [TestCase(TestQueryStatus.RUNNING, true)] - [TestCase(TestQueryStatus.RESUMING_WAREHOUSE, true)] - [TestCase(TestQueryStatus.QUEUED, true)] - [TestCase(TestQueryStatus.QUEUED_REPARING_WAREHOUSE, true)] - [TestCase(TestQueryStatus.NO_DATA, true)] - [TestCase(TestQueryStatus.ABORTING, false)] - [TestCase(TestQueryStatus.SUCCESS, false)] - [TestCase(TestQueryStatus.FAILED_WITH_ERROR, false)] - [TestCase(TestQueryStatus.ABORTED, false)] - [TestCase(TestQueryStatus.FAILED_WITH_INCIDENT, false)] - [TestCase(TestQueryStatus.DISCONNECTED, false)] - [TestCase(TestQueryStatus.RESTARTED, false)] - [TestCase(TestQueryStatus.BLOCKED, false)] + [TestCase(QueryStatus.Running, true)] + [TestCase(QueryStatus.ResumingWarehouse, true)] + [TestCase(QueryStatus.Queued, true)] + [TestCase(QueryStatus.QueuedReparingWarehouse, true)] + [TestCase(QueryStatus.NoData, true)] + [TestCase(QueryStatus.Aborting, false)] + [TestCase(QueryStatus.Success, false)] + [TestCase(QueryStatus.FailedWithError, false)] + [TestCase(QueryStatus.Aborted, false)] + [TestCase(QueryStatus.FailedWithIncident, false)] + [TestCase(QueryStatus.Disconnected, false)] + [TestCase(QueryStatus.Restarted, false)] + [TestCase(QueryStatus.Blocked, false)] public void TestIsStillRunning(QueryStatus status, bool expectedResult) { - QueryStatus queryStatus; - Enum.TryParse(status.ToString(), true, out queryStatus); - Assert.AreEqual(expectedResult, QueryStatuses.IsStillRunning(queryStatus)); + Assert.AreEqual(expectedResult, QueryStatuses.IsStillRunning(status)); } [Test] - [TestCase(TestQueryStatus.ABORTING, true)] - [TestCase(TestQueryStatus.FAILED_WITH_ERROR, true)] - [TestCase(TestQueryStatus.ABORTED, true)] - [TestCase(TestQueryStatus.FAILED_WITH_INCIDENT, true)] - [TestCase(TestQueryStatus.DISCONNECTED, true)] - [TestCase(TestQueryStatus.BLOCKED, true)] - [TestCase(TestQueryStatus.RUNNING, false)] - [TestCase(TestQueryStatus.RESUMING_WAREHOUSE, false)] - [TestCase(TestQueryStatus.QUEUED, false)] - [TestCase(TestQueryStatus.QUEUED_REPARING_WAREHOUSE, false)] - [TestCase(TestQueryStatus.NO_DATA, false)] - [TestCase(TestQueryStatus.SUCCESS, false)] - [TestCase(TestQueryStatus.RESTARTED, false)] + [TestCase(QueryStatus.Aborting, true)] + [TestCase(QueryStatus.FailedWithError, true)] + [TestCase(QueryStatus.Aborted, true)] + [TestCase(QueryStatus.FailedWithIncident, true)] + [TestCase(QueryStatus.Disconnected, true)] + [TestCase(QueryStatus.Blocked, true)] + [TestCase(QueryStatus.Running, false)] + [TestCase(QueryStatus.ResumingWarehouse, false)] + [TestCase(QueryStatus.Queued, false)] + [TestCase(QueryStatus.QueuedReparingWarehouse, false)] + [TestCase(QueryStatus.NoData, false)] + [TestCase(QueryStatus.Success, false)] + [TestCase(QueryStatus.Restarted, false)] public void TestIsAnError(QueryStatus status, bool expectedResult) { - QueryStatus queryStatus; - Enum.TryParse(status.ToString(), true, out queryStatus); - Assert.AreEqual(expectedResult, QueryStatuses.IsAnError(queryStatus)); + Assert.AreEqual(expectedResult, QueryStatuses.IsAnError(status)); } } } diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index bdfcf2a12..4b160f04d 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -14,6 +14,7 @@ using System.Threading; using System.Threading.Tasks; using System.Text; +using System.ComponentModel; namespace Snowflake.Data.Core { @@ -22,24 +23,44 @@ namespace Snowflake.Data.Core /// public enum QueryStatus { + [Description("NO_DATA")] + NoData, + [Description("RUNNING")] Running, + [Description("ABORTING")] Aborting, + [Description("SUCCESS")] Success, + [Description("FAILED_WITH_ERROR")] FailedWithError, + [Description("ABORTED")] Aborted, + [Description("QUEUED")] Queued, + [Description("FAILED_WITH_INCIDENT")] FailedWithIncident, + [Description("DISCONNECTED")] Disconnected, + [Description("RESUMING_WAREHOUSE")] ResumingWarehouse, // purposeful typo + [Description("QUEUED_REPARING_WAREHOUSE")] QueuedReparingWarehouse, + [Description("RESTARTED")] Restarted, + [Description("BLOCKED")] Blocked, - NoData, } internal static class QueryStatuses { + internal static QueryStatus GetQueryStatusByDescription(string description) + { + return Enum.GetValues(typeof(QueryStatus)) + .Cast() + .FirstOrDefault(v => v.GetAttribute().Description == description); + } + internal static bool IsStillRunning(QueryStatus status) { switch (status) @@ -813,10 +834,11 @@ internal QueryStatus GetQueryStatus(string queryId) queryId); } - var status = response.data.queries.Count != 0 ? response.data.queries[0].status.Replace("_", string.Empty) : QueryStatus.NoData.ToString(); - - QueryStatus queryStatus; - Enum.TryParse(status, true, out queryStatus); + QueryStatus queryStatus = QueryStatus.NoData; + if (response.data.queries.Count != 0) + { + queryStatus = QueryStatuses.GetQueryStatusByDescription(response.data.queries[0].status); + } return queryStatus; } @@ -867,10 +889,11 @@ internal async Task GetQueryStatusAsync(string queryId, Cancellatio queryId); } - var status = response.data.queries.Count != 0 ? response.data.queries[0].status.Replace("_", string.Empty) : QueryStatus.NoData.ToString(); - - QueryStatus queryStatus; - Enum.TryParse(status, true, out queryStatus); + QueryStatus queryStatus = QueryStatus.NoData; + if (response.data.queries.Count != 0) + { + queryStatus = QueryStatuses.GetQueryStatusByDescription(response.data.queries[0].status); + } return queryStatus; } From 7185f790b464116df1b891ff06991776442c34ca Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Thu, 21 Mar 2024 14:58:08 -0700 Subject: [PATCH 29/47] Re-add check if token is cancelled --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 9 --------- Snowflake.Data/Client/SnowflakeDbCommand.cs | 6 ++++++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index ea0bc3046..14d9bbe61 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -295,20 +295,11 @@ public async Task TestAsyncExecCancelWhileGettingResultsAsync() // Act cancelToken.Cancel(); -#if NET471 var thrown = Assert.ThrowsAsync(async () => await cmd.GetResultsFromQueryIdAsync(queryId, cancelToken.Token).ConfigureAwait(false)); -#else - var thrown = Assert.ThrowsAsync(async () => - await cmd.GetResultsFromQueryIdAsync(queryId, cancelToken.Token).ConfigureAwait(false)); -#endif // Assert -#if NET472 - Assert.IsTrue(thrown.Message.Contains("A task was canceled")); -#else Assert.IsTrue(thrown.Message.Contains("The operation was canceled")); -#endif } await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index 5eaee9c62..b4aa1f7cf 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -366,6 +366,12 @@ internal async Task RetryUntilQueryResultIsAvailable(string queryId, Cancellatio QueryStatus status; while (true) { + if (cancellationToken.IsCancellationRequested) + { + logger.Debug("Cancellation requested for getting results from query id"); + cancellationToken.ThrowIfCancellationRequested(); + } + status = isAsync ? await GetQueryStatusAsync(queryId, cancellationToken) : GetQueryStatus(queryId); if (!QueryStatuses.IsStillRunning(status)) From 6ec657b642bd1e8cd397145cbbc017807fff46df Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 22 Mar 2024 11:57:44 -0700 Subject: [PATCH 30/47] Remove unused imports --- Snowflake.Data/Core/SFStatement.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index 4b160f04d..886f12e1e 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -3,13 +3,10 @@ */ using System; -using System.Web; -using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.IO; using System.Linq; using Snowflake.Data.Client; -using Snowflake.Data.Core.FileTransfer; using Snowflake.Data.Log; using System.Threading; using System.Threading.Tasks; From 1d7dafb0f64ab44792f54569ec5f591f0a0990a4 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 22 Mar 2024 12:30:36 -0700 Subject: [PATCH 31/47] Create variable for max status retry --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 14d9bbe61..0f6609734 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -317,13 +317,14 @@ public async Task TestFailedAsyncExecQueryThrowsErrorAsync() using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { // Arrange + var statusMaxRetryCount = 5; var statusRetryCount = 0; cmd.CommandText = $"SELECT * FROM FAKE_TABLE;"; // Act var queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); - while (statusRetryCount < 5 && QueryStatuses.IsStillRunning(queryStatus)) + while (statusRetryCount < statusMaxRetryCount && QueryStatuses.IsStillRunning(queryStatus)) { Thread.Sleep(1000); queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); @@ -1410,12 +1411,13 @@ public void TestFailedAsyncExecQueryThrowsError() using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { // Arrange + var statusMaxRetryCount = 5; var statusRetryCount = 0; cmd.CommandText = $"SELECT * FROM FAKE_TABLE;"; // Act queryId = cmd.ExecuteInAsyncMode(); - while (statusRetryCount < 5 && QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))) + while (statusRetryCount < statusMaxRetryCount && QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))) { Thread.Sleep(1000); statusRetryCount++; From ce10981818043baaaa66c941433ebccf3e638e3a Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 22 Mar 2024 13:42:57 -0700 Subject: [PATCH 32/47] Refactor QueryStatuses --- .../IntegrationTests/SFDbCommandIT.cs | 14 +++++++------- Snowflake.Data.Tests/UnitTests/SFStatementTest.cs | 4 ++-- Snowflake.Data/Core/SFStatement.cs | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 0f6609734..8c46072fe 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -197,7 +197,7 @@ public async Task TestAsyncExecQueryAsync() var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); + Assert.IsTrue(QueryStatusExtensions.IsStillRunning(queryStatus)); // Act DbDataReader reader = await cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None).ConfigureAwait(false); @@ -237,7 +237,7 @@ public async Task TestExecuteNormalQueryWhileAsyncExecQueryIsRunningAsync() var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); + Assert.IsTrue(QueryStatusExtensions.IsStillRunning(queryStatus)); } // Execute a normal query @@ -291,7 +291,7 @@ public async Task TestAsyncExecCancelWhileGettingResultsAsync() var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(queryStatus)); + Assert.IsTrue(QueryStatusExtensions.IsStillRunning(queryStatus)); // Act cancelToken.Cancel(); @@ -324,7 +324,7 @@ public async Task TestFailedAsyncExecQueryThrowsErrorAsync() // Act var queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); - while (statusRetryCount < statusMaxRetryCount && QueryStatuses.IsStillRunning(queryStatus)) + while (statusRetryCount < statusMaxRetryCount && QueryStatusExtensions.IsStillRunning(queryStatus)) { Thread.Sleep(1000); queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); @@ -1326,7 +1326,7 @@ public void TestAsyncExecQuery() queryId = cmd.ExecuteInAsyncMode(); // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))); + Assert.IsTrue(QueryStatusExtensions.IsStillRunning(cmd.GetQueryStatus(queryId))); // Act DbDataReader reader = cmd.GetResultsFromQueryId(queryId); @@ -1364,7 +1364,7 @@ public void TestExecuteNormalQueryWhileAsyncExecQueryIsRunning() queryId = cmd.ExecuteInAsyncMode(); // Assert - Assert.IsTrue(QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))); + Assert.IsTrue(QueryStatusExtensions.IsStillRunning(cmd.GetQueryStatus(queryId))); } // Execute a normal query @@ -1417,7 +1417,7 @@ public void TestFailedAsyncExecQueryThrowsError() // Act queryId = cmd.ExecuteInAsyncMode(); - while (statusRetryCount < statusMaxRetryCount && QueryStatuses.IsStillRunning(cmd.GetQueryStatus(queryId))) + while (statusRetryCount < statusMaxRetryCount && QueryStatusExtensions.IsStillRunning(cmd.GetQueryStatus(queryId))) { Thread.Sleep(1000); statusRetryCount++; diff --git a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs index b1b77a811..0a35a9fe5 100755 --- a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs @@ -140,7 +140,7 @@ public void TestTrimSqlLineCommentWithClosingNewline() [TestCase(QueryStatus.Blocked, false)] public void TestIsStillRunning(QueryStatus status, bool expectedResult) { - Assert.AreEqual(expectedResult, QueryStatuses.IsStillRunning(status)); + Assert.AreEqual(expectedResult, QueryStatusExtensions.IsStillRunning(status)); } [Test] @@ -159,7 +159,7 @@ public void TestIsStillRunning(QueryStatus status, bool expectedResult) [TestCase(QueryStatus.Restarted, false)] public void TestIsAnError(QueryStatus status, bool expectedResult) { - Assert.AreEqual(expectedResult, QueryStatuses.IsAnError(status)); + Assert.AreEqual(expectedResult, QueryStatusExtensions.IsAnError(status)); } } } diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index 886f12e1e..2eeda3105 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -49,9 +49,9 @@ public enum QueryStatus Blocked, } - internal static class QueryStatuses + internal static class QueryStatusExtensions { - internal static QueryStatus GetQueryStatusByDescription(string description) + internal static QueryStatus GetQueryStatusByStringValue(string description) { return Enum.GetValues(typeof(QueryStatus)) .Cast() @@ -834,7 +834,7 @@ internal QueryStatus GetQueryStatus(string queryId) QueryStatus queryStatus = QueryStatus.NoData; if (response.data.queries.Count != 0) { - queryStatus = QueryStatuses.GetQueryStatusByDescription(response.data.queries[0].status); + queryStatus = QueryStatusExtensions.GetQueryStatusByStringValue(response.data.queries[0].status); } return queryStatus; @@ -889,7 +889,7 @@ internal async Task GetQueryStatusAsync(string queryId, Cancellatio QueryStatus queryStatus = QueryStatus.NoData; if (response.data.queries.Count != 0) { - queryStatus = QueryStatuses.GetQueryStatusByDescription(response.data.queries[0].status); + queryStatus = QueryStatusExtensions.GetQueryStatusByStringValue(response.data.queries[0].status); } return queryStatus; From 198a39b2ad37fa07636f7890d8720b43704d1da1 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 22 Mar 2024 13:52:46 -0700 Subject: [PATCH 33/47] Refactor QueryStatuses --- Snowflake.Data/Client/SnowflakeDbCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index b4aa1f7cf..4a0a0f45c 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -374,7 +374,7 @@ internal async Task RetryUntilQueryResultIsAvailable(string queryId, Cancellatio status = isAsync ? await GetQueryStatusAsync(queryId, cancellationToken) : GetQueryStatus(queryId); - if (!QueryStatuses.IsStillRunning(status)) + if (!QueryStatusExtensions.IsStillRunning(status)) { return; } From 6438057659b34da7a564def03bb565b2c9ef7ae2 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 22 Mar 2024 15:17:04 -0700 Subject: [PATCH 34/47] Make comparison case insensitive --- Snowflake.Data/Core/SFStatement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index 2eeda3105..6bfdab7b3 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -55,7 +55,7 @@ internal static QueryStatus GetQueryStatusByStringValue(string description) { return Enum.GetValues(typeof(QueryStatus)) .Cast() - .FirstOrDefault(v => v.GetAttribute().Description == description); + .FirstOrDefault(v => v.GetAttribute().Description.Equals(description, StringComparison.OrdinalIgnoreCase)); } internal static bool IsStillRunning(QueryStatus status) From 8957d17452219dfd4a3d0a4e08b26dd0868f710d Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 22 Mar 2024 17:09:36 -0700 Subject: [PATCH 35/47] Add Unknown status to enum --- Snowflake.Data.Tests/UnitTests/SFStatementTest.cs | 2 ++ Snowflake.Data/Core/SFStatement.cs | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs index 0a35a9fe5..a268ef867 100755 --- a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs @@ -138,6 +138,7 @@ public void TestTrimSqlLineCommentWithClosingNewline() [TestCase(QueryStatus.Disconnected, false)] [TestCase(QueryStatus.Restarted, false)] [TestCase(QueryStatus.Blocked, false)] + [TestCase(QueryStatus.Unknown, false)] public void TestIsStillRunning(QueryStatus status, bool expectedResult) { Assert.AreEqual(expectedResult, QueryStatusExtensions.IsStillRunning(status)); @@ -157,6 +158,7 @@ public void TestIsStillRunning(QueryStatus status, bool expectedResult) [TestCase(QueryStatus.NoData, false)] [TestCase(QueryStatus.Success, false)] [TestCase(QueryStatus.Restarted, false)] + [TestCase(QueryStatus.Unknown, false)] public void TestIsAnError(QueryStatus status, bool expectedResult) { Assert.AreEqual(expectedResult, QueryStatusExtensions.IsAnError(status)); diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index 6bfdab7b3..2f60a8e08 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -20,6 +20,8 @@ namespace Snowflake.Data.Core /// public enum QueryStatus { + [Description("UNKNOWN")] + Unknown, [Description("NO_DATA")] NoData, [Description("RUNNING")] @@ -53,9 +55,16 @@ internal static class QueryStatusExtensions { internal static QueryStatus GetQueryStatusByStringValue(string description) { - return Enum.GetValues(typeof(QueryStatus)) + var status = Enum.GetValues(typeof(QueryStatus)) .Cast() .FirstOrDefault(v => v.GetAttribute().Description.Equals(description, StringComparison.OrdinalIgnoreCase)); + + if (status == QueryStatus.Unknown) + { + throw new Exception("The query status returned by the server is not recognized"); + } + + return status; } internal static bool IsStillRunning(QueryStatus status) From 293f58d53e770e71c9adec5f13bba12555c389d8 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 22 Mar 2024 18:54:30 -0700 Subject: [PATCH 36/47] Add test for GetQueryStatusByStringValue --- .../UnitTests/SFStatementTest.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs index a268ef867..ea1a7dda4 100755 --- a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs @@ -124,6 +124,50 @@ public void TestTrimSqlLineCommentWithClosingNewline() Assert.AreEqual("1", resultSet.GetString(0)); } + [Test] + [TestCase("UNKNOWN", QueryStatus.Unknown)] + [TestCase("RANDOM_STATUS", QueryStatus.Unknown)] + [TestCase("aBcZyX", QueryStatus.Unknown)] + [TestCase("running", QueryStatus.Running)] + [TestCase("RUNNING", QueryStatus.Running)] + [TestCase("resuming_warehouse", QueryStatus.ResumingWarehouse)] + [TestCase("RESUMING_WAREHOUSE", QueryStatus.ResumingWarehouse)] + [TestCase("queued", QueryStatus.Queued)] + [TestCase("QUEUED", QueryStatus.Queued)] + [TestCase("queued_reparing_warehouse", QueryStatus.QueuedReparingWarehouse)] + [TestCase("QUEUED_REPARING_WAREHOUSE", QueryStatus.QueuedReparingWarehouse)] + [TestCase("no_data", QueryStatus.NoData)] + [TestCase("NO_DATA", QueryStatus.NoData)] + [TestCase("aborting", QueryStatus.Aborting)] + [TestCase("ABORTING", QueryStatus.Aborting)] + [TestCase("success", QueryStatus.Success)] + [TestCase("SUCCESS", QueryStatus.Success)] + [TestCase("failed_with_error", QueryStatus.FailedWithError)] + [TestCase("FAILED_WITH_ERROR", QueryStatus.FailedWithError)] + [TestCase("aborted", QueryStatus.Aborted)] + [TestCase("ABORTED", QueryStatus.Aborted)] + [TestCase("failed_with_incident", QueryStatus.FailedWithIncident)] + [TestCase("FAILED_WITH_INCIDENT", QueryStatus.FailedWithIncident)] + [TestCase("disconnected", QueryStatus.Disconnected)] + [TestCase("DISCONNECTED", QueryStatus.Disconnected)] + [TestCase("restarted", QueryStatus.Restarted)] + [TestCase("RESTARTED", QueryStatus.Restarted)] + [TestCase("blocked", QueryStatus.Blocked)] + [TestCase("BLOCKED", QueryStatus.Blocked)] + public void TestGetQueryStatusByStringValue(string status, QueryStatus expectedStatus) + { + if (expectedStatus == QueryStatus.Unknown) + { + var thrown = Assert.Throws(() => QueryStatusExtensions.GetQueryStatusByStringValue(status)); + Assert.IsTrue(thrown.Message.Contains("The query status returned by the server is not recognized")); + } + else + { + var actualStatus = QueryStatusExtensions.GetQueryStatusByStringValue(status); + Assert.AreEqual(expectedStatus, actualStatus); + } + } + [Test] [TestCase(QueryStatus.Running, true)] [TestCase(QueryStatus.ResumingWarehouse, true)] From 29b814908ad3bbdc6c5e7f79dbf1b4b9120d222a Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Fri, 22 Mar 2024 19:44:53 -0700 Subject: [PATCH 37/47] Replaced Description attribute with StringAttr attribute --- Snowflake.Data/Core/SFStatement.cs | 35 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index 2f60a8e08..bcbf99af6 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -20,44 +20,49 @@ namespace Snowflake.Data.Core /// public enum QueryStatus { - [Description("UNKNOWN")] + [StringAttr(value = "UNKNOWN")] Unknown, - [Description("NO_DATA")] + [StringAttr(value = "NO_DATA")] NoData, - [Description("RUNNING")] + [StringAttr(value = "RUNNING")] Running, - [Description("ABORTING")] + [StringAttr(value = "ABORTING")] Aborting, - [Description("SUCCESS")] + [StringAttr(value = "SUCCESS")] Success, - [Description("FAILED_WITH_ERROR")] + [StringAttr(value = "FAILED_WITH_ERROR")] FailedWithError, - [Description("ABORTED")] + [StringAttr(value = "ABORTED")] Aborted, - [Description("QUEUED")] + [StringAttr(value = "QUEUED")] Queued, - [Description("FAILED_WITH_INCIDENT")] + [StringAttr(value = "FAILED_WITH_INCIDENT")] FailedWithIncident, - [Description("DISCONNECTED")] + [StringAttr(value = "DISCONNECTED")] Disconnected, - [Description("RESUMING_WAREHOUSE")] + [StringAttr(value = "RESUMING_WAREHOUSE")] ResumingWarehouse, // purposeful typo - [Description("QUEUED_REPARING_WAREHOUSE")] + [StringAttr(value = "QUEUED_REPARING_WAREHOUSE")] QueuedReparingWarehouse, - [Description("RESTARTED")] + [StringAttr(value = "RESTARTED")] Restarted, - [Description("BLOCKED")] + [StringAttr(value = "BLOCKED")] Blocked, } + class StringAttr : Attribute + { + public string value { get; set; } + } + internal static class QueryStatusExtensions { internal static QueryStatus GetQueryStatusByStringValue(string description) { var status = Enum.GetValues(typeof(QueryStatus)) .Cast() - .FirstOrDefault(v => v.GetAttribute().Description.Equals(description, StringComparison.OrdinalIgnoreCase)); + .FirstOrDefault(v => v.GetAttribute().value.Equals(description, StringComparison.OrdinalIgnoreCase)); if (status == QueryStatus.Unknown) { From 95f09866dbdae7434e1034b686263828cc7c46e4 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 09:24:29 -0700 Subject: [PATCH 38/47] Remove unused import --- Snowflake.Data/Core/SFStatement.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index bcbf99af6..65f039378 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; using System.Text; -using System.ComponentModel; namespace Snowflake.Data.Core { From 470907c99e8cc03fb4ec4f3021ae241a60ca2c86 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 09:28:44 -0700 Subject: [PATCH 39/47] Rename parameter --- Snowflake.Data/Core/SFStatement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index 65f039378..ed6a02a39 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -57,11 +57,11 @@ class StringAttr : Attribute internal static class QueryStatusExtensions { - internal static QueryStatus GetQueryStatusByStringValue(string description) + internal static QueryStatus GetQueryStatusByStringValue(string stringValue) { var status = Enum.GetValues(typeof(QueryStatus)) .Cast() - .FirstOrDefault(v => v.GetAttribute().value.Equals(description, StringComparison.OrdinalIgnoreCase)); + .FirstOrDefault(v => v.GetAttribute().value.Equals(stringValue, StringComparison.OrdinalIgnoreCase)); if (status == QueryStatus.Unknown) { From d49d83dad8fb4a4c8f59d50c1ba94d4f3d070e73 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 10:13:54 -0700 Subject: [PATCH 40/47] Split test case into positive/negative cases --- .../UnitTests/SFStatementTest.cs | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs index ea1a7dda4..9f642cb4e 100755 --- a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs @@ -125,9 +125,6 @@ public void TestTrimSqlLineCommentWithClosingNewline() } [Test] - [TestCase("UNKNOWN", QueryStatus.Unknown)] - [TestCase("RANDOM_STATUS", QueryStatus.Unknown)] - [TestCase("aBcZyX", QueryStatus.Unknown)] [TestCase("running", QueryStatus.Running)] [TestCase("RUNNING", QueryStatus.Running)] [TestCase("resuming_warehouse", QueryStatus.ResumingWarehouse)] @@ -154,18 +151,19 @@ public void TestTrimSqlLineCommentWithClosingNewline() [TestCase("RESTARTED", QueryStatus.Restarted)] [TestCase("blocked", QueryStatus.Blocked)] [TestCase("BLOCKED", QueryStatus.Blocked)] - public void TestGetQueryStatusByStringValue(string status, QueryStatus expectedStatus) + public void TestGetQueryStatusByStringValue(string stringValue, QueryStatus expectedStatus) { - if (expectedStatus == QueryStatus.Unknown) - { - var thrown = Assert.Throws(() => QueryStatusExtensions.GetQueryStatusByStringValue(status)); - Assert.IsTrue(thrown.Message.Contains("The query status returned by the server is not recognized")); - } - else - { - var actualStatus = QueryStatusExtensions.GetQueryStatusByStringValue(status); - Assert.AreEqual(expectedStatus, actualStatus); - } + Assert.AreEqual(expectedStatus, QueryStatusExtensions.GetQueryStatusByStringValue(stringValue)); + } + + [Test] + [TestCase("UNKNOWN")] + [TestCase("RANDOM_STATUS")] + [TestCase("aBcZyX")] + public void TestGetQueryStatusByStringValueThrowsErrorForUnknownStatus(string stringValue) + { + var thrown = Assert.Throws(() => QueryStatusExtensions.GetQueryStatusByStringValue(stringValue)); + Assert.IsTrue(thrown.Message.Contains("The query status returned by the server is not recognized")); } [Test] From e6ab842b63f89070c8724a2ae903b13713944896 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 10:52:37 -0700 Subject: [PATCH 41/47] Remove Unknown enum and refactor GetQueryStatusByStringValue --- Snowflake.Data.Tests/UnitTests/SFStatementTest.cs | 2 -- Snowflake.Data/Core/SFStatement.cs | 14 +++----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs index 9f642cb4e..24f0f4b0a 100755 --- a/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs +++ b/Snowflake.Data.Tests/UnitTests/SFStatementTest.cs @@ -180,7 +180,6 @@ public void TestGetQueryStatusByStringValueThrowsErrorForUnknownStatus(string st [TestCase(QueryStatus.Disconnected, false)] [TestCase(QueryStatus.Restarted, false)] [TestCase(QueryStatus.Blocked, false)] - [TestCase(QueryStatus.Unknown, false)] public void TestIsStillRunning(QueryStatus status, bool expectedResult) { Assert.AreEqual(expectedResult, QueryStatusExtensions.IsStillRunning(status)); @@ -200,7 +199,6 @@ public void TestIsStillRunning(QueryStatus status, bool expectedResult) [TestCase(QueryStatus.NoData, false)] [TestCase(QueryStatus.Success, false)] [TestCase(QueryStatus.Restarted, false)] - [TestCase(QueryStatus.Unknown, false)] public void TestIsAnError(QueryStatus status, bool expectedResult) { Assert.AreEqual(expectedResult, QueryStatusExtensions.IsAnError(status)); diff --git a/Snowflake.Data/Core/SFStatement.cs b/Snowflake.Data/Core/SFStatement.cs index ed6a02a39..9252af40e 100644 --- a/Snowflake.Data/Core/SFStatement.cs +++ b/Snowflake.Data/Core/SFStatement.cs @@ -19,8 +19,6 @@ namespace Snowflake.Data.Core /// public enum QueryStatus { - [StringAttr(value = "UNKNOWN")] - Unknown, [StringAttr(value = "NO_DATA")] NoData, [StringAttr(value = "RUNNING")] @@ -59,16 +57,10 @@ internal static class QueryStatusExtensions { internal static QueryStatus GetQueryStatusByStringValue(string stringValue) { - var status = Enum.GetValues(typeof(QueryStatus)) + var statuses = Enum.GetValues(typeof(QueryStatus)) .Cast() - .FirstOrDefault(v => v.GetAttribute().value.Equals(stringValue, StringComparison.OrdinalIgnoreCase)); - - if (status == QueryStatus.Unknown) - { - throw new Exception("The query status returned by the server is not recognized"); - } - - return status; + .Where(v => v.GetAttribute().value.Equals(stringValue, StringComparison.OrdinalIgnoreCase)); + return statuses.Any() ? statuses.First() : throw new Exception("The query status returned by the server is not recognized"); } internal static bool IsStillRunning(QueryStatus status) From 73358b171f9e072969ab39f2750efdbb2ba921dd Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 15:09:20 -0700 Subject: [PATCH 42/47] Refactor async query retry into its own class --- .../IntegrationTests/SFDbCommandIT.cs | 57 ++++++- Snowflake.Data/Client/SnowflakeDbCommand.cs | 102 ++----------- Snowflake.Data/Core/QueryResultsAwaiter.cs | 144 ++++++++++++++++++ 3 files changed, 209 insertions(+), 94 deletions(-) create mode 100644 Snowflake.Data/Core/QueryResultsAwaiter.cs diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 8c46072fe..d19329cb4 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -418,7 +418,8 @@ public async Task TestGetStatusOfUnknownQueryIdAsync() } [Test] - public async Task TestGetResultsOfUnknownQueryIdAsync() + [Ignore("The test takes too long to finish when using the default retry")] + public async Task TestGetResultsOfUnknownQueryIdAsyncWithDefaultRetry() { string unknownQueryId = "ab123fed-1abc-987f-987f-1234a56b789c"; @@ -440,6 +441,32 @@ public async Task TestGetResultsOfUnknownQueryIdAsync() await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); } } + + [Test] + public async Task TestGetResultsOfUnknownQueryIdAsyncWithConfiguredRetry() + { + var queryResultsRetryCount = 3; + var queryResultsRetryPattern = new int[] { 1, 2 }; + var unknownQueryId = "ab123fed-1abc-987f-987f-1234a56b789c"; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); + + using (SnowflakeDbCommand cmd = new SnowflakeDbCommand(conn, new QueryResultsRetryConfig(queryResultsRetryCount, queryResultsRetryPattern))) + { + // Act + var thrown = Assert.ThrowsAsync(async () => + await cmd.GetResultsFromQueryIdAsync(unknownQueryId, CancellationToken.None).ConfigureAwait(false)); + + // Assert + Assert.IsTrue(thrown.Message.Contains($"Max retry for no data is reached")); + } + + await conn.CloseAsync(CancellationToken.None).ConfigureAwait(false); + } + } } [TestFixture] @@ -1540,7 +1567,8 @@ public void TestGetStatusOfUnknownQueryId() } [Test] - public void TestGetResultsOfUnknownQueryId() + [Ignore("The test takes too long to finish when using the default retry")] + public void TestGetResultsOfUnknownQueryIdWithDefaultRetry() { string unknownQueryId = "ba987def-1abc-987f-987f-1234a56b789c"; @@ -1561,5 +1589,30 @@ public void TestGetResultsOfUnknownQueryId() conn.Close(); } } + + [Test] + public void TestGetResultsOfUnknownQueryIdWithConfiguredRetry() + { + var queryResultsRetryCount = 3; + var queryResultsRetryPattern = new int[] { 1, 2 }; + var unknownQueryId = "ba987def-1abc-987f-987f-1234a56b789c"; + + using (SnowflakeDbConnection conn = new SnowflakeDbConnection()) + { + conn.ConnectionString = ConnectionString; + conn.Open(); + + using (SnowflakeDbCommand cmd = new SnowflakeDbCommand(conn, new QueryResultsRetryConfig(queryResultsRetryCount, queryResultsRetryPattern))) + { + // Act + var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(unknownQueryId)); + + // Assert + Assert.IsTrue(thrown.InnerException.Message.Contains($"Max retry for no data is reached")); + } + + conn.Close(); + } + } } } diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index 4a0a0f45c..b35db7970 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -9,9 +9,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; using Snowflake.Data.Log; -using System.Text.RegularExpressions; namespace Snowflake.Data.Client { @@ -26,11 +24,7 @@ public class SnowflakeDbCommand : DbCommand private SFLogger logger = SFLoggerFactory.GetLogger(); - // Async max retry and retry pattern - private const int AsyncNoDataMaxRetry = 24; - private readonly int[] _asyncRetryPattern = { 1, 1, 2, 3, 4, 8, 10 }; - - private static readonly Regex UuidRegex = new Regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); + private QueryResultsAwaiter _queryResultsAwaiter = new QueryResultsAwaiter(); public SnowflakeDbCommand() { @@ -50,6 +44,11 @@ public SnowflakeDbCommand(SnowflakeDbConnection connection, string cmdText) : th this.CommandText = cmdText; } + public SnowflakeDbCommand(SnowflakeDbConnection connection, QueryResultsRetryConfig queryResultsRetryConfig) : this(connection) + { + _queryResultsAwaiter = new QueryResultsAwaiter(queryResultsRetryConfig); + } + public override string CommandText { get; set; @@ -314,18 +313,7 @@ public async Task ExecuteAsyncInAsyncMode(CancellationToken cancellation public QueryStatus GetQueryStatus(string queryId) { logger.Debug($"GetQueryStatus"); - - if (UuidRegex.IsMatch(queryId)) - { - var sfStatement = new SFStatement(connection.SfSession); - return sfStatement.GetQueryStatus(queryId); - } - else - { - var errorMessage = $"The given query id {queryId} is not valid uuid"; - logger.Error(errorMessage); - throw new Exception(errorMessage); - } + return _queryResultsAwaiter.GetQueryStatus(connection, queryId); } /// @@ -337,77 +325,7 @@ public QueryStatus GetQueryStatus(string queryId) public async Task GetQueryStatusAsync(string queryId, CancellationToken cancellationToken) { logger.Debug($"GetQueryStatusAsync"); - - // Check if queryId is valid uuid - if (UuidRegex.IsMatch(queryId)) - { - var sfStatement = new SFStatement(connection.SfSession); - return await sfStatement.GetQueryStatusAsync(queryId, cancellationToken).ConfigureAwait(false); - } - else - { - var errorMessage = $"The given query id {queryId} is not valid uuid"; - logger.Error(errorMessage); - throw new Exception(errorMessage); - } - } - - /// - /// Checks query status until it is done executing. - /// - /// - /// - /// - internal async Task RetryUntilQueryResultIsAvailable(string queryId, CancellationToken cancellationToken, bool isAsync) - { - int retryPatternPos = 0; - int noDataCounter = 0; - - QueryStatus status; - while (true) - { - if (cancellationToken.IsCancellationRequested) - { - logger.Debug("Cancellation requested for getting results from query id"); - cancellationToken.ThrowIfCancellationRequested(); - } - - status = isAsync ? await GetQueryStatusAsync(queryId, cancellationToken) : GetQueryStatus(queryId); - - if (!QueryStatusExtensions.IsStillRunning(status)) - { - return; - } - - // Timeout based on query status retry rules - if (isAsync) - { - await Task.Delay(TimeSpan.FromSeconds(_asyncRetryPattern[retryPatternPos]), cancellationToken).ConfigureAwait(false); - } - else - { - Thread.Sleep(TimeSpan.FromSeconds(_asyncRetryPattern[retryPatternPos])); - } - - // If no data, increment the no data counter - if (status == QueryStatus.NoData) - { - noDataCounter++; - - // Check if retry for no data is exceeded - if (noDataCounter > AsyncNoDataMaxRetry) - { - var errorMessage = "Max retry for no data is reached"; - logger.Error(errorMessage); - throw new Exception(errorMessage); - } - } - - if (retryPatternPos < _asyncRetryPattern.Length - 1) - { - retryPatternPos++; - } - } + return await _queryResultsAwaiter.GetQueryStatusAsync(connection, queryId, cancellationToken); } /// @@ -419,7 +337,7 @@ public DbDataReader GetResultsFromQueryId(string queryId) { logger.Debug($"GetResultsFromQueryId"); - Task task = RetryUntilQueryResultIsAvailable(queryId, CancellationToken.None, false); + Task task = _queryResultsAwaiter.RetryUntilQueryResultIsAvailable(connection, queryId, CancellationToken.None, false); task.Wait(); SFBaseResultSet resultSet = sfStatement.GetResultWithId(queryId); @@ -437,7 +355,7 @@ public async Task GetResultsFromQueryIdAsync(string queryId, Cance { logger.Debug($"GetResultsFromQueryIdAsync"); - await RetryUntilQueryResultIsAvailable(queryId, cancellationToken, true); + await _queryResultsAwaiter.RetryUntilQueryResultIsAvailable(connection, queryId, cancellationToken, true); SFBaseResultSet resultSet = await sfStatement.GetResultWithIdAsync(queryId, cancellationToken).ConfigureAwait(false); diff --git a/Snowflake.Data/Core/QueryResultsAwaiter.cs b/Snowflake.Data/Core/QueryResultsAwaiter.cs new file mode 100644 index 000000000..5a9bc0744 --- /dev/null +++ b/Snowflake.Data/Core/QueryResultsAwaiter.cs @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ + +using Snowflake.Data.Log; +using System.Threading.Tasks; +using System.Threading; +using System; +using System.Text.RegularExpressions; +using Snowflake.Data.Client; + +namespace Snowflake.Data.Core +{ + public class QueryResultsRetryConfig + { + private const int DefaultAsyncNoDataMaxRetry = 24; + + private readonly int[] _defaultAsyncRetryPattern = { 1, 1, 2, 3, 4, 8, 10 }; + + internal readonly int _asyncNoDataMaxRetry; + + internal readonly int[] _asyncRetryPattern; + + internal QueryResultsRetryConfig() + { + _asyncNoDataMaxRetry = DefaultAsyncNoDataMaxRetry; + _asyncRetryPattern = _defaultAsyncRetryPattern; + } + + internal QueryResultsRetryConfig(int asyncNoDataMaxRetry, int[] asyncRetryPattern) + { + _asyncNoDataMaxRetry = asyncNoDataMaxRetry; + _asyncRetryPattern = asyncRetryPattern; + } + } + + internal class QueryResultsAwaiter + { + private static readonly SFLogger s_logger = SFLoggerFactory.GetLogger(); + + private static readonly Regex UuidRegex = new Regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); + + private QueryResultsRetryConfig _queryResultsRetryConfig; + + internal QueryResultsAwaiter() + { + _queryResultsRetryConfig = new QueryResultsRetryConfig(); + } + + internal QueryResultsAwaiter(QueryResultsRetryConfig queryResultsRetryConfig) + { + _queryResultsRetryConfig = queryResultsRetryConfig; + } + + internal QueryStatus GetQueryStatus(SnowflakeDbConnection connection, string queryId) + { + if (UuidRegex.IsMatch(queryId)) + { + var sfStatement = new SFStatement(connection.SfSession); + return sfStatement.GetQueryStatus(queryId); + } + else + { + var errorMessage = $"The given query id {queryId} is not valid uuid"; + s_logger.Error(errorMessage); + throw new Exception(errorMessage); + } + } + + internal async Task GetQueryStatusAsync(SnowflakeDbConnection connection, string queryId, CancellationToken cancellationToken) + { + if (UuidRegex.IsMatch(queryId)) + { + var sfStatement = new SFStatement(connection.SfSession); + return await sfStatement.GetQueryStatusAsync(queryId, cancellationToken).ConfigureAwait(false); + } + else + { + var errorMessage = $"The given query id {queryId} is not valid uuid"; + s_logger.Error(errorMessage); + throw new Exception(errorMessage); + } + } + + /// + /// Checks query status until it is done executing. + /// + /// + /// + /// + /// + internal async Task RetryUntilQueryResultIsAvailable(SnowflakeDbConnection connection, string queryId, CancellationToken cancellationToken, bool isAsync) + { + int retryPatternPos = 0; + int noDataCounter = 0; + + QueryStatus status; + while (true) + { + if (cancellationToken.IsCancellationRequested) + { + s_logger.Debug("Cancellation requested for getting results from query id"); + cancellationToken.ThrowIfCancellationRequested(); + } + + status = isAsync ? await GetQueryStatusAsync(connection, queryId, cancellationToken) : GetQueryStatus(connection, queryId); + + if (!QueryStatusExtensions.IsStillRunning(status)) + { + return; + } + + // Timeout based on query status retry rules + if (isAsync) + { + await Task.Delay(TimeSpan.FromSeconds(_queryResultsRetryConfig._asyncRetryPattern[retryPatternPos]), cancellationToken).ConfigureAwait(false); + } + else + { + Thread.Sleep(TimeSpan.FromSeconds(_queryResultsRetryConfig._asyncRetryPattern[retryPatternPos])); + } + + // If no data, increment the no data counter + if (status == QueryStatus.NoData) + { + noDataCounter++; + + // Check if retry for no data is exceeded + if (noDataCounter > _queryResultsRetryConfig._asyncNoDataMaxRetry) + { + var errorMessage = "Max retry for no data is reached"; + s_logger.Error(errorMessage); + throw new Exception(errorMessage); + } + } + + if (retryPatternPos < _queryResultsRetryConfig._asyncRetryPattern.Length - 1) + { + retryPatternPos++; + } + } + } + } +} From e1c50b612b7895bd2beb16ef1dd81039dcace899 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 15:54:05 -0700 Subject: [PATCH 43/47] Add util functions for user to check query status is still running or an error --- .../IntegrationTests/SFDbCommandIT.cs | 17 ++++++++++------- Snowflake.Data/Client/SnowflakeDbConnection.cs | 10 ++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index d19329cb4..ff155b69d 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -197,7 +197,8 @@ public async Task TestAsyncExecQueryAsync() var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.IsTrue(QueryStatusExtensions.IsStillRunning(queryStatus)); + Assert.IsTrue(conn.IsStillRunning(queryStatus)); + Assert.IsFalse(conn.IsAnError(queryStatus)); // Act DbDataReader reader = await cmd.GetResultsFromQueryIdAsync(queryId, CancellationToken.None).ConfigureAwait(false); @@ -237,7 +238,7 @@ public async Task TestExecuteNormalQueryWhileAsyncExecQueryIsRunningAsync() var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.IsTrue(QueryStatusExtensions.IsStillRunning(queryStatus)); + Assert.IsTrue(connections[0].IsStillRunning(queryStatus)); } // Execute a normal query @@ -291,7 +292,7 @@ public async Task TestAsyncExecCancelWhileGettingResultsAsync() var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.IsTrue(QueryStatusExtensions.IsStillRunning(queryStatus)); + Assert.IsTrue(conn.IsStillRunning(queryStatus)); // Act cancelToken.Cancel(); @@ -324,7 +325,7 @@ public async Task TestFailedAsyncExecQueryThrowsErrorAsync() // Act var queryId = await cmd.ExecuteAsyncInAsyncMode(CancellationToken.None).ConfigureAwait(false); var queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); - while (statusRetryCount < statusMaxRetryCount && QueryStatusExtensions.IsStillRunning(queryStatus)) + while (statusRetryCount < statusMaxRetryCount && conn.IsStillRunning(queryStatus)) { Thread.Sleep(1000); queryStatus = await cmd.GetQueryStatusAsync(queryId, CancellationToken.None).ConfigureAwait(false); @@ -1351,9 +1352,11 @@ public void TestAsyncExecQuery() // Act queryId = cmd.ExecuteInAsyncMode(); + var queryStatus = cmd.GetQueryStatus(queryId); // Assert - Assert.IsTrue(QueryStatusExtensions.IsStillRunning(cmd.GetQueryStatus(queryId))); + Assert.IsTrue(conn.IsStillRunning(queryStatus)); + Assert.IsFalse(conn.IsAnError(queryStatus)); // Act DbDataReader reader = cmd.GetResultsFromQueryId(queryId); @@ -1391,7 +1394,7 @@ public void TestExecuteNormalQueryWhileAsyncExecQueryIsRunning() queryId = cmd.ExecuteInAsyncMode(); // Assert - Assert.IsTrue(QueryStatusExtensions.IsStillRunning(cmd.GetQueryStatus(queryId))); + Assert.IsTrue(connections[0].IsStillRunning(cmd.GetQueryStatus(queryId))); } // Execute a normal query @@ -1444,7 +1447,7 @@ public void TestFailedAsyncExecQueryThrowsError() // Act queryId = cmd.ExecuteInAsyncMode(); - while (statusRetryCount < statusMaxRetryCount && QueryStatusExtensions.IsStillRunning(cmd.GetQueryStatus(queryId))) + while (statusRetryCount < statusMaxRetryCount && conn.IsStillRunning(cmd.GetQueryStatus(queryId))) { Thread.Sleep(1000); statusRetryCount++; diff --git a/Snowflake.Data/Client/SnowflakeDbConnection.cs b/Snowflake.Data/Client/SnowflakeDbConnection.cs index 218e89337..cce4974fc 100755 --- a/Snowflake.Data/Client/SnowflakeDbConnection.cs +++ b/Snowflake.Data/Client/SnowflakeDbConnection.cs @@ -402,6 +402,16 @@ internal void registerConnectionCancellationCallback(CancellationToken externalC } } + public bool IsStillRunning(QueryStatus status) + { + return QueryStatusExtensions.IsStillRunning(status); + } + + public bool IsAnError(QueryStatus status) + { + return QueryStatusExtensions.IsAnError(status); + } + ~SnowflakeDbConnection() { Dispose(false); From 3801171b0248eafe923d2e500eab53a024204f42 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 16:37:12 -0700 Subject: [PATCH 44/47] Remove public constructor --- .../IntegrationTests/SFDbCommandIT.cs | 15 ++++++++++----- Snowflake.Data/Client/SnowflakeDbCommand.cs | 5 ----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index ff155b69d..ce92272db 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -455,11 +455,13 @@ public async Task TestGetResultsOfUnknownQueryIdAsyncWithConfiguredRetry() conn.ConnectionString = ConnectionString; await conn.OpenAsync(CancellationToken.None).ConfigureAwait(false); - using (SnowflakeDbCommand cmd = new SnowflakeDbCommand(conn, new QueryResultsRetryConfig(queryResultsRetryCount, queryResultsRetryPattern))) + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { + QueryResultsAwaiter queryResultsAwaiter = new QueryResultsAwaiter(new QueryResultsRetryConfig(queryResultsRetryCount, queryResultsRetryPattern)); + // Act var thrown = Assert.ThrowsAsync(async () => - await cmd.GetResultsFromQueryIdAsync(unknownQueryId, CancellationToken.None).ConfigureAwait(false)); + await queryResultsAwaiter.RetryUntilQueryResultIsAvailable(conn, unknownQueryId, CancellationToken.None, true).ConfigureAwait(false)); // Assert Assert.IsTrue(thrown.Message.Contains($"Max retry for no data is reached")); @@ -1604,11 +1606,14 @@ public void TestGetResultsOfUnknownQueryIdWithConfiguredRetry() { conn.ConnectionString = ConnectionString; conn.Open(); - - using (SnowflakeDbCommand cmd = new SnowflakeDbCommand(conn, new QueryResultsRetryConfig(queryResultsRetryCount, queryResultsRetryPattern))) + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { + // Arrange + QueryResultsAwaiter queryResultsAwaiter = new QueryResultsAwaiter(new QueryResultsRetryConfig(queryResultsRetryCount, queryResultsRetryPattern)); + var task = queryResultsAwaiter.RetryUntilQueryResultIsAvailable(conn, unknownQueryId, CancellationToken.None, false); + // Act - var thrown = Assert.Throws(() => cmd.GetResultsFromQueryId(unknownQueryId)); + var thrown = Assert.Throws(() => task.Wait()); // Assert Assert.IsTrue(thrown.InnerException.Message.Contains($"Max retry for no data is reached")); diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index b35db7970..1cf70e464 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -44,11 +44,6 @@ public SnowflakeDbCommand(SnowflakeDbConnection connection, string cmdText) : th this.CommandText = cmdText; } - public SnowflakeDbCommand(SnowflakeDbConnection connection, QueryResultsRetryConfig queryResultsRetryConfig) : this(connection) - { - _queryResultsAwaiter = new QueryResultsAwaiter(queryResultsRetryConfig); - } - public override string CommandText { get; set; From d05c248d86c4dcde704bc4636fc51eb1d3021c50 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 16:39:39 -0700 Subject: [PATCH 45/47] Revert removing newline --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index ce92272db..3fe617703 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -1606,6 +1606,7 @@ public void TestGetResultsOfUnknownQueryIdWithConfiguredRetry() { conn.ConnectionString = ConnectionString; conn.Open(); + using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { // Arrange From 1877755d936611f211fd503eb45babf911824b72 Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 16:46:55 -0700 Subject: [PATCH 46/47] Add missing test comment --- Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs index 3fe617703..a4c84caeb 100755 --- a/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs +++ b/Snowflake.Data.Tests/IntegrationTests/SFDbCommandIT.cs @@ -457,6 +457,7 @@ public async Task TestGetResultsOfUnknownQueryIdAsyncWithConfiguredRetry() using (SnowflakeDbCommand cmd = (SnowflakeDbCommand)conn.CreateCommand()) { + // Arrange QueryResultsAwaiter queryResultsAwaiter = new QueryResultsAwaiter(new QueryResultsRetryConfig(queryResultsRetryCount, queryResultsRetryPattern)); // Act From 91f9ac8cf96695828d92e96251945a5ca08d861e Mon Sep 17 00:00:00 2001 From: sfc-gh-ext-simba-lf Date: Mon, 25 Mar 2024 17:29:24 -0700 Subject: [PATCH 47/47] Mark class internal and use static instance --- Snowflake.Data/Client/SnowflakeDbCommand.cs | 2 +- Snowflake.Data/Core/QueryResultsAwaiter.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Snowflake.Data/Client/SnowflakeDbCommand.cs b/Snowflake.Data/Client/SnowflakeDbCommand.cs index 1cf70e464..ce004df5c 100755 --- a/Snowflake.Data/Client/SnowflakeDbCommand.cs +++ b/Snowflake.Data/Client/SnowflakeDbCommand.cs @@ -24,7 +24,7 @@ public class SnowflakeDbCommand : DbCommand private SFLogger logger = SFLoggerFactory.GetLogger(); - private QueryResultsAwaiter _queryResultsAwaiter = new QueryResultsAwaiter(); + private readonly QueryResultsAwaiter _queryResultsAwaiter = QueryResultsAwaiter.Instance; public SnowflakeDbCommand() { diff --git a/Snowflake.Data/Core/QueryResultsAwaiter.cs b/Snowflake.Data/Core/QueryResultsAwaiter.cs index 5a9bc0744..5ea187fbe 100644 --- a/Snowflake.Data/Core/QueryResultsAwaiter.cs +++ b/Snowflake.Data/Core/QueryResultsAwaiter.cs @@ -11,7 +11,7 @@ namespace Snowflake.Data.Core { - public class QueryResultsRetryConfig + internal class QueryResultsRetryConfig { private const int DefaultAsyncNoDataMaxRetry = 24; @@ -42,6 +42,8 @@ internal class QueryResultsAwaiter private QueryResultsRetryConfig _queryResultsRetryConfig; + internal static readonly QueryResultsAwaiter Instance = new QueryResultsAwaiter(); + internal QueryResultsAwaiter() { _queryResultsRetryConfig = new QueryResultsRetryConfig();