Лучший подход к тайм-ауту функции после определенного периода времени и повторной попытке

Немного предыстории. Мы переходим от хранения хешированных паролей пользователей в базе данных к аутентификации непосредственно в Active Directory. К сожалению, у нас так много внутренних приложений, что обновление каждого приложения на данный момент невозможно.

Поэтому вместо того, чтобы требовать от всех наших разработчиков изменения своего кода, я подумал, что могу просто создать .NET CLR Assembly для SQL и просто изменить существующую хранимую процедуру аутентификации, чтобы проверять пароль пользователя на наших контроллерах домена, а не на таблице.

Это было успешным в нашем первоначальном тестировании, за исключением случаев, когда контроллер домена не может быть подключен по какой-либо причине (выключение, перезапуск, сеть и т. д.). Когда это происходит, мы получаем таймауты. Я добавил цикл try / catch и while, чтобы просто перейти к следующему контроллеру домена и попытаться выполнить аутентификацию на нем, но до того, как он попытается пройти аутентификацию на следующем сервере, первая попытка все равно потерпит неудачу более 20 секунд. Поскольку это сборка / функция SQL и вызывается в хранимой процедуре, 20 секунд - это слишком долго.

Как лучше всего изменить мой код и не выполнить попытку аутентификации на сервере и попробовать следующий сервер, скажем, через 3 секунды? Каким-то образом зарезервировать?

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

Выдает ли ValidateCredential исключение, если не может проверить?

Simo 13.09.2018 18:49

Да, это вызовет исключение. Сервер не найден в сети или что-то в этом роде, если DC-E-01 выключен, перезагружается и т. д. Когда генерируется исключение, он пытается подключиться ко второму серверу DC-E-02 и т. д. до тех пор, пока он в конечном итоге не будет подтвержден. Это ожидается, однако, если он не может связаться с первым сервером, он должен попробовать второй, затем третий, затем четвертый, пока не найдет тот, против которого можно выполнить аутентификацию. Проблема в том, что функция слишком долго генерирует исключение. Я хотел бы просто прервать эту попытку через 3 секунды и перейти к следующему серверу.

Robert 13.09.2018 20:12
0
2
244
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете установить значение тайм-аута для своей функции на sql server 2008 или новее с помощью следующих строк кода. Просто поместите его в верхнюю часть своего блока:

SET LOCK_TIMEOUT 3000;  
GO

Значение тайм-аута выражается в milliseconds.

По данным Документация MS:

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

Это приведет к тому, что ваша функция займет менее 3 секунд или выйдет из строя. Затем следует перехватить исключение и выполнить откат при необходимости.

Я не хочу, чтобы оператор SQL дал сбой. Я хочу, чтобы метод внутри сборки завершился ошибкой (быстрее или завершился) и попытался выполнить аутентификацию на следующем сервере. Пока не получит надлежащий ответ от одного из 4 контроллеров домена.

Robert 13.09.2018 20:17

Думаю, нужно найти обходной путь, альтернатив не так уж и много: c

Simo 13.09.2018 21:22

Должен быть способ распределить вызовы внутри функции и прервать выполнение через определенное время, чтобы попробовать следующий сервер.

Robert 13.09.2018 23:04

Возможно, вы можете использовать решение @Simo и поймать конкретную возвращаемую ошибку, а затем рекурсивно вызвать свою функцию.

Mary 14.09.2018 08:07
Ответ принят как подходящий

Вы можете сделать это в отдельной ветке:

Установить тайм-аут для операции

Код написан на C#, но его легко преобразовать.

Мне удалось успешно применить этот подход. Спасибо!

Robert 18.09.2018 20:29

Другие вопросы по теме