Memory leak when using windows service with timer and sql server - memory-leaks

I have windows service that periodically checks the database every 10 seconds, retrieves the mails not yet sent and performs the task for pending mails.
The problem is that the memory increases step by step. Please help if any adjustment needed.
My code visual basic:
Private Sub TimerMail_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles TimerMail.Elapsed
Dim SqlConnection As New SqlConnection("Data Source=.;Initial Catalog=xxx;Integrated Security=True")
Dim cmd As New System.Data.SqlClient.SqlCommand
cmd.CommandType = System.Data.CommandType.Text
cmd.CommandText = "SELECT DISTINCT UserName, objet, contenu, email FROM message WHERE envoimail='false'"
SqlConnection.Open()
cmd.Connection = SqlConnection
Dim monReader As SqlDataReader = cmd.ExecuteReader()
While monReader.Read
Try
EnvoiMail(monReader("objet"), monReader("contenu"), monReader("email"), mailport, serveur, username, password, True)
Dim SqlConnection2 As New SqlConnection("Data Source=.;Initial Catalog=CGP;Integrated Security=True")
Dim cmd2 As New System.Data.SqlClient.SqlCommand
cmd2.CommandType = System.Data.CommandType.Text
cmd2.CommandText = "UPDATE destinataire SET envoimail='true' WHERE UserName='" & monReader("UserName") & "'"
SqlConnection2.Open()
cmd2.Connection = SqlConnection2
cmd2.ExecuteNonQuery()
Catch ex As Exception
logger.Error(ex.Message)
End Try
Threading.Thread.Sleep(60000)
End While
End Sub

What you have is not a resource leak, but rather delayed finalization of resources, due to non-deterministic nature of garbage collection in .Net. There are quite a few instances of SqlConnection objects created in your code -- in the reader loop you create a new connection for every row in your SELECT query results. You have to make sure you close SqlConnection. Every instance of SqlConnection should look like this:
Using connection As New SqlConnection(connectionString)
connection.Open()
' Do work here; connection closed on following line.
End Using
The Using statement will ensure connection is closed when execution goes outside of the Using block. See more details in MSDN.

Inside my solution I used COM3 port to connect the windows service to modem device and using GSMComm library.
after several tests, i constat that my problem is not located on sending mail function, but in senidng sms.
The service is stable and every think is going well, but after the service is running (COM3 port open by the service programme) and device unplagued suddenly, the service still running but the memory increased potentially from some Mo to more than Go.
comm = New GsmCommMain(3, 10000, 30000)
If comm.IsConnected Then
If comm.IsOpen Then
'------------
Else
Me.Stop()
End If
Else
Me.Stop()
End If

Related

Agent for UnprocessedDocuments After New Mail Arrives

I wish to modify some documents when they arrive in my mail-in application. (I need to remove the $REF field which then allows dragging and dropping in to folders, but that's not the point.)
I have my agent set to After New Mail Arrives and to select unprocessed documents. Designer Help for Unprocessed documents states...
With the agent properties as ....
But the agent selects any document in the Inbox which has been modified or edited rather than just the new arriving email. There is also a delay of up to a minute before the agent runs.
Sub Initialize
Dim session As New NotesSession
Dim db As NotesDatabase
Dim collection As NotesDocumentCollection
Dim doc As NotesDocument
Set db = session.CurrentDatabase
Set collection = db.UnprocessedDocuments
Set doc = collection.GetFirstDocument()
While Not(doc Is Nothing)
If doc.Hasitem("$REF") Then
doc.Subject= "($Ref) " & doc.Subject(0) 'Only testing here will remove item if exists
Else
doc.Subject = "(No Ref) " & doc.Subject(0)
End If
Call doc.save(True,False)
' Call session.UpdateProcessedDoc( doc ) 'This doesn't appear to make any difference.
Set doc = collection.GetNextDocument(doc)
Wend
End Sub
Any help in running this type of script on only new mail and faster than at present, as users may edit the doc before the agent has run, would be appreciated.
Thanks
You might want to use trigger "Before new mail arrives" instead of "After new mail has arrived".
"Before new mail arrives" gets executed for every single new email immediately.
You get access to email document with
Dim session as New NotesSession
Dim doc as NotesDocument
Set doc = session.DocumentContext
You can find a good comparison of both triggers here.
The delay is normal. The agent manager doesn't guarantee immediate execution.
What you might be seeing is a "first-time effect". I.e., the first time you run the agent, all of the messages are "new" because they are newer compared to Last-Run timestamp on the agent - which defaults to day zero in the distant past. Have you tried this multiple times in the same database to see if what is in unprocessed documents on the second run?

Reference for DB2 access from Excel?

I am having a terrible time trying to get the most simple code to execute consistently in an Excel VBA module.
I'm just trying to assign the value of a field (column) in DB2 to a variable in the module. I've tried the following:
Connection:
Dim odbcName as String
Dim conn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strCmd As String
'ACME is the name of the ODBC connection I've established and verified/validated.
conn.Open("ACME")
strCmd = "SELECT UNIQUEID, MARKET, SECTOR, CLIENTID FROM PROJECTINFO WHERE UNIQUEID=1234567"
rs.Open Source:=strCmd, ActiveConnection:=conn, CursorType:=adOpenDynamic, LockType:=adLockOptimistic
All is good to that point. The next bit of code is:
If rs.EOF then
'Do some work to add a record if it doesn't already exist - this works fine.
Else
Dim sqlMarket as String
sqlMarket = rs.Fields("MARKET").Value
End If
I also tried:
sqlMarket = rs!MARKET
Both of those attempts, ON THAT LINE where I was attempting to assign a local variable the contents of the MARKET field in the recordset, caused it to crash. I mean, the entire workbook closes, then Excel reopens and tries to open the worksheets, but the module has crashed completely.
Ultimately, my intent is to check all the fields in the recordset against another set of data (in another Excel workbook), and if anything has changed, to update the DB2 recordset. But right now, I can't even seem to LOOK AT the contents of the DB2 recordset without Excel crashing.
Can anyone offer some advice as to what I'm either doing wrong, or what I'm missing?
Excel 365 (Version 1908) 64-bit
IBM DB21085I 64-bit (SQL11050 with level identifier 0601010F)
DB2 v11.5.0.1077
Windows 10 64-bit Version1909 (Build 18363.900)

Refreshing multiple ODBC connections via background query

I am using a set of scripts that pull a lot of different data from iSeries via ODBC.
ActiveWorksheets.RefreshAll does not work as it doesn't leave enough time to run the background queries
I've tried the below,but to no avail
Dim qry As Connections
'Set qry =
For Each qry In ActiveWorksheets.Connections
qry.BackgroundQuery = False
qry.RefreshAll
DoEvents
Next qry
This gives me the expected Error 424 Object expected.
I don't expect to use Set qry = here, as I need to run through 30 different connections
Let's just call them connection1, connection2 etc for now, as their names are all over the place
Is the simplest option to stop the background query, refresh, activate background query - before the data import, or is there a better way?
I've looked all over SO - but can't find info on multiple ODBC connections
EDIT:
Dim qry As WorkbookConnection
For Each qry In ActiveWorkbook.Connections
qry.Refresh
DoEvents
Next qry
I believe your
Dim qry As Connections
should read
Dim qry As WorkbookConnection
The ActiveWorksheets.Connections.Item property returns an object of type WorkbookConnection. If you are trying to refresh the connections one at a time as it seems from your For Each statement, that object represents a single connection with methods like Refresh rather than the collection of all connections.
I managed to work this out. So all who may need this in the future can see:
Dim qry As WorkbookConnection
For Each qry In ActiveWorkbook.Connections
qry.ODBCConnection.BackgroundQuery = False
qry.Refresh
qry.ODBCConnection.BackgroundQuery = True
Next qry
Although it doesn't look like BackgroundQuery = True/False doesn't look like it's vital here. Turning it off means when you qry.Refresh, it pulls the data and refreshes it.
Also using For Each qry means that rather than writing out out 20 times, I can just look every connection and turn them off, refresh, and turn them back on

Language Service Submit: Error '91'

I followed the solution posted in below link to execute a simple SAS Query through VBA:
Calling SAS through VBA
Dim obObjectFactory As New SASObjectManager.ObjectFactory
Dim obObjectKeeper As New SASObjectManager.ObjectKeeper
Dim obServer As New SASObjectManager.ServerDef
Dim obSAS As SAS.Workspace
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
obServer.MachineDNSName = "xxxx#company.com"
obServer.Protocol = ProtocolBridge
obServer.Port = 8561
obObjectFactory.LogEnabled = True
Set obSAS = obObjectFactory.CreateObjectByServer("sas", False, obServer, "", "")
obSAS.LanguageService.Submit "PROC SQL; CREATE TABLE ME.TABLE1; RUN;" 'Error at submit
But I'm getting error at the above submit line as "Object variable or With block variable not set"
You are trying to create an Asynchronous connection. I would try to create a syncronous connection instead (change second parameter to true).
Set obSAS = obObjectFactory.CreateObjectByServer("sas", True, obServer, "", "")
Synchronous indicates whether a connection is synchronous or
asynchronous. The synchronous connection is most frequently used and
is the simplest connection to create. If this parameter equals TRUE,
CreateObjectByServer does not return until a connection is made. If
this parameter equals FALSE, the function returns a null value
immediately. When a connection is made, the object is stored in the
ObjectKeeper.
From SAS documentation. Thus why your variable is Nothing right away.
http://support.sas.com/rnd/itech/doc9/dev_guide/dist-obj/winclnt/omcreate.html
Now for the Submit method, I suggest you add some error checking code like
If obSAS Is Nothing Then
MsgBox("Connection failed")
Else
obSAS.LanguageService.Submit "PROC SQL; CREATE TABLE ME.TABLE1; RUN;" 'Error at submit
End If
That above will answer your question of why you are getting the error Object variable or With block variable not set.
For the later Run Time Error '-2147213310 (80042002)' that is a different issue/question. And you should try to contact SAP tech support, with the information you provided there is nothing much we can do. The error must give you additional information in form of html tags. From the link you supplied, it seems this error is caused by a bad connection settings/parameters.
The workspaceservers default port is 8591. Perhaps the
workspaceserver also runs on a different machine. Connect to your
profile, go to the serverlist, locate the server you want to connect
to and right click the context menu "properties" to see the right
connection profile for your workspaceserver.
BTW: if you are using IWA (integrated windows authentication) you must
not provide a username and password (Nothing).
Set obSAS = obObjectFactory.CreateObjectByServer("sas", True, obServer, Nothing, Nothing)

Pass progress from multi-step ADO query to VBA using Raiserror: Is this possible?

Preface
I'm making this question specific to conform to SO asking guidelines, but feel free to suggest wholesale redesign if you wish. I may be using some bad practices.
Basic Question
I use ADO to execute a multi-step SQL Server query that takes several minutes to execute. I use Raiserror in my tsql queries to let myself know more verbosely which steps have finished. Is it possible to pass these messages to VBA before the complete query finishes, while still continuing with the query?
Details and Code
I use the vba below to execute the t-SQL query underneath. As you can see, there are two errors raised in the t-SQL that display "Step 1 complete" and "Step 2 complete". Could I pass these messages (or alternately use error numbers and pass those) back to VBA in a way that would allow me to detect them and update a progress bar while continuing to execute the query?
VBA used to execute the query:
Set cmd = New ADODB.Command
cmd.ActiveConnection = cnn
cmd.CommandTimeout = 0
cmd.CommandText = strQuery
Set rst = New ADODB.Recordset
rst.Open cmd
'Go to the second to last recordset of the multi-step query
String1 = Replace(strQuery, ";", "")
For Loop2 = 1 To (Len(strQuery) - (Len(String1) + 1))
Set rst = rst.NextRecordset
Next Loop2
'Copy results
If Not rst.EOF Then
(snip - actions)
Else
MsgBox "Error: No records returned."
End If
Stripped-down piece of multi-step tSQL query:
--#DRS1: The numbers being researched
select distinct numbers
into #DRS1
from Table1 (nolock)
where numbers in ()
--#DRS1: Index
create nonclustered index Idx_DRS1
on #DRS1(numbers);
Raiserror(“Step 1 complete”,1,1) with nowait;
--#DRS2: Table2 for numbers being researched
select distinct
DRS1.numbers
,a.ID
into #DRS2
from #DRS1 DRS1
join Table2 (nolock) a
on DRS1.numbers = a.numbers
Raiserror(“Step 2 complete”,1,1) with nowait;
--MORE STEPS
(more steps)
(more raiserror statements)
Clarification
I am not interested in:
A method that doesn't allow me to update a progress bar until the query is completely done.
A method that uses Progress/MaxProgress, because as I understand it that would return separate numbers for each of the steps in my query, rather than one progress measure for the entire query.
I am less interested in:
Using # records affected messages to determine progress, because some steps may return equal numbers of records to previous steps.
Research
The closest thing I have found to what I'm looking for is here, but as the discussion of that solution here says:
This approach would only work for stored procedures that are not intended to return results, say procs that insert data into tables. Another approach would be needed if your stored proc returns a result set.
Since I return results in the final step of my query to be manipulated in Excel I don't think this would work for me.
External link code for reference
SQL:
CREATE PROCEDURE dbo.updTesting As
Declare #RetVal integer
Exec #RetVal = updTesting2
Return #RetVal
GO
CREATE PROCEDURE dbo.updTesting2 As
raiserror('Error From Testing 2 procedure',16,1)
Return -2
GO
VBA:
Private Sub Command1_Click()
On Error GoTo ErrorHandler
Dim db As ADODB.Connection
Dim cmd As ADODB.Command
Set db = New ADODB.Connection
db.CursorLocation = adUseClient
db.Open "provider=sqloledb;data source=handel;initial catalog=northwind;integrated security=sspi"
Set cmd = New ADODB.Command
With cmd
Set .ActiveConnection = db
.CommandText = "updTesting"
.CommandType = adCmdStoredProc
.Parameters.Append .CreateParameter("#RetVal", adInteger, adParamReturnValue)
.Execute , , adExecuteNoRecords
End With
ExitPoint:
On Error Resume Next
Set cmd.ActiveConnection = Nothing
Set cmd = Nothing
db.Close
Set db = Nothing
Exit Sub
ErrorHandler:
MsgBox "Error # " & Err.Number & vbNewLine & vbNewLine & Err.Description
Resume ExitPoint
End Sub
There are several possibilities to work out a solution for your problem:
(1) Capture the error messages as they occur while the query is running. That's the requested approach.
(2) Break-down the big, long query into several smaller chunks and run them one after the other. Like this you know which part is completed and you can update your progress bar based on that information just before sending the next chunk to the server.
(3) Update the big, long query to log its progress on the server in a temp table and then read out this log while the other query is still running.
While I'd recommend to use errors only when errors occur and not to "abuse" them for logging, tracking, or feedback, both options (1 & 2) are quite feasible with events:
Similar to Worksheet events Worksheet_Change, Worksheet_Activate, or Worksheet_BeforeDoubleClick there are also ADODB events for ADODB.Connection and ADODB.Recordset. Both are well documented and can be easily viewed within the VBE by (1) adding a reference to Microsoft ActiveX Data Objects x.x Library (2) pressing F2 (3) selecting the ADODB library in the drop-down menu at the top (4) and finally looking up Recordset or Connection within the classes. Here are the available events for Connection:
As you can see, all events are marked with a lightning. To capture / use these events you need to create a Class Module in the VBE and add the following line to it:
Dim WithEvents adoConnection As ADODB.Connection
Afterwards, you can make use of the newly created ADODB.Connection event and select the required event from the top of the list:
The applicable event for option (1) is the InfoMessage event which occurs "[...] whenever a warning occurs during a ConnectionEvent operation." The import part here is during a connection. So, this event fires automatically whenever an ADODB connection "gets" an error.
Of course, this means that the original query to the server must be sent without waiting for an answer. Instead you should use the above event to capture any errors while the query is executing and create yet another event to automatically fire when the entire query completed.
For some more help in respect to asynchronous ADODB connection and possible problems with them you might want to have a look at the following two posts here:
ExecuteComplete ADODB Connection event not fired with adAsyncExecute parameter
Running multiple async queries with ADODB - callbacks not always firing
A similar approach can be used with option (3) as described above and asynchronous ADODB connections.
Let me know if this solves your problems or if you have any further questions.
All available ADODB events can be reviewed here https://msdn.microsoft.com/en-us/library/ms675083%28v=vs.85%29.aspx

Resources