VB.net Windows service response time - windows

I have a windows service that utilizes an infinite loop and thread.sleep. The sleep time for the thread is 10 seconds.
When the service is initially started AND has work to do (data exist in SQL), it immediately does it's processes. It continues to be responsive and process tasks every 10 seconds as long as it has work to do. If no data is present in SQL for an extended period (i.e. 15-20 minutes) then the service starts responding very slowly. It will eventually pick the data up and process it, but it takes more like 10 minutes instead of 10 seconds.
There's no logic in the code telling the service to sleep. Any ideas?

Without seeing your code, it is impossible to give a definitive answer; It could be the case that you aren't tidying up your database connections quickly enough.
If you've something like:
While( True )
Dim con as new SqlConnection(connectionString)
con.Open()
Dim cmd as New SqlCommand("usp_getJob", con)
cmd.CommandType = CommandType.StoredProcedure
Dim dr as SqlDataReader = cmd.ExecuteReader(CommandBehavior.SingleResult Or CommandBehavior.SingleRow Or CommandBehavior.CloseConnection)
If( dr.Read() )
DoSomething(dr)
Else
Thread.Sleep(10)
End If
End While
...then it will open connections faster than they will go away by themselves.
There are several alternates, and I'd advocate using Using blocks to allow the connection to be returned to the connection pool as fast as possible:
While( True )
Using con as new SqlConnection(connectionString)
con.Open()
Using cmd as New SqlCommand("usp_getJob", con)
cmd.CommandType = CommandType.StoredProcedure
Using dr as SqlDataReader = cmd.ExecuteReader(CommandBehavior.SingleResult Or CommandBehavior.SingleRow Or CommandBehavior.CloseConnection)
If( dr.Read() )
DoSomething(dr)
dr.Close()
Else
Thread.Sleep(10)
End If
End Using
End Using
End Using
End While
These will then call Dispose() on the SqlDataReader, SqlConnection and SqlCommand - either of the first two will cause the connection to be released to the connection pool (given we've asked the SqlDataReader to CloseConnection on its close)

Totally agree with Rowland on this one. Without seeing the code it would hard to determine but most likely culprit is your db code.

Related

VB6 Winsock multiple TCP connections > problems with DoEvents

I made a software couple years ago using VB6 that works as a TCP server, receives multiple connections from clients.
The basic Idea of the software is to listen on a specific port, accept connections from different clients and pass each connection to a separate winsock which analyzes the data, looks in DB, replies with the proper message, and then closes the connection.
Here's some code:
Initializing the sockets when the application starts:
For i = 1 To MaxCon
Load sckAccept(i)
Next i
sckListen.Listen
Accepting connections:
Private Sub sckListen_ConnectionRequest(ByVal requestID As Long)
Dim aFreeSocket As Integer
aFreeSocket = GetFreeSocket
If aFreeSocket = 0 Then
sckAccept(0).Accept requestID
sckAccept(0).SendData "Server is full!"
sckAccept(0).Close
Else
sckAccept(aFreeSocket).Accept requestID
End Sub
Receiving data, analyzing it, and reply:
Private Sub sckAccept_DataArrival(Index As Integer, ByVal bytesTotal As Long)
Dim sData As String
sckAccept(Index).GetData sData
'Do lots of analyizing and search in DB
'
'
sckAccept(Index).SendData "Message"
'
'
DoEvents
sckAccept(Index).Close
End Sub
Everything was working fine, but now the number of connections has increased (couple dozens per second), so the software started getting Out of stack space exception (because of DoEvents).
I know that in many cases DoEvents is evil, but if I remove it, the application UI won't respond (because of the over load on the thread) and some data might not be delivered.
So, my question is: does anyone have an idea of how to get around this problem with/without using DoEvents?
Note: I know that VB6 doesn't really support multi-threading and might be a PITA for such situations. I'm actually planning to upgrade the software and re-create it using .Net, but that will take some time. That's why I need to fix this problem in VB6 since the software is written in VB6 for now.
Well, I managed to figure out the problem, and have solved it.
The short answer
Do NOT use DoEvents.. Some data won't be delivered? Well, close the connection ONLY in the SendComplete event.
The long answer
First thing first:
Why I used DoEvents in the first place? because some of the sent messages were not being delivered. A lot of articles/questions on the internet suggest using DoEvents after Socket.SendData in order to guarantee the data arrival to the receiver.
I digged deeper into this trying to figure out why the messages aren't delivered. I found out that this problem only occurs when closing the connection after sending the message:
Socket.SendData "Message"
'
'
Socket.Close
So, I simply moved the line that closes the connection to the SendComplete event, removed the DoEvents sentence -since I don't need it anymore-, and the problem is gone :)
Private Sub sckAccept_SendComplete(Index As Integer)
sckAccept_Close (Index)
End Sub
I hope this could help someone who has the same problem.

VB6 ADODB.Connection Execute() Retry until successful

I'm trying to fix a little utility which seems to be losing connection to the database after some idle time. I already set the timeout to 0 but that didn't seem to work.
Instead of simply crashing and displaying a couple of error messages I would like to try to re-establish the connection and execute the query until successful (I realize this is probably a bad use of resources) but even then that's what I'm trying to accomplish here. Or if possible display a Message Box saying that connection was lost which will then be closed once connection is established.
Any suggestions would be greatly appreciated.
Public connMain As ADODB.Connection
Public rsMain As ADODB.Recordset
......
Function Picture_Exists() As Boolean
On Error Resume Next
sqlstr = "select * .... "
Set rsMain = connMain.Execute(sqlstr)
Your connection is probably being dropped at the database end due to non-use. It isn't good practice to maintain a connection when it isn't being used; connections are expensive in terms of resources so any such practice wouldn't scale well. Dbadmins aren't likely to leave unused connections open for very long.
Your potential solution says to try to connect and if you can't ignore the error. We don't say "never" in this business very often, but you should never use "On Error Resume Next" without also evaluating Err.Number to see whether it equals 0 (if it does, there's no error). This is called "inline error handling."
In any case, I wouldn't use this method. I would evaluate the Connection object's State Property, and if it's closed (cn.State = adoStateClosed) then I would reopen it.
You can try the following:
On Error Resume ''''instead of On Error Resume Next
Dim rsMain As New ADODB.Recordset
sqlstr = "select * .... "
If rsMain.State = adStateOpen Then rsMain.Close
rsMain.Open sqlstr, sProvider, adOpenKeyset, adLockOptimistic

In VS2010, is there a way to know which application pool a given w3wp.exe is serving, to then decide to attach the debugger to?

So I'm debugging some websites (one from trunk, one from branch) running locally, in separate apppools. I have trunk and branch solutions open in two VS instances. I'd like to debug trunk in one, and branch in the other. I'd like to know if there's a way to know which application pool each w3wp.exe is serving, to know which one is which when attaching the debugger.
Update: the point of this is to write a macro within VS to then let me have a button (per app-pool that is interesting) which will be clickable to attach the debugger to. So solutions should preferably not involve other programs.
Update2: this is on Windows 7 against IIS7.
Below is my core macro. Write a few one-line subs calling it, like AttachToW3wp("DefaultAppPool") naming each app pool you’re interested in, and make buttons and hotkeys for them.
Private Sub AttachToW3wp(ByVal appPoolName As String)
Const processName As String = "w3wp.exe"
Dim userName As String = String.Format("IIS APPPOOL\{0}", appPoolName)
Try
Dim debugger As EnvDTE90.Debugger3 = CType(DTE.Debugger, EnvDTE90.Debugger3)
'debugger.DetachAll()
Dim transport As EnvDTE80.Transport = debugger.Transports.Item("Default")
Dim qualifier As String = Environment.MachineName '= My.Computer.Name
Dim engines(3) As EnvDTE80.Engine
engines(0) = transport.Engines.Item("Managed")
engines(1) = transport.Engines.Item("Script")
engines(2) = transport.Engines.Item("T-SQL")
Dim successMessage As String = String.Empty
For Each process As EnvDTE80.Process2 In debugger.GetProcesses(transport, qualifier)
With process
Dim fi As New System.IO.FileInfo(.Name)
If fi.Name = processName AndAlso (String.Compare(.UserName, 0, userName, 0, Len(userName), True) = 0) Then
If .IsBeingDebugged Then Throw New Exception(String.Format("{0} {1} is already attached to a debugger.", processName, userName))
process.Attach2(engines)
successMessage = String.Format("Attached to {0} for {1} ({2})", processName, userName, .ProcessID)
Exit For
End If
End With
Next
If successMessage = String.Empty Then
Throw New Exception(String.Format("{0} {1} not found.", processName, userName))
Else
Trace.WriteLine(successMessage)
End If
Catch ex As System.Exception
MsgBox(ex.Message)
End Try
End Sub
Another option would be the WADA - W3WP Advanced Attacher available in the Visual Studio Gallery. I found it by searching in the Online Gallery of Extension Manager for "attach worker".
Look at the answers to this question. There are built in scripts you can run from a command window to do this.
If you can execute a request on each branch, you could use something like Process Explorer or Task Manager to see which ID is which possibly as one may be taking up CPU cycles that is currently processing a request assuming you can get such separation.
You can use task manager to view the user name under which the process is running (which in general is the same as the application pool name) and the process ID, but you have to turn on these columns in task manager, and also the process name has to be the same as the application pool (which is the default as far as I know).
Also note that all methods listed on this page might only display the processes that are currently running, which means that if your particular process has shut down due to idle time you have first to use the site in order to bring the process up in the list, and in your case it means you should first access all sites to make sure that the process associated with them is runing.

ORA-08177: can't serialize access for this transaction

I have a very simple code using ADO.NET which throws ORA-08177 exception. I am not sure what's wrong with this. I am trying this on a windows vista machine which has oracle 32 bit client installed. My compile option for visual studio is set to x86 platform.
Dim connection As OracleConnection = Nothing
Dim transaction As OracleTransaction = Nothing
Try
connection = New OracleConnection("Data Source=ora10;User Id=userid;Password=passwd;")
connection.Open()
transaction = connection.BeginTransaction(IsolationLevel.Serializable)
Dim inputStream As New System.IO.FileStream("Dummy.xls", IO.FileMode.Open)
Dim fileLength As Integer = CType(inputStream.Length, Integer)
Dim input(fileLength) As Byte
Try
inputStream.Read(input, 0, fileLength)
Finally
If inputStream IsNot Nothing Then inputStream.Close()
End Try
Dim deleteSql As String = "DELETE FROM TABLE1 WHERE Version = 'v1' "
Dim cmd As New OracleCommand(deleteSql, connection, transaction)
cmd.ExecuteNonQuery()
Dim insertQuery As String = "INSERT INTO TABLE1 (VERSION, DATA) VALUES (:VERSION, :DATA) "
Dim insertCmd As OracleCommand = New OracleCommand(insertQuery, connection, transaction)
insertCmd.Parameters.Clear()
insertCmd.CommandType = Data.CommandType.Text
insertCmd.Parameters.AddWithValue(":VERSION", "v1")
insertCmd.Parameters.AddWithValue(":DATA", input)
insertCmd.ExecuteNonQuery()
transaction.Commit()
Catch
If transaction IsNot Nothing Then transaction.Rollback()
Throw
Finally
If transaction IsNot Nothing Then transaction.Dispose()
If connection IsNot Nothing AndAlso connection.State <> ConnectionState.Closed Then connection.Close()
End Try
Important thing to note: (I am not sure if they are connected) but I do not face this problem if I uninstall latest windows updates from my machine.
Has anyone faced this or have any clue about what is going on here?
Edit:-
I have some progress where I have found out that this problem occurs only when we have blob column type in question. for simple columns it works fine.
Other details (not sure if that makes a difference)
I am working on 64 bit windows vista business machine.
I have installed 32 bit oracle client for windows vista (since 64 bit oracle client does not work on vista).
I am compiling my project for a x86 (32 bit environment) in visual studio.
And this is a console application and I know that nobody else is hitting the database at this time. so there cannot be multiple transactions.
And I do not see this problem if I uninstall latest windows update. (KB963027, KB967190, KB959426, KB960225, KB960803, KB952004, KB956572, KB958687, KB958690, KB958481, KB958483, KB943729)
You are using a serializable transaction which waits for some other transaction locking the same table to ROLLBACK.
If this other transaction does not rollback but commits instead, you will get this error.
The scenario seems to be as following:
Alice opens her browser session which calls DELETE FROM TABLE1 WHERE Version = 'v1'
Bob opens his session which calls DELETE FROM TABLE1 WHERE Version = 'v1' after Alice did it but before she commited.
Bob's transaction waits since Alice locked the rows with Version = 'v1'
Alice commits her transaction
Bob's transaction fails with Cannot serialize access
To work around this, set TRANSACTION ISOLATION LEVEL to READ COMMITTED:
transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted)
In this case, Bob's query will be reissued after Alice commits her changes, as if Bob's transaction were started after Alice's one was committed.
Update
Could you please post a trace of your connection?
To do this, issue this command right after connecting:
(New OracleCommand("ALTER SESSION SET SQL_TRACE=TRUE", connection, transaction)).ExecuteNonQuery();
, then look in $ORACLE_HOME\admin\udump for a fresh *.trc file

VB6 ADO Connection Pooling

We have a bunch of VB6 apps at our company. We are trying to debug a random SQL timeout error and did a trace with SQL Server Profiler on the Audit Login event. We noticed that the connections were coming in as nonpooled. We use the SQLOLEDB provider with SQL Server 2000 & 2005. I searched the internet and everything I come across says that connections are pooled by default in the SQLOLEDB provider, but we are not seeing this. Below is the code we use to connect to the database. We really need to have these connections pooled because we think this may be the problem with our random timeout error. Could any one shine some light on why connection pooling isn't working and any way to make it work? Thanks.
Dim cnn As New ADODB.Connection
cnn.ConnectionString = "Provider=SQLOLEDB;Data Source=xxx;Catalog=xxx;User ID=xxx Password=xxx;"
Call cnn.Open
Dim cmd As New ADODB.Command
Set cmd.ActiveConnection = cnn
cmd.CommandText = "SELECT * FROM [Table]"
Dim rs As New ADODB.RecordSet
Call rs.Open(cmd, , adOpenStatic, adLockOptimistic)
While Not rs.eof
'Do stuff
Call rs.MoveNext
Wend
'Close and Dispose connection here
Disposing the connection on every call could prevent pooling
...at least one instance of a Connection
object instantiated for each unique
user—at all times. Otherwise, the pool
will be destroyed when the last
Connection object for that string is
closed.
http://msdn.microsoft.com/en-us/library/ms810829.aspx
I messed around and opened a connection at app startup and kept it open through the entire time the app was running. Connection pooling did start after the second opened and closed connection.
You mentioned that you were trying to track down a random timeout problem. I've had the same, generally when I was doing a SELECT that returned a lot of rows. Two things:
Cnn.CursorLocation=ADODB.adUseServer
(The other option is adUseClient) - I believe adUseServer gave me faster queries, which reduced the likelihood of timeouts. You do this before you open the connection, I believe.
Cnn.CommandTimeout=0
Also before the open(), tells it that you want an infinite timeout. I think the default timeout is something like 30s, which was way too short for some queries. The CommandTimeout will be used for Recordset queries. If you use a Command object, it has it's own CommandTimeout member, which doesn't appear to inherit from the Connection (ie, I set it before I Execute a command).
Sorry if the syntax isn't quite right, I'm cutting from some C++ code.

Resources