Skip to content

Commit 4c85f5b

Browse files
authored
Improve slow searches involving installed items (#5701)
Fixes #5629 (and probably quite a few more list/upgrade some-query-provided performance related issues) While things could still be made better, the good case (where files were already cached locally) of the previous behavior trying to complete `winget list Micro` was taking 51 seconds. With this change, it takes 6 seconds on the debug build. Given that there is intentionally little to no I/O and much of the CPU time is likely spent in our code, that should speed up more with the release. ## Issue The indexed `IPackage` implementation for v2 needed to acquire the intermediate manifest to provide the response for `GetVersionKeys`. Since this is used extensively to perform correlation between available packages and local items during a search, any available packages found by the query would then result in a download/disk read of the intermediate manifest. ## Change Introduces `IPackage::GetMultiProperty`, allowing code that just wanted to get all of these values across all versions to do so more efficiently. For the v2 index, this is how the data is already stored in the database. For the other `IPackage` implementations, the data was also present and is just aggregated from each version's output.
1 parent ddb86d4 commit 4c85f5b

File tree

13 files changed

+375
-98
lines changed

13 files changed

+375
-98
lines changed

src/AppInstallerCLITests/CompositeSource.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,26 @@ struct CompositeTestSetup
166166
Composite.AddAvailableSource(Source{ Available });
167167
}
168168

169-
SearchResult Search()
169+
SearchResult Search(bool disableDataChecks = false)
170170
{
171+
size_t initialCountOfCallsRequiringVersionData = Available->CountOfCallsRequiringVersionData;
172+
size_t initialCountOfCallsRequiringManifestData = Available->CountOfCallsRequiringManifestData;
173+
171174
SearchRequest request;
172175
request.Query = RequestMatch(MatchType::Exact, s_Everything_Query);
173-
return Composite.Search(request);
176+
auto result = Composite.Search(request);
177+
178+
// We want to prevent calls to these functions for Search as they can require network or I/O activity.
179+
size_t countOfCallsRequiringVersionData = Available->CountOfCallsRequiringVersionData - initialCountOfCallsRequiringVersionData;
180+
size_t countOfCallsRequiringManifestData = Available->CountOfCallsRequiringManifestData - initialCountOfCallsRequiringManifestData;
181+
if (!disableDataChecks && (countOfCallsRequiringVersionData || countOfCallsRequiringManifestData))
182+
{
183+
std::ostringstream stream;
184+
stream << "Version data calls [" << countOfCallsRequiringVersionData << "] : Manifest data calls [" << countOfCallsRequiringManifestData << "]";
185+
FAIL(stream.str());
186+
}
187+
188+
return result;
174189
}
175190

176191
TestPackageHelper MakeInstalled(std::shared_ptr<ISource> source)
@@ -572,7 +587,8 @@ TEST_CASE("CompositePackage_AvailableVersions_ChannelFilteredOut", "[CompositeSo
572587
return result;
573588
};
574589

575-
SearchResult result = setup.Search();
590+
// Disable data checks as we call one of the data methods as validation in the search
591+
SearchResult result = setup.Search(true);
576592

577593
REQUIRE(result.Matches.size() == 1);
578594
REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1);
@@ -614,7 +630,8 @@ TEST_CASE("CompositePackage_AvailableVersions_NoChannelFilteredOut", "[Composite
614630
return result;
615631
};
616632

617-
SearchResult result = setup.Search();
633+
// Disable data checks as we call one of the data methods as validation in the search
634+
SearchResult result = setup.Search(true);
618635

619636
REQUIRE(result.Matches.size() == 1);
620637
REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1);

src/AppInstallerCLITests/SQLiteIndexSource.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,29 @@ TEST_CASE("SQLiteIndexSource_IsSame", "[sqliteindexsource]")
233233

234234
REQUIRE(result1.Matches[0].Package->GetAvailable()[0]->IsSame(result2.Matches[0].Package->GetAvailable()[0].get()));
235235
}
236+
237+
TEST_CASE("SQLiteIndexSource_Package_ProductCodes", "[sqliteindexsource]")
238+
{
239+
TempFile tempFile{ "repolibtest_tempdb"s, ".db"s };
240+
INFO("Using temporary file named: " << tempFile.GetPath());
241+
242+
SourceDetails details;
243+
Manifest manifest;
244+
std::string relativePath;
245+
std::shared_ptr<SQLiteIndexSource> source = SimpleTestSetup(tempFile, details, manifest, relativePath);
246+
247+
SearchRequest request;
248+
request.Query = RequestMatch(MatchType::Exact, manifest.Id);
249+
250+
auto results = source->Search(request);
251+
REQUIRE(results.Matches.size() == 1);
252+
REQUIRE(results.Matches[0].Package);
253+
254+
auto package = results.Matches[0].Package->GetAvailable()[0];
255+
256+
auto manifestPCs = manifest.GetProductCodes();
257+
auto propertyPCs = package->GetMultiProperty(PackageMultiProperty::ProductCode);
258+
REQUIRE(manifestPCs.size() == 1);
259+
REQUIRE(propertyPCs.size() == 1);
260+
REQUIRE(manifestPCs[0] == propertyPCs[0].get());
261+
}

src/AppInstallerCLITests/TestSource.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ namespace TestCommon
1616
static std::atomic_size_t packageId(0);
1717
return ++packageId;
1818
}
19+
20+
TestSource* GetTestSourceFromWeakPtr(const std::weak_ptr<const ISource>& weakSource)
21+
{
22+
if (auto source = weakSource.lock())
23+
{
24+
if (auto testSource = const_cast<ISource*>(source.get())->CastTo(TestSource::SourceType))
25+
{
26+
return reinterpret_cast<TestSource*>(testSource);
27+
}
28+
}
29+
30+
return nullptr;
31+
}
1932
}
2033

2134
TestPackageVersion::TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr<const ISource> source) :
@@ -100,6 +113,11 @@ namespace TestCommon
100113

101114
TestPackageVersion::Manifest TestPackageVersion::GetManifest()
102115
{
116+
if (auto source = GetTestSource())
117+
{
118+
source->IncrementCountOfCallsRequiringManifestData();
119+
}
120+
103121
return VersionManifest;
104122
}
105123

@@ -126,6 +144,11 @@ namespace TestCommon
126144
}
127145
}
128146

147+
TestSource* TestPackageVersion::GetTestSource() const
148+
{
149+
return GetTestSourceFromWeakPtr(Source);
150+
}
151+
129152
TestPackage::TestPackage(const std::vector<Manifest>& available, std::weak_ptr<const ISource> source, bool hideSystemReferenceStringsOnVersion) :
130153
Source(source)
131154
{
@@ -168,8 +191,34 @@ namespace TestCommon
168191
}
169192
}
170193

194+
std::vector<TestPackage::LocIndString> TestPackage::GetMultiProperty(PackageMultiProperty property) const
195+
{
196+
std::vector<LocIndString> result;
197+
PackageVersionMultiProperty mappedProperty = PackageMultiPropertyToPackageVersionMultiProperty(property);
198+
199+
for (const auto& version : Versions)
200+
{
201+
for (auto&& string : version->GetMultiProperty(mappedProperty))
202+
{
203+
auto itr = std::lower_bound(result.begin(), result.end(), string);
204+
205+
if (itr == result.end() || *itr != string)
206+
{
207+
result.emplace(itr, std::move(string));
208+
}
209+
}
210+
}
211+
212+
return result;
213+
}
214+
171215
std::vector<PackageVersionKey> TestPackage::GetVersionKeys() const
172216
{
217+
if (auto source = GetTestSource())
218+
{
219+
source->IncrementCountOfCallsRequiringVersionData();
220+
}
221+
173222
std::vector<PackageVersionKey> result;
174223
for (const auto& version : Versions)
175224
{
@@ -190,6 +239,14 @@ namespace TestCommon
190239

191240
std::shared_ptr<IPackageVersion> TestPackage::GetVersion(const PackageVersionKey& versionKey) const
192241
{
242+
if (!versionKey.IsDefaultLatest())
243+
{
244+
if (auto source = GetTestSource())
245+
{
246+
source->IncrementCountOfCallsRequiringVersionData();
247+
}
248+
}
249+
193250
for (const auto& version : Versions)
194251
{
195252
if ((versionKey.Version.empty() || versionKey.Version == version->GetProperty(PackageVersionProperty::Version).get()) &&
@@ -234,6 +291,11 @@ namespace TestCommon
234291
return nullptr;
235292
}
236293

294+
TestSource* TestPackage::GetTestSource() const
295+
{
296+
return GetTestSourceFromWeakPtr(Source);
297+
}
298+
237299
TestCompositePackage::TestCompositePackage(const std::vector<Manifest>& available, std::weak_ptr<const ISource> source, bool hideSystemReferenceStringsOnVersion)
238300
{
239301
if (!available.empty())
@@ -332,6 +394,16 @@ namespace TestCommon
332394
return nullptr;
333395
}
334396

397+
void TestSource::IncrementCountOfCallsRequiringVersionData()
398+
{
399+
++CountOfCallsRequiringVersionData;
400+
}
401+
402+
void TestSource::IncrementCountOfCallsRequiringManifestData()
403+
{
404+
++CountOfCallsRequiringManifestData;
405+
}
406+
335407
std::string_view TestSourceFactory::TypeName() const
336408
{
337409
return "*TestSource"sv;

src/AppInstallerCLITests/TestSource.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
namespace TestCommon
1212
{
13+
struct TestSource;
14+
1315
// IPackageVersion for TestSource
1416
struct TestPackageVersion : public AppInstaller::Repository::IPackageVersion
1517
{
@@ -40,6 +42,7 @@ namespace TestCommon
4042

4143
protected:
4244
static void AddIfHasValueAndNotPresent(const AppInstaller::Utility::NormalizedString& value, std::vector<LocIndString>& target, bool folded = false);
45+
TestSource* GetTestSource() const;
4346
};
4447

4548
// IPackage for TestSource
@@ -65,6 +68,7 @@ namespace TestCommon
6568
}
6669

6770
AppInstaller::Utility::LocIndString GetProperty(AppInstaller::Repository::PackageProperty property) const override;
71+
std::vector<AppInstaller::Utility::LocIndString> GetMultiProperty(AppInstaller::Repository::PackageMultiProperty property) const override;
6872
std::vector<AppInstaller::Repository::PackageVersionKey> GetVersionKeys() const override;
6973
std::shared_ptr<AppInstaller::Repository::IPackageVersion> GetLatestVersion() const override;
7074
std::shared_ptr<AppInstaller::Repository::IPackageVersion> GetVersion(const AppInstaller::Repository::PackageVersionKey& versionKey) const override;
@@ -76,6 +80,9 @@ namespace TestCommon
7680
std::weak_ptr<const ISource> Source;
7781
size_t DefaultIsSameIdentity = 0;
7882
std::function<bool(const IPackage*, const IPackage*)> IsSameOverride;
83+
84+
protected:
85+
TestSource* GetTestSource() const;
7986
};
8087

8188
// ICompositePackage for TestSource
@@ -127,6 +134,13 @@ namespace TestCommon
127134

128135
TestSource() = default;
129136
TestSource(const AppInstaller::Repository::SourceDetails& details) : Details(details) {}
137+
138+
// Tracking for potential network impacts
139+
void IncrementCountOfCallsRequiringVersionData();
140+
size_t CountOfCallsRequiringVersionData = 0;
141+
142+
void IncrementCountOfCallsRequiringManifestData();
143+
size_t CountOfCallsRequiringManifestData = 0;
130144
};
131145

132146
struct TestSourceReference : public AppInstaller::Repository::ISourceReference

0 commit comments

Comments
 (0)