/// <summary> /// Returns a random existing Primary Key value /// </summary> private int GetRandomPK(Random rnd, TableMetadata table) { using (DataStressConnection conn = CreateConnection()) { conn.Open(); // This technique to get a random row comes from http://www.4guysfromrolla.com/webtech/042606-1.shtml // When you set rowcount and then select into a scalar value, then the query is optimised so that // just the last value is selected. So if n = ROWCOUNT then the query returns the n'th row. int rowNumber = rnd.Next(Depth); DbCommand com = conn.CreateCommand(); string cmdText = string.Format( @"SET ROWCOUNT {0}; DECLARE @PK INT; SELECT @PK = PrimaryKey FROM {1} WITH(NOLOCK) SELECT @PK", rowNumber, table.TableName); com.CommandText = cmdText; object result = com.ExecuteScalarSyncOrAsync(CancellationToken.None, rnd).Result; if (result == DBNull.Value) { throw DataStressErrors.TestError(string.Format("Table {0} returned DBNull for primary key", table.TableName)); } else { int primaryKey = (int)result; return(primaryKey); } } }
/// <summary> /// Returns a random command object /// </summary> public DbCommand GetCommand(Random rnd, TableMetadata table, DataStressConnection conn, bool query, bool isXml = false) { if (query) { return(GetSelectCommand(rnd, table, conn, isXml)); } else { // make sure arguments are correct DataStressErrors.Assert(!isXml, "wrong usage of GetCommand: cannot create command with FOR XML that is not query"); int select = rnd.Next(4); switch (select) { case 0: return(GetUpdateCommand(rnd, table, conn)); case 1: return(GetInsertCommand(rnd, table, conn)); case 2: return(GetDeleteCommand(rnd, table, conn)); default: return(GetSelectCommand(rnd, table, conn)); } } }
public virtual void GlobalTestSetup() { // Preconditions - ensure this setup is only called once DataStressErrors.Assert(string.IsNullOrEmpty(s_scenario), "Scenario was already set"); DataStressErrors.Assert(s_source == null, "Source was already set"); DataStressErrors.Assert(s_factory == null, "Factory was already set"); // Set m_scenario string userProvidedScenario; TestMetrics.Overrides.TryGetValue("scenario", out userProvidedScenario); // Empty means default scenario for the test group s_scenario = (userProvidedScenario ?? string.Empty); s_scenario = s_scenario.ToUpperInvariant(); // Set m_source // Empty means that test group will peek the default data source from the config file based on the scenario string userProvidedSourceName; if (TestMetrics.Overrides.TryGetValue("source", out userProvidedSourceName)) { s_source = DataStressSettings.Instance.GetSourceByName(userProvidedSourceName); } // Set m_factory s_factory = CreateFactory(ref s_scenario, ref s_source); s_factory.InitializeSharedData(s_source); // Postconditions DataStressErrors.Assert(!string.IsNullOrEmpty(s_scenario), "Scenario was not set"); DataStressErrors.Assert(s_source != null, "Source was not set"); DataStressErrors.Assert(s_factory != null, "Factory was not set"); }
private void HandleObjectDisposedException(ObjectDisposedException e, bool async) { // Race condition in DbConnectionFactory.TryGetConnection results in an ObjectDisposedException when calling OpenAsync on a non-pooled connection string methodName = async ? "OpenAsync()" : "Open()"; throw DataStressErrors.ProductError( "Hit ObjectDisposedException in SqlConnection." + methodName, e); }
public void TestCommandTimeout() { Random rnd = RandomInstance; DataStressConnection conn = null; try { // Use a transaction 50% of the time if (rnd.NextBool()) { } // Create a select command conn = Factory.CreateConnection(rnd); if (!OpenConnection(conn)) { return; } DataStressFactory.TableMetadata table = Factory.GetRandomTable(rnd); DbCommand com = Factory.GetSelectCommand(rnd, table, conn); // Setup timeout. We want to see various possibilities of timeout happening before, after, or at the same time as when the result comes in. int delay = rnd.Next(0, 10); // delay is from 0 to 9 seconds inclusive int timeout = rnd.Next(1, 10); // timeout is from 1 to 9 seconds inclusive com.CommandText += string.Format("; WAITFOR DELAY '00:00:0{0}'", delay); com.CommandTimeout = timeout; // Execute command and catch timeout exception try { CommandExecute(rnd, com, true); } catch (DbException e) { if (e is SqlException && ((SqlException)e).Number == 3989) { throw DataStressErrors.ProductError("Timing issue between OnTimeout and ReadAsyncCallback results in SqlClient's packet parsing going out of sync", e); } else if (!e.Message.ToLower().Contains("timeout")) { throw; } } } finally { if (conn != null) { conn.Dispose(); } } }
/// <summary> /// Creates a new connection and initializes it with random connection string generated from the factory's source /// Note: if rnd is null, create a connection with minimal string required to connect to the target database /// </summary> /// <param name="rnd">Randomizes Connection Pool enablement, the application Name to randomize connection pool</param> /// <param name="options"></param> /// <returns></returns> public DataStressConnection CreateConnection(Random rnd = null, ConnectionStringOptions options = ConnectionStringOptions.Default) { // Determine connection options (connection string, identity, etc) string connectionString = CreateBaseConnectionString(rnd, options); bool clearPoolBeforeClose = false; if (rnd != null) { // Connection string and/or identity are randomized // We implement this using the Application Name field in the connection string since this field // should not affect behaviour other than connection pooling, since all connections in a pool // must have the exact same connection string (including Application Name) if (rnd.NextBool(.1)) { // Disable pooling connectionString = connectionString + ";Pooling=false;"; } else if (rnd.NextBool(0.001)) { // Use a unique Application Name to get a new connection from a new pool. We do this in order to // stress the code that creates/deletes pools. connectionString = string.Format("{0}; Pooling=true; Application Name=\"{1}\";", connectionString, GetRandomApplicationName()); // Tell DataStressConnection to call SqlConnection.ClearPool when closing the connection. This ensures // we do not keep a large number of connections in the pool that we will never use again. clearPoolBeforeClose = true; } else { switch (CurrentPoolingStressMode) { case PoolingStressMode.RandomizeConnectionStrings: // Use one of the pre-generated Application Names in order to get a pooled connection with a randomized connection string connectionString = string.Format("{0}; Pooling=true; Application Name=\"{1}\";", connectionString, _applicationNames[rnd.Next(_applicationNames.Count)]); break; default: throw DataStressErrors.UnhandledCaseError(CurrentPoolingStressMode); } } } // All options have been determined, now create DbConnection con = DbFactory.CreateConnection(); con.ConnectionString = connectionString; return(new DataStressConnection(con, clearPoolBeforeClose)); }
/// <summary> /// Kills the given connection using "kill [spid]" if the parameter is nonzero /// </summary> private void KillConnection() { DataStressErrors.Assert(_spid != 0, "Called KillConnection with spid != 0"); using (var killerConn = DataTestGroup.Factory.CreateConnection()) { killerConn.Open(); using (var killerCmd = killerConn.CreateCommand()) { killerCmd.CommandText = "begin try kill " + _spid + " end try begin catch end catch"; killerCmd.ExecuteNonQuery(); } } }
protected virtual object GetRandomData(Random rnd, DbType dbType, int maxLength) { byte[] buffer; switch (dbType) { case DbType.Boolean: return(rnd.Next(2) == 0 ? false : true); case DbType.Byte: return(rnd.Next(byte.MinValue, byte.MaxValue + 1)); case DbType.Int16: return(rnd.Next(short.MinValue, short.MaxValue + 1)); case DbType.Int32: return(rnd.Next(2) == 0 ? int.MaxValue / rnd.Next(1, 3) : int.MinValue / rnd.Next(1, 3)); case DbType.Int64: return(rnd.Next(2) == 0 ? long.MaxValue / rnd.Next(1, 3) : long.MinValue / rnd.Next(1, 3)); case DbType.Single: return(rnd.NextDouble() * (rnd.Next(2) == 0 ? float.MaxValue : float.MinValue)); case DbType.Double: return(rnd.NextDouble() * (rnd.Next(2) == 0 ? double.MaxValue : double.MinValue)); case DbType.Decimal: return(rnd.Next(short.MinValue, short.MaxValue + 1)); case DbType.DateTime: case DbType.DateTime2: return(DateTime.Now); case DbType.Date: return(DateTime.Now.Date); case DbType.Time: return(DateTime.Now.TimeOfDay.ToString("c")); case DbType.DateTimeOffset: return(DateTimeOffset.Now); case DbType.Guid: buffer = new byte[16]; rnd.NextBytes(buffer); return(new Guid(buffer)); case DbType.Object: case DbType.Binary: rnd.NextBytes(buffer = new byte[rnd.Next(1, maxLength)]); return(buffer); case DbType.String: case DbType.Xml: string openTag = "<Data>"; string closeTag = "</Data>"; int tagLength = openTag.Length + closeTag.Length; if (tagLength > maxLength) { // Case (1): tagLength > maxTargetLength return(""); } else { StringBuilder builder = new StringBuilder(maxLength); builder.Append(openTag); // The data is just a repeat of one character because to the managed provider // it is only really the length that matters, not the content of the data char characterToUse = (char)rnd.Next((int)'@', (int)'~'); // Choosing random characters in this range to avoid special // xml chars like '<' or '&' int numRepeats = rnd.Next(0, maxLength - tagLength); // Case (2): tagLength == maxTargetLength // Case (3): tagLength < maxTargetLength <-- most common builder.Append(characterToUse, numRepeats); builder.Append(closeTag); DataStressErrors.Assert(builder.Length <= maxLength, "Incorrect length of randomly generated string"); return(builder.ToString()); } default: throw DataStressErrors.UnhandledCaseError(dbType); } }
private DbType GetDbType(TableColumn column) { switch (column.ColumnName) { case "bit_FLD": return(DbType.Boolean); case "tinyint_FLD": return(DbType.Byte); case "smallint_FLD": return(DbType.Int16); case "int_FLD": return(DbType.Int32); case "PrimaryKey": return(DbType.Int32); case "bigint_FLD": return(DbType.Int64); case "real_FLD": return(DbType.Single); case "float_FLD": return(DbType.Double); case "smallmoney_FLD": return(DbType.Decimal); case "money_FLD": return(DbType.Decimal); case "decimal_FLD": return(DbType.Decimal); case "numeric_FLD": return(DbType.Decimal); case "datetime_FLD": return(DbType.DateTime); case "smalldatetime_FLD": return(DbType.DateTime); case "datetime2_FLD": return(DbType.DateTime2); case "timestamp_FLD": return(DbType.Binary); case "date_FLD": return(DbType.Date); case "time_FLD": return(DbType.Time); case "datetimeoffset_FLD": return(DbType.DateTimeOffset); case "uniqueidentifier_FLD": return(DbType.Guid); case "sql_variant_FLD": return(DbType.Object); case "image_FLD": return(DbType.Binary); case "varbinary_FLD": return(DbType.Binary); case "binary_FLD": return(DbType.Binary); case "char_FLD": return(DbType.String); case "varchar_FLD": return(DbType.String); case "text_FLD": return(DbType.String); case "ntext_FLD": return(DbType.String); case "nvarchar_FLD": return(DbType.String); case "nchar_FLD": return(DbType.String); case "nvarcharmax_FLD": return(DbType.String); case "varbinarymax_FLD": return(DbType.Binary); case "varcharmax_FLD": return(DbType.String); case "xml_FLD": return(DbType.Xml); default: throw DataStressErrors.UnhandledCaseError(column.ColumnName); } }
protected DataStressFactory(DbProviderFactory factory) { DataStressErrors.Assert(factory != null, "Argument to DataStressFactory constructor is null"); this.DbFactory = factory; }