@@ -16,6 +16,7 @@ using namespace System.Reflection
16
16
using namespace System.Text
17
17
using namespace System.Threading
18
18
using namespace System.Threading.Tasks
19
+ using namespace System.Runtime.Caching
19
20
20
21
# Because we are changing state, we want to be safe
21
22
# 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 {
276
277
switch ($PSCmdlet.ParameterSetName ) {
277
278
' Specification' {
278
279
foreach ($ModuleToInstall in $Specification ) {
279
- $duplicate = $ModulesToInstall.Add ($ModuleToInstall )
280
- if ($duplicate ) {
280
+ $isDuplicate = -not $ModulesToInstall.Add ($ModuleToInstall )
281
+ if ($isDuplicate ) {
281
282
Write-Warning " $ModuleToInstall was specified twice, skipping duplicate"
282
283
}
283
284
}
@@ -1315,6 +1316,8 @@ enum HashtableType {
1315
1316
1316
1317
# region Helpers
1317
1318
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' )
1318
1321
function Get-ModuleInfoAsync {
1319
1322
[CmdletBinding ()]
1320
1323
[OutputType ([Task [String ]])]
@@ -1337,14 +1340,19 @@ function Get-ModuleInfoAsync {
1337
1340
$ModuleId = $Name
1338
1341
1339
1342
# 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
1342
1343
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
1345
1351
}
1346
1352
1347
- $registrationBase = $SCRIPT :__registrationIndex
1353
+ $registrationIndex = $endpointTask.GetAwaiter ().GetResult()
1354
+
1355
+ $registrationBase = $registrationIndex
1348
1356
| ConvertFrom-Json
1349
1357
| Select-Object - ExpandProperty resources
1350
1358
| Where-Object {
@@ -1353,13 +1361,19 @@ function Get-ModuleInfoAsync {
1353
1361
| Sort-Object - Property ' @type' - Descending
1354
1362
| Select-Object - ExpandProperty ' @id' - First 1
1355
1363
1356
- $uri = " $registrationBase /$ ( $ModuleId.ToLower ()) /$Path "
1364
+ $Uri = " $registrationBase /$ ( $ModuleId.ToLower ()) /$Path "
1357
1365
}
1358
1366
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 ]
1361
1368
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
1363
1377
}
1364
1378
1365
1379
function Add-DestinationToPSModulePath {
0 commit comments