sqlvb.netthread-safetywindows-services

Connection settings being overwritten


A service (Windows service) is used to create multiple threads and execute the different areas each thread is responsible for. For two specific threads the connection string and settings seems to be overwritten. Both these threads calls the same class at a point to execute the requirements. Both these threads are using a different connection string to a database, thus two different areas of where the database is hosted.

The problem is, the record that needs to be processed is picked up from the correct database and the error (due to incorrect setting) is returned and logged on the incorrect database. This problem is intermittent and does not occur regularly. A record being processed will fail with this problem, but when this same record is run 1 min later, everything succeeds (correct db connection and settings)

The two threads initiated in the class:

The connection string is located in a database that is retrieved via the DBUtils class

    Public Class MyClassName
       Private WithEvents MyTimer As Timers.Timer = Nothing 
       Private eveHUBSPOTUPDATEEVENTS_NZ As New Threading.Thread(AddressOf EventHubSpotUpdateEvents)
       Private eveSYSEVENTS_AUS As New Threading.Thread(AddressOf EventSystemEvents_Aus)
    End Class

Constructor for class

   Public Sub New()
   InitializeComponent()
   //other config settings

   ServicePointManager.SecurityProtocol = Net.SecurityProtocolType.Tls12 Or Net.SecurityProtocolType.Tls11 Or Net.SecurityProtocolType.Tls Or Net.SecurityProtocolType.Ssl3
   End Sub

OnStart event:

Protected Overrides Sub OnStart(ByVal args() As String)
     SetTimerProps(False)
End Sub

Private Sub SetTimerProps(ByVal doDisable As Boolean)
ClearCounters()

If doDisable Then
    If MyTimer IsNot Nothing Then
        MyTimer.Enabled = False
        MyTimer = Nothing
    End If
Else
    _mustDoHubspotEventsNZ = MustDOHubSpotEventsNZ()
    _mustDoHubspotEvents_Aus = MustDoHubSpotEvents_Aus()

    MyTimer = New Timers.Timer With {
        .Interval = 10000,                '10 Seconds
        .AutoReset = False,
        .Enabled = True
    }
End If
End Sub

The threads are then created on a timer (same class as above):

Private Sub MyTimer_Elapsed(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs) Handles MyTimer.Elapsed
       Try
         MyTimer.Enabled = False
         IncrementCounters()

         If Not eveHUBSPOTUPDATEEVENTS_NZ.IsAlive Then
           eveHUBSPOTUPDATEEVENTS = New Threading.Thread(AddressOf EventHubSpotUpdateEvents) With {
          .IsBackground = True,
          .Priority = Threading.ThreadPriority.Normal
         }
           eveHUBSPOTUPDATEEVENTS_NZ.Start()
       End If

      If nSYSEVENTS_AUS_Counter > 1 AndAlso Not eveSYSEVENTS_AUS.IsAlive Then
        nSYSEVENTS_AUS_Counter = 0

        If _mustDoSystemEvents_Aus Then
           eveSYSEVENTS_AUS = New Threading.Thread(AddressOf EventSystemEvents_Aus) With {
           .IsBackground = True,
           .Priority = Threading.ThreadPriority.Highest
         }
         eveSYSEVENTS_AUS.Start()
        End If
     End If
      Catch ex As Exception
       //Error Handling
       MyTimer.Enabled = True
End Sub

Both these threads then goes to a method to retrieve the records that needs to be processed.

For the NZ process the connection string

    Public Class EventsNZ
       Public Sub New()
         _connectionString = DBUtils.SureSetting.GetSetting("ConnStringNZ")
         _environment = DBUtils.SureSetting.GetSetting("Environment")
       End Sub
    End Class

And for the Aus process the connection string:

    Public Class EventsAus
       Public Sub New()
         _connectionString = DBUtils.SureSetting.GetSetting("ConnStringAus")
         _environment = DBUtils.SureSetting.GetSetting("Environment")
       End Sub
    End Class

The records is then retrieved (NZ):

    Private Sub HandleEventsNZ()
      Try
        Const sqlEvents = "script to retrieve the required records"

        Dim systemEventsData = _dbHelper.GetLongRunningSQLDataTable(sqlEvents, 600, Nothing)
    
        For Each eventItem As DataRow In systemEventsData.Rows()
         Dim feedback = HandleHubspotEvent(eventItem, _connectionString )

         UpdateEventToCompleted(StringAsLong(eventItem("Sys_Key")), feedback)
       Next

       systemEventsData.Dispose()
    Catch ex As Exception
      //some error handling
    End Try
  End Sub

    Private Function HandleHubspotEvent(eventData As DataRow, dbConnectionString As String) As String
      Try
       //some validations

       Dim hubspotWorker As New HubspotIntegrator(_connectionString, productId, StringAsInteger(eventData("User_Cde")))

       Return hubspotWorker.HubspotCreateOrUpdate(eventData("Area_Cde").ToString(),
                                        StringAsLong(eventData("Reference_Cde")),
                                        eventData("Other_Ref2").ToString(), eventResult)

   Catch ex As Exception
    //error handling
   End Try
 End Sub
End Function

The records is then retrieved (Aus):

Similar process to retrieve the records. Aus is only in its own class due to migration that will take place. Once the Aus data is migrated to the NZ side, this class and the corresponding thread will be removed.

Aus will reach this method:

    Private Function HandleHubspotEvent(ByRef eventData As DataRow) As String
       Try
         //some validation

         Dim hubspotWorker As New HubspotIntegrator(_connectionString, productId, StringAsInteger(eventData("Event_User_Cde")))

         Return hubspotWorker.HubspotCreateOrUpdate(eventData("Event_Area_Cde").ToString(),
                                        StringAsLong(eventData("Event_Reference_Cde")),
                                        eventData("Event_Other_Ref2").ToString(), "")

    Catch ex As Exception
       //error handling
    End Try
  End Function

Up until this point the correct connection is used. They both reach the same class to handle the process.

The connection is set up with immutable properties when calling the HubspotIntegrator class. The connection is not changed anywhere in the process (or any setting). The connection string is passed to this class. But for some reason, the connection string ends up being changed during the process.

    Public Sub New(ByVal dbConnectionString As String, ByVal productId As Integer, ByVal userId As Integer)
If String.IsNullOrEmpty(dbConnectionString) OrElse dbConnectionString = "" Then
    Throw New ArgumentException("Invalid Connection String!")
End If
If productId = 0 Then
    Throw New ArgumentException("Invalid Product ID!")
End If

_dbConnectionString = dbConnectionString
_dbHelper = New DBUtil(_dbConnectionString)
_dbLookup = New SureDBLookup(dbConnectionString)
_sureQuote = New SureQuote(dbConnectionString)
_productId = productId
_systemMappingWorker = New SureSystemMapping(_dbConnectionString)
_userId = userId
_hsDataHelpers = New HubspotDataHelpers(dbConnectionString)

Dim productRuleData = _dbLookup.GetProductRuleData(productId, 0, "ALLOW_HUBSPOT", Nothing)
If productRuleData Is Nothing OrElse productRuleData("Pro_Val1").ToString <> "Y" Then
    Throw New ArgumentException("Invalid Product ID! Not a HubSpot product")
End If

_hubspotBearerToken = DBUtils.SureSetting.GetSetting(productRuleData("Pro_Val2").ToString().Trim())

If _hubspotBearerToken = Nothing Then
    Throw New ArgumentException("Invalid Hubspot BearerToken - HUBSPOT_BEARERTOKEN is not defined")
End If

End Sub

Any advice will be appreciated on why/how this can happen? and if there is something I'm missing that I can look at (been looking at this for a week and still not seeing the issue)


Solution

  • It looks to me like both of your functions that retrieve your connection string and environment are setting the same top-level variables _connectionString and _environment. Since these are running in separate threads, it may look like things are partially working, but as you noted, the connection string gets changed because one of the two function calls is overwriting it.

    How is your HandleEventsNZ sub going to know which connection string to use?