简体   繁体   中英

Best approach to timeout a function after a certain period of time and retry

Just a little bit of background. We are moving away from storing our user passwords hashed in a database to authenticating directly against Active Directory. Unfortunately we have so many internal applications that updating every application is not an option at this point.

So instead of requiring all our developers to change their code, I thought I could just build a .NET CLR Assembly for SQL and just modify the existing authentication stored procedure to validate the users password against our domain controllers instead of a table.

This has been successful in our initial testing, except when the domain controller cannot be communicated with for whatever reason (shutdown, restart, network, etc). When this happens we get timeouts. I added a try/catch and while loop to just skip to the next domain controller and attempt to authenticate against it, but it still takes the first attempt well over 20 seconds to fail before it tries to authenticate against the next server. Since this is a SQL assembly/function and is being called in a stored procedure 20 seconds is way too long.

What is the best approach to modify my code and fail the authentication attempt against a server and try the next server, say, after 3 seconds? Threading somehow?

Imports System
Imports System.Data
Imports System.Collections
Imports System.Text
Imports Microsoft.SqlServer.Server
Imports System.Data.SqlTypes
Imports System.Runtime.InteropServices
Imports System.DirectoryServices.AccountManagement

Partial Public Class UserDefinedFunctions

    <SqlFunction(Name:="ValidCredentials")>
    Public Shared Function ValidCredentials(ByVal Domain As SqlString, ByVal Username As SqlString, ByVal Password As SqlString) As SqlBoolean
        'Validates the username and password are correct against the domain.

        Dim ServerNum As Integer = 1
        Dim Success As Boolean = False
        Dim ValidCreds As SqlBoolean = False

        'Loop through all 4 domain controllers to attempt authentication against each of them.
        While Success = False And ServerNum <= 4

            Try

                Dim pc As PrincipalContext
                If Domain = "employees" Then
                    pc = New PrincipalContext(ContextType.Domain, "DC-E-0" & ServerNum.ToString & ".employees.ourdomain.loc", "EMPLOYEES\BindAcct", "PASS")
                Else
                    pc = New PrincipalContext(ContextType.Domain, "DC-S-0" & ServerNum.ToString & ".students.ourdomain.loc", "STUDENTS\BindAcct", "PASS")
                End If

                ValidCreds = pc.ValidateCredentials(Username, Password)
                Success = True
                Return ValidCreds

            Catch ex As Exception

                ValidCreds = False
                Success = False
                ServerNum += 1

            End Try

        End While

        Return ValidCreds

    End Function

End Class

You can set timeout value for your function on sql server 2008 or above with the following lines of code. Just put it on the top of your block:

SET LOCK_TIMEOUT 3000;  
GO

The value of the timeout is expressed in milliseconds .

According to MS Documentation :

When a wait for a lock exceeds the time-out value, an error is returned.

That will cause that your function takes less than 3 seconds or fail. Then you should catch the exception and rollback if needed.

You can do this with a separate thread:

Set timeout to an operation

The code is in C#, but it should be straightforward to convert.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM