Skip to content

Commit 7dcc3c3

Browse files
authored
Index v2 (#4387)
## Change This change introduces schema 2.0 for the `SQLiteIndex`. This new major version takes the learnings on how we actually used the index to shift to a package centralized store. It also moves some of the data that is not directly needed for search and correlation to intermediate manifests per-package. The goal is that `search` and `list` functionality (including `update` probing) should not need the intermediate files; only operations that change the system state (write operations) should. Using a recent index as a starting point for comparison, the 2.0 index reduced the size of a ZIP archive (containing the 1 file and produced by Windows Explorer) by **79%**. This change only implements the index creation. A future change will implement the consumption, including a correct implementation of the 2.0 search functionality. The current search is just copied/commented from a previous schema version for now. ### Implementation The 1.* index schema allows easy updating from individual manifest changes, without the need to inspect any other manifests for the same package. The final 2.* schema will not allow that. Instead, 2.0 actually uses a 1.7 schema internally, with an additional table to track changes so that we can produce the intermediate manifests. When `PrepareForPackaging` is called, the schema 2.0 tables are created and the data migrated to them. The intermediate manifest files that have changed are also written to disk. ### Intermediate Manifests The intermediate manifest files (`PackageVersionDataManifest`) are YAML with shortened key values. This saves some bytes since humans are neither authoring them nor reading them (except to debug). They are also stored in a compressed stream, using the MSZIP compression algorithm. The compression brings the average size down from 1236 bytes to 463 bytes, and the median down from 338 bytes to 206 bytes. The future consumption change will cache these intermediate manifests (and likely the version manifests as well), allowing their reuse as long as they have not changed. ### Additional Functionality To support our use in creating the index in our services, some additional functionality was added to the `SQLiteIndex`. #### Migration A schema version migration function is added, allowing the target schema to migrate from the existing one as it sees fit. If the migration is not supported, it can simply return a value indicating that. Only 1.7 => 2.0 migration is implemented. #### Properties Properties can be set on the index object, some of which are for that object only and some of which are persisted into the database itself. An implicit property of the database file name is stored when appropriate. The caller can set the directory path to output intermediate files to. The caller can also set the time (in Unix epoch) to use as the baseline for which intermediate files should be output [in practice, only an empty string (for "now") and "0" (to output everything) are likely to be used].
1 parent 91eda88 commit 7dcc3c3

File tree

73 files changed

+5084
-362
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+5084
-362
lines changed

.github/actions/spelling/expect.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ CODEOWNERS
7272
COINIT
7373
COMGLB
7474
commandline
75+
compressapi
7576
contactsupport
7677
contentfiles
7778
contoso
@@ -273,6 +274,8 @@ msftrubengu
273274
MSIHASH
274275
MSIXHASH
275276
msstore
277+
MSZIP
278+
mszyml
276279
Mta
277280
Mugiwara
278281
Multideclaration

src/AppInstallerCLITests/AppInstallerCLITests.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@
232232
<ClCompile Include="PackageCollection.cpp" />
233233
<ClCompile Include="PackageDependenciesValidationUtil.cpp" />
234234
<ClCompile Include="PackageTrackingCatalog.cpp" />
235+
<ClCompile Include="PackageVersionDataManifest.cpp" />
235236
<ClCompile Include="PathVariable.cpp" />
236237
<ClCompile Include="PinFlow.cpp" />
237238
<ClCompile Include="PinningIndex.cpp" />

src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,9 @@
344344
<ClCompile Include="RestInterface_1_7.cpp">
345345
<Filter>Source Files\Repository</Filter>
346346
</ClCompile>
347+
<ClCompile Include="PackageVersionDataManifest.cpp">
348+
<Filter>Source Files\Common</Filter>
349+
</ClCompile>
347350
</ItemGroup>
348351
<ItemGroup>
349352
<None Include="PropertySheet.props" />
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#include "pch.h"
4+
#include "TestCommon.h"
5+
#include <winget/PackageVersionDataManifest.h>
6+
7+
using namespace TestCommon;
8+
using namespace AppInstaller;
9+
using namespace AppInstaller::Manifest;
10+
using namespace AppInstaller::Utility;
11+
12+
void RequireVersionDataEqual(const PackageVersionDataManifest::VersionData& first, const PackageVersionDataManifest::VersionData& second)
13+
{
14+
REQUIRE(first.Version == second.Version);
15+
REQUIRE(first.ArpMinVersion == second.ArpMinVersion);
16+
REQUIRE(first.ArpMaxVersion == second.ArpMaxVersion);
17+
REQUIRE(first.ManifestRelativePath == second.ManifestRelativePath);
18+
REQUIRE(first.ManifestHash == second.ManifestHash);
19+
}
20+
21+
TEST_CASE("PackageVersionDataManifest_Empty", "[PackageVersionDataManifest]")
22+
{
23+
PackageVersionDataManifest original;
24+
25+
PackageVersionDataManifest copy;
26+
copy.Deserialize(original.Serialize());
27+
28+
REQUIRE(original.Versions().empty());
29+
REQUIRE(copy.Versions().empty());
30+
}
31+
32+
TEST_CASE("PackageVersionDataManifest_Single_Simple", "[PackageVersionDataManifest]")
33+
{
34+
PackageVersionDataManifest original;
35+
original.AddVersion({ VersionAndChannel{ Version{ "1.0" }, Channel{} }, {}, {}, "path", "hash" });
36+
37+
PackageVersionDataManifest copy;
38+
copy.Deserialize(original.Serialize());
39+
40+
REQUIRE(original.Versions().size() == 1);
41+
REQUIRE(copy.Versions().size() == 1);
42+
43+
RequireVersionDataEqual(copy.Versions()[0], original.Versions()[0]);
44+
}
45+
46+
TEST_CASE("PackageVersionDataManifest_Single_Complete", "[PackageVersionDataManifest]")
47+
{
48+
PackageVersionDataManifest original;
49+
original.AddVersion({ VersionAndChannel{ Version{ "1.0" }, Channel{} }, ".99", "1.01", "path", "hash"});
50+
51+
PackageVersionDataManifest copy;
52+
copy.Deserialize(original.Serialize());
53+
54+
REQUIRE(original.Versions().size() == 1);
55+
REQUIRE(copy.Versions().size() == 1);
56+
57+
RequireVersionDataEqual(copy.Versions()[0], original.Versions()[0]);
58+
}
59+
60+
TEST_CASE("PackageVersionDataManifest_Multiple", "[PackageVersionDataManifest]")
61+
{
62+
PackageVersionDataManifest original;
63+
original.AddVersion({ VersionAndChannel{ Version{ "1.0" }, Channel{} }, ".99", "1.01", "path", "hash" });
64+
original.AddVersion({ VersionAndChannel{ Version{ "1.1" }, Channel{} }, "1.99", "2.01", "path2", "hash2" });
65+
original.AddVersion({ VersionAndChannel{ Version{ "1.2" }, Channel{} }, {}, {}, "path2", "hash2" });
66+
original.AddVersion({ VersionAndChannel{ Version{ "2.0" }, Channel{} }, "3.99", "15.01", "path4", "hash4" });
67+
68+
PackageVersionDataManifest copy;
69+
copy.Deserialize(original.Serialize());
70+
71+
REQUIRE(original.Versions().size() == copy.Versions().size());
72+
73+
for (size_t i = 0; i < original.Versions().size(); ++i)
74+
{
75+
INFO(i);
76+
RequireVersionDataEqual(copy.Versions()[i], original.Versions()[i]);
77+
}
78+
}
79+
80+
TEST_CASE("PackageVersionDataManifest_CompressionRoundTrip", "[PackageVersionDataManifest]")
81+
{
82+
PackageVersionDataManifest original;
83+
original.AddVersion({ VersionAndChannel{ Version{ "1.0" }, Channel{} }, ".99", "1.01", "path", "hash" });
84+
original.AddVersion({ VersionAndChannel{ Version{ "1.1" }, Channel{} }, "1.99", "2.01", "path2", "hash2" });
85+
original.AddVersion({ VersionAndChannel{ Version{ "1.2" }, Channel{} }, {}, {}, "path2", "hash2" });
86+
original.AddVersion({ VersionAndChannel{ Version{ "2.0" }, Channel{} }, "3.99", "15.01", "path4", "hash4" });
87+
88+
std::string serialized = original.Serialize();
89+
auto compressed = PackageVersionDataManifest::CreateCompressor().Compress(serialized);
90+
91+
auto decompressed = PackageVersionDataManifest::CreateDecompressor().Decompress(compressed);
92+
93+
PackageVersionDataManifest copy;
94+
copy.Deserialize(decompressed);
95+
96+
REQUIRE(original.Versions().size() == copy.Versions().size());
97+
98+
for (size_t i = 0; i < original.Versions().size(); ++i)
99+
{
100+
INFO(i);
101+
RequireVersionDataEqual(copy.Versions()[i], original.Versions()[i]);
102+
}
103+
}

0 commit comments

Comments
 (0)