Technical Article

Execute SQL Scripts in paralel for real performance tests

,

Ever tried to simulate a production enviromnent in a development one but realized that you couln't easily start 500 executions simultaneously ?

 

The Problem:

In a development environment we cetainly dont have the tools to simulate a production environment, that's why some companies prepare a pré-production ( or staging ) machine just for a "as close as possible" test environment.

One hard task is to simulate real paralellism between executions of the same script, checking how SQL server will handle transaction isolation levels, locks ( deadlocks ), performance issues or even hardware bottlenecks.

There are stress tests scripts and tools that can put your CPU at 100%, memory and disk IO at maximum, but this doesn't test how your code will react with multiple execution of your own code.

 

The Solution:

A simple line can provide you with paralelism and data required for your testing environment:

C:\For /L %i in (1,1,100) do start /b osql -Syourserver -Uyouruser -Pyourpass -q"yourSQLscripts" < C:\EXIT

 

I encapsulated it within this procedure:

PROCEDURE sp_multiple_execute(
@executions INT -- times your script will be executed
, @sql VARCHAR(8000) -- the script to be executed
, @server VARCHAR(255) -- connection info: server name of IP address
, @user VARCHAR(255) -- connection info: username
, @pass VARCHAR(255) -- connection info: password
, @debug INT = 0 -- turns extra prints and resultset display
)

 

 

Particularities:

  • I used Shell version of FOR to make it simple for the SO to handle.
  • I used the Start command with /b to generate the desired paralellism but on a single "window".
  • I used the osql command to execute the sql script desired with -S -U -P parameters
  • i had to use the "< C:\EXIT" because for some reason, even when returning the prompt after execution, on some tests the cmd sessions never closed by its own, and i had to kill them manually on the server.
  • I dind't use any 2005/2008 stuff like varchar(MAX) so that this script could be used under sql server 2000, but obviously that the script you're trying to run is what determine the server requirements. This can cause problems with large resultsets because of the limit of 8000 of the #temp tables used, but fell free to change it to MAX to get enough space for your resultset.
  • You will need permission to read/write C:\ or any other directory, change this path on lines 20, 32, 43 to the final destination of the temp file named "EXIT"
  • You will also need permission to run master..xp_cmdshell, check sp_configure options to learn how to enable this.

 

Execution example:

This code starts 100 almost simultaneously executions of a script that, insert its start time at a test table, waits 5 seconds, and update that row with the finishing time.

 

CREATE TABLE master..test ( id INT IDENTITY(1,1), start DATETIME, finish DATETIME )

DECLARE @sql varchar(1024)
SET @sql = ' insert into master..test(start) values ( getdate() )' + char(13)

+ ' waitfor delay ''00:00:05''' + char(13)
+ ' update master..test set finish = getdate() where id = @@identity'

EXEC sp_multiple_execute 100, @sql, 'smonreal', 'sa', '', 0

SELECT * FROM MASTER..TEST ORDER BY 1
DROP TABLE MASTER..TEST

 

Table Master..Test should contain values like this one:
IDStartFinish
12009-09-16 12:12:00.3732009-09-16 12:12:05.420
22009-09-16 12:12:00.3902009-09-16 12:12:05.420
32009-09-16 12:12:00.3902009-09-16 12:12:05.437
42009-09-16 12:12:00.3902009-09-16 12:12:05.437
.........
1002009-09-16 12:12:03.0302009-09-16 12:12:08.047

 

If @debug parameter is set to 0, then only the total duration time is recorded, by total i mean the moment the last of the 100 sessions returned the propmt to the main window.

Otherwise the executed script along with all resultsets and the duration is returned.

 

 

Thanks,

and happy testings.

 

 

 

CREATE PROCEDURE sp_multiple_execute(
@executions INT-- times your script will be executed
, @sql VARCHAR(8000)-- the script to be executed
, @server VARCHAR(255)-- connection info: server name of IP address
, @user VARCHAR(255)-- connection info: username
, @pass VARCHAR(255)-- connection info: password
, @debug INT = 0-- turns extra prints and resultset display
) 
AS BEGIN

-- disable inconvenient resultsets
SET NOCOUNT ON

-- Temp table to get rid ( or not if @debug is set to 1 ) of the resultset
CREATE TABLE #debug_resultset ( resultset varchar(8000) )

-- Pre-requisite file to correctly finish a START CMD session
-- couln't find a way to end it without this dirt trick
INSERT #debug_resultset
EXEC master..xp_cmdshell 'ECHO EXIT > C:\EXIT'

-- Gathers start date
DECLARE @duration DATETIME
SET@duration = GETDATE()

        
-- build the line that does the trick.
-- ex: For /L %i in (1,1,100) do start /b osql -Syourserver -Uyouruser -Pyourpass -q"yourSQLscripts" < C:\EXIT
-- ps: the purpose of the "< C:\EXIT" is to really close the shell session in case your OS cant.
-- pps: the program will return the prompt to you, but could never really close the CMD session on the server.
DECLARE @cmd NVARCHAR(4000)
SELECT @cmd = 'For /L %i in (1,1,' + CONVERT(VARCHAR,@executions) + ') do start /b osql -S'+ ISNULL(@server,'') + ' -U'+ISNULL(@user,'')+' -P'+ISNULL(@pass,'')+' -q"' + ISNULL(@sql,'') + '" < C:\EXIT'

-- Execute the cmd command and save all resultset
INSERT INTO #debug_resultset( resultset )
EXEC master..xp_cmdshell @cmd

-- I prerefed to allways display the duration, but this line could be inside if ( @debug = 1 )
SELECT GETDATE() - @duration as Duration

-- Remove the inconvenient file.
INSERT #debug_resultset
EXEC master..xp_cmdshell 'DEL C:\EXIT'

-- If initial parameter @debug was set to 1, print extra usefull information. 
IF ( @debug = 1 )
BEGIN 
PRINT @cmd
SELECT resultset FROM #debug_resultset
END

-- drop temp tables
DROP TABLE #debug_resultset
END
GO

Rate

4.5 (2)

You rated this post out of 5. Change rating

Share

Share

Rate

4.5 (2)

You rated this post out of 5. Change rating