1 //Copyright (c) 2010-2011 Glyn Astill <glyn@8kb.co.uk>
\r
2 //Copyright Notice: GPL
\r
5 using System.Collections.Generic;
\r
9 using System.Threading;
\r
11 using System.Data.SqlClient;
\r
13 using System.Configuration;
\r
14 using System.Diagnostics;
\r
20 public static string sServerDescription = (string)ConfigurationManager.AppSettings["ServerDescription"];
\r
21 public static string sMode = (string)ConfigurationManager.AppSettings["Mode"];
\r
22 public static int iClients = Convert.ToInt32(ConfigurationManager.AppSettings["Clients"]);
\r
23 public static int iClientsScale = Convert.ToInt32(ConfigurationManager.AppSettings["ClientsScale"]);
\r
24 public static int iClientsMax = Convert.ToInt32(ConfigurationManager.AppSettings["ClientsMax"]);
\r
25 public static int iIterations = Convert.ToInt32(ConfigurationManager.AppSettings["Iterations"]);
\r
26 public static string sLogFile = (string)ConfigurationManager.AppSettings["LogFile"];
\r
27 public static string sCsvLogFile = (string)ConfigurationManager.AppSettings["CsvLogFile"];
\r
28 public static int iLogLevel = Convert.ToInt32(ConfigurationManager.AppSettings["LogLevel"]);
\r
29 public static bool bVerboseScreen = Convert.ToBoolean(ConfigurationManager.AppSettings["VerboseScreen"]);
\r
30 public static bool bOnFailureRetry = Convert.ToBoolean(ConfigurationManager.AppSettings["ConnectionRetry"]);
\r
31 public static bool bConnPerIteration = Convert.ToBoolean(ConfigurationManager.AppSettings["ConnectionPerIteration"]);
\r
32 public static string sConn = (string)ConfigurationManager.AppSettings["ConnectionString"];
\r
33 public static string sTransactionsFile = (string)ConfigurationManager.AppSettings["TransactionsFile"];
\r
34 public static int iSleepTime = Convert.ToInt32(ConfigurationManager.AppSettings["SleepTime"]);
\r
35 private static List<string> lsValidModes = new List<string> { "pgsql", "mssql" };
\r
37 public static Object oTransCounterLock = new Object();
\r
38 public static Object oIterationCounterLock = new Object();
\r
39 public static Object oTransDurationLock = new Object();
\r
40 public static Object oLogLock = new Object();
\r
42 public static XmlDocument xmlTransactions = readTransactionsFile(sTransactionsFile);
\r
44 private static PerformanceCounter oCpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
\r
45 private static PerformanceCounter oSysCpuCounter = new PerformanceCounter("Processor", "% Privileged Time", "_Total");
\r
46 private static PerformanceCounter oUseCpuCounter = new PerformanceCounter("Processor", "% User Time", "_Total");
\r
48 public static string timeStamp = "dd/MM/yyyy HH:mm";
\r
49 public static string[] aLogArray = new string[10000000];
\r
50 public static double[] aTransactionTimeArray = new double[iClientsMax * iIterations * xmlTransactions.SelectNodes("//transaction").Count];
\r
52 public static int iLogIndex, iRunningAtMax, iRunningAtMin, iMaxRunning, iCompletedIterations, iCompletedTransactions;
\r
53 public static int iCompletedQueries, iRunningClients, iRunningTransactions, iConnectionTimeouts;
\r
54 public static int iConnectionCeilingHit, iConnectionFailure, iConnectionRetries, iFailure, iTransactionTimeIndex;
\r
55 public static double iMaxTransDuration, iMinTransDuration;
\r
56 public static bool bRunning = false;
\r
58 static void Main(string[] args)
\r
60 if (!lsValidModes.Contains(sMode))
\r
62 Console.WriteLine("Invalid operating mode '{0}': Supported modes are 'mssql' or 'pgsql'", sMode);
\r
63 Environment.Exit(1);
\r
67 MainWorker(iClients);
\r
69 else if (iClientsScale != 0)
\r
71 if (iClientsMax == 0)
\r
73 iClientsMax = iClientsScale;
\r
75 for (int i = iClientsScale; i <= iClientsMax; i= i+iClientsScale)
\r
82 public static void MainWorker(int iClientsRun)
\r
84 iClients = iClientsRun;
\r
86 Array.Clear(aLogArray, 0, iLogIndex);
\r
88 Array.Clear(aTransactionTimeArray, 0, iTransactionTimeIndex);
\r
89 iTransactionTimeIndex = 0;
\r
90 iMaxTransDuration = 0;
\r
91 iMinTransDuration = 100000000000000000;
\r
95 iCompletedIterations = 0;
\r
96 iCompletedTransactions = 0;
\r
97 iCompletedQueries = 0;
\r
98 iRunningClients = 0;
\r
99 iRunningTransactions = 0;
\r
100 iConnectionTimeouts = 0;
\r
101 iConnectionCeilingHit = 0;
\r
102 iConnectionFailure = 0;
\r
103 iConnectionRetries = 0;
\r
109 Info("-------------------- Test Start --------------------", iLogLevel, false);
\r
110 Info("Server Description: " + sServerDescription, iLogLevel, false);
\r
111 Info("Database load tester.", iLogLevel, true);
\r
112 Info("Iterations per client = " + iIterations, iLogLevel, true);
\r
113 Info("launching " + iClients + " clients...", iLogLevel, true);
\r
115 Thread[] workerThreads = new Thread[iClients];
\r
118 Thread infoThread = new Thread(new ThreadStart(InfoScreen));
\r
119 infoThread.Start();
\r
121 for (int i = 0; i < workerThreads.Length; i++)
\r
123 workerThreads[i] = new Thread(new ParameterizedThreadStart(ClientWorker));
\r
124 workerThreads[i].Start(i);
\r
128 for (int i = 0; i < workerThreads.Length; i++)
\r
130 workerThreads[i].Join();
\r
137 Info("-------------------- Test Complete --------------------", iLogLevel, false);
\r
141 public static void InfoScreen()
\r
143 DateTime startTime = DateTime.Now;
\r
144 DateTime currTime = DateTime.Now;
\r
145 TimeSpan duration = currTime - startTime;
\r
147 double iMeanTransactionDuration = 0;
\r
148 double iStandatdDeviation = 0;
\r
155 if (!bVerboseScreen)
\r
159 fCpuAll = fCpuAll + oCpuCounter.NextValue();
\r
160 fCpuSys = fCpuSys + oSysCpuCounter.NextValue();
\r
161 fCpuUse = fCpuUse + oUseCpuCounter.NextValue();
\r
164 currTime = DateTime.Now;
\r
165 duration = currTime - startTime;
\r
167 Console.SetCursorPosition(0, 4);
\r
168 Console.WriteLine("Running Clients {0} ", iRunningClients);
\r
169 Console.SetCursorPosition(0, 5);
\r
170 Console.WriteLine("Running Transactions {0} : {1} max ", iRunningTransactions, iMaxRunning);
\r
172 Console.SetCursorPosition(0, 7);
\r
173 Console.WriteLine("Completed Queries {0} : {1} qps ", iCompletedQueries, (iCompletedQueries / (Convert.ToInt32(duration.TotalSeconds) + 1)));
\r
174 Console.SetCursorPosition(0, 8);
\r
175 Console.WriteLine("Completed Transactions {0} : {1} tps ", iCompletedTransactions, (iCompletedTransactions / (Convert.ToInt32(duration.TotalSeconds) + 1)));
\r
176 Console.SetCursorPosition(0, 9);
\r
177 Console.WriteLine("Completed Iterations {0} / {1} ", iCompletedIterations, (iIterations * iClients));
\r
179 Console.SetCursorPosition(0, 11);
\r
180 Console.WriteLine("Maximum Transaction+Read Time {0} ms : {1} Running ", iMaxTransDuration, iRunningAtMax);
\r
181 Console.SetCursorPosition(0, 12);
\r
182 Console.WriteLine("Minimum Transaction+Read Time {0} ms : {1} Running ", iMinTransDuration, iRunningAtMin);
\r
184 Console.SetCursorPosition(0, 14);
\r
185 Console.WriteLine("Connection Timeout/Ceiling Hit/Failure {0} / {1} / {2} ", iConnectionTimeouts, iConnectionCeilingHit, iConnectionFailure);
\r
186 Console.SetCursorPosition(0, 15);
\r
187 Console.WriteLine("Other Failure {0} ", iFailure);
\r
188 Console.SetCursorPosition(0, 16);
\r
189 Console.WriteLine("Connection Retries {0} ", iConnectionRetries);
\r
190 Console.SetCursorPosition(0, 17);
\r
191 Console.WriteLine("Average Client CPU All/User/Sys {0}% / {1}% / {2}% ", Math.Round((fCpuAll / iSamples), 2), Math.Round((fCpuUse / iSamples), 2), Math.Round((fCpuSys / iSamples), 2));
\r
193 Console.SetCursorPosition(0, 23);
\r
194 Console.WriteLine("{0} s ", (Convert.ToInt32(duration.TotalSeconds) + 1));
\r
203 Console.SetCursorPosition(0, 4);
\r
204 Console.WriteLine("Running Clients {0} ", iRunningClients);
\r
205 Console.SetCursorPosition(0, 5);
\r
206 Console.WriteLine("Running Transactions {0} : {1} max ", iRunningTransactions, iMaxRunning);
\r
208 Console.SetCursorPosition(0, 7);
\r
209 Console.WriteLine("Completed Queries {0} : {1} qps ", iCompletedQueries, (iCompletedQueries / (Convert.ToInt32(duration.TotalSeconds) + 1)));
\r
210 Console.SetCursorPosition(0, 8);
\r
211 Console.WriteLine("Completed Transactions {0} : {1} tps ", iCompletedTransactions, (iCompletedTransactions / (Convert.ToInt32(duration.TotalSeconds) + 1)));
\r
212 Console.SetCursorPosition(0, 9);
\r
213 Console.WriteLine("Completed Iterations {0} / {1} ", iCompletedIterations, (iIterations * iClients));
\r
215 Console.SetCursorPosition(0, 11);
\r
216 Console.WriteLine("Maximum Transaction+Read Time {0} ms : {1} Running ", iMaxTransDuration, iRunningAtMax);
\r
217 Console.SetCursorPosition(0, 12);
\r
218 Console.WriteLine("Minimum Transaction+Read Time {0} ms : {1} Running ", iMinTransDuration, iRunningAtMin);
\r
220 Console.SetCursorPosition(0, 14);
\r
221 Console.WriteLine("Connection Timeout/Ceiling Hit/Failure {0} / {1} / {2} ", iConnectionTimeouts, iConnectionCeilingHit, iConnectionFailure);
\r
222 Console.SetCursorPosition(0, 15);
\r
223 Console.WriteLine("Other Failure {0} ", iFailure);
\r
224 Console.SetCursorPosition(0, 16);
\r
225 Console.WriteLine("Connection Retries {0} ", iConnectionRetries);
\r
226 Console.SetCursorPosition(0, 17);
\r
227 Console.WriteLine("Average Client CPU All/User/Sys {0}% / {1}% / {2}% ", Math.Round((fCpuAll / iSamples),2), Math.Round((fCpuUse / iSamples),2), Math.Round((fCpuSys / iSamples),2));
\r
229 Console.SetCursorPosition(0, 23);
\r
230 Console.WriteLine("{0} s ", (Convert.ToInt32(duration.TotalSeconds) + 1));
\r
232 Info("==========================================================", iLogLevel, bVerboseScreen);
\r
233 Info(" Running Clients " + iRunningClients, iLogLevel, bVerboseScreen);
\r
234 Info(" Running Transactions " + iRunningTransactions, iLogLevel, bVerboseScreen);
\r
235 Info(" Completed Queries " + iCompletedQueries + " : " + (iCompletedQueries / (Convert.ToInt32(duration.TotalSeconds) + 1)) + " qps", iLogLevel, bVerboseScreen);
\r
236 Info(" Completed Transactions " + iCompletedTransactions + " : " + (iCompletedTransactions / (Convert.ToInt32(duration.TotalSeconds) + 1)) + " tps", iLogLevel, bVerboseScreen);
\r
237 Info(" Completed Iterations " + iCompletedIterations + " / " + (iIterations * iClients), iLogLevel, bVerboseScreen);
\r
238 Info(" Maximum Transaction+Read Time " + iMaxTransDuration + " ms", iLogLevel, bVerboseScreen);
\r
239 Info(" Minimum Transaction+Read Time " + iMinTransDuration + " ms", iLogLevel, bVerboseScreen);
\r
240 Info(" Connection Timeout/Ceiling Hit/Failure " + iConnectionTimeouts + " / " + iConnectionCeilingHit + " / " + iConnectionFailure, iLogLevel, bVerboseScreen);
\r
241 Info(" Other Failure " + iFailure, iLogLevel, bVerboseScreen);
\r
242 Info(" Connection Retries " + iConnectionRetries, iLogLevel, bVerboseScreen);
\r
243 Info(" Total Time " + Convert.ToInt32(duration.TotalSeconds) + 1, iLogLevel, bVerboseScreen);
\r
244 Info(" Max Concurrent Transactions " + iMaxRunning, iLogLevel, bVerboseScreen);
\r
245 Info(" Average Client CPU All/User/Sys " + Math.Round((fCpuAll / iSamples), 2) + "% " + Math.Round((fCpuUse / iSamples), 2) + "% " + Math.Round((fCpuSys / iSamples), 2) + "%", iLogLevel, bVerboseScreen);
\r
246 Info("==========================================================", iLogLevel, bVerboseScreen);
\r
248 iMeanTransactionDuration = 0;
\r
249 iStandatdDeviation = 0;
\r
251 for (int i = 0; i < iTransactionTimeIndex; i++)
\r
253 iMeanTransactionDuration = (aTransactionTimeArray[i] + iMeanTransactionDuration);
\r
255 iMeanTransactionDuration = (iMeanTransactionDuration / iTransactionTimeIndex);
\r
257 for (int i = 0; i < iTransactionTimeIndex; i++)
\r
260 iStandatdDeviation = (Math.Pow((aTransactionTimeArray[i] - iMeanTransactionDuration), 2) + iStandatdDeviation);
\r
262 iStandatdDeviation = Math.Sqrt(iStandatdDeviation / iTransactionTimeIndex);
\r
264 CsvLogWriter("\"" + sServerDescription + "\"," + iClients + "," + iIterations + "," + iMaxTransDuration + "," + iMinTransDuration + "," + iRunningAtMax + "," + iRunningAtMin + "," + iMeanTransactionDuration + "," + iStandatdDeviation + "," + (iCompletedTransactions / (Convert.ToInt32(duration.TotalSeconds) + 1)) + "," + (iCompletedQueries / (Convert.ToInt32(duration.TotalSeconds) + 1)) + "," + iMaxRunning + "," + (Convert.ToInt32(duration.TotalSeconds) + 1) + "," + (fCpuAll / iSamples));
\r
268 public static void ClientWorker(object iThread)
\r
271 Info("Starting client thread " + iThread.ToString(), iLogLevel, bVerboseScreen);
\r
273 Random random = new Random();
\r
277 int iRandomStmt = 0;
\r
278 int iRandomTran = 0;
\r
279 SqlConnection msconn = null;
\r
280 NpgsqlConnection conn = null;
\r
281 if (!bConnPerIteration)
\r
283 if (sMode == "pgsql") {
\r
284 conn = new NpgsqlConnection(sConn);
\r
287 else if (sMode == "mssql")
\r
289 msconn = new SqlConnection(sConn);
\r
294 for (int i = 0; i < iIterations; i++)
\r
298 DateTime startTime = DateTime.Now;
\r
299 if (bConnPerIteration)
\r
301 if (sMode == "pgsql")
\r
303 conn = new NpgsqlConnection(sConn);
\r
306 else if (sMode == "mssql")
\r
308 msconn = new SqlConnection(sConn);
\r
314 foreach (XmlNode node in xmlTransactions.SelectNodes("//transaction"))
\r
317 XmlNode randomtransaction = node.Attributes["random"];
\r
318 if (randomtransaction != null && node.Attributes["random"].Value.Trim().ToLower() == "true")
\r
320 iRandomTran = random.Next(0, 2);
\r
323 if (iRandomTran == 0)
\r
325 NpgsqlTransaction tran = null;
\r
326 SqlTransaction mstran = null;
\r
328 if (sMode == "pgsql")
\r
330 tran = conn.BeginTransaction();
\r
332 else if (sMode == "mssql")
\r
334 mstran = msconn.BeginTransaction();
\r
337 DateTime beginTime = DateTime.Now;
\r
339 lock (oTransCounterLock)
\r
341 iRunningTransactions++;
\r
345 foreach (XmlNode subnode in node.SelectNodes("//sql"))
\r
348 XmlNode randomsql = subnode.Attributes["random"];
\r
349 if (randomsql != null && subnode.Attributes["random"].Value.Trim().ToLower() == "true")
\r
351 iRandomStmt = random.Next(0, 2);
\r
354 if (iRandomStmt == 0)
\r
356 sSql = subnode.InnerText;
\r
358 if (sMode == "pgsql")
\r
360 NpgsqlCommand command = new NpgsqlCommand(sSql.Replace("#client_id#", iThread.ToString()), conn, tran);
\r
361 NpgsqlDataReader dr = command.ExecuteReader();
\r
363 iCompletedQueries++;
\r
366 //Just pull the data back into our dataset (but we don't care about it)
\r
371 else if (sMode == "mssql")
\r
373 SqlCommand command = new SqlCommand(sSql.Replace("#client_id#", iThread.ToString()), msconn, mstran);
\r
374 SqlDataReader dr = command.ExecuteReader();
\r
376 iCompletedQueries++;
\r
379 //Just pull the data back into our dataset (but we don't care about it)
\r
387 Info("Client " + iThread.ToString() + " Transaction = " + iTransOn + " Statement = " + iSqlOn + " SQL = \"" + sSql + "\" Running", iLogLevel, bVerboseScreen);
\r
394 Info("Client " + iThread.ToString() + " Transaction = " + iTransOn + " Statement = " + iSqlOn + " Skipping as random statement = true", iLogLevel, bVerboseScreen);
\r
400 if (sMode == "pgsql")
\r
404 else if (sMode == "mssql")
\r
409 DateTime commitTime = DateTime.Now;
\r
410 TimeSpan transactionDuration = commitTime - beginTime;
\r
412 if (iRunningClients == iClients)
\r
415 if (transactionDuration.TotalMilliseconds > iMaxTransDuration)
\r
417 lock (oTransDurationLock)
\r
419 iMaxTransDuration = transactionDuration.TotalMilliseconds;
\r
420 iRunningAtMax = iRunningTransactions;
\r
423 if (transactionDuration.TotalMilliseconds < iMinTransDuration)
\r
425 lock (oTransDurationLock)
\r
427 iMinTransDuration = transactionDuration.TotalMilliseconds;
\r
428 iRunningAtMin = iRunningTransactions;
\r
432 lock (oTransCounterLock)
\r
434 if (iRunningTransactions > iMaxRunning)
\r
436 iMaxRunning = iRunningTransactions;
\r
438 iCompletedTransactions++;
\r
439 aTransactionTimeArray[iTransactionTimeIndex] = transactionDuration.TotalMilliseconds;
\r
440 iTransactionTimeIndex++;
\r
441 iRunningTransactions--;
\r
448 Info("Client " + iThread.ToString() + " Transaction = " + iTransOn + " Skipping as random transaction = true", iLogLevel, bVerboseScreen);
\r
453 lock (oIterationCounterLock)
\r
455 iCompletedIterations++;
\r
458 if (bConnPerIteration)
\r
460 if (sMode == "pgsql")
\r
464 else if (sMode == "mssql")
\r
470 DateTime stopTime = DateTime.Now;
\r
471 TimeSpan duration = stopTime - startTime;
\r
472 Info("Client " + iThread.ToString() + " Iteration " + i.ToString() + " Iteration Time " + duration.TotalMilliseconds + " ms (including application read)", iLogLevel, bVerboseScreen);
\r
475 catch (Exception e)
\r
477 if (String.Compare(e.Message, 0, "A timeout has occured", 0, 21) == 0)
\r
479 iConnectionTimeouts++;
\r
481 else if (String.Compare(e.Message, 0, "Failed to establish a connection", 0, 32) == 0)
\r
483 iConnectionFailure++;
\r
485 else if ((e.Message == "ERROR: 08P01: no more connections allowed") || (e.Message == "FATAL: 53300: connection limit exceeded for non-superusers") || (e.Message == "FATAL: 53300: sorry, too many clients already"))
\r
487 iConnectionCeilingHit++;
\r
493 Info("Client " + iThread.ToString() + " Iteration " + i.ToString() + " FAILURE " + e.Message + " TARGET " + e.TargetSite + " INFO = " + e.ToString(), iLogLevel, bVerboseScreen);
\r
494 if (bOnFailureRetry)
\r
497 iConnectionRetries++;
\r
502 if (iSleepTime > 0)
\r
504 Thread.Sleep(iSleepTime);
\r
507 if (!bConnPerIteration)
\r
513 public static void Info(string sInfoText, int iInfoLevel, bool bScreen)
\r
515 if (iInfoLevel >= 1)
\r
517 AddLog(sInfoText);
\r
521 Console.WriteLine(sInfoText);
\r
525 public static void AddLog(string sLogText)
\r
527 DateTime currTime = DateTime.Now;
\r
529 if (sLogText.Length != 0)
\r
533 aLogArray[iLogIndex] = currTime.ToString(timeStamp) + " : " + sLogText;
\r
539 public static void LogWriter()
\r
541 if ((sLogFile != "") && (iLogLevel >=1))
\r
543 FileStream fs = new FileStream(sLogFile, FileMode.OpenOrCreate, FileAccess.Write);
\r
544 StreamWriter m_streamWriter = new StreamWriter(fs);
\r
545 m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
\r
547 for (int i = 0; i < iLogIndex; i++)
\r
549 m_streamWriter.WriteLine(aLogArray[i]);
\r
552 m_streamWriter.Flush();
\r
553 m_streamWriter.Close();
\r
557 public static void CsvLogWriter(string sLogText)
\r
559 bool bWriteHeader = false;
\r
561 if (sCsvLogFile != "")
\r
563 if (!File.Exists(sCsvLogFile))
\r
565 bWriteHeader = true;
\r
568 FileStream fs = new FileStream(sCsvLogFile, FileMode.OpenOrCreate, FileAccess.Write);
\r
569 StreamWriter m_streamWriter = new StreamWriter(fs);
\r
570 m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
\r
574 m_streamWriter.WriteLine("Server name, Clients, Iterations/client, Max transaction duration (ms), Min transaction duration (ms), Concurrent transactions at max duration, Concurrent transactions at min duration, Mean transaction duration (ms), Transaction duration standard deviation (ms), tps, qps, Max concurrent transactions, Total time (s), Avg CPU Time %");
\r
576 m_streamWriter.WriteLine(sLogText);
\r
578 m_streamWriter.Flush();
\r
579 m_streamWriter.Close();
\r
583 public static XmlDocument readTransactionsFile(string sTransactionsFile)
\r
585 DataSet ds = new DataSet();
\r
586 XmlDocument transfile = new XmlDocument();
\r
589 transfile.Load(sTransactionsFile);
\r
591 catch (Exception e)
\r
593 Info("Error reading transaction file " + e.ToString(), iLogLevel, bVerboseScreen);
\r