The best way to detect Oracle connection leak? - c#

A program written in C# Oracle client that proved to have "Connection leak" which it is not closing all database connections and so after some time it can no longer connect to the database as there are too many open connections.
I wrote the following helper function (quite expansive):
private static int tryFindConnCount(){
var connstk = new Stack<Oracle.ManagedDataAccess.Client.OracleConnection>();
try
{
for (var i = 0; i < 10000; ++i)
{
var conn = new Oracle.ManagedDataAccess.Client.OracleConnection(
myDatabaseConnection);
conn.Open();
connstk.Push(conn);
}
}
catch(Exception e)
{
foreach (var conn in connstk)
{
conn.Close();
}
}
return connstk.Count;
}
Here is the code in a test case that uses the above:
var co = tryFindConnCount();
CodeThatMayLeakConnection();
var cn = tryFindConnCount();
Assert.That(cn, Is.EqaulTo(co));
It helped me identify at least one case that have connection leak.
The problem of tryFindConnCount is that it should never be used in production. And I think there should be some way to obtain the same value much cheaper.
How can I do this in the code so I can monitor this value in production?

Trying to find places where connections where not closed is a difficult task.
If you leave the program and forget to close the connection the last sql which was executed is stored in column SQL_ID in v$session (gv$session for RAC). You can search v$session for idle/dead sessions. You can then use v$sql to find the SQL text which may tell you more about what was done last. By this you may get a hint where to search in your code.
select a.sid, a.username, a.program, a.machine, a.sql_id, b.sql_fulltext
from v$session a, v$sql b
where b.sql_id(+) = a.sql_id
and a.username is not null -- filter system processes, maybe filter more stuff
;

You can query Oracle DB on "gv$session" view to get the info that you need. With a query on this view you can cyclically monitor the DB every 10-15 minutes for a count of connections from this program.
Example query below :
select count(*)
from gv$session
where machine = 'XXXXX'
and username = 'YYYYY'
and program = 'ZZZZZ';
You only need values that uniquely identify those connections like for example machine from which the connections originate.
Also the query is very light and doesn't add performance overhead.

Related

Opening and closing database connection for each query

I have written following code.
I have opened the database connection for once for one query
I want to execute another query.
I have written the code below.
But i think there is a mistake
Can anyone help me please?
public void check()
{
try
{
OdbcConnection myOdbcConnection = new OdbcConnection(con1);
OdbcCommand myOdbcCommand = myOdbcConnection.CreateCommand();
String sSQL = "SELECT * FROM(select tdate from tbl_IThelpdesk order by call_no desc)where ROWNUM = 1"; //last record of the call_no column
myOdbcCommand.CommandText = sSQL;
myOdbcConnection.Open();
OdbcDataReader myOdbcDataReader = myOdbcCommand.ExecuteReader();
if (!myOdbcDataReader.Read())
{
txtDate.Text = DateTime.Now.ToShortDateString();
string strcallno = DateTime.Now.Year.ToString("d2") + DateTime.Now.Month.ToString("d2") + DateTime.Now.Day.ToString("d2");
txtcall.Text = "ITHD" + strcallno + "001";
myOdbcConnection.Close();
myOdbcDataReader.Close();
}
else
{
DateTime today = DateTime.Parse(DateTime.Now.ToShortDateString());
if (myOdbcDataReader[0].ToString() == today.ToString())
{
myOdbcConnection.Close();
myOdbcDataReader.Close();
myOdbcConnection.Open();
OdbcCommand myOdbcCommand1 = myOdbcConnection.CreateCommand();
String SQLmax = "SELECT max(call_no) FROM TBL_IThelpdesk";
myOdbcCommand1.CommandText = SQLmax;
OdbcDataReader myOdbcDataReader1 = myOdbcCommand1.ExecuteReader();
while (myOdbcDataReader1.Read() != false)
{
txtcall.Text = myOdbcDataReader1[0].ToString().Trim();
}
myOdbcDataReader1.Close();
myOdbcDataReader.Close();
myOdbcConnection.Close();
}
}
}
catch (Exception e)
{
lblEmpty.Text = e.Message;
lblEmpty.Visible = true;
}
}
Since database connections use a pool, you don't have to maintain the same connection for multiple queries; instead, open a connection when you need it, and close it as soon as possible to free up the resources.
See: http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.80).aspx
See also: C# SQLConnection pooling
Note that you've not used using() { } pattern. Given that OdbcConnection and similar types implement IDisposable, you should embed them into a using in order for them to be disposed without waiting the garbage collector.
See: http://msdn.microsoft.com/en-us/library/yh598w02.aspx
You're hitting the database twice when you only need to hit it once. You're getting the latest date then going to the database again to get the corresponding call_no. This is unsafe as the max(call_no) could change even in the small amount of time between the 2 queries.
//get the latest [call_no] and [tdate]. No need for a 2cd trip with max(call_no)
SELECT * FROM(select call_no, tdate from tbl_IThelpdesk order by call_no desc)where ROWNUM = 1
Also the data access code is mixed with UI code. You should create data access methods that do 1 thing; return the data you want. This will make it much easier to follow the main flow of your algorithm.

Problem with MySQL's LAST_INSERT_ID()

I am having a problem with LAST_INSERT_ID().
Every time I use it I need to open and close a new connection.
This slows down my application a great deal.
i need to do many insert statements end get for each one his inserted id
all on the same connection!
How can I get the last inserted ID on the same connection
for with MySQl, ODBC and C# from a specific table with no concurrency problems?
here is my code:
#region Private Variables
private static DataAccess _DataAccess = null;
private static object _SyncLock = new object();
private OdbcConnection myConnection = null;
#endregion
#region Instance Method
public static DataAccess Instance
{
get
{
lock (_SyncLock)
{
if (_DataAccess == null)
_DataAccess = new DataAccess();
return _DataAccess;
}
}
}
#endregion
#region Constractors
public DataAccess()
{
myConnection = new OdbcConnection(stringConnDB);
myConnection.Open();
}
#endregion
Every time I use it i need to open and close a new connection
No, you don't need a new connection. You are supposed to use it on the same connection just after an insert statement.
From the manual:
The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by [LAST_INSERT_ID] to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client. This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions.
Why do you think that you need to open a new connection? Actually you should not open a new connection, you should use last_insert_id() on the same connection to be sure to get the correct result.
If you have tried it with a new connection and it seems to work, then it's only because the connections are pooled, and you happen to get the same connection the second time. When you start to use this with more than one single user, it will fail.

Can someone help me with some beginners JDBC?

As a C# developer new to Java, i thought it might be easiest if i simply show a bit of C# code so i can see what the equivalent Java JDBC calls are:
String myConnectionString = "...";
String mySql = "select name from people where id = #myId";
int myId = 123456;
List<Field> fields = new List<Field>();
using (SqlConnection conn = new SqlConnection(myConnectionString)
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(mySql,conn))
{
cmd.Parameters.AddWithValue("#myId", myId);
using(SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
String name = rdr.GetString(0);
fields.Add(name);
}
}
}
}
Now i know that the using statements above will safely close the database if anything goes wrong, whereas with java it's a bit more complicated (try..finally or something). And in java, i'm not sure what exactly needs to be disposed or closed (or whatever) - the connection only? Or the statement as well?
If you could give me a leg up, that'd be great. Thanks a lot
edit: I really like the code here: (Java Exception Handling - Style)
Connection conn = MyDatabaseLayer.getConnection();
try {
... use conn ...
}
finally {
conn.close();
}
However, do i need to do any further exception handling to ensure the statement and reader and all that other stuff gets closed? Or is closing the connection enough?
Here's a rough explanation of the steps, as liberally copied from some random page:
first, you load the driver. This will be a class in your driver jar file. In many environments you get this, actually, from a datasource, this is sort of old fashioned, but probably better to see the nuts and bolts.
Class.forName("com.imaginary.sql.msql.MsqlDriver");
then you get a connection:
Connection conn = DriverManager.getConnection("jdbc:msql://www.myserver.com:1114/....", "user1", "password");
The url string tends to be different for different db vendors. Luckily we don't swap databases too often, so you only need to look it up once. Now you can finally use the damned thing.
PreparedStatement ps = conn.prepareStatement("SELECT * FROM FOO WHERE A=?", 1);
A prepared statement gets cached, so you can use it with inseted parameters. It will work with a plain SQL statement, but for that you can just use a statement. You can also just call conn.executeQuery(...) to get a resultSet, which is what you want.
ResultSet rs = ps.executeQuery();
Now you can loop through the rs, and get whatever:
while (rs.next())
{
..
}
ResultSets also have ResultSetmetadata which gives you things like the column names, # of columns (but not the total # of results, which would be too easy).
As for try catch, you need to close your statement/result set after you use them. Every time. Otherwise bad things will happen. Like leaving open resources on your db. Since your db connect method can throw errors, you rap the whole thing in a try catch, and close your statement (and connection, if you've made it here) in a finally block.
This is why people use ORM frameworks in java.
You indeed need a try-finally block here. The Java equivalent of the using keyword will be introduced in the upcoming Java 7. A Java port of your code would look like:
// Prepare.
String url = "...";
String sql = "SELECT name FROM people WHERE id = ?";
int id = 123456;
List<String> names = new ArrayList<String>();
// Declare before try.
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// Acquire inside try.
connection = DriverManager.getConnection(url);
statement = connection.prepareStatement(sql);
statement.setInt(1, id);
resultSet = statement.executeQuery();
// Process results.
while (resultSet.next()) {
names.add(resultSet.getString("name"));
}
} finally {
// Close in reversed order in finally.
if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {}
if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
}
When not using connection pooling, closing the Connection alone would in most cases also close the Statement and ResultSet. Although not strictly specified in the JDBC API, the average JDBC driver would implicitly do that. But this is not the normal JDBC idiom. You should really close all the resources explicitly. This makes your code safely reusable for the case that you'd like to introduce connection pooling.
See also:
Equivalent of "using" keyword in Java
JDBC tutorial
JDBC connection pooling practices
JDBC connectivity with MySQL

Querying remote MySql server

I think I have a straight forward question. I'm writing a system that allows users from company A to single sign on to the system and for this I go back to the central database of users at company A and validate the user credentials passed to me.
Currently my implementation involves building up my query using a stringbuilder and then passing the string as command text. My question is; is there a nicer way of doing this. below is my code;
public User LoginSSO(string UserName, Int32 sectorCode)
{
using (OdbcConnection con = new OdbcConnection(ConfigurationManager.ConnectionStrings["ComapnyA"].ConnectionString))
{
con.Open();
StringBuilder sb = new StringBuilder();
sb.AppendLine("Select mx.Id, mx.UserName, mx.firstname,mx.surname,mx.sectorCode,");
sb.AppendLine("mx.deleteFlag, dn.sectorGroupCode, dn.region, dn.district");
sb.AppendLine("from users mx");
sb.AppendLine("Inner Join sector dn on dn.sectorCode = mx.sectorCode");
sb.AppendLine("Where (mx.UserName = '{0}')");
string commandText = string.Format(sb.ToString(), UserName, sectorCode);
using (OdbcCommand comm = new OdbcCommand(commandText, con))
{
using (OdbcDataReader reader = comm.ExecuteReader())
{
if (reader.Read())
{
User user = new User();
user.Id = Convert.ToInt32(reader["Id"]);
user.Username = Convert.ToString(reader["UserName"]);
user.Firstname = Convert.ToString(reader["firstname"]);
user.Surname = Convert.ToString(reader["surname"]);
_dealerGroupCode = Convert.ToString(reader["sectorGroupCode"]);
_region = Convert.ToInt32(reader["region"]);
_district = Convert.ToInt32(reader["district"]);
_dealerCode = dealerCode;
_accessLevel = AccessLevel.Sector;
return user;
}
}
}
}
return null;
}
I don't like the fact that I am building up my sql which is ultimately a static script. Please note that I can't manipulate the remote server in any way or add any stored procedures to it. For the rest of the app I have been using LINQ but I'm assuming that isn't an option.
This is the most low-level way of querying a database with ADO.NET. Open connection, send command, read out results. You should however use parametrized queries instead of String.Format, since that will open up your program to SQL injection. Just consider what would happen if UserName has a ' character in it. The following would be much better:
string sql = #"Select mx.Id, mx.UserName, mx.firstname, mx.surname,
mx.sectorCode, mx.deleteFlag, dn.sectorGroupCode,
dn.region, dn.district
From users mx
Inner Join sector dn on dn.sectorCode = mx.sectorCode
Where (mx.UserName = ?)";
var command = new OleDbCommand(sql);
command.Parameters.AddWithValue(0, UserName);
If you want a higher level interface, look into DataSets/DataAdapters. They aren't as fancy as LINQ, but they'll give you an easy fill/update, and work with any database adapter. If you're using Visual Studio, you even get a visual designer that can generate Typed Datasets in drag-and-drop fashion that'll give you strong-typed accessors for all your data.
You might also want to look into the native MySql connector classes, instead of using ODBC.
You can use ‘sp_addlinkedserver’ system store procedure to link to the remote server server and then fire a query. following is the sample command.:
EXEC sp_addlinkedserver
#server = ‘SourceServer’
, #Srvproduct = ”
, #Provider = ‘SQLNCLI’
, #datasrc = ‘Remote SQL Server instance name’
I suggest you to please refer following link to know about how to run sql query on remote server http://ashishkhandelwal.arkutil.com/sql-server/sql-query-to-the-remote-sql-server/

C# - ExecuteNonQuery() isn't working with SQL Server CE

I got some data inputed by the user that should be added to a Database File (.sdf). I've choose Sql Server CE because this application is quite small, and i didn't saw need to work with a service based database.
Well any way.
Here goes the code:
public class SqlActions
{
string conStr = String.Format("Data Source = " + new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\basedados.sdf");
public SqlCeConnection SQLCEConnect()
{
SqlCeConnection Connection = new SqlCeConnection(conStr);
Connection.Open();
return Connection;
}
public Boolean AdicionarAuditorio(string Nome, int Capacidade)
{
string Query = "INSERT INTO auditorios (nome, capacidade) VALUES (#Nome, #Capacidade)";
using (var SQLCmd = new SqlCeCommand(Query, SQLCEConnect()))
{
SQLCmd.Parameters.AddWithValue("#Nome", Nome);
SQLCmd.Parameters.AddWithValue("#Capacidade", Capacidade);
if (SQLCmd.ExecuteNonQuery() == 1)
{
return true;
} else {
return false;
}
}
}
}
I use the AdicionarAuditorio(string Nome, int Capacidade) function to Insert the data. running ExecuteNonQuery() which is supposed to return the number of affected rows after he as run the query.
So it should return 1 if the query as successful, right?
In the end he returns 1, but if I browser the table data, the data that the query should add isn't there.
So whats wrong here?
NOTE. If your thinking that the
problem is the connection: I can't see
why is the problem once i got some
Select statements that use that
connection function SQLCEConnect()
and they all work pretty well.
Thanks in advance.
Are you sure you are looking at the right file? When you build your app in VS, it copies the SDF file as content to the target folder, so the database in your project will not reflect any updates. Your code is picking up the the file location there.
This is btw not a good practice, because once deployed, the program folders are not writable to your app (could this be the problem - did you already deploy?). Instead, the database file should reside in your appdata folder.
Is it possible that you make the call to AdicionarAuditorio in a TransactionScope without calling transactionScope.Complete()?

Categories

Resources