Skip to content

Commit 3f0d2e5

Browse files
authored
Add concurrency for parent packages (#2016)
1 parent e160b6f commit 3f0d2e5

8 files changed

Lines changed: 796 additions & 306 deletions

File tree

src/code/ContainerRegistryServerAPICalls.cs

Lines changed: 156 additions & 80 deletions
Large diffs are not rendered by default.

src/code/FindHelper.cs

Lines changed: 273 additions & 27 deletions
Large diffs are not rendered by default.

src/code/InstallHelper.cs

Lines changed: 183 additions & 172 deletions
Large diffs are not rendered by default.

src/code/NuGetServerAPICalls.cs

Lines changed: 161 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,9 @@ public override Task<FindResults> FindVersionAsync(string packageName, string ve
6363
});
6464
var filterBuilder = queryBuilder.FilterBuilder;
6565

66-
// We need to explicitly add 'Id eq <packageName>' whenever $filter is used, otherwise arbitrary results are returned.
67-
filterBuilder.AddCriterion($"Id eq '{packageName}'");
68-
filterBuilder.AddCriterion($"NormalizedVersion eq '{packageName}'");
69-
66+
// We need to explicitly add 'Id eq <packageName>' whenever $filter is used, otherwise arbitrary results are returned.
67+
filterBuilder.AddCriterion($"Id eq '{packageName}'");
68+
filterBuilder.AddCriterion($"NormalizedVersion eq '{version}'");
7069
var requestUrl = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}";
7170
string response = HttpRequestCallAsync(requestUrl, debugMsgs, out ErrorRecord errRecord);
7271
FindResults findResponse = new FindResults(stringResponse: new string[] { response }, hashtableResponse: emptyHashResponses, responseType: FindResponseType);
@@ -87,13 +86,46 @@ public override Task<FindResults> FindVersionAsync(string packageName, string ve
8786
public override Task<FindResults> FindVersionGlobbingAsync(string packageName, VersionRange versionRange, bool includePrerelease, ResourceType type, bool getOnlyLatest, ConcurrentQueue<ErrorRecord> errorMsgs, ConcurrentQueue<string> warningMsgs, ConcurrentQueue<string> debugMsgs, ConcurrentQueue<string> verboseMsgs)
8887
{
8988
debugMsgs.Enqueue("In NuGetServerAPICalls::FindVersionGlobbingAsync()");
90-
FindResults findResponse = FindVersionGlobbing(packageName, versionRange, includePrerelease, type, getOnlyLatest, out ErrorRecord errRecord);
89+
List<string> responses = new List<string>();
90+
int skip = 0;
91+
92+
var initialResponse = FindVersionGlobbingAsync(packageName, versionRange, includePrerelease, skip, getOnlyLatest, debugMsgs, out ErrorRecord errRecord);
9193
if (errRecord != null)
9294
{
9395
errorMsgs.Enqueue(errRecord);
96+
return Task.FromResult(new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: FindResponseType));
9497
}
9598

96-
return Task.FromResult(findResponse);
99+
responses.Add(initialResponse);
100+
101+
if (!getOnlyLatest)
102+
{
103+
int initialCount = GetCountFromResponse(initialResponse, out errRecord);
104+
if (errRecord != null)
105+
{
106+
errorMsgs.Enqueue(errRecord);
107+
return Task.FromResult(new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: FindResponseType));
108+
}
109+
110+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
111+
112+
while (count > 0)
113+
{
114+
// skip 100
115+
skip += 100;
116+
var tmpResponse = FindVersionGlobbingAsync(packageName, versionRange, includePrerelease, skip, getOnlyLatest, debugMsgs, out errRecord);
117+
if (errRecord != null)
118+
{
119+
errorMsgs.Enqueue(errRecord);
120+
return Task.FromResult(new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: FindResponseType));
121+
}
122+
123+
responses.Add(tmpResponse);
124+
count--;
125+
}
126+
}
127+
128+
return Task.FromResult(new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: FindResponseType));
97129
}
98130
/// <summary>
99131
/// Find method which allows for searching for all packages from a repository and returns latest version for each.
@@ -122,7 +154,7 @@ public override FindResults FindAll(bool includePrerelease, ResourceType type, o
122154
return new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: FindResponseType);
123155
}
124156

125-
int count = initialCount / 6000;
157+
int count = (int)Math.Ceiling((double)initialCount / 6000) - 1;
126158
// if more than 100 count, loop and add response to list
127159
while (count > 0)
128160
{
@@ -166,7 +198,7 @@ public override FindResults FindTags(string[] tags, bool includePrerelease, Reso
166198
return new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: FindResponseType);
167199
}
168200

169-
int count = initialCount / 100;
201+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
170202
// if more than 100 count, loop and add response to list
171203
while (count > 0)
172204
{
@@ -238,7 +270,19 @@ public override FindResults FindName(string packageName, bool includePrerelease,
238270
public override Task<FindResults> FindNameAsync(string packageName, bool includePrerelease, ResourceType type, ConcurrentQueue<ErrorRecord> errorMsgs, ConcurrentQueue<string> warningMsgs, ConcurrentQueue<string> debugMsgs, ConcurrentQueue<string> verboseMsgs)
239271
{
240272
debugMsgs.Enqueue("In NuGetServerAPICalls::FindNameAsync()");
241-
FindResults findResponse = FindName(packageName, includePrerelease, type, out ErrorRecord errRecord);
273+
var queryBuilder = new NuGetV2QueryBuilder(new Dictionary<string, string>{
274+
{ "id", $"'{packageName}'" },
275+
});
276+
var filterBuilder = queryBuilder.FilterBuilder;
277+
278+
filterBuilder.AddCriterion(includePrerelease ? "IsAbsoluteLatestVersion" : "IsLatestVersion");
279+
280+
// We need to explicitly add 'Id eq <packageName>' whenever $filter is used, otherwise arbitrary results are returned.
281+
filterBuilder.AddCriterion($"Id eq '{packageName}'");
282+
283+
var requestUrl = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}";
284+
string response = HttpRequestCallAsync(requestUrl, debugMsgs, out ErrorRecord errRecord);
285+
FindResults findResponse = new FindResults(stringResponse: new string[] { response }, hashtableResponse: emptyHashResponses, responseType: FindResponseType);
242286
if (errRecord != null)
243287
{
244288
errorMsgs.Enqueue(errRecord);
@@ -310,7 +354,7 @@ public override FindResults FindNameGlobbing(string packageName, bool includePre
310354
return new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: FindResponseType);
311355
}
312356

313-
int count = initialCount / 100;
357+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
314358
// if more than 100 count, loop and add response to list
315359
while (count > 0)
316360
{
@@ -355,7 +399,7 @@ public override FindResults FindNameGlobbingWithTag(string packageName, string[]
355399
return new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: FindResponseType);
356400
}
357401

358-
int count = initialCount / 100;
402+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
359403
// if more than 100 count, loop and add response to list
360404
while (count > 0)
361405
{
@@ -404,7 +448,7 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange
404448
return new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: FindResponseType);
405449
}
406450

407-
int count = initialCount / 100;
451+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
408452

409453
while (count > 0)
410454
{
@@ -521,7 +565,19 @@ public override Stream InstallPackage(string packageName, string packageVersion,
521565
public override Task<Stream> InstallPackageAsync(string packageName, string packageVersion, bool includePrerelease, ConcurrentQueue<ErrorRecord> errorMsgs, ConcurrentQueue<string> warningMsgs, ConcurrentQueue<string> debugMsgs, ConcurrentQueue<string> verboseMsgs)
522566
{
523567
debugMsgs.Enqueue("In NuGetServerAPICalls::InstallPackageAsync()");
524-
Stream results = InstallPackage(packageName, packageVersion, includePrerelease, out ErrorRecord errRecord);
568+
Stream results = new MemoryStream();
569+
if (string.IsNullOrEmpty(packageVersion))
570+
{
571+
errorMsgs.Enqueue(new ErrorRecord(
572+
exception: new ArgumentNullException($"Package version could not be found for {packageName}"),
573+
"PackageVersionNullOrEmptyError",
574+
ErrorCategory.InvalidArgument,
575+
_cmdletPassedIn));
576+
577+
return Task.FromResult(results);
578+
}
579+
580+
results = InstallVersionAsync(packageName, packageVersion, debugMsgs, out ErrorRecord errRecord);
525581
if (errRecord != null)
526582
{
527583
errorMsgs.Enqueue(errRecord);
@@ -628,6 +684,55 @@ private HttpContent HttpRequestCallForContent(string requestUrl, out ErrorRecord
628684
return content;
629685
}
630686

687+
/// <summary>
688+
/// Helper method that makes the HTTP request for install APIs on worker threads; enqueues diagnostics instead of writing to cmdlet streams.
689+
/// </summary>
690+
private HttpContent HttpRequestCallForContentAsync(string requestUrl, ConcurrentQueue<string> debugMsgs, out ErrorRecord errRecord)
691+
{
692+
debugMsgs.Enqueue("In NuGetServerAPICalls::HttpRequestCallForContentAsync()");
693+
errRecord = null;
694+
HttpContent content = null;
695+
696+
try
697+
{
698+
debugMsgs.Enqueue($"Request url is: '{requestUrl}'");
699+
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
700+
701+
content = SendRequestForContentAsync(request, _sessionClient).GetAwaiter().GetResult();
702+
}
703+
catch (HttpRequestException e)
704+
{
705+
errRecord = new ErrorRecord(
706+
exception: e,
707+
"HttpRequestFailure",
708+
ErrorCategory.ConnectionError ,
709+
this);
710+
}
711+
catch (ArgumentNullException e)
712+
{
713+
errRecord = new ErrorRecord(
714+
exception: e,
715+
"HttpRequestFailure",
716+
ErrorCategory.InvalidData,
717+
this);
718+
}
719+
catch (InvalidOperationException e)
720+
{
721+
errRecord = new ErrorRecord(
722+
exception: e,
723+
"HttpRequestFailure",
724+
ErrorCategory.InvalidOperation,
725+
this);
726+
}
727+
728+
if (string.IsNullOrEmpty(content?.ToString()))
729+
{
730+
debugMsgs.Enqueue("Response is empty");
731+
}
732+
733+
return content;
734+
}
735+
631736
/// <summary>
632737
/// Helper method that makes the HTTP request for the NuGet server protocol url passed in for async find APIs.
633738
/// This helper writes diagnostics to the provided debug queue and avoids cmdlet stream writes.
@@ -912,6 +1017,25 @@ private string FindNameGlobbingWithTag(string packageName, string[] tags, bool i
9121017
private string FindVersionGlobbing(string packageName, VersionRange versionRange, bool includePrerelease, int skip, bool getOnlyLatest, out ErrorRecord errRecord)
9131018
{
9141019
_cmdletPassedIn.WriteDebug("In NuGetServerAPICalls::FindVersionGlobbing()");
1020+
var requestUrl = GetVersionGlobbingRequestUrl(packageName, versionRange, includePrerelease, skip, getOnlyLatest);
1021+
return HttpRequestCall(requestUrl, out errRecord);
1022+
}
1023+
1024+
/// <summary>
1025+
/// Worker-thread counterpart of FindVersionGlobbing(); enqueues diagnostics instead of writing to cmdlet streams.
1026+
/// </summary>
1027+
private string FindVersionGlobbingAsync(string packageName, VersionRange versionRange, bool includePrerelease, int skip, bool getOnlyLatest, ConcurrentQueue<string> debugMsgs, out ErrorRecord errRecord)
1028+
{
1029+
debugMsgs.Enqueue("In NuGetServerAPICalls::FindVersionGlobbingAsync()");
1030+
var requestUrl = GetVersionGlobbingRequestUrl(packageName, versionRange, includePrerelease, skip, getOnlyLatest);
1031+
return HttpRequestCallAsync(requestUrl, debugMsgs, out errRecord);
1032+
}
1033+
1034+
/// <summary>
1035+
/// Builds the FindPackagesById() request url for version-globbing searches.
1036+
/// </summary>
1037+
private string GetVersionGlobbingRequestUrl(string packageName, VersionRange versionRange, bool includePrerelease, int skip, bool getOnlyLatest)
1038+
{
9151039
//https://www.powershellgallery.com/api/v2//FindPackagesById()?id='blah'&includePrerelease=false&$filter= NormalizedVersion gt '1.0.0' and NormalizedVersion lt '2.2.5' and substringof('PSModule', Tags) eq true
9161040
//https://www.powershellgallery.com/api/v2//FindPackagesById()?id='PowerShellGet'&includePrerelease=false&$filter= NormalizedVersion gt '1.1.1' and NormalizedVersion lt '2.2.5'
9171041
// NormalizedVersion doesn't include trailing zeroes
@@ -980,9 +1104,7 @@ private string FindVersionGlobbing(string packageName, VersionRange versionRange
9801104
// We need to explicitly add 'Id eq <packageName>' whenever $filter is used, otherwise arbitrary results are returned.
9811105
filterBuilder.AddCriterion($"Id eq '{packageName}'");
9821106

983-
var requestUrl = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}";
984-
985-
return HttpRequestCall(requestUrl, out errRecord);
1107+
return $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}";
9861108
}
9871109

9881110
/// <summary>
@@ -1040,6 +1162,29 @@ private Stream InstallVersion(string packageName, string version, out ErrorRecor
10401162
return response.ReadAsStreamAsync().Result;
10411163
}
10421164

1165+
/// <summary>
1166+
/// Worker-thread counterpart of InstallVersion(); enqueues diagnostics instead of writing to cmdlet streams.
1167+
/// </summary>
1168+
private Stream InstallVersionAsync(string packageName, string version, ConcurrentQueue<string> debugMsgs, out ErrorRecord errRecord)
1169+
{
1170+
debugMsgs.Enqueue("In NuGetServerAPICalls::InstallVersionAsync()");
1171+
var requestUrl = $"{Repository.Uri}/Packages(Id='{packageName}',Version='{version}')/Download";
1172+
var response = HttpRequestCallForContentAsync(requestUrl, debugMsgs, out errRecord);
1173+
1174+
if (response is null)
1175+
{
1176+
errRecord = new ErrorRecord(
1177+
new Exception($"No content was returned by repository '{Repository.Name}'"),
1178+
"InstallFailureContentNullNuGetServer",
1179+
ErrorCategory.InvalidResult,
1180+
this);
1181+
1182+
return null;
1183+
}
1184+
1185+
return response.ReadAsStreamAsync().Result;
1186+
}
1187+
10431188
/// <summary>
10441189
/// Helper method that makes gets 'count' property from http response string.
10451190
/// The count property is used to determine the number of total results found (for pagination).

src/code/Utils.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1737,7 +1737,7 @@ public static void EnqueueIfNotNull<T>(ConcurrentQueue<T> queue, T value)
17371737
}
17381738

17391739

1740-
public static void WriteOutConcurrentQueue(PSCmdlet cmdletPassedIn, ConcurrentQueue<ErrorRecord> errorMsgs, ConcurrentQueue<string> warningMsgs, ConcurrentQueue<string> debugMsgs, ConcurrentQueue<string> verboseMsgs)
1740+
public static void WriteOutConcurrentQueue(PSCmdlet cmdletPassedIn, ConcurrentQueue<ErrorRecord> errorMsgs, ConcurrentQueue<string> warningMsgs, ConcurrentQueue<string> debugMsgs, ConcurrentQueue<string> verboseMsgs, ConcurrentQueue<InformationRecord> informationMsgs = null)
17411741
{
17421742

17431743
while (errorMsgs.TryDequeue(out ErrorRecord error))
@@ -1756,6 +1756,10 @@ public static void WriteOutConcurrentQueue(PSCmdlet cmdletPassedIn, ConcurrentQu
17561756
{
17571757
cmdletPassedIn.WriteVerbose(verboseMsg);
17581758
}
1759+
while (informationMsgs?.TryDequeue(out InformationRecord informationRecord) == true)
1760+
{
1761+
cmdletPassedIn.WriteInformation(informationRecord);
1762+
}
17591763
}
17601764

17611765
#endregion

src/code/V2ServerAPICalls.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public override FindResults FindTags(string[] tags, bool includePrerelease, Reso
206206
if (initialScriptCount != 0)
207207
{
208208
responses.Add(initialScriptResponse);
209-
int count = initialScriptCount / 100;
209+
int count = (int)Math.Ceiling((double)initialScriptCount / 100) - 1;
210210
// if more than 100 count, loop and add response to list
211211
while (count > 0)
212212
{
@@ -242,7 +242,7 @@ public override FindResults FindTags(string[] tags, bool includePrerelease, Reso
242242
if (initialModuleCount != 0)
243243
{
244244
responses.Add(initialModuleResponse);
245-
int count = initialModuleCount / 100;
245+
int count = (int)Math.Ceiling((double)initialModuleCount / 100) - 1;
246246
// if more than 100 count, loop and add response to list
247247
while (count > 0)
248248
{
@@ -296,7 +296,7 @@ public override FindResults FindCommandOrDscResource(string[] tags, bool include
296296
if (initialCount != 0)
297297
{
298298
responses.Add(initialResponse);
299-
int count = (int)Math.Ceiling((double)(initialCount / 100));
299+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
300300

301301
while (count > 0)
302302
{
@@ -596,7 +596,7 @@ public override FindResults FindNameGlobbing(string packageName, bool includePre
596596
return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType);
597597
}
598598

599-
int count = (int)Math.Ceiling((double)(initialCount / 100));
599+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
600600
// if more than 100 count, loop and add response to list
601601
while (count > 0)
602602
{
@@ -648,7 +648,7 @@ public override FindResults FindNameGlobbingWithTag(string packageName, string[]
648648
return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType);
649649
}
650650

651-
int count = (int)Math.Ceiling((double)(initialCount / 100));
651+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
652652
// if more than 100 count, loop and add response to list
653653
while (count > 0)
654654
{
@@ -704,7 +704,7 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange
704704

705705
if (!getOnlyLatest)
706706
{
707-
int count = (int)Math.Ceiling((double)(initialCount / 100));
707+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
708708

709709
while (count > 0)
710710
{
@@ -1735,7 +1735,7 @@ public override async Task<FindResults> FindVersionGlobbingAsync(string packageN
17351735

17361736
if (!getOnlyLatest)
17371737
{
1738-
int count = (int)Math.Ceiling((double)(initialCount / 100));
1738+
int count = (int)Math.Ceiling((double)initialCount / 100) - 1;
17391739

17401740
while (count > 0)
17411741
{

0 commit comments

Comments
 (0)