简体   繁体   中英

How to force connection timeout in Powershell when failing to connect to SQL Server

I would like to create simple ping algorithm to continuously query remote SQL Server instance using Powershell. My goal is to detect small network outages, caused most likely by firewall rule changes.

while($true)
{
    $ClientDate = Get-Date
    $ServerDate = Invoke-Sqlcmd -Query "SELECT SYSDATETIME() AS [Time];" -ServerInstance "RemoteSQLInstance" -ConnectionTimeout 1

    "Client = " + $ClientDate + "| Server = " + $ServerDate.Time

    Start-Sleep -Seconds 1
}

Mentioned code works just fine when the instances is reachable, but when the instance becomes unreachable, it takes the Powershell a standard 30 seconds to output an error message (A network-related or instance-specific error occurred while establishing a connection to SQL Server). I was hoping that by introducing -ConnectionTimeout 1 parameter I can force the error message to appear right away (after 1 second) and not in default 30 seconds, but it doesn't change the default 30 seconds timeout. I guess this parameter works only if SQL Server instance is reached through network and SQL Server is failing to establish a handshake within given time.

How can I tell Powershell to not wait default 30 seconds when making remote connection to SQL Instance? Or any remote connections in general?

In some instances, it does appear that the -ConnectionTimeout parameter is not honored by the Invoke-SqlCmd cmdlet.

In the past I've worked around this issue by rolling my own function that uses the SqlConnection class directly and specifies the timeout as part of the connection string.

Here's the code I've used previously:

Function Execute-SqlCommand {
    Param(
        [Parameter(Mandatory = $true)][string]$ServerInstance,
        [Parameter(Mandatory = $true)][string]$Database,
        [Parameter(Mandatory = $true)][string]$Query,
        [Parameter(Mandatory = $false)][int]$ConnectionTimeout = 3,
        [Parameter(Mandatory = $false)][int]$QueryTimeout = 0
    )

    $connection = New-Object System.Data.SqlClient.SqlConnection
    $connection.ConnectionString = "Data source=$($ServerInstance);Initial Catalog=$($Database);Integrated Security=true;Connection Timeout=$($ConnectionTimeout)"
    
    $command = New-Object System.Data.SqlClient.SqlCommand
    $command.Connection = $connection
    $command.CommandTimeout = $QueryTimeout
    $command.CommandText = $Query

    try {
        $connection.Open()
        $reader = $command.ExecuteReader()

        $rows = @()
        while ($reader.Read()) {
            $row = New-Object PSObject
            for ($i = 0; $i -lt $reader.FieldCount; $i++) {
                $row | Add-Member -MemberType NoteProperty -Name $reader.GetName($i) -Value $reader.GetValue($i)
            } 
            $rows += $row
        }

        return $rows
    }
    catch [Exception] {
        Write-Error $_.Exception.Message
        return $null
    }
    finally {
        $connection.Dispose()
        $command.Dispose()
    }
}

It's executed like this and the timeout can be configured using the -ConnectionTimeout parameter, as well as the query timeout via the -QueryTimeout parameter.

Execute-SqlCommand -ServerInstance $ServerInstance -Database $DatabaseName -Query $Query -ConnectionTimeout 10

Successful queries will get you back a collection of PSObjects that you can work with:

查询结果

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