|

Introduction
As any developer knows, the tuning phase of any data access application development implies the analysis of the database response time, possibly followed by a refinement of some database design aspects (i.e. reviewing some poorly coded stored procedures or the adopted indexing strategies).
This kind of optimization can't be unaware of high load scenarios, so it's a good habit to test the application under "stressed" conditions. For example, an ASP.NET Web Application will be typically tested with stress tools like Microsoft Application Center Test (a.k.a. ACT), that allow you to simulate multiple simultaneous users requesting the web site's ASPX pages.
Under some circumstances, however, these stress test environments are not suitable to point out performance bottlenecks on the database side. With ACT, for example, you can take some measurements about the web server response times, on a page by page basis; but what you are measuring is the web server's response time, that consists of the time spent to process/render the web page plus the time spent in accessing data from the database server. And sometimes it is not simple to understand if the poor performance cause resides in the ASPX page code or in the involved database activity.
So, a new need arises: the need to measure the pure database response time. This is true especially during the development and testing of Web Applications (where you want to measure the database response time, without the noise introduced by the web server processing), but also in many other multi-tier situations, where you want to evaluate the database performance excluding the influences of upper software layers.
Of course, this kind of measurements on the database side can be collected through tools like Microsoft SQL Server Profiler while the web application pages are being hit by the ACT-simulated workload. But, if you collect measures in this way, you need to keep in mind that the database workload comes always from the web server, and this can be sometimes not realistic in case of differences in the web infrastructure between the test environment and the real environment. To make an example: suppose that your production architecture includes a web server farm that you don't have in the test environment; after observing the test environment performance, you can perhaps imagine how your web application will scale on production by the ASP.NET point of view, but it's hard to tell how the database will behave in production, where it will be exposed to the workload generated by multiple web servers.
The tool presented in this article, named DBstressUtil, is a simple database stress utility that tries to address these problems, focusing on the database performance analysis and keeping away any other application layer (data access, business logic, user interface components) that might add noise to the database response time measurement.
Background
When I faced the problem of simulating a workload to stress a SQL Server database, someone suggested me to take a look of the Microsoft SQL Server 2000 Resource Kit. In fact, the Resource Kit contains a VB6 utility named Database Hammer, a tool to test the performance of a SQL Server 2000 instance: it creates its own work tables and then submits tons of INSERT/UPDATE statements on that tables, simulating concurrent users connected to the database. So you can observe if a particular installation of SQL Server performs well.
But my goal was different: I needed to test not a generic instance of SQL Server, but a specific database, with its indexes, stored procedures, triggers and so on. So, Database Hammer was not useful for me, but I liked the basic idea of simulating concurrent database users through a Windows Application. From there to conceive DBstressUtil, the step was short.
How the utility works
DBstressUtil is coded as a Windows Forms VB.NET application and it is designed to work on a SQL Server 2000 database (of course, using the SQL Server .NET Managed Provider classes of the System.Data.SqlClient namespace).
DBstressUtil simulates multiple database users submitting T-SQL scripts (executing stored procedures or simply sending DML commands). These scripts are taken from an input text file (that we'll call "test script" from now on), that has to be prepared in advance, with a simple text editor.
When you run DBstressUtil, on the main form, you can configure some execution options as:
- the number of simultaneous users (a.k.a. "client instances") to be simulated (see
txtInstances),
- the time between the start of the activity of a user and of the subsequent user (see
txtClientShift),
- the interval between each command executed by each single simulated user (see
txtInterval).
When you start the stress test, a number of concurrent threads are created and each of these begins the execution of the T-SQL commands taken from the "test script". Each command execution time (in milliseconds) is measured and recorded on a CSV output file, for an easy post-processing of collected data. During the test, you can monitor the number of currently running simulated client instances (see txtRunningInstances), and the number of the total executed commands (see txtDoneCommands).
A test run has to be considered over when the number of the total executed commands equals the number of client instances multiplied by the number of commands included in the "test script". At this time, you can inspect the CSV log file produced by DBstressUtil; this output file will contain records made up of these fields:
- the starting date and time of the execution of a particular command by a particular client instance;
- the ID (ranging from 1 to
txtInstances value) of the simulated client instance that executed the command;
- the ID (ranging from 1 to the total number of commands included in the "test script") of the executed command;
- the execution time (in milliseconds) related to that particular command submitted by that particular client instance.
And now, something about the code...
Each simulated client instance is an instance of the ClientProc class. The StartClientProcess method of this class is responsible for all the simulation processes of a single client, and it is invoked for each instance of ClientProc on a new thread by the main program, as shown here: Dim i As Integer
Dim x() As ClientProc
...
x(i) = New ClientProc
x(i).ConnectString = ...
x(i).WaitInterval = Convert.ToInt32(txtInterval.Text)
x(i).CommandFile = txtCommandFile.Text
x(i).LogFile = txtLogFile.Text
...
Dim t As New Thread(AddressOf x(i).StartClientProcess)
t.Start()
Steps of the StartClientProcess method are:
- when starting, to increment the "currently running instances" counter;
- to load the "test script" in a string array (see
LoadCommandFile() private method);
- to loop through the commands of the "test script", and, for each of them:
- to execute it via a
SqlCommand.ExecuteNonQuery() method;
- to log its execution time on the CSV output file;
- to increment the "total executed commands" counter;
- to wait the specified pause interval before the next command execution;
- when ending, to decrement the "currently running instances" counter.
All the code accessing resources that are shared among multiple client instances is protected by a Mutex (for example: the output file access, or the update of UI elements of the MainForm).
Finally, keep in mind...
- that the "test script" must contain a valid T-SQL script on each line; so, a single line in the "test script" can be in the form of:
- a single
EXEC stored_procedure @param=value command;
- a single
SELECT / INSERT / UPDATE / DELETE command;
- a more complicated T-SQL script, composed by multiple DML commands.
- that in the "test script" you can include comment lines, that make more readable your scripts and are ignored by DBstressUtil (also in the implicit numbering of the submitted commands); a comment line is a line beginning with "--" (two hypens); all other lines are considered as valid commands (or complete T-SQL scripts) to be submitted towards the SQL Server database;
- that DBstressUtil uses the same connection string for each simulated client instance: so, evaluating your measurements, you need to keep into account the effects of database connection pooling (if that feature is enabled);
- that DBstressUtil currently doesn't manage database errors: so, evaluating your measurements, remember that an extremely fast command might be a command that actually produces a SQL error...
- that DBstressUtil currently doesn't support the "smooth" interruption of a test run: so, be careful when you configure your "test scripts" and your execution parameters, to avoid the need to kill the application because of a too long running test.
- that the commands.txt file included in the Zip archive contains very simple commands that operate on the sample Northwind database: of course, due to those trivial commands and the database size, using the commands.txt file on the Northwind database is NOT the circumstance where you will find DBstressUtil useful! Please, try DBstressUtil on bigger databases, especially if they are full of business logic coded in complex T-SQL code... and enjoy!
| You must Sign In to use this message board. |
|
| | Msgs 1 to 19 of 19 (Total in Forum: 19) (Refresh) | FirstPrevNext |
|
|
 |
|
|
Can this great-sounding tool be run against SQL Server 2005? I am trying to test server configs before rolling out a new server (using copies of existing, real, production databases), but the app doesn't do anything. I have started debugging in VS2005 and it is failing before it ever gets to the SQL script, even with just one client instance set. Exception information is below. Any suggestions - I am not a multi-thread qualified developer, just an SQL guy.
System.InvalidOperationException was unhandled Message="Cross-thread operation not valid: Control 'txtRunningInstances' accessed from a thread other than the thread it was created on." Source="System.Windows.Forms" StackTrace: at System.Windows.Forms.Control.get_Handle() at System.Windows.Forms.Control.set_WindowText(String value) at System.Windows.Forms.TextBoxBase.set_WindowText(String value) at System.Windows.Forms.Control.set_Text(String value) at System.Windows.Forms.TextBoxBase.set_Text(String value) at System.Windows.Forms.TextBox.set_Text(String value) at DBstressUtil.ClientProc.StartClientProcess() in C:\DBstressUtil\ClientProc.vb:line 69 at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
The Exception message you're reporting is about concurrent access to the textbox txtRunningInstances from the various thread the application creates. So, it is not a problem of accessing SQL Server 2005 (the application should be compatible with SQL2005, indeed). I never got this error while running it on Visual Studio 2003 (that is: Microsoft .NET 1.1): maybe the conversion you did to VS2005 introduced some instability in the multithreaded operations. I have to review the code after recompiling it on VS2005 (the application is quite aged... it needs a technology refresh!) Stay tuned... AV
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Here I am. The problem is indeed on the multithreaded access done to some UI elements by the various generated threads. Microsoft .NET 2.0 enhanced some checks in this scenario, and the DBStressUtil stopped to work correctly. In this particular case, being the two textboxes "Running instances" and "Done commands" used just to inform about the progress of the work, I'm not worried about inconsistencies on their content or race conditions or other similar problems may occur.
Then, the quickest workaround is to make .NET 2.0 behave like .NET 1.1, letting the code do what he wants to do.
In order to obtain this, add the following line at the very beginning of the cmdStart_Click procedure:
TextBox.CheckForIllegalCrossThreadCalls = False
It's *not* an elegant solution, but -as stated- just a workaround to make the code work.
For a better and elegant solution, see: http://msdn2.microsoft.com/en-us/library/ms171728.aspx[^]
Regards, AV
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Language : VB.NET Database : MSSQLserver2000 i have developed a tool to compare two databases, but it is taking long time to compare. First it will take the whole table into main memory(order by primary key). Same procedure for table2 of second database. Then it will start comparison by taking row by row. i tested with one third party tool, which is taking only 10 sec to compare 80000 records of a table. My tool is taking 1 hour,40 mins to do that. Is there any other optimized method?
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
Well, in the past I wrote something similar (if interested, email me: we can compare our code). I think this approach is very resource-intensive for the client: I figured out you're loading all data in two DataTables. So, you have to be sure you're not running out of resources. For example, if you use a DataSet too big, the physical RAM of your client could be not enough, causing an intensive use of virtual memory swap file, with dramatic effects on the performance of your application. Then, be sure the code you've written for the data comparison is completely optimized: avoid excessive casting of variables (a single cast operation could slow down your application, when appied in huge loops), use indexed DataTables, and so on. Anyway, this is not the correct place where to discuss this kind of things. Please, contact me via email to have further discussions. Regards, AV
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I can't find the log file either. I put it in root of C: still can't find it. I'm also not sure if it's running as I see no instances.
Thanks for any help
PhilC
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
Hi. Both the "CommandFile" and the "LogFile" strings you typed in the corresponding textboxes are used "as they are" by DBstressUtil. Then, if you simply specify "MyFile.txt" it is searched in the "current" Windows folder; but "current" folder could not be equal to the application folder. So, as a tip, it's better specifying a full-pathed filename like "C:\MyFolder\MyFile.txt" in order to be sure DBstressUtil will find it. If you don't see any instance running, verify the correctness of the command file and of the parameters you supplied on the form. The, as an "extrema ratio", try to run the application from Visual Studio 2003, executing source code step-by-step.
AV
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Ok, Thanks, I'll try your suggestions. One more question, if there are no instances running, the log file won't be written ?
Thanks for your help.
PhilC
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Well... let me say your question shows you're quite new to "tuning" issues. The context is quite complex... It's not possible to say in advance that a particular query performs well if it takes less than "x" time: everything depends on lots of factors. Of course, only you can decide if your SQL Server database is performing well or not. Performance tuning is not an "already known formula", it's instead like an "art". You need to understand your performance target for your specific needs and workload; you need to define a performance baseline in order to detect performance degradations; you need to inspect lots of DB server aspects in order to find performance issues and solve them. Lots of tools exist to help in monitoring and tuning a SQL Server database. DBstressUtil is just another one. And you have to use it jointly with other tools (SQL Server Profiler, Windows Performance Monitor, etc.) to achieve your monitoring/tuning goals. AV
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi
I try to run this on a Wn2003 Server but get the following error:
Application has generated an exception that could not be handled.
Process id=0x4720(18208), Thread id=0x47c0(18368)
Any ideas please?
ALI
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Please, be sure that the machine where you're trying to run the tool does have .NET Framework installed.
Then, verify all the information you're supplying are correct: the server name must be in the form "ServerName\InstanceName" (or "ServerIPaddress\InstanceName") if you're connecting to a named instance of SQL Server; as UserID/Password pair you have to use a valid SQL Server login (trusted connection with a Windows user is currently not supported by the tool, unless you change the connection string in the source code).
Finally, for first runs, try to set the client instances number to 1.
AV
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
zashah@gmail.com wrote: Application has generated an exception that could not be handled.
Process id=0x4720(18208), Thread id=0x47c0(18368)
Quoted message might come when you don't have .NET Framework installed on the system on which you are trying to execute the application.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Very nice tool, and many thanks for this VB project.
Based on it I am now toying on the following 'enhancements' (this is my first VB project, so I'm discovering VB aswell) -randomized which T-SQL command is launched -executed command is also logged in csv -added some errorhandling in the clientproc, so time-outs became more visible -check connection before fireing the threads -added a little stopwatch and progress-bar -added a menubar -added open/save file dialogs -added tooltips -did some playing with enable/disable gui components
and a lot on my todo list!
Arno
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Alberto,
I am not able to find the log.csv file. Please advice. What are the .pdb files and how to view them.
Thanks a lot for the excellent tool.
Regards, zia
zia ahmed
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hey Alberto!
Did you ever run into deadlock problems with the multiple threads? I am encountering deadlock and am having trouble getting around the problem. Our commands file calls a stored procedure that accesses records using a randomize function. Sometimes there is collision on a particular record/page when writing the same record. Also, the greater number of threads, the more likely the deadlock, but I can generate deadlock with only 2 threads running. If I change your utility to trap for error 1205 and try the database operation again, how much load is it really generating on the database server, with a process being blocked 5 to 10 seconds per deadlock? (SQL-Server's deadlock detection thread fires off every 5 seconds.) Your help or suggestions would be appreciated. Thanks!
|
| Sign In·View Thread·PermaLink | 1.00/5 (4 votes) |
|
|
|
 |
|
|
Hi Alberto - thanks for this utility, it came in handy for me while checking a web-based app (yes, I had to test just the SQL performance as you suggested, and not the web server response).
I know this is a simple utility and doesn't come with "bells and whistles" by design. If I can suggest two improvements: allow integrated security by leaving the username/password blank (and append "Integrated Security=True" to the connection string), and perhaps use the common dialogs to allow a user to select command and output files?
Thanks again for this small app you have written.
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
great tool... but i havent used it yet.. just downloaded it just now it would be great if you could write it in C# of course.. 
just an idea anyways... 
==* ThE MoRe yOu KnoW, tHe mOrE yOu dOn'T kNoW *==
|
| Sign In·View Thread·PermaLink | 2.50/5 (3 votes) |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|