Last active
May 10, 2021 22:10
-
-
Save cheenamalhotra/fcc840913562c534c5905aaea553e5a1 to your computer and use it in GitHub Desktop.
Reproduces slow performance of ReadAsync when reading a large row.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Data; | |
using Microsoft.Data.SqlClient; | |
using System.Diagnostics; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace ReproApps | |
{ | |
class ReadAsyncTest | |
{ | |
const int TimeoutInSeconds = 5; | |
static string s_connectionString = $"Server=localhost;Initial Catalog=master;Integrated Security=true;"; | |
static readonly string s_tableName = "SOMELARGETABLE"; | |
static readonly string s_tableCreateQuery = $"CREATE TABLE {s_tableName}(c1 nvarchar(max));"; | |
static readonly string s_tableInsertParamQuery = $"INSERT {s_tableName} values (@)"; | |
static readonly string s_tableUpdateParamQuery = $"UPDATE {s_tableName} SET c1 = c1 + @ "; | |
static readonly string s_tableSelectQuery = $"SELECT * FROM {s_tableName};"; | |
static readonly string s_tableDropQuery = $"DROP TABLE IF EXISTS {s_tableName}"; | |
static readonly CancellationTokenSource s_tokenSource = new(); | |
static Stopwatch s_stopwatch; | |
public static void RunTest(string connString = null) | |
{ | |
Console.WriteLine($"Application started. Timeout in seconds: {TimeoutInSeconds}"); | |
s_stopwatch = Stopwatch.StartNew(); | |
if (connString != null) | |
{ | |
s_connectionString = connString; | |
} | |
using SqlConnection con = new(s_connectionString); | |
con.Open(); | |
try | |
{ | |
DropTable(con); | |
CreateTable(con); | |
GenerateAndInsertLargeDataRow(con); | |
s_tokenSource.CancelAfter(TimeoutInSeconds * 1000); | |
Task.WaitAll(ReadLargeFile(), TimeStat()); | |
} | |
catch (AggregateException ex) | |
{ | |
foreach (Exception e in ex.InnerExceptions) | |
{ | |
Console.WriteLine(e); | |
} | |
} | |
finally | |
{ | |
s_tokenSource.Dispose(); | |
DropTable(con); | |
s_stopwatch.Stop(); | |
Console.WriteLine($"Total Elapsed time: --------------- {s_stopwatch.Elapsed}\nApplication ending."); | |
} | |
} | |
static async Task ReadLargeFile() | |
{ | |
using SqlConnection con = new(s_connectionString); | |
await con.OpenAsync(s_tokenSource.Token).ConfigureAwait(false); | |
using SqlCommand command = new(s_tableSelectQuery, con); | |
command.CommandTimeout = TimeoutInSeconds; | |
SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.Default, s_tokenSource.Token).ConfigureAwait(false); | |
DataTable schemaTable = reader.GetSchemaTable(); | |
// while(reader.Read()) // Problem 1: Performance very slow for ReadAsync() when compared to Read() | |
while (await reader.ReadAsync(s_tokenSource.Token).ConfigureAwait(false)) // Problem 2: Cancellation does not stop ReadAsync from continuing to Read. | |
{ | |
Console.WriteLine($"Finished Read iteration. Checking cancellation >"); | |
s_tokenSource.Token.ThrowIfCancellationRequested(); | |
} | |
} | |
static async Task TimeStat(int secondsDelay = 10) | |
{ | |
try | |
{ | |
while (!s_tokenSource.IsCancellationRequested) | |
{ | |
Console.WriteLine($"#TotalSeconds: {s_stopwatch.Elapsed.TotalSeconds}"); | |
await Task.Delay(new TimeSpan(0, 0, secondsDelay), s_tokenSource.Token).ConfigureAwait(false); | |
} | |
} | |
catch (TaskCanceledException) | |
{ | |
Console.WriteLine("Cancellation has been requested. Finishing TimeStat task."); | |
} | |
} | |
private static void CreateTable(SqlConnection con) | |
{ | |
using (SqlCommand command = new(s_tableCreateQuery, con)) | |
{ | |
command.ExecuteNonQuery(); | |
} | |
Console.WriteLine("Table created"); | |
} | |
private static void DropTable(SqlConnection con) | |
{ | |
using (SqlCommand command = new(s_tableDropQuery, con)) | |
{ | |
command.ExecuteNonQuery(); | |
} | |
Console.WriteLine("Table dropped"); | |
} | |
private static void GenerateAndInsertLargeDataRow(SqlConnection con) | |
{ | |
string large = new('a', 512 * 1024); | |
using (var command = new SqlCommand(s_tableInsertParamQuery, con)) | |
{ | |
command.Parameters.AddWithValue("@", large); | |
command.ExecuteNonQuery(); | |
} | |
// Increase this counter to make a larger string. | |
for (int i = 0; i < 15; i++) | |
{ | |
using SqlCommand command = new(s_tableUpdateParamQuery, con); | |
command.Parameters.AddWithValue("@", large); | |
command.ExecuteNonQuery(); | |
} | |
using (SqlCommand command = new(s_tableInsertParamQuery, con)) | |
{ | |
command.Parameters.AddWithValue("@", "next data"); | |
command.ExecuteNonQuery(); | |
} | |
using (SqlCommand command = new(s_tableInsertParamQuery, con)) | |
{ | |
command.Parameters.AddWithValue("@", "next data"); | |
command.ExecuteNonQuery(); | |
} | |
Console.WriteLine("Table inserted with large data"); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment