Origine dati utilizzo di memoria del pool di dati con ottimizzazione per la memoria di SQL Server 2016

Microsoft.SQLServer.2016.DataSource.PoolMemoryConsumption (DataSourceModuleType)

L'origine dati restituisce l'utilizzo di memoria per la risorsa di dati con ottimizzazione per la memoria di SQL Server 2016

Element properties:

TypeDataSourceModuleType
IsolationAny
AccessibilityInternal
RunAsMicrosoft.SQLServer.MonitoringAccount
OutputTypeSystem.PropertyBagData

Member Modules:

ID Module Type TypeId RunAs 
Scheduler DataSource System.Scheduler Default
Probe ProbeAction Microsoft.Windows.PowerShellPropertyBagProbe Default
PoolFilter ConditionDetection System.ExpressionFilter Default

Overrideable Parameters:

IDParameterTypeSelectorDisplay NameDescription
IntervalSecondsint$Config/IntervalSeconds$Intervallo (secondi)Intervallo di tempo ricorrente in secondi in cui eseguire il flusso di lavoro.
SyncTimestring$Config/SyncTime$Ora di sincronizzazioneOra di sincronizzazione specificata usando il formato a 24 ore. Può essere omessa.
TimeoutSecondsint$Config/TimeoutSeconds$Timeout (secondi)Specifica il tempo di esecuzione consentito per il flusso di lavoro prima che venga chiuso e contrassegnato come non riuscito.
ScriptDelayMsecint$Config/ScriptDelayMsec$Ritardo script (millisecondi)Questo parametro imposta il ritardo tra le query T-SQL consecutive eseguite dal flusso di lavoro. Favorisce la riduzione del footprint generato dal flusso di lavoro in presenza di un numero elevato di oggetti di destinazione. Prima di cambiare questo parametro, consultare il Supporto tecnico Microsoft.

Source Code:

<DataSourceModuleType ID="Microsoft.SQLServer.2016.DataSource.PoolMemoryConsumption" Accessibility="Internal" RunAs="GPMP!Microsoft.SQLServer.MonitoringAccount">
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="IntervalSeconds" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SyncTime" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ServerName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SqlInstanceName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="PoolID" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="TimeoutSeconds" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ScriptDelayMsec" type="xsd:int"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IntervalSeconds" ParameterType="int" Selector="$Config/IntervalSeconds$"/>
<OverrideableParameter ID="SyncTime" ParameterType="string" Selector="$Config/SyncTime$"/>
<OverrideableParameter ID="TimeoutSeconds" ParameterType="int" Selector="$Config/TimeoutSeconds$"/>
<OverrideableParameter ID="ScriptDelayMsec" ParameterType="int" Selector="$Config/ScriptDelayMsec$"/>
</OverrideableParameters>
<ModuleImplementation>
<Composite>
<MemberModules>
<DataSource ID="Scheduler" TypeID="System!System.Scheduler">
<Scheduler>
<SimpleReccuringSchedule>
<Interval>$Config/IntervalSeconds$</Interval>
<SyncTime>$Config/SyncTime$</SyncTime>
</SimpleReccuringSchedule>
<ExcludeDates/>
</Scheduler>
</DataSource>
<ProbeAction ID="Probe" TypeID="Windows!Microsoft.Windows.PowerShellPropertyBagProbe">
<ScriptName>GetHKPoolMemoryConsumption.ps1</ScriptName>
<ScriptBody><Script>#GetHKPoolMemoryConsumption.ps1
param($computerName, $sqlInstanceName, $connectionString, $tcpPort, $delay, $serviceName)


#TODO: Discuss event id
$SCRIPT_EVENT_ID = 4211
$DEBUG_MODE = 0
$DEBUG_MODULE = "GetHKPoolMemoryConsumption.ps1"
$DEBUG_MSG = ""
$DEBUG_SA = $null
$DEBUG_PWD = $null

#Event Severity values
$INFORMATION_EVENT_TYPE = 0
$ERROR_EVENT_TYPE = 1

#debug information
function PrintDebug( $api, $error_msg )
{
if($DEBUG_MODE -eq 1) {
$header = "Management Group: $Target/ManagementGroup/Name$. Script: {0} Module: {1}" -f ($MyInvocation.MyCommand).Name.ToString(), $DEBUG_MODULE
$DEBUG_MSG += [Environment]::NewLine
$DEBUG_MSG += $error_msg
$api.LogScriptEvent($header, $SCRIPT_EVENT_ID, $INFORMATION_EVENT_TYPE, $DEBUG_MSG)
$DEBUG_MSG
}
}

function GetTotalPhysicalMemoryMB() {

[double]$totalPhysicalMemory = 0
$totalPhysicalMemory = (Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory / 1024 / 1024
return $totalPhysicalMemory
}

function GetMaxServerMemoryMB($SqlConnection) {

$query = "SELECT [value_in_use] as [value] FROM sys.configurations WHERE name = 'max server memory (MB)' ORDER BY name OPTION (RECOMPILE);"

[double]$maxMemory = SqlQueryScalar $SqlConnection $query

# if sql max memory isn't configured get system memory
if ($maxMemory -eq 2147483647) {
$maxMemory = GetTotalPhysicalMemoryMB
}

return $maxMemory
}


function GetAvailableHKMemoryMB($memoryMB)
{
$percent = GetAvailableHKMemoryPercent($memoryMB)
return [double](($memoryMB/100)*$percent)
}

function GetAvailableHKMemoryPercent($memoryMB)
{
$percent = 0
if ($memoryMB -lt 8*1024) {
$percent = 70
}
elseif ($memoryMB -lt 16*1024) {
$percent = 75
}
elseif ($memoryMB -lt 32*1024) {
$percent = 80
}
elseif ($memoryMB -lt 96*1024) {
$percent = 85
}
else {
$percent = 90
}
return $percent
}

function Main(){
ModuleAction $computerName $sqlInstanceName $connectionString $tcpPort $delay $serviceName
}

function ModuleAction($ComputerName, $InstanceName, $connectionString, $tcpPort, $delay, $serviceName) {
#error messege has to be logged duaring execution
$log_error_msg = $false
$error_msg = ""
if($DEBUG_MODE -eq 1)
{
$DEBUG_MSG = "ComputerName: $computerName InstanceName: $InstanceName"
$DEBUG_MSG += " ConnectionString: $connectionString tcpPort: $tcpPort delay: $delay" + [Environment]::NewLine
}
#
# Prepare MOM API and property bag object
#
$api = New-Object -comObject "MOM.ScriptAPI"
$SqlConnectionMaster = $null
#if service is not in running state when exit without any error
$state = GetServiceState $computerName $serviceName $sqlInstanceName
if(($state -ne "Running") -and ($state -ne "Unknown"))
{
return
}

try
{
$SqlConnectionMaster = SmartConnect $connectionString "master" $computerName $InstanceName $DEBUG_SA $DEBUG_PWD
#if connection is null when exit the script without error

if($SqlConnectionMaster -eq $null )
{
PrintDebug $api "The server was not found or was not accessible."
return
}

#select all database per instance
$queryDB = "DECLARE @dbName sysname, @sql nvarchar(2000)

IF OBJECT_ID ('tempdb.dbo.#FileGroupFx') IS NOT NULL DROP TABLE #FileGroupFx;
CREATE TABLE #FileGroupFx (
[DatabaseName] SYSNAME
,[Status] INT
,[Error] INT
,[ErrorMessage] nvarchar(max)
,[GroupName] SYSNAME
,[ReadOnly] BIT
,[GroupType] nvarchar(2)
,[TypeDesc] nvarchar(60)
)

DECLARE fileCursor CURSOR LOCAL FOR
SELECT
[name]
FROM sys.databases

OPEN fileCursor;
FETCH NEXT FROM fileCursor INTO @dbName;

WHILE @@Fetch_Status=0 BEGIN
SET @sql = N'USE '+ QUOTENAME(@dbName ,'""') + N';
SELECT TOP 1
DB_NAME() AS [DatabaseName]
,HAS_DBACCESS(DB_NAME()) as Status
,0 as [Error]
,'''' as [ErrorMessage]
,fg.[name] AS [GroupName]
,fg.is_read_only AS [ReadOnly]
,fg.[type] AS [GroupType]
,fg.type_desc AS [TypeDesc]
FROM sys.filegroups fg WITH (NOLOCK)
WHERE fg.[Type] = ''FX''
';

BEGIN TRY
INSERT INTO #FileGroupFx
EXEC sp_executesql @sql

END TRY
BEGIN CATCH
INSERT INTO #FileGroupFx
SELECT
@dbName as [DatabaseName]
,HAS_DBACCESS(@dbName) as Status
,ERROR_NUMBER() as [Error]
,ERROR_MESSAGE() as [ErrorMessage]
,'' AS [GroupName]
,0 AS [ReadOnly]
,'' AS [GroupType]
,'' AS [TypeDesc]

END CATCH;

FETCH NEXT FROM fileCursor INTO @dbName;
END

CLOSE fileCursor;
DEALLOCATE fileCursor;



SELECT
TOP 1 WITH TIES
db.name as dbName
, db.database_id as dbID
, grp.name as name
, grp.min_memory_percent as min_memory_percent
, grp.max_memory_percent as max_memory_percent
, grp.max_memory_kb/1024 AS max_memory_mb
, grp.used_memory_kb/1024 AS used_memory_mb
, grp.target_memory_kb/1024 AS target_memory_mb
, grp.pool_id AS pool_id
, CONVERT(bit, CASE
WHEN fg.GroupName IS NULL THEN 0
ELSE 1
END) AS isFx
FROM master.sys.databases as db
JOIN master.sys.dm_resource_governor_resource_pools AS grp
ON grp.pool_id = CASE
WHEN db.resource_pool_id IS NULL THEN 2
ELSE db.resource_pool_id
END
LEFT JOIN #FileGroupFx fg
ON fg.DatabaseName = db.name
WHERE db.source_database_id IS NULL
AND db.state = 0 AND db.is_read_only = 0
AND db.name not in ('master', 'model', 'msdb', 'tempdb')
ORDER BY ROW_NUMBER() OVER (PARTITION BY grp.pool_id ORDER BY db.Name)"
$res = SqlQueryTables $SqlConnectionMaster $queryDB

$maxMemorySQL = GetMaxServerMemoryMB($SqlConnectionMaster)
$maxHekatonPercent = GetAvailableHKMemoryPercent($maxMemorySQL)
$totalPhysicalMemory = GetTotalPhysicalMemoryMB

$res | foreach {
$dbName = $_.dbName
$dbID = $_.dbID
$poolID = $_.pool_id
$availableMemoryMB = $_.max_memory_mb
$usedMemoryMB = $_.used_memory_mb
$maxPercent = $_.max_memory_percent
$isFx = $_.isFx
try {

if($isFx) {
#default pool
if ($poolID -eq 2) {
#available for in-memory tables before OOM notification
$availableMemoryMb = GetAvailableHKMemoryMB $availableMemoryMB
}
else {
if ($maxPercent -igt $maxHekatonPercent) {
$sqlServerMemoryMB = 100 * ($availableMemoryMB / $maxPercent);
$availableMemoryMB = GetAvailableHKMemoryMB $sqlServerMemoryMB
}
}
#calculate metrix
$usedMemoryPercent = 100 * $usedMemoryMB/$availableMemoryMB
$freeMemoryMB = $availableMemoryMB - $usedMemoryMB
$freeMemoryPercent = 100 - $usedMemoryPercent
#save to propertybag
$bag = $api.CreatePropertyBag()
$bag.AddValue("Database", $dbName)
$bag.AddValue("FileGroupGuid", $resPool.filegroup_guid)
$bag.AddValue("PoolID", $poolID )
$bag.AddValue("PoolUsedMemoryMB", $usedMemoryMB)
$bag.AddValue("PoolUsedMemoryPercent", $usedMemoryPercent)
$bag.AddValue("PoolAvailableMemoryMB", $availableMemoryMb)
$bag.AddValue("PoolFreeMemoryMB", $freeMemoryMB)
$bag.AddValue("PoolFreeMemoryPercent", $freeMemoryPercent)
$bag.AddValue("MemoryForSQLInstanceMB", $maxMemorySQL)
$bag.AddValue("TotalMemoryOnTheServerMB", $totalPhysicalMemory)
$bag

if($DEBUG_MODE -eq 1) {
$DEBUG_MSG += "Database: $dbName Pool ID: $poolID Used: $usedMemoryMB MB ( $usedMemoryPercent% ) Available: $availableMemoryMB MB Free: $freeMemoryMB MB ( $freeMemoryPercent% ) " +
"MemoryForSQLInstance $maxMemorySQL MB Total Memory On The Server $totalPhysicalMemory MB" + [Environment]::NewLine
}
}
Start-Sleep -m $delay
}
catch {
#check if it
$flag = AlwaysOnReplicaAllowConnections $SqlConnectionMaster $dbID
if($flag -ne 0)
{
$log_error_msg = $true
$error_msg += "Error occurred during Request count data source executing.{0}Computer: {1} {0}Reason: {2} {0}Position:{3} {0}Offset:{4} {0}Instance:{5} {0}Database:{6}" -f [Environment]::NewLine, $env:COMPUTERNAME, $_.Exception.Message, $_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.OffsetInLine, $InstanceName, $dbName
}
}
}
}
catch {
$log_error_msg = $true
$error_msg += "Error occurred during Request count data source executing.{0}Computer:{1} {0}Reason: {2} {0}Position:{3} {0}Offset:{4} {0}Instance:{5}" -f [Environment]::NewLine, $env:COMPUTERNAME, $_.Exception.Message, $_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.OffsetInLine, $InstanceName
$error_msg += "{0}Detailed error output: {1}" -f [Environment]::NewLine, [String]::Join("{0}--------{0}" -f [Environment]::NewLine, $Error.ToArray())
}
finally{
if($SqlConnectionMaster -ne $null){
$SqlConnectionMaster.Dispose()
}
}

#if an error takes place when message has to be printed
if($log_error_msg -eq $true) {
$header = "Management Group: $Target/ManagementGroup/Name$. Script: {0} Module: {1} Version: {2}" -f ($MyInvocation.MyCommand).Name.ToString(), $DEBUG_MODULE, $MANAGEMENT_PACK_VERSION
$api.LogScriptEvent($header, $SCRIPT_EVENT_ID, $ERROR_EVENT_TYPE, $error_msg)
}
#debug information
PrintDebug $api $error_msg
}

#SQL2016Constants.ps1

$MANAGEMENT_PACK_VERSION = "7.0.7.0"

$ManagementGroupName = '$Target/ManagementGroup/Name$'
$ManagementGroupID = '$Target/ManagementGroup/Id$'

#SqlConnectionCommon.ps1

function SqlTcpPortIsEmpty($tcpPort) {
return [string]::IsNullOrEmpty($tcpPort) -or $tcpPort -eq '0'
}

function GetDataSource($dataSource, $tcpPort) {
$targetDataSource = $dataSource

if (!(SqlTcpPortIsEmpty $tcpPort)){
$nameParts = $dataSource.Split("{\}")
$targetDataSource = $nameParts[0] + "," + $tcpPort
}
return $targetDataSource
}

function GetSqlServerHostName(
[string] $strDNSComputerName,
[string] $instanceName,
[string] $namespace) {

$serviceName = GetSQLServiceName $instanceName
$escapedServiceName = EscapeWmiString $serviceName

$advProperties = Get-WmiObject -ComputerName $strDNSComputerName -Namespace ("ROOT\Microsoft\SqlServer\$namespace") -Class "SqlServiceAdvancedProperty" -Property "PropertyName", "PropertyNumValue", "PropertyStrValue" -Filter "SqlServiceType = 1 AND ServiceName = '$escapedServiceName' AND (PropertyName = 'Clustered' OR PropertyName = 'VSNAME')" -ErrorAction Stop

$isClustered = ($advProperties | Where {$_.PropertyName -eq "CLUSTERED"} | Select -First 1).PropertyNumValue

$hostName = 0
if($isClustered -eq 0){
$hostName = (Get-WmiObject -ComputerName $strDNSComputerName -Namespace ("ROOT\Microsoft\SqlServer\$namespace") -Class "SqlService" -Filter "SQLServiceType = 1 AND ServiceName = '$escapedServiceName'" -ErrorAction Stop | Select -First 1).HostName
}
else{
$hostName = ($advProperties | Where {$_.PropertyName -eq "VSNAME"} | Select -First 1).PropertyStrValue
}

return New-Object PSObject -Property @{HostName = $hostName; IsClustered = $isClustered}
}

function GetSqlServerHostNameEx(
[string] $strDNSComputerName,
[string] $instanceName,
[string] $namespace) {

$statePath = "SQL2016MP\SqlHostNames"
$hostValueName = EscapeCacheValueName $strDNSComputerName
$isClusteredValueName = $hostValueName + "_IsClustered"
$cacheExpirationTime = 7200

$isClustered = $null
$hostName = TryGetValueFromCache $statePath $hostValueName $cacheExpirationTime
if(![string]::IsNullOrEmpty($hostName)){
$isClustered = TryGetValueFromCache $statePath $isClusteredValueName $cacheExpirationTime
}

if(![string]::IsNullOrEmpty($hostName) -and ![string]::IsNullOrEmpty($isClustered) -and '01'.Contains($isClustered)){
return New-Object PSObject -Property @{HostName = $hostName; IsClustered = if($isClustered -eq '0') {$false} else {$true}}
}

$hostNameData = GetSqlServerHostName $strDNSComputerName $instanceName $namespace

PutValueToCache $statePath $hostValueName $hostNameData.HostName | Out-Null
PutValueToCache $statePath $isClusteredValueName "$(if($hostNameData.IsClustered){1} else {0})" | Out-Null

return $hostNameData
}

function BuildDataSourceFromParts($computerName, $instanceName, $tcpPort){
$dataSource = $computerName
if($instanceName -ne "MSSQLSERVER"){
$dataSource = "$computerName\$instanceName"
}
return GetDataSource $dataSource $tcpPort
}

function BuildConnectionString(
[String] $dataSource,
[string] $databaseName,
[String] $timeout = 100,
[String] $user = $null,
[String] $password = $null) {
$builder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder

$builder["Data Source"] = $dataSource
$builder["Initial Catalog"] = $databaseName
$builder['Connection Timeout'] = $timeout
if (($user.Length -ne 0) -and ($password.Length -ne 0)) {
$builder["User ID"] = $user
$builder["Password"] = $password
} else {
$builder["Integrated Security"] = 'SSPI'
}
return $builder.ConnectionString
}

function GetEnabledSqlServerProtocols($computerName, $instanceName) {
$protocolsWmi = Get-WmiObject -ComputerName $computerName -Namespace "root\Microsoft\SqlServer\ComputerManagement13" -Class 'ServerNetworkProtocol' -Filter "InstanceName = '$instanceName'"
$protocolsArr = @()

if($protocolsWmi -ne $null)
{
foreach($protocol in $protocolsWmi){
if($protocol.Enabled){
$protocolsArr+=($protocol.ProtocolName.ToLower())
}
}
}

return (,$protocolsArr)
}

function GetSqlServerTcpIpSettings([string] $instanceName){
$ipSettings = @{}

$settingsWmi = Get-WmiObject -Namespace 'root\Microsoft\SqlServer\ComputerManagement13' -Class 'ServerNetworkProtocolProperty' -Filter "ProtocolName = 'Tcp' and InstanceName = '$instanceName'"

$listenAllObj = $settingsWmi | Where-Object {($_.ProtocolName -eq "Tcp") -and ($_.InstanceName -eq $instanceName) -and($_.PropertyName -eq "ListenOnAllIPs")}

$listenAll = $false
if($listenAllObj.PropertyNumVal -eq 1){
$listenAll = $true
}

if($listenAll) {
$portArr = @()
$tcpIpAll = $settingsWmi | Where-Object {($_.ProtocolName -eq "Tcp") -and ($_.InstanceName -eq $instanceName) -and ($_.IPAddressName -eq "IPAll") -and (($_.PropertyName -eq "TcpPort") -or ($_.PropertyName -eq "TcpDynamicPorts")) -and ($_.PropertyStrVal -ne '')}

foreach($port in $tcpIpAll)
{
$splittedPorts = $port.PropertyStrVal.Split("{,}", [System.StringSplitOptions]::RemoveEmptyEntries) | %{$_.Trim()} | ?{-not (SqlTcpPortIsEmpty $_)}
$portArr += $splittedPorts
}
$ipSettings.Add("IPAll", $portArr);
}
else{
$ipAddresses = ($settingsWmi | Where-Object { ($_.ProtocolName -eq "Tcp") -and ($_.InstanceName -eq $instanceName) -and($_.IPAddressName -ne "")-and($_.PropertyName -eq "Enabled") -and ($_.PropertyNumVal -eq 1)})

foreach($ipAddress in $ipAddresses){
$ipAddressName = $ipAddress.IPAddressName

$ip = $settingsWmi | Where-Object {($_.ProtocolName -eq "Tcp") -and ($_.InstanceName -eq $instanceName) -and($_.IPAddressName -eq $ipAddressName)-and( $_.PropertyName -eq "IpAddress") -and ($_.PropertyStrVal -ne '')} | select -ExpandProperty PropertyStrVal

$ports = $settingsWmi | Where-Object {($_.ProtocolName -eq "Tcp") -and ($_.InstanceName -eq $instanceName) -and($_.IPAddressName -eq $ipAddressName)-and( ($_.PropertyName -eq "TcpPort") -or ($_.PropertyName -eq "TcpDynamicPorts")) -and ($_.PropertyStrVal -ne '')}

$portArr = @()
foreach($port in $ports)
{
$splittedPorts = $port.PropertyStrVal.Split("{,}", [System.StringSplitOptions]::RemoveEmptyEntries) | %{$_.Trim()} | ?{-not (SqlTcpPortIsEmpty $_)}
$portArr += $splittedPorts
}
$ipSettings.Add($ip, $portArr);
}
}
return New-Object -TypeName PSObject -Property @{'ListenAllIPs' = $listenAll; 'IpSettings' = $ipSettings }
}

function SqlTryToConnectAndValidate(
[String] $dataSource,
[string] $databaseName,
[String] $hostName,
[String] $instanceName,
[string] $timeout = 30,
[String] $user = $null,
[String] $password = $null){

$connectionString = BuildConnectionString $dataSource $databaseName $timeout $user $password
$query = "SELECT SERVERPROPERTY('MachineName') AS ServerName, @@servicename AS InstanceName"

$sqlConnection = New-Object System.Data.SqlClient.SqlConnection ($connectionString)
$sqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter ($query,$sqlConnection)
$dataSet = New-Object System.Data.DataSet
try{
$sqlConnection.Open()
$sqlAdapter.Fill($dataSet) | Out-Null

$res = $dataSet.Tables | Select -First 1
if ($res -ne $null) {
$queryServerName = ($res | Select -ExpandProperty ServerName).ToUpperInvariant()
$queryInstanceName = ($res | Select -ExpandProperty InstanceName).ToUpperInvariant()
if (($hostName.ToUpperInvariant() -eq $queryServerName) -and ($instanceName.ToUpperInvariant() -eq $queryInstanceName)) {
return $sqlConnection;
}
throw "Connection target check failed: connected to $hostName\$instanceName, but got $queryServerName\$queryInstanceName."
}
}
catch{
$sqlConnection.Dispose()
throw
}
}

function SmartConnectNTE($connectionDataSource, $databaseName, $machineName, $instanceName, $debug_user = $null, $debug_password = $null){

try{
return SmartConnect $connectionDataSource $databaseName $machineName $instanceName $debug_user $debug_password
}
catch{}

return $null
}

function SmartConnect($connectionDataSource, $databaseName, $machineName, $instanceName, $debug_user = $null, $debug_password = $null){

$hostNamesPath = "SQL2016MP\SqlHostNames"

$hostValueName = EscapeCacheValueName $machineName
$instanceValueName = EscapeCacheValueName $instanceName

$paramsPath = "SQL2016MP\SmartConnectParams\{0}\{1}" -f $hostValueName, $instanceValueName

$cacheExpirationTime = [int]::MaxValue

$netBiosHostName = TryGetValueFromCache $hostNamesPath $hostValueName $cacheExpirationTime
$targetDataSource = TryGetValueFromCache $paramsPath "TargetDataSource" $cacheExpirationTime
$timeout = TryGetValueFromCache $paramsPath "ConnectionTimeout" $cacheExpirationTime

$lastError = $null
$errorColl = @()

if($netBiosHostName -ne $null -and $targetDataSource -ne $null -and $timeout -ne $null) {
try {
return SqlTryToConnectAndValidate $targetDataSource $databaseName $netBiosHostName $instanceName $timeout $debug_user $debug_password
}
catch {
$lastError = $_.Exception
$errorColl += "Failed to connect to data source '$targetDataSource': $($_.Exception.Message)"
}
}

$connectionString = [string]::Empty
$targetDataSource = ""

$netBiosHostNameData = GetSqlServerHostNameEx $machineName $instanceName 'ComputerManagement13'
$netBiosHostName = $netBiosHostNameData.HostName
$dnsHostName = $machineName.Split(".")[0]

$enabledServerProtocols = $null
$connStr = $connectionDataSource

if(($netBiosHostName -eq $env:COMPUTERNAME) -and ($netBiosHostName -ne $dnsHostName) -and (!$netBiosHostNameData.IsClustered)) {
$enabledServerProtocols = GetEnabledSqlServerProtocols $machineName $instanceName
if($enabledServerProtocols -contains "sm") {
$connStr = 'lpc:' + (BuildDataSourceFromParts $netBiosHostName $instanceName '')
}
}

try{
$targetDataSource = GetDataSource $connStr ""
SqlTryToConnectAndValidate $targetDataSource $databaseName $netBiosHostName $instanceName 15 $debug_user $debug_password
PutValueToCache $paramsPath "TargetDataSource" $targetDataSource | Out-Null
PutValueToCache $paramsPath "ConnectionTimeout" 15 | Out-Null
return
}
catch{
$lastError = $_.Exception
$errorColl += "Failed to connect to data source '$targetDataSource': $($_.Exception.Message)"
}

if($enabledServerProtocols -eq $null){
$enabledServerProtocols = GetEnabledSqlServerProtocols $machineName $instanceName
}

if ($enabledServerProtocols -contains "tcp") {
$tcpIpSettings = GetSqlServerTcpIpSettings $instanceName
$pathArray = $connectionDataSource.Split("{'\'}")
$targetName = $pathArray[0]

if ($tcpIpSettings.ListenAllIps) {
foreach($port in $tcpIpSettings.IpSettings["IPAll"]){
try {
$targetDataSource = GetDataSource $targetName $port
SqlTryToConnectAndValidate $targetDataSource $databaseName $netBiosHostName $instanceName 10 $debug_user $debug_password
PutValueToCache $paramsPath "TargetDataSource" $targetDataSource | Out-Null
PutValueToCache $paramsPath "ConnectionTimeout" 10 | Out-Null
return
}
catch {
$lastError = $_.Exception
$errorColl += "Failed to connect to data source '$targetDataSource': $($_.Exception.Message)"
}
}
}
else{
$upc = New-Object "System.Collections.Generic.HashSet[string]"
foreach($portArr in $tcpIpSettings.IpSettings.Values){
if(($portArr.Length -gt 0) -and $upc.Add($portArr[0])){
try {
$targetDataSource = GetDataSource $targetName $portArr[0]
SqlTryToConnectAndValidate $targetDataSource $databaseName $netBiosHostName $instanceName 10 $debug_user $debug_password
PutValueToCache $paramsPath "TargetDataSource" $targetDataSource | Out-Null
PutValueToCache $paramsPath "ConnectionTimeout" 10 | Out-Null
return
}
catch {
$lastError = $_.Exception
$errorColl += "Failed to connect to data source '$targetDataSource': $($_.Exception.Message)"
}
}
}
foreach($pair in $tcpIpSettings.IpSettings.GetEnumerator()){
foreach($port in $pair.Value){
try {
$targetDataSource = GetDataSource $pair.Key $port
SqlTryToConnectAndValidate $targetDataSource $databaseName $netBiosHostName $instanceName 10 $debug_user $debug_password
PutValueToCache $paramsPath "TargetDataSource" $targetDataSource | Out-Null
PutValueToCache $paramsPath "ConnectionTimeout" 10 | Out-Null
return
}
catch {
$lastError = $_.Exception
$errorColl += "Failed to connect to data source '$targetDataSource': $($_.Exception.Message)"
}
}
}
}
}
throw [Exception] ("Cannot connect to the target Sql Server instance.`nConnection log:`n" + [string]::Join([Environment]::NewLine, $errorColl)) #, $lastError
}

function SqlQueryTablesCommon($sqlConnection, $inputQuery, [bool]$useDbName, $dbName, $queryParams){
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$dataSet = New-Object System.Data.DataSet
try {
$query = $inputQuery
if($useDbName) {
$query = $inputQuery -f ($dbName -replace '"', '""')
}
$sqlCmd.CommandText = $query
$sqlCmd.Connection = $sqlConnection
$sqlCmd.CommandTimeout = 60000
for ($i = 0; $i -lt $queryParams.Length; $i++) {
$sqlCmd.Parameters.AddWithValue(("@p" + ($i+1)), [object]($queryParams[$i])) | Out-Null
}
$sqlAdapter.SelectCommand = $sqlCmd
$sqlAdapter.Fill($dataSet) | Out-Null
if(($dataSet.Tables.Count -eq 0) -or ($DataSet.Tables[0] -eq $null))
{
throw 'Can not query data from {0} database. Please check read permissions for this db.' -f $SqlConnection.Database
}
}
finally{
$sqlAdapter.Dispose()
$sqlCmd.Dispose()
}
return $dataSet.Tables
}

function SqlQueryTables($sqlConnection, $query) {
return SqlQueryTablesCommon $sqlConnection $query $false $null $args
}

function SqlConnQueryTables($sqlConnection, $query, $dbName) {
return SqlQueryTablesCommon $sqlConnection $query $true $dbName $args
}

function SqlQueryRows($sqlConnection, $query){
return (SqlQueryTablesCommon $sqlConnection $query $false $null $args | Select -First 1).Rows
}

function SqlConnQueryRows($sqlConnection, $query, $dbName){
return (SqlQueryTablesCommon $sqlConnection $query $true $dbName $args | Select -First 1).Rows
}

function SqlQueryScalar($SqlConnection, $query) {
$sqlAdapter = New-Object 'System.Data.SqlClient.SqlDataAdapter' ($query, $SqlConnection)
$dataSet = New-Object System.Data.DataSet
$sqlAdapter.SelectCommand.CommandTimeout = 60000
$SqlAdapter.Fill($dataSet) | Out-Null
if(($dataSet.Tables.Count -eq 0) -or ($DataSet.Tables[0] -eq $null))
{
throw 'Can not query data from {0} database. Please check read permissions for this db.' -f $SqlConnection.Database
}
return ($dataSet.Tables[0]|Select -first 1)[0]
}

#''' Returns -1: If DB is not in AlwaysOn
#''' Returns 0: If DB is in AlwaysOn and replica allow connections is NO
#''' Returns 1: If DB is in AlwaysOn and replica allow connections is YES
function AlwaysOnReplicaAllowConnections([System.Data.SqlClient.SqlConnection]$SqlConnection, $DatabaseID)
{
$query = " SELECT columns.id, CASE WHEN OBJECT_ID('sys.availability_replicas') IS NOT NULL THEN 1 ELSE 0 END AS HasAlwaysOn " +
" FROM master.sys.syscolumns columns where name = 'replica_id' and id = OBJECT_ID('sys.databases')"
$dr = SqlConnQueryTables $SqlConnection $query | Select -First 1

if($dr -eq $null -or $dr.HasAlwaysOn -ne 1) {
return -1
}

$query = "SELECT d.name, d.database_id, drs.is_primary_replica AS db_is_primary_replica
, CASE WHEN d.replica_id IS NULL THEN 0 ELSE 1 END AS is_replica
, CASE WHEN drs.is_primary_replica = 1 THEN ar.primary_role_allow_connections ELSE ar.secondary_role_allow_connections END AS role_allow_connections
, CASE WHEN drs.is_suspended = 0 THEN -1 ELSE suspend_reason END AS db_suspended_state
FROM sys.databases as d
JOIN sys.dm_hadr_database_replica_states drs ON drs.database_id = d.database_id
JOIN sys.availability_replicas ar on d.replica_id = ar.replica_id
WHERE drs.is_local = 1 AND d.database_id = @p1"
$rdr = SqlQueryTables $SqlConnection $query $DatabaseID | Select -First 1

if($rdr -ne $null -and $rdr.is_replica -eq 1) {
if($rdr.role_allow_connections -le 1) {
return 0
}
else {
if($rdr.db_suspended_state -gt 0 -and (!$rdr.db_is_primary_replica -or ($rdr.db_is_primary_replica -and $rdr.db_suspended_state -ne 5))){
return 0
}
return 1
}
}
return -1
}

#Common.ps1

#-------------------------------------------------------------------------------
#The function returns service or "Unknown" state
#Input:
# server - compute name
# service - system service name
# InstanceName - sql server instance name
#Output:
# service state or "Unknown" state
function GetServiceState($server, $service, $InstanceName)
{
try {
if ($service -eq "MSSQL") {
$service = "MSSQL`${0}" -f $InstanceName
}
$namespace = "root/cimv2"
$obje = Get-WmiObject -Property 'Name,State' -Namespace $namespace -ComputerName $server -Class "win32_service" -Filter "Name = '$service'" -ErrorAction SilentlyContinue
if ($obje -ne $null) {
return $obje.State
}
}
catch {
}
return "Unknown"
}

function GetSQLServiceName($InstanceName)
{
if ($InstanceName -eq "MSSQLSERVER")
{
return "MSSQLSERVER"
}
return 'MSSQL$' + $InstanceName
}

function IsNullOrZero($count)
{
return ($count -eq 0) -or ($count -eq $null)
}

function LogEventDebug($message) {
if($DEBUG_MODE -eq 1) {
$api.LogScriptEvent($DEBUG_MODULE, $SCRIPT_EVENT_ID, $INFORMATION_EVENT_TYPE, $message)
}
}

function ConvertToInt($v) {
if ($v -eq $null) {
return $null;
}
if ([int].IsInstanceOfType($v)) {
return $v;
}
$res = $null;
if([int]::TryParse($v.ToString(),[ref] $res)) {
return $res;
}
return $null;
}

function EscapeWmiString($str) {
return $str -replace "\\","\\" -replace "'","\'"
}

function CreatePathRecursive($path) {
if (Test-Path $path) {
return;
}
$parts = $path.Split('\');
$curPath = $parts[0];
for ($i=1;$i -lt $parts.Length;$i++) {
$curPath = $curPath + '\'+$parts[$i];
if (!(Test-Path $curPath)) {
New-Item -Path $curPath -ErrorAction SilentlyContinue|Out-Null
}
}
}

function PutValueToCache($path,$key, $value) {
if ($cacheExpirationTime -eq $null -or $cacheExpirationTime -eq 0) {
return;
}
$registryKey = $api.GetScriptStateKeyPath($ManagementGroupID);
$keyPath = "HKLM:\" + $registryKey + "\" + $path;
CreatePathRecursive $keyPath
try {
$creationKey = $key+"_CreationTime";
$ip = Get-ItemProperty -Path $keyPath -Name $creationKey -ErrorAction SilentlyContinue;
if ($ip -ne $null) {
Remove-ItemProperty -Path $keyPath -Name $creationKey
}

$ip = Get-ItemProperty -Path $keyPath -Name $key -ErrorAction SilentlyContinue;
# recreate key to avoid type problem
if ($ip -ne $null) {
Remove-ItemProperty -Path $keyPath -Name $key
}

# null - delete old entry
if ($value -ne $null) {
New-ItemProperty -Path $keyPath -Name $key -Value $value|Out-Null
$creationDate = [DateTime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ',[System.Globalization.CultureInfo]::InvariantCulture)
New-ItemProperty -Path $keyPath -Name $creationKey -Value $creationDate|Out-Null
}
} catch {
LogEventDebug "Someone is refreshing data at $keyPath $key or no access"
}
LogEventDebug "Set at $keyPath $key value $value"
return $ip.$key;
}

# Returns all non-expired properties from specified path in cache as hashtable
function GetValuesFromCache($statePath, $cacheExpirationTime) {

function GetActualValue($name, $props) {
$creationKey = $name + "_CreationTime"

if (($props -eq $null) -or ($props[$creationKey] -eq $null)) {
return $null;
}

[ref]$creationDate = [DateTime]::MinValue;

[System.Globalization.CultureInfo]$ci = [System.Globalization.CultureInfo]::InvariantCulture;

$v = [System.DateTime]::TryParseExact($props[$creationKey].Value,'yyyy-MM-ddTHH:mm:ssK',$ci,[System.Globalization.DateTimeStyles]::None,$creationDate);

if (!$v) {
return $null;
}
$diff = [DateTime]::UtcNow - $creationDate.Value;

if ($diff.TotalSeconds -gt $cacheExpirationTime) {
return $null;
}
return $props[$name].Value;
}


$result = @{}

$registryKey = $api.GetScriptStateKeyPath($ManagementGroupID);
$path = "HKLM:\" + $registryKey + "\" + $statePath;

if (!(Test-Path $path) -or $cacheExpirationTime -eq $null -or $cacheExpirationTime -eq 0) {
return $result;
}

$ip = Get-ItemProperty -Path $path
$properties = $ip.psobject.Properties;

$msg = "Got from $path : ";
if($properties -ne $null) {
foreach ($property in $properties) {
$pn = $property.Name;
$pv = GetActualValue $pn $properties;
if ($pv -ne $null) {
$result[$pn] = $pv
$msg += "$pn :$pv ;"
}
}
}
LogEventDebug $msg
return $result;
}

function TryGetValueFromCache($statePath, $key, $cacheExpirationTime) {
$registryKey = $api.GetScriptStateKeyPath($ManagementGroupID);
$path = "HKLM:\" + $registryKey + "\" + $statePath;

if (!(Test-Path $path) -or $cacheExpirationTime -eq $null -or $cacheExpirationTime -eq 0) {
return $null;
}

$creationKey = $key + "_CreationTime"
$regObj = Get-ItemProperty -Path $path -Name $creationKey -ErrorAction SilentlyContinue

if ($regObj -eq $null) {
return $null;
}

$creationDateStr = $regObj.$creationKey

[ref]$creationDate = [DateTime]::MinValue;

[System.Globalization.CultureInfo]$ci = [System.Globalization.CultureInfo]::InvariantCulture;

$v = [System.DateTime]::TryParseExact($creationDateStr,'yyyy-MM-ddTHH:mm:ssK',$ci,[System.Globalization.DateTimeStyles]::None,$creationDate);

if (!$v) {
return $null;
}
$diff = [DateTime]::UtcNow - $creationDate.Value;

if ($diff.TotalSeconds -gt $cacheExpirationTime) {
LogEventDebug "Cache entry at $path key $key got expired"
return $null;
}

$valueObj = Get-ItemProperty -Path $path -Name $key -ErrorAction SilentlyContinue

if ($valueObj -eq $null) {
return $null;
}

$value = $valueObj.$key
LogEventDebug "Got from $path key $key value $value"
return $value
}

function EscapeCacheValueName([string] $name) {
return $name -replace "_", "__"
}
Main
</Script></ScriptBody>
<SnapIns/>
<Parameters>
<Parameter>
<Name>computerName</Name>
<Value>$Config/ServerName$</Value>
</Parameter>
<Parameter>
<Name>sqlInstanceName</Name>
<Value>$Config/SqlInstanceName$</Value>
</Parameter>
<Parameter>
<Name>connectionString</Name>
<Value>$Target/Host/Host/Property[Type="SQL2016Core!Microsoft.SQLServer.2016.DBEngine"]/ConnectionString$</Value>
</Parameter>
<Parameter>
<Name>tcpPort</Name>
<Value>$Target/Host/Host/Property[Type="SQL2016Core!Microsoft.SQLServer.2016.DBEngine"]/TcpPort$</Value>
</Parameter>
<Parameter>
<Name>delay</Name>
<Value>$Config/ScriptDelayMsec$</Value>
</Parameter>
<Parameter>
<Name>serviceName</Name>
<Value>$Target/Host/Host/Property[Type="SQL2016Core!Microsoft.SQLServer.2016.DBEngine"]/ServiceName$</Value>
</Parameter>
</Parameters>
<TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>
<StrictErrorHandling>true</StrictErrorHandling>
</ProbeAction>
<ConditionDetection ID="PoolFilter" TypeID="System!System.ExpressionFilter">
<Expression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="String">Property[@Name='PoolID']</XPathQuery>
</ValueExpression>
<Operator>Equal</Operator>
<ValueExpression>
<Value Type="Integer">$Config/PoolID$</Value>
</ValueExpression>
</SimpleExpression>
</Expression>
</ConditionDetection>
</MemberModules>
<Composition>
<Node ID="PoolFilter">
<Node ID="Probe">
<Node ID="Scheduler"/>
</Node>
</Node>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.PropertyBagData</OutputType>
</DataSourceModuleType>