@@ -16,6 +16,7 @@ using namespace System.Reflection
1616using namespace System.Text
1717using namespace System.Threading
1818using namespace System.Threading.Tasks
19+ using namespace System.Runtime.Caching
1920
2021# Because we are changing state, we want to be safe
2122# TODO: Implement logic to only fail on module installs, such that one module failure doesn't prevent others from installing.
@@ -276,8 +277,8 @@ function Install-ModuleFast {
276277 switch ($PSCmdlet.ParameterSetName ) {
277278 ' Specification' {
278279 foreach ($ModuleToInstall in $Specification ) {
279- $duplicate = $ModulesToInstall.Add ($ModuleToInstall )
280- if ($duplicate ) {
280+ $isDuplicate = -not $ModulesToInstall.Add ($ModuleToInstall )
281+ if ($isDuplicate ) {
281282 Write-Warning " $ModuleToInstall was specified twice, skipping duplicate"
282283 }
283284 }
@@ -1315,6 +1316,8 @@ enum HashtableType {
13151316
13161317# region Helpers
13171318
1319+ # This is used as a simple caching mechanism to avoid multiple simultaneous fetches for the same info. For example, Az.Accounts. It will persist for the life of the PowerShell session
1320+ [MemoryCache ]$SCRIPT :RequestCache = [MemoryCache ]::new(' PowerShell-ModuleFast-RequestCache' )
13181321function Get-ModuleInfoAsync {
13191322 [CmdletBinding ()]
13201323 [OutputType ([Task [String ]])]
@@ -1337,14 +1340,19 @@ function Get-ModuleInfoAsync {
13371340 $ModuleId = $Name
13381341
13391342 # This call should be cached by httpclient after first attempt to speed up future calls
1340- # TODO: Only select supported versions
1341- # TODO: Cache this index more centrally to be used for other services
13421343 Write-Debug (' {0}fetch registration index from {1}' -f ($ModuleId ? " $ModuleId `: " : ' ' ), $Endpoint )
1343- if (-not $SCRIPT :__registrationIndex ) {
1344- $SCRIPT :__registrationIndex = $HttpClient.GetStringAsync ($Endpoint , $CancellationToken ).GetAwaiter().GetResult()
1344+ $endpointTask = $SCRIPT :RequestCache [$Endpoint ]
1345+
1346+ if ($endpointTask ) {
1347+ Write-Debug " REQUEST CACHE HIT for Registration Index $Endpoint "
1348+ } else {
1349+ $endpointTask = $HttpClient.GetStringAsync ($Endpoint , $CancellationToken )
1350+ $SCRIPT :RequestCache [$Endpoint ] = $endpointTask
13451351 }
13461352
1347- $registrationBase = $SCRIPT :__registrationIndex
1353+ $registrationIndex = $endpointTask.GetAwaiter ().GetResult()
1354+
1355+ $registrationBase = $registrationIndex
13481356 | ConvertFrom-Json
13491357 | Select-Object - ExpandProperty resources
13501358 | Where-Object {
@@ -1353,13 +1361,19 @@ function Get-ModuleInfoAsync {
13531361 | Sort-Object - Property ' @type' - Descending
13541362 | Select-Object - ExpandProperty ' @id' - First 1
13551363
1356- $uri = " $registrationBase /$ ( $ModuleId.ToLower ()) /$Path "
1364+ $Uri = " $registrationBase /$ ( $ModuleId.ToLower ()) /$Path "
13571365 }
13581366
1359- # TODO: System.Text.JSON serialize this with fancy generic methods in 7.3?
1360- Write-Debug (' {0}fetch info from {1}' -f ($ModuleId ? " $ModuleId `: " : ' ' ), $uri )
1367+ $requestTask = $SCRIPT :RequestCache [$Uri ]
13611368
1362- return $HttpClient.GetStringAsync ($uri , $CancellationToken )
1369+ if ($requestTask ) {
1370+ Write-Debug " REQUEST CACHE HIT for $Uri "
1371+ } else {
1372+ Write-Debug (' {0}fetch info from {1}' -f ($ModuleId ? " $ModuleId `: " : ' ' ), $uri )
1373+ $requestTask = $HttpClient.GetStringAsync ($uri , $CancellationToken )
1374+ $SCRIPT :RequestCache [$Uri ] = $requestTask
1375+ }
1376+ return $requestTask
13631377}
13641378
13651379function Add-DestinationToPSModulePath {
0 commit comments