A patch may take as long or longer to install than the target product

Heath Stewart

Often I’m asked why installing a Windows Installer patch (MSP) takes as long or longer to install than the target product (MSI). While this isn’t always the case for every patch, it’s certainly possible for a number of reasons. It may also come as a surprise that the size of the patch can have little to do with the time to install the patch.

Several copies of the patch may be created

When you install a Windows Installer package, many copies of either the MSI or MSP package are created. But even before you start an installation, a bootstrap application will often download or copy the package locally so already time has been spent copying the package.

The first time a patch is installed – as identified by its unique patch code – Windows Installer will make multiple copies. When the client application (msiexec.exe or a setup bootstrap application) installs the package, eventually the package and other information is passed to the Windows Installer that does the actual installation (copying files, writing registry values, etc.). The Windows Installer service will validate the file and make a copy into a protected location as seen below.

MSI (s) (48:88) [15:52:28:971]: Original patch ==> \serversharepatch.msp
MSI (s) (48:88) [15:52:28:973]: Patch we're running from ==> C:WindowsInstaller16005163.msp

If the patch is installed successfully, the patch is cached and the temporary copy is deleted.

MSI (s) (48:88) [15:56:47:457]: Executing op: PatchCache(PatchId={29A69684-DEB7-31EE-9559-B29ECDFF0563},PatchPath=C:WindowsInstaller16005163.msp)
...
MSI (s) (48:88) [15:56:56:194]: Executing op: CleanupTempFiles(TempFiles=C:WindowsInstaller16005163.msp)
MSI (s) (48:88) [15:56:56:196]: Scheduling file 'C:WindowsInstaller16005163.msp' for deletion during post-install cleanup (not post-reboot).
...
MSI (s) (48:88) [16:00:16:380]: Attempting to delete file C:WindowsInstaller16005163.msp

If the patch is applied to another product or reapplied, the cached patch is copied again to avoid any sharing violates or changes to the patch while it is being installed.

MSI (s) (48:B0) [16:02:42:683]: Original patch ==> \serversharepatch.msp
MSI (s) (48:B0) [16:02:42:683]: Patch we're running from ==> C:WindowsInstaller16005237.msp
MSI (s) (48:B0) [16:02:42:688]: Opening existing patch 'C:WindowsInstaller16005235.msp'.

Since the patch was already cached, it is not cached again but the temporary copy is deleted.

MSI (s) (48:B0) [16:04:05:827]: Executing op: PatchCache(PatchId={29A69684-DEB7-31EE-9559-B29ECDFF0563},PatchPath=C:WindowsInstaller16005235.msp)
MSI (s) (48:B0) [16:04:05:827]: Patch {29A69684-DEB7-31EE-9559-B29ECDFF0563} is already present in this context, so it will not be cached again.
...
MSI (s) (48:B0) [16:04:05:977]: Executing op: CleanupTempFiles(TempFiles=C:WindowsInstaller16005237.msp)
MSI (s) (48:B0) [16:04:05:977]: Scheduling file 'C:WindowsInstaller16005237.msp' for deletion during post-install cleanup (not post-reboot).

Like any other file, the larger the patch package file the longer it will take to copy. Copying larger patch packages can account for significant time during installation.

The patch may apply to multiple products

To maintain the integrity of the machine in case any products are repaired or uninstalled, a patch will apply to all products that it targets and that are currently installed.

For example, if you have Visual Studio 2010 Ultimate and Visual Studio 2010 Express for Windows Phone installed and then install SP1, some patches in SP1 are applied multiple times. Patching Express will take less time than Ultimate, but still accounts for additional time.

Packages may be verified several times

The first time any Windows Installer package is installed, it is validated using WinVerifyTrust() against its Authenticode signature (if any) and through a SAFER policy check. This makes sure the package has not been tampered with and is allowed to install on the machine for the current user. If an Authenticode signature is present, the entire package must be read in order to generate a file hash and compare that against the signed hash. If the package was not installed from a local directory, WinVerifyTrust() streams the file from across the network as shown below.

MSI (s) (48:88) [15:52:28:971]: Original patch ==> \serversharepatch.msp
MSI (s) (48:88) [15:52:28:973]: Patch we're running from ==> C:WindowsInstaller16005163.msp
MSI (s) (48:88) [15:52:28:976]: SOFTWARE RESTRICTION POLICY: Verifying patch --> '\serversharepatch.msp' against software restriction policy
MSI (s) (48:88) [15:52:28:976]: SOFTWARE RESTRICTION POLICY: \serversharepatch.msp has a digital signature
MSI (s) (48:88) [15:54:31:055]: SOFTWARE RESTRICTION POLICY: \serversharepatch.msp is permitted to run at the 'unrestricted' authorization level.

Any time a package is opened from outside the cache, this validation occurs. For example, if you were prompted for source and provided the path to an MSI file, Windows Installer will perform these same checks to validate the file.

Mitigation

Deployment developers can mitigate this problem by shipping fewer patches that target more products. This will prevent having to validate packages multiple times. Copying the package locally may also improve performance since validating the package across the network may be prone to network latency and disconnections.

A single change will reinstall the entire parent feature

By default Windows Installer will detect which components have updated – those components which have newer files or updated registry values, for example. But because state of the package depends on features defined within the package, any features that contain those components are reinstalled in whole. This means that if a feature has many components and only just one of those is updated, all the components in that feature are reinstalled. That doesn’t necessarily mean the files are copied again – that depends on what REINSTALLMODE is set to (default is “omus” if not set) – but can incur a noticeable performance penalty to compare the current machine state with the expected machine state. Any non-versioned resources like text files and registry values may also be rewritten.

Mitigation

Deployment developers can mitigate this problem by fragmenting components into smaller features in the target product – patches cannot refactor components out of existing features. To maintain parity with the visible feature tree, refactor components into hidden child features that follow their parents’ install actions.

Several copies of updated files may be created

Windows Installer is a transactional installation engine. Any time a file is to be replaced, a copy of the installed file is created in case the installation fails and must rollback. With patch packages, a second copy may be created to support binary patch installation and patch uninstall for any patch that supports it. If a component is not authored with attribute msidbComponentAttributesShared (0x800), files are only copied to the baseline cache when they are first replace. So at least for any shared files, only a single persistent copy of the file replaced is retained. If a file is authored with tha t attribute, a copy is made across all products that installed the component (clients). This feature was introduced in Windows Installer 4.5 to prevent files from being downgraded during patch uninstall.

MSI (s) (48:88) [15:55:08:389]: Caching file.dll from C:Program Files (x86)Productfile.dll for baseline 0
...
MSI (s) (48:88) [15:55:35:803]: Executing op: SetBaseline(Baseline=0,ProductVersion=1.0.0)
MSI (s) (48:88) [15:55:35:813]: Executing op: SetBaseline(Baseline=1,)
...
MSI (s) (48:88) [15:55:52:947]: Executing op: CacheBaselineFile(Baseline=0,FileKey=file.dll,FilePath=C:Program Files (x86)Productfile.dll,,Existing=1)
MSI (s) (48:88) [15:55:53:062]: Executing op: CacheRTMFile(SourceFilePath=C:Program Files (x86)Productfile.dll,FileKey=file.dll,,ProductCode={CFB91CB0-17D9-44EB-BFB2-5307AB7E7DDC},ProductVersion=1.0.0,Attributes=4608,,,,CopierFlags=0,,,,,,)
MSI (s) (48:88) [15:55:53:062]: Executing op: RegisterSharedComponentProvider(PatchGUID={29A69684-DEB7-31EE-9559-B29ECDFF0563},MediaCabinet=#patch.cab,File=file.dll,Component={35F5C74C-4322-4729-9053-228044B7FD10},ComponentVersion=1.1.0.0,ProductCode={CFB91CB0-17D9-44EB-BFB2-5307AB7E7DDC},ProductVersion=1.0.0,PatchSize=0,PatchAttributes=0,PatchSequence=10045,SharedComponent=0,IsFullFile=1)
MSI (s) (48:88) [15:55:53:062]: Executing op: FileCopy(SourceName=600|file.dll,SourceCabKey=file.dll,DestName=file.dll,Attributes=4608,FileSize=508416,PerTick=65536,,VerifyMedia=1,,,,,CheckCRC=0,Version=1.1.0.0,Language=1033,InstallMode=58982400,,,,,,,)
MSI (s) (48:88) [15:55:53:067]: File: C:Program Files (x86)Productfile.dll;    Overwrite;    Won't patch;    Existing file is a lower version

Mitigation

Administrators or end users can disable the baseline cache by setting the MaxPatchCacheSize policy. Deployment developers should not set this policy. Developers may consider not authoring a component as a shared components, or just avoid authoring shared components all together. However, if shared components are necessary this features in Windows Installer 4.5 and newer can help maintain the stability of your product when patches are uninstalled.

Custom actions may be run again

Because a change to even a single component will reinstall the parent feature and all its child components, custom actions may be run again. This may often be by design, but can impact performance if the custom action performs a significant amount of work.

Mitigation

Deployment developers may be able to mitigate the impact of custom actions by designing when they run carefully, or eliminate them all together. Conditioning custom actions on components is most often a good strategy, assuming custom actions execute against component resources like native image generation (NGEN). As with the reinstallation of entire features, developers with such custom actions can reduce how often the custom actions run or even how much work they perform by refactoring components into more features.

Other general factors

There are also a number of factors that affect even initial installation of an MSI.

Hard disk performance

The speed of a hard disk will affect disk I/O. Multiply the effect of disk performance times the number of file copies mentioned above and it will have a negative impact on the overall install time.

Network performance

If the file is being installed from the network, before it is copied locally it is verified across the network first. This results in an extra read across the network. A bootstrap application itself may download the package before attempting to install which will mitigate this extra read operation.


Updated (3/14/2011): Added section about patches that update multiple installed products.

0 comments

Discussion is closed.

Feedback usabilla icon