Sloq SQL execution (calling store procedures) on huge site - c#

First of all, I realize that my question MAY be broad, please bear with me as I've been thinking on how to form it for a month and I still am not 100% sure how to express my issues.
I'm currently developing a website, that will be used by many thousands of users daily. The bottle neck is the communication with the Data Base.
Each and every conversation with the tables is done through stored procedures, whose calls look like this:
public void storedProcedure(int id, out DataSet ds)
{
ds = new DataSet("resource");
SqlDataReader objReader = null;
SqlCommand cmd = new SqlCommand("storedProcedure", DbConn.objConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#id", id));
openConnection(cmd);
SqlDataAdapter objDataAdapter = new SqlDataAdapter();
objDataAdapter.SelectCommand = cmd;
objDataAdapter.Fill(ds);
cmd.Connection.Close();
}
Or
public void anotherStoredProcedure(int var1, int var2, int var3, int var4, string var5, out DataSet ds)
{
ds = new DataSet("ai");
SqlCommand cmd = new SqlCommand("anotherStoredProcedure", DbConn.objConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#var1", var1));
cmd.Parameters.Add(new SqlParameter("#var2", var2));
cmd.Parameters.Add(new SqlParameter("#var3", var3));
cmd.Parameters.Add(new SqlParameter("#var4", var4));
cmd.Parameters.Add(new SqlParameter("#var5", var5));
openConnection(cmd);
SqlDataAdapter objDataAdapter = new SqlDataAdapter();
objDataAdapter.SelectCommand = cmd;
objDataAdapter.Fill(ds);
cmd.Connection.Close();
}
My objConn is defined as following:
public static string DatabaseConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["objConnLocal"].ConnectionString;
public static SqlConnection objConn = new SqlConnection(DatabaseConnectionString);
And ofcourse in web.config I have
<add name="objConnLocal" connectionString="Initial Catalog=t1;Data Source=1.2.3.4;Uid=id;pwd=pwd;Min Pool Size=20;Max Pool Size=200;" providerName="SQLOLEDB.1"/>
Now the issue is: On every page_load there a few sp calls (above), and when the user starts navigating through the page, more calls are made.
At the moment only the developing and testing team are on the site and at times the speed is really slow. Frequently it would keep loading till it times out (err 504).
Another problem (only ever now and then, but certainly frequent enough to be noticeable) on first user login is it would keep trying to run a call but the connection would claim to be opened, even though it shouldn't be. A fairly not-working work-around is
private void openConnection(SqlCommand cmd){
if (cmd.Connection.State != ConnectionState.Closed)
{
cmd.Connection.Close();
}
if (cmd.Connection.State == ConnectionState.Open)
{
cmd.Connection.Close();
}
try
{
cmd.Connection.Open();
}
catch (Exception ex)
{
System.Threading.Thread.Sleep(1000);
HttpContext.Current.Response.Redirect("/");
}
}
Which makes connecting slow but at least doesn't show the YSOD.
So, what am I doing wrong on my SQL calls so that it is so slow for only 5-10 users? What I have so far:
I've read on Stack Overflow that using "using" is quite nice, but am not entirely sure why and how come as it was a single line comment under an answer. Another idea for improvement was to use several connection strings and not only one.
Resolved:
Changing the wait the connection is established in the connection string from username/pwd to Integrated Security resolved the issue. IF anyone's having similar issue refer to http://www.codeproject.com/Articles/17768/ADO-NET-Connection-Pooling-at-a-Glance

You're right - it's a broad question!
For context - many "thousands of users daily" isn't huge from a performance point of view. A well-built ASP.Net application can typically support hundreds of concurrent users on a decently specified developer laptop; assuming 10K users per day, you probably only have a few dozen concurrent users at peak times (of course this depends entirely on the application domain).
The first thing to do is to use a profiler on your running code to see where the performance bottleneck is. This is available in VS, and there are several 3rd party solutions (I like RedGate and JetBrains).
The profiler will tell you where your code is slow - it should be pretty obvious if it's taking seconds for pages to render.
At first glance, it looks like you have a problem with the database. So you can also use the SQLServer activity monitor to look at long-running queries.

Now the issue is: On every page_load there a few sp calls (above), and
when the user starts navigating trough the page, more calls are made.
This sounds like you've written webpages which won't display anything until the Stored Procedure calls have completed. This is never a good idea.
Shift these SP calls into a background thread, so the user at least sees something when they go onto the webpage (like a "Please wait" message). This can also help prevent timeout messages.
One other thing: you don't say why your SPs take so long to run.
If you're dealing with lots of records, its worth running a SQL script (described on the link below) to check for missing SQL Server indexes.
Finding missing indexes
This script shows the missing indexes which have made the most impact to your users, also tells you the syntax of the CREATE INDEX command you'd need to run to add those indexes.

Related

How do I raise error if the execution of an SQL Query is not completed in specified time in C#?

I'm trying to raise a timeout error in my SQL Server query using c#. I thought a timer would work; however I'm new to c# and have been having trouble figuring it out. I've searched through several questions on this site, and on other sites trying to find out how to raise a timeout error of my query.
Here is my code:
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = query;
cmd.CommandTimeout = timeOut;
cmd.Connection = DBAction.GetSqlConnection(projectId);
SqlDataReader reader = cmd.ExecuteReader();
string[] colname = Enumerable.Range(0, reader.FieldCount).Select(x => reader.GetName(x)).ToArray();
while (reader.Read())
{
//do something
}
}
I'm not sure on how to add a timer to this that returns an error when the execution of the query takes longer than the specified time, or even where to put it. I've tried finding other posts about it but none of the ones I've found are similar to this; I could also just be really bad at my searches. Any kind of help would be appreciated, and if you need any more information let me know. I renamed a few things for security, but this is how the code is otherwise. To note: I'm using this for testing purposes, and not for production, so only a few people will ever actually see this but it's necessary.

How to work progressbar with values?

My concept is For example here[COU] =45,then the progressbar load with that count and want to show completed successfully.
But am not getting the progress simultaneosly work with count here and does not show message also,please help me to do,my code is shown below
public void proget()
{
System.Data.DataRowView selectedFile =
(System.Data.DataRowView)dataGrid1.SelectedItems[i];
string iid = Convert.ToString(selectedFile.Row.ItemArray[10]);
SqlConnection con = new SqlConnection("Data Source=TNT3FMR\\SQLEXPRESS;Initial Catalog=xxx;Integrated Security=True");
string query = "SELECT [COU] FROM _Count_Series WHERE STUID='"+iid+"'";
SqlCommand cmd = new SqlCommand(query, con);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
progressBar1.Value++;
}
MessageBox.Show("Completed Successfully");
}
i'm not sure your design is right. best practices say you should close the db connection as soon as possible in a connected scenario. this means reading the data first and disposing of the reader before processing it. what you're doing is a little strange to me.
a problem i noticed in your code is that you build the query dynamically by concatenating strings. this opens you up to SQL injection attacks. you should use parametrized queries instead.
another problem is that you are not using the 'using pattern'. what happens if you get an error while connecting? how are the connection resources released?
from my personal experience, when i access a database i do it in an async manner and set the progressBar to an indeterminate state because i don't know how much it will take to read the data. your code is synchronous. this might be part of the reason why your UI doesn't update correctly.
hope this helps

.NET SQL connection code is blocking all other connections

Apologies if the subject does not accurately reflect my exact issues, I'm struggling to explain the issue I'm having, although it seems quite straight-forward.
I've built a simple "db helper" class which executes sql statements for me, given some parameters, etc. Here's the code block:
public DataSet selectSprocData(string sprocName, SqlParameter[] parameterArray, out int returnValue)
{
//processes the specified Select stored procedure based on parameter array provided;
//this is the only place anywhere in the application we will do a simple SELECT using a sproc.
DataSet dataset = new DataSet();
using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["MyServer"].ToString()))
{
cn.Open();
SqlDataAdapter adapter = new SqlDataAdapter(sprocName, cn);
adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
adapter.SelectCommand.Parameters.AddRange(parameterArray);
SqlParameter retValParam = adapter.SelectCommand.Parameters.Add("#RETURN_VALUE", SqlDbType.Int);
retValParam.Direction = ParameterDirection.ReturnValue;
adapter.SelectCommand.CommandTimeout = 600;
adapter.Fill(dataset);
returnValue = (int)retValParam.Value;
adapter.Dispose();
cn.Close();
}
return dataset;
}
When I take a long-running sproc and execute it within SSMS it will run and eventually time out. Meanwhile I can open another query window in SSMS and execute any other select or queries against my db.
Now, when I call this sproc through my web-app using the code-block above it, the page will spin and load and load until eventually (a few minutes later) the process will time out.
However, during this web-based call I can NOT open any other window and execute any other UI functions that use the same db-code to call other sprocs.
Essentially, one user executing a sproc/function from the UI which takes long seems to be blocking everyone else from doing anything on my app.
I understand that first and foremost I need to have better queries that don't time out, but is there something I'm missing or not doing right in .net/c# that would be causing all other connections or command attempts to be blocked until the other one has finished or timed out?
My web.config connectionstring has no special parameters, simply:
Persist Security Info=False;User ID=sa;Password=xxxx;Initial Catalog=db_live;Data Source=my.host.com"
Any help would be greatly appreciated
You can't really compare a Windows Application (SQL Management Studio) to an ASP.NET web app (running in IIS)
ALl of your ASP.NET code is running on a single thread, so that thread has to wait for the database code to complete, which blocks all everything else, until it's done.
Use an ASP.NET Update panel to perform your long running stored procedure and UI binding.
ASP.NET Update Panel

SQL CPU peaks with SqlDataAdapter.Fill Command

I've noticed some peculiar behaviour getting SQL data into a c# application. I used a dataset xsd but this began to time out. I then changed my approach as I already had a class which would generate and return a datatable for other operations I tried that. This too timed out.
I had opened the Activity Monitor in SSMS to get an idea of what was happening in SQL when the code ran and noticed that which ever way I run it the fill command casuses the SQL Sever to peak at 100% CPU and stay there until the command is cancelled. This is a good server with plenty of oomph 240Ghz processors and 30GB RAM . The query is not exactly zippy but returns 100k rows in under 3 seconds in SSMS.
Here is my code for the dataset:
public DataTable UKDataRefresh ()
{
UKREFRESH.UKNewContactsDataTable dt =
new UKREFRESH.UKNewContactsDataTable();
UKREFRESHTableAdapters.UKNewContactsTableAdapter ta =
new UKREFRESHTableAdapters.UKNewContactsTableAdapter();
ta.Connection.ConnectionString += ";Password = xxxxxx;";
try
{
ta.Fill(dt, global::SilverPopTransfer.Properties.Settings.Default.UKDATELASTACTION);
}
catch (SqlException )
{
throw;
}
return dt;
}
Here is my code for building it on the fly:
public DataTableOperations (string strconnection, string strSelect,string tablename)
{
SqlConnection c = new SqlConnection(strconnection);
connection = c;
SqlDataAdapter da = new SqlDataAdapter(strSelect, connection);
DataSet ds = new DataSet();
//added this to see what would happen.
da.SelectCommand.CommandTimeout = 0;
connection.Open();
da.Fill(ds, tablename);
connection.Close();
Datatable = ds.Tables[tablename];
_disposed = false;
}
Im looking for clues as to what might cause the problem, not a full solution.
Incidentally I ran a similar function in a pre-existing console application before posting and it connected and ran without error: the query was almost identical.
The sole difference is that for the pre-existing application I use Integrated security but in this case I specify the user and password. I have confirmed the login credentials.
Check index and set nocount on before execution. SSMS performance is not a good performance indicator. It get only partial data and executes asynchronous. Try calling a small subset of data or running the query without to fill the table. It can be the bottleneck.
If your are running the select with parameters, sql server will execute it using sp_execute then server compile it creating CPE peaks.

Database file is inexplicably locked during SQLite commit

I'm performing a large number of INSERTS to a SQLite database. I'm using just one thread. I batch the writes to improve performance and have a bit of security in case of a crash. Basically I cache up a bunch of data in memory and then when I deem appropriate, I loop over all of that data and perform the INSERTS. The code for this is shown below:
public void Commit()
{
using (SQLiteConnection conn = new SQLiteConnection(this.connString))
{
conn.Open();
using (SQLiteTransaction trans = conn.BeginTransaction())
{
using (SQLiteCommand command = conn.CreateCommand())
{
command.CommandText = "INSERT OR IGNORE INTO [MY_TABLE] (col1, col2) VALUES (?,?)";
command.Parameters.Add(this.col1Param);
command.Parameters.Add(this.col2Param);
foreach (Data o in this.dataTemp)
{
this.col1Param.Value = o.Col1Prop;
this. col2Param.Value = o.Col2Prop;
command.ExecuteNonQuery();
}
}
this.TryHandleCommit(trans);
}
conn.Close();
}
}
I now employ the following gimmick to get the thing to eventually work:
private void TryHandleCommit(SQLiteTransaction trans)
{
try
{
trans.Commit();
}
catch (Exception e)
{
Console.WriteLine("Trying again...");
this.TryHandleCommit(trans);
}
}
I create my DB like so:
public DataBase(String path)
{
//build connection string
SQLiteConnectionStringBuilder connString = new SQLiteConnectionStringBuilder();
connString.DataSource = path;
connString.Version = 3;
connString.DefaultTimeout = 5;
connString.JournalMode = SQLiteJournalModeEnum.Persist;
connString.UseUTF16Encoding = true;
using (connection = new SQLiteConnection(connString.ToString()))
{
//check for existence of db
FileInfo f = new FileInfo(path);
if (!f.Exists) //build new blank db
{
SQLiteConnection.CreateFile(path);
connection.Open();
using (SQLiteTransaction trans = connection.BeginTransaction())
{
using (SQLiteCommand command = connection.CreateCommand())
{
command.CommandText = DataBase.CREATE_MATCHES;
command.ExecuteNonQuery();
command.CommandText = DataBase.CREATE_STRING_DATA;
command.ExecuteNonQuery();
//TODO add logging
}
trans.Commit();
}
connection.Close();
}
}
}
I then export the connection string and use it to obtain new connections in different parts of the program.
At seemingly random intervals, though at far too great a rate to ignore or otherwise workaround this problem, I get unhandled SQLiteException: Database file is locked. This occurs when I attempt to commit the transaction. No errors seem to occur prior to then. This does not always happen. Sometimes the whole thing runs without a hitch.
No reads are being performed on these files before the commits finish.
I have the very latest SQLite binary.
I'm compiling for .NET 2.0.
I'm using VS 2008.
The db is a local file.
All of this activity is encapsulated within one thread / process.
Virus protection is off (though I think that was only relevant if you were connecting over a network?).
As per Scotsman's post I have implemented the following changes:
Journal Mode set to Persist
DB files stored in C:\Docs + Settings\ApplicationData via System.Windows.Forms.Application.AppData windows call
No inner exception
Witnessed on two distinct machines (albeit very similar hardware and software)
Have been running Process Monitor - no extraneous processes are attaching themselves to the DB files - the problem is definitely in my code...
Does anyone have any idea whats going on here?
I know I just dropped a whole mess of code, but I've been trying to figure this out for way too long. My thanks to anyone who makes it to the end of this question!
brian
UPDATES:
Thanks for the suggestions so far! I've implemented many of the suggested changes. I feel that we are getting closer to the answer...however...
The code above technically works however it is non-deterministic! It is not guaranteed to do anything aside from spin in neutral forever. In practice it seems to work somewhere between the 1st and 10th iteration. If i batch my commits at a reasonable interval damage will be mitigated but I really do not want to leave things in this state...
More suggestions welcome!
It looks like you failed to link the command with the transaction you've created.
Instead of:
using (SQLiteCommand command = conn.CreateCommand())
You should use:
using (SQLiteCommand command = new SQLiteCommand("<INSERT statement here>", conn, trans))
Or you can set its Transaction property after its construction.
While we are at it - your handling of failures is incorrect:
The command's ExecuteNonQuery method can also fail and you are not really protected. You should change the code to something like:
public void Commit()
{
using (SQLiteConnection conn = new SQLiteConnection(this.connString))
{
conn.Open();
SQLiteTransaction trans = conn.BeginTransaction();
try
{
using (SQLiteCommand command = conn.CreateCommand())
{
command.Transaction = trans; // Now the command is linked to the transaction and don't try to create a new one (which is probably why your database gets locked)
command.CommandText = "INSERT OR IGNORE INTO [MY_TABLE] (col1, col2) VALUES (?,?)";
command.Parameters.Add(this.col1Param);
command.Parameters.Add(this.col2Param);
foreach (Data o in this.dataTemp)
{
this.col1Param.Value = o.Col1Prop;
this. col2Param.Value = o.Col2Prop;
command.ExecuteNonQuery();
}
}
trans.Commit();
}
catch (SQLiteException ex)
{
// You need to rollback in case something wrong happened in command.ExecuteNonQuery() ...
trans.Rollback();
throw;
}
}
}
Another thing is that you don't need to cache anything in memory. You can depend on SQLite journaling mechanism for storing incomplete transaction state.
Run Sysinternals Process Monitor and filter on filename while running your program to rule out if any other process does anything to it and to see what exacly your program is doing to the file. Long shot, but might give a clue.
We had a very similar problem using nested Transactions with the TransactionScope class. We thought all database actions occurred on the same thread...however we were caught out by the Transaction mechanism...more specifically the Ambient transaction.
Basically there was a transaction higher up the chain which, by the magic of ado, the connection automatically enlisted in. The result was that, even though we thought we were writing to the database on a single thread, the write didn't really happen until the topmost transaction was committed. At this 'indeterminate' point the database was written to causing it to be locked outside of our control.
The solution was to ensure that the sqlite database did not directly take part in the ambient transaction by ensuring we used something like:
using(TransactionScope scope = new TransactionScope(TransactionScopeOptions.RequiresNew))
{
...
scope.Complete()
}
Things to watch for:
don't use connections across multiple threads/processes.
I've seen it happen when a virus scanner would detect changes to the file and try to scan it. It would lock the file for a short interval and cause havoc.
I started facing this same problem today: I'm studying asp.net mvc, building my first application completely from scratch. Sometimes, when I'd write to the database, I'd get the same exception, saying the database file was locked.
I found it really strange, since I was completely sure that there was just one connection open at that time (based on process explorer's listing of active file handles).
I've also built the whole data access layer from scratch, using System.Data.SQLite .Net provider, and, when I planned it, I took special care with connections and transactions, in order to ensure no connection or transaction was left hanging around.
The tricky part was that setting a breakpoint on ExecuteNonQuery() command and running the application in debug mode would make the error disappear!
Googling, I found something interesting on this site: http://www.softperfect.com/board/read.php?8,5775. There, someone replied the thread suggesting the author to put the database path on the anti-virus ignore list.
I added the database file to the ignore list of my anti-virus (Microsoft Security Essentials) and it solved my problem. No more database locked errors!
Is your database file on the same machine as the app or is it stored on a server?
You should create a new connection in every thread. I would simplefy the creation of a connection, use everywhere: connection = new SQLiteConnection(connString.ToString());
and use a database file on the same machine as the app and test again.
Why the two different ways of creating a connection?
These guys were having similiar problems (mostly, it appears, with the journaling file being locked, maybe TortoiseSVN interactions ... check the referenced articles).
They came up with a set of recommendations (correct directories, changing journaling types from delete to persist, etc). http://sqlite.phxsoftware.com/forums/p/689/5445.aspx#5445
The journal mode options are discussed here: http://www.sqlite.org/pragma.html . You could try TRUNCATE.
Is there a stack trace during the exception into SQL Lite?
You indicate you "batch my commits at a reasonable interval". What is the interval?
I would always use a Connection, Transaction and Command in a using clause. In your first code listing you did, but your third (creating the tables) you didn't. I suggest you do that too, because (who knows?) maybe the commands that create the table somehow continue to lock the file. Long shot... but worth a shot?
Do you have Google Desktop Search (or another file indexer) running? As previously mentioned, Sysinternals Process Monitor can help you track it down.
Also, what is the filename of the database? From PerformanceTuningWindows:
Be VERY, VERY careful what you name your database, especially the extension
For example, if you give all your databases the extension .sdb (SQLite Database, nice name hey? I thought so when I choose it anyway...) you discover that the SDB extension is already associated with APPFIX PACKAGES.
Now, here is the cute part, APPFIX is an executable/package that Windows XP recognizes, and it will, (emphasis mine) ADD THE DATABASE TO THE SYSTEM RESTORE FUNCTIONALITY
This means, stay with me here, every time you write ANYTHING to the database, the Windows XP system thinks a bloody executable has changed and copies your ENTIRE 800 meg database to the system restore directory....
I recommend something like DB or DAT.
While the lock is reported on the COMMIT, the lock is on the INSERT/UPDATE command. Check for record locks not being released earlier in your code.

Categories

Resources