From 477b15da3d51a1a42e52d0599813a1a4e3942ca5 Mon Sep 17 00:00:00 2001 From: sidneyh Date: Fri, 6 May 2016 15:56:06 -0700 Subject: [PATCH 1/3] new files for telemetry --- .../PoolTelemetry.ps1 | 219 ++++++++++++++++++ .../PoolTelemetryJobRunner.ps1 | 190 +++++++++++++++ 2 files changed, 409 insertions(+) create mode 100644 samples/manage/azure-sql-db-elastic-pools/PoolTelemetry.ps1 create mode 100644 samples/manage/azure-sql-db-elastic-pools/PoolTelemetryJobRunner.ps1 diff --git a/samples/manage/azure-sql-db-elastic-pools/PoolTelemetry.ps1 b/samples/manage/azure-sql-db-elastic-pools/PoolTelemetry.ps1 new file mode 100644 index 0000000000..cbf2b40b19 --- /dev/null +++ b/samples/manage/azure-sql-db-elastic-pools/PoolTelemetry.ps1 @@ -0,0 +1,219 @@ +# ---------------------------------------------------------------------------------- +# +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# --------------------------------------------------------------------------------- +# +# Sample script for loading SQL Database telemetry for pools and elastic databases on a single server +# to a user-supplied Azure SQL database. +# +# +# See additional comments in PoolTelemetryRunner.ps1, which includes script and instructions for running +# this script as a PowerShell job, enabling data gathering for large numbers of servers in the background. +# +# +function Load-PoolTelemetryForServer { + param ( + [Parameter(Mandatory=$true)][string]$SubscriptionId, # Azure subscription owning the server from which telemetry will be gathered - see https://portal.azure.com + [Parameter(Mandatory=$true)][string]$ResourceGroupName, # name of resource group containing the server from which telemetry will be gathered - see https://portal.azure.com + [Parameter(Mandatory=$true)][string]$ServerName, # name of server from which telemetry will be gathered - e.g. "myappserver" + [Parameter(Mandatory=$true)][string]$Location, # location of server, e.g. "Australia Southeast" + [Parameter(Mandatory=$true)][PSCredential]$ServerCred, + [Parameter(Mandatory=$true)][string]$OutputServerName, # server name of telemetry database, like "telemetryserver" + [Parameter(Mandatory=$true)][string]$OutputDatabaseName, # telemetry database name, like "telemetrydb" + [Parameter(Mandatory=$true)][PSCredential]$OutputServerCred, + [Parameter(Mandatory=$true)][int]$IntervalMinutes, # interval for collection of telemetry + [Parameter(Mandatory=$true)][int]$DurationMinutes, # total duration for collection of telemetry + [Parameter(Mandatory=$true)][bool]$IncludeDatabases # indicates if telemetry should be gathered for databases as well as pools + ) + + # Create output database metrics collection tables if it does not already exist. + + $OutputServerCred.Password.MakeReadOnly() + $sqlCred = new-object ("System.Data.SqlClient.SqlCredential") -ArgumentList $OutputServerCred.UserName, $OutputServerCred.Password + + $outputConnection = New-Object ("System.Data.SqlClient.SqlConnection") "Data Source=$OutputServerName.database.windows.net;Integrated Security=false;Initial Catalog=$OutputDatabaseName" + $outputConnection.Credential = $sqlCred + $outputConnection.Open() + + $poolResourceStatsTable = "pool_resource_stats" # <<< update if a different table name is required + $dbResourceStatsTable = "db_resource_stats" # <<< update if a different table name is required + + $sql =` + "-- Create table for holding collected pool resource stats + IF NOT EXISTS (SELECT * FROM sys.objects + WHERE object_id = OBJECT_ID(N'$($poolResourceStatsTable)') AND type in (N'U')) + + BEGIN + Create Table $($poolResourceStatsTable) (subscription_guid uniqueidentifier, resource_group_name varchar(128), server_name varchar(128), location varchar(128), elastic_pool_name varchar(128), end_time datetime, + elastic_pool_DTU_limit int, avg_cpu_percent decimal(5,2), avg_data_io_percent decimal(5,2), avg_log_io_percent decimal(5,2), max_worker_percent decimal(5,2), max_session_percent decimal(5,2) + , avg_DTU_percent decimal(5,2), avg_storage_percent decimal(5,2), elastic_pool_storage_limit_mb bigint); + Create Clustered Index ci_endtime ON $($poolResourceStatsTable) (end_time); + END + + -- Create table for holding collected database resource stats + IF NOT EXISTS (SELECT * FROM sys.objects + WHERE object_id = OBJECT_ID(N'$($dbResourceStatsTable)') AND type in (N'U')) + + BEGIN + Create Table $($dbResourceStatsTable) (subscription_guid uniqueidentifier, resource_group_name varchar(128),server_name varchar(128), location varchar(128), elastic_pool_name varchar(128), database_name varchar(128), end_time datetime, + database_DTU_limit int, avg_cpu_percent decimal(5,2), avg_data_io_percent decimal(5,2), avg_log_io_percent decimal(5,2), max_worker_percent decimal(5,2), max_session_percent decimal(5,2), avg_DTU_percent decimal(5,2), db_size float); + Create Clustered Index ci_endtime ON $($dbResourceStatsTable) (end_time); + END + + -- Create a function to get aggregated metrics for a given time interval + IF NOT EXISTS (SELECT * FROM sys.objects + WHERE name = N'get_aggregated_pool_metrics' AND type in ('IF')) + EXEC sp_executesql @Statement = N' + Create function get_aggregated_pool_metrics( + @start datetime + ,@end datetime) + RETURNS TABLE + AS + RETURN + ( + SELECT + location, server_name, elastic_pool_name + ,avg([avg_dtu_percent]) as avg_eDTU_percent + ,avg([avg_cpu_percent]) as avg_cpu_percent + ,avg([avg_data_io_percent]) as avg_data_io_percent + ,avg([avg_log_io_percent]) as avg_log_io_percent + ,avg([avg_storage_percent]) as avg_storage_percent + ,max([avg_dtu_percent]) as max_of_avg_eDTU_percent + ,max([avg_cpu_percent]) as max_of_avg_cpu_percent + ,max([avg_data_io_percent]) as max_of_data_io_percent + ,max([avg_log_io_percent]) as max_of_avg_log_io_percent + ,max([avg_storage_percent]) as max_of_avg_storage_percent + ,max([max_worker_percent]) as max_workers_percent + ,max([max_session_percent]) as max_session_percent + FROM [dbo].[pool_resource_stats] + WHERE end_time between @start and @end + group by location, server_name, elastic_pool_name + )' + " + + + $outputServerFullname = $OutputServerName + '.database.windows.net' # assumes server is in Azure SQL Database + + Invoke-Sqlcmd -ServerInstance $outputServerFullName -Database $OutputDatabaseName -Username $OutputServerCred.UserName -Password $OutputServerCred.GetNetworkCredential().Password -Query $sql -ConnectionTimeout 120 -QueryTimeout 120 + + $sourceServerFullName = $ServerName + '.database.windows.net' + + $interval = $IntervalMinutes + $now = [DateTime]::UtcNow + [DateTime]$startTime = $now.AddMinutes(-$interval) # sets the start time for the first telemetry collection + [DateTime]$endTime = $now # sets the end time + [DateTime]$finishTime = $now.AddMinutes($DurationMinutes) # sets the overall finish time for a telemetry collection session + + Write-Host "Starting to collect telemetry for" $ServerName + + $poolLagMinutes = 30 # This accommodates the lag in pool-level metrics being available in the DMV, does not affect per database telemetry + + while($startTime -lt $finishTime) + { + $poolStartTime = $startTime.AddMinutes(-$poolLagMinutes) # sets the start of query window + $poolEndTime = $endTime.AddMinutes(-$poolLagMinutes) # sets end of query window + + Write-Host "Starting to collect elastic pool telemetry for period" $poolStartTime "to" $poolEndTime "(UTC)" + + # Collect metrics for all elastic pools in this server. + $sql = ` + "SELECT subscription_guid = CAST ('$($SubscriptionId)' AS uniqueidentifier), resource_group_name = '$($ResourceGroupName)', server_name = '$($ServerName)', location = '$($Location)', elastic_pool_name + , end_time, elastic_pool_dtu_limit, avg_cpu_percent, avg_data_io_percent, avg_log_write_percent as avg_log_io_percent, max_worker_percent, max_session_percent + ,(SELECT Max(v) FROM (VALUES (avg_cpu_percent), (avg_data_io_percent), (avg_log_write_percent)) AS value(v)) AS avg_DTU_percent , avg_storage_percent, elastic_pool_storage_limit_mb FROM sys.elastic_pool_resource_stats + WHERE end_time > '$($poolStartTime)' and end_time <= '$($poolEndTime)';" + + $poolResult = Invoke-Sqlcmd -ServerInstance $sourceServerFullName -Database "master" -Username $ServerCred.UserName -Password $ServerCred.GetNetworkCredential().Password -Query $sql -ConnectionTimeout 120 -QueryTimeout 3600 + + if ($poolResult -ne $null) + { + #bulk copy the pool telemetry metrics to output database + $bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $outputConnection + $bulkCopy.BulkCopyTimeout = 600 + $bulkCopy.DestinationTableName = "$poolResourceStatsTable"; + $bulkCopy.WriteToServer($poolResult); + + Write-Host "Elastic pool telemetry loaded for server" $ServerName + } + else + { + Write-Host "No elastic pool telemetry found for server" $ServerName "for period" $poolStartTime "to" $poolEndTime "(UTC)" + } + + # gather elastic database telemetry if requested + if ($IncludeDatabases) + { + # Get the list of current elastic databases + $sql =` + "select Name, elastic_pool_name from sys.databases as db + inner join sys.database_service_objectives as dbso on db.database_id = dbso.database_id + where (dbso.service_objective = 'ElasticPool') and db.Name != 'master'" + + $dbList = Invoke-Sqlcmd -ServerInstance $sourceServerFullName -Database "master" -Username $ServerCred.UserName -Password $ServerCred.GetNetworkCredential().Password -Query $sql -ConnectionTimeout 120 -QueryTimeout 3600 + + if ($dbList -ne $null) + { + Write-Host $dbList.Count "elastic databases found on server" $ServerName + + # Collect telemetry for each elastic database + foreach ($db in $dbList) + { + $sql= ` + "Declare @db_size float; + SELECT @db_size = SUM(reserved_page_count) * 8.0/1024/1024 FROM sys.dm_db_partition_stats + SELECT subscription_guid = CAST ('$($SubscriptionId)' AS uniqueidentifier), resource_group_name = '$($ResourceGroupName)',server_name = '$($ServerName)', location = '$($Location)', elastic_pool_name = '$($db.elastic_pool_name)' + , '$($db.Name)' as database_name, end_time, dtu_limit as database_dtu_limit, avg_cpu_percent, avg_data_io_percent, avg_log_write_percent as avg_log_io_percent, max_worker_percent, max_session_percent + ,(SELECT Max(v) FROM (VALUES (avg_cpu_percent), (avg_data_io_percent), (avg_log_write_percent)) AS value(v)) AS avg_DTU_percent ,@db_size as db_size FROM sys.dm_db_resource_stats + WHERE end_time > '$($startTime)' and end_time <= '$($endTime)';" + + $dbResult = Invoke-Sqlcmd -ServerInstance $SourceServerFullName -Database $db.Name -Username $ServerCred.UserName -Password $ServerCred.GetNetworkCredential().Password -Query $sql -ConnectionTimeout 120 -QueryTimeout 3600 + + if ($dbResult -ne $null) + { + #bulk copy the data to the telemetry database + $bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $outputConnection + $bulkCopy.BulkCopyTimeout = 600 + $bulkCopy.DestinationTableName = "$dbResourceStatsTable"; + $bulkCopy.WriteToServer($dbResult); + + Write-Host "Telemetry loaded for elastic database" $db.Name + } + else + { + Write-Host "No telemetry found for elastic database " $db.Name "for period" $startTime "to" $endTime + } + } + } + else + { + $now = [DateTime]::UtcNow + + Write-Host "No elastic databases found for" $ServerName "when checking at" $now + } + } + + Write-Host "Finished collection for server" $ServerName "for period" $startTime "to" $endTime + + # set up time period for next collection + $startTime = $startTime.AddMinutes($interval) + $endTime = $endTime.AddMinutes($interval) + + # end if the next period doesn't start before the finish time + If ($startTime -ge $finishTime) {break} + + Write-Host "Sleeping until" $endTime "(UTC)" + + do + { + Start-Sleep 1 + } until (([DateTime]::UtcNow) -ge $endTime) + } +} \ No newline at end of file diff --git a/samples/manage/azure-sql-db-elastic-pools/PoolTelemetryJobRunner.ps1 b/samples/manage/azure-sql-db-elastic-pools/PoolTelemetryJobRunner.ps1 new file mode 100644 index 0000000000..207500ac42 --- /dev/null +++ b/samples/manage/azure-sql-db-elastic-pools/PoolTelemetryJobRunner.ps1 @@ -0,0 +1,190 @@ +# ---------------------------------------------------------------------------------- +# +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ---------------------------------------------------------------------------------- +# +# Powershell script for loading telemetry data from elastic pools and elastic database +# into a telemetry database. To be used in conjunction with PoolTelemetry.ps1, which +# should be installed in the same directory. +# +# This script should be customized as required (see <<<) to select one or more source +# servers, and will then spawn a telemetry gathering job for each server. Each job +# will use Load-PoolTelemetryForServer in PoolTelemetry.ps1 to load telemetry for all +# pools and elastic databases on the server. Each spawned job will run for an +# extended period in the background, waking up periodically to load more telemetry. +# The interval between telemetry gathering and the total duration can be controlled +# by parameters set in this script. +# +# Pool telemetry is loaded from the server master database, database telemetry is +# optionally loaded from each elastic database. This script assumes a common +# user name and password is used to connect to all source servers and databases. +# +#------------------------------------------------------------------------------ +# +## Prompt for Azure login +Login-AzureRMAccount + +# Set the Azure subscription, needed if your Microsoft account is associated with multiple subscriptions <<< *** +$AzureSubscriptionName = '' +$Subscription = Get-AzureRmSubscription -SubscriptionName $AzureSubscriptionName | Select-AzureRmSubscription + +$SubscriptionId = $Subscription.Subscription.SubscriptionId + +## Get SQL Server credentials. NOTE: same credential assumed for all source servers <<< *** +$sourceCred = Get-Credential -Message 'User name and password for source server' -UserName ''# add user name here +$outputServerCred = Get-Credential -Message 'User name and password for telemetry database' -UserName ''# add user name here + +## Resource group and server used for source server selection <<< *** +$resourceGroupName = '' # name of resource group containing the server from which telemetry will be gathered - see https://portal.azure.com +$serverName = '' # name of server from which telemetry will be gathered - e.g. "myappserver" + +## Telemetry database server and database name <<< *** +$outputServerName = '' # server name of telemetry database, like "telemetryserver" +$outputDatabaseName = '' # telemetry database name, like "ElasticPoolTelemetry" + +## Set server list checking <<< *** +$staticServerList = $false # set to $true if server list will not change during telemetry gathering period + +## Set telemetry collection timing <<< *** +$intervalMinutes = 15 # interval between telemetry collections used by spawned server job (15-30 mins suggested) +$durationMinutes = 60 # total duration for collection of telemetry and checking servers; 0 for one time execution, or a multiple of the interval. + +# Set to $true to gather 15 sec telemetry for elastic databases. Caution: large volumes of data may be returned. <<< *** +[bool]$includeDatabases = $false # or $False + +$now = [DateTime]::UtcNow +[DateTime]$startTime = $now # sets the start time for the first telemetry collection +[DateTime]$finishTime = $now.AddMinutes($DurationMinutes) # sets the overall finish time for a telemetry collection session + +Write-Host "Starting telemetry gathering for period" $startTime "to" $finishTime + +# Initialize the jobs list +$jobs = @{} + +# Check for the server(s) from which to gather telemetry. As servers may be added or removed during the telemetry gathering +# period the server list is re-evaluated periodically + +# The following identifies the servers to be analyzed and starts a job for each server to gather telemetry data +# In each iteration the latest server list is compared to the running jobs list and additional jobs started or +# current jobs stopped as required. + +while ($startTime -le $finishTime) +{ + Write-Host "Finding servers as at" $startTime "(UTC)" + + # initialize the servers list for each iteration + $servers = [ordered]@{} + + # Use or adapt one of the following queries as needed to select the source servers from which to load telemetry. <<< *** + + ## Get a specific server + #$server = Get-AzureRmSqlServer -ResourceGroupName $resourceGroupName -ServerName $serverName + #$servers.Add($server.ServerName, $server) + + ## Get all servers in a specific resource group + #$serverList = Get-AzureRmSqlServer -ResourceGroupName $resourceGroupName + + ## Get all resources of type server in the subscription + #$resourceList = Find-AzureRmResource -ResourceType microsoft.sql/servers + + ## Get all resources of type server in a specific region + $resourceList = Find-AzureRmResource -ResourceType microsoft.sql/servers -ODataQuery "(Location eq 'australiasoutheast' or Location eq 'australiaeast')" + + ## Get all resources of type server with common name pattern + #$resourceList = Find-AzureRmResource -ResourceType Microsoft.Sql/servers -ResourceNameContains '' + + # If selecting resources via a $resourceList convert to an equivalent $serverList + foreach ($resource in $resourceList) + { + $server = Get-AzureRmSqlServer -ResourceGroupName $resource.ResourceGroupName -ServerName $resource.Name + $servers.Add($server.ServerName, $server) + } + + Write-Host $servers.Count "servers found" + + $scriptPath = "$PSScriptRoot\PoolTelemetry.ps1" + $initScript = (Get-Command $scriptPath).ScriptBlock + + # For each server, start a job to collect telemetry. Set job name to the server name and put the job in $jobs + foreach($server in $servers.Values) + { + if ($jobs.Contains($server.ServerName) -eq $false) + { + $job = Start-Job -Name $server.ServerName -ScriptBlock { + param ($sp, $inc, $sub, $rgn, $sn, $loc, $sc, $osn, $odn, $osc, $im, $dm) + . $sp + Load-PoolTelemetryForServer -IncludeDatabases $inc ` + -SubscriptionId $sub -ResourceGroupName $rgn -ServerName $sn -Location $loc -ServerCred $sc -OutputServerName $osn -OutputDatabaseName $odn -OutputServerCred $osc -IntervalMinutes $im -DurationMinutes $dm} ` + -ArgumentList $scriptPath, $includeDatabases, $SubscriptionId, $server.ResourceGroupName, $server.ServerName, $server.Location, $sourceCred, $outputServerName, $outputDatabaseName, $outputServerCred, $intervalMinutes, $durationMinutes + + $jobs.Add($server.ServerName, $job) + + Write-Host "Job started for server" $server.ServerName + + # Following is useful for debugging changes to PoolTelemetry.ps1. Best used with a single server unless you set the duration to 0 + #. $scriptPath + #Load-PoolTelemetryForServer -IncludeDatabases $IncludeDatabases -SubscriptionId $SubscriptionId -ResourceGroupName $server.ResourceGroupName ` + # -ServerName $server.ServerName -Location $server.Location -ServerCred $sourceCred -OutputServerName $outputServerName ` + # -OutputDatabaseName $outputDatabaseName -OutputServerCred $outputServerCred -IntervalMinutes $intervalMinutes -DurationMinutes $durationMinutes + } + } + + # if server list doesn't change then no need to re-evaluate server list or manage the jobs. Already spawned jobs will continue to run. + if ($staticServerList) {break} + + # Stop previously started jobs if the server has been deleted + # For each started job in $jobs, check if the server is still in the most recent server list; if not, stop the job + foreach($job in $jobs.Values) + { + if ($servers.Contains($job.Name) -eq $false) + { + Write-Host "Server" $server.ServerName "no longer exists. Stopping its telemetry gathering job" + + Stop-Job $job.Name + + # Remove-Job + # leaving above commented-out allows use of Receive-Job to inspect the trace info emitted by the job + } + } + + # set up start time for next evaluation of the server list + $startTime = $startTime.AddMinutes($intervalMinutes) + + # sleep until next start time to ensure telemetry continues to be gathered + Write-Host "Sleeping until" $startTime "(UTC)" + + do + { + Start-Sleep 1 + + } until (([DateTime]::UtcNow) -ge $startTime) +} + +# For a static server list this runner script terminates after one pass and the jobs are left running +# Otherwise the jobs are stopped as they will at this point have concluded telemetry collection. +# In both cases Job trace output can be inspected with Receive-Job unless the jobs are removed. +# use Get-Job and Receive-Job [n] -Keep where n is the job number to see the output. + +if (-not $staticServerList) +{ + # gathering period is complete, now stop all the jobs + + Write-Host "Session complete, stopping all jobs" + + Stop-Job * + + #Remove-Job * + # leaving above commented-out allows use of Receive-Job to inspect the trace info emitted by the job + + get-job +} + From 1f2380d0fd8dcd58b44d77924a07f8838bb1f385 Mon Sep 17 00:00:00 2001 From: sidneyh Date: Fri, 6 May 2016 16:39:05 -0700 Subject: [PATCH 2/3] readme --- .../azure-sql-db-elastic-pools/readme.md | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 samples/manage/azure-sql-db-elastic-pools/readme.md diff --git a/samples/manage/azure-sql-db-elastic-pools/readme.md b/samples/manage/azure-sql-db-elastic-pools/readme.md new file mode 100644 index 0000000000..dc4e11ae50 --- /dev/null +++ b/samples/manage/azure-sql-db-elastic-pools/readme.md @@ -0,0 +1,77 @@ +# Sample name + +Briefly describe what the sample does. Describe the benefit of running this sample. + + + +![Alt text](/media/image-name.png "") + + +### Contents + +[About this sample](#about-this-sample)
+[Before you begin](#before-you-begin)
+[Run this sample](#run-this-sample)
+[Sample details](#sample-details)
+[Disclaimers](#disclaimers)
+[Related links](#related-links)
+ + + + +## About this sample + + +1. **Applies to:** SQL Server 2016 (or higher), Azure SQL Database, Azure SQL Data Warehouse, Parallel Data Warehouse +1. **Key features:*Elastic pools* +1. **Workload:** +1. **Programming Language:*PowerShell* +1. **Authors:*sriniachyara* +1. **Update history:*5/6/16* + + + +## Before you begin + +To run this sample, you need the following prerequisites. + +**Software prerequisites:** + + +1. Azure SQL Database +2. Azure PowerShell + +**Azure prerequisites:** + + +1. Permission to create an Azure SQL Database + + + +## Run this sample + + + +1. Open the ps in PowerShell. + +2. Fill in the values for Resource Manager name, server name, user name, password. + + + + +## Sample details + +Collects data from the listed server. + + + +## Disclaimers +The code included in this sample is not intended to be ... + + + +## Related Links + + +For more information, see these articles: + From 8904f6143271de35474a6fa74be09a39aad5893c Mon Sep 17 00:00:00 2001 From: sidneyh Date: Fri, 6 May 2016 16:48:10 -0700 Subject: [PATCH 3/3] users guide --- .../Pool Telemetry PS User Guide.docx | Bin 0 -> 30882 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 samples/manage/azure-sql-db-elastic-pools/Pool Telemetry PS User Guide.docx diff --git a/samples/manage/azure-sql-db-elastic-pools/Pool Telemetry PS User Guide.docx b/samples/manage/azure-sql-db-elastic-pools/Pool Telemetry PS User Guide.docx new file mode 100644 index 0000000000000000000000000000000000000000..b8e1b65b1eebd46b3f7d6c8f693910b9207a0104 GIT binary patch literal 30882 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