Monday, November 27, 2017

Installing Hotfixes - Prepare vs Apply

Quick note on installing hotfixes and common questions around the usage of the prepare option. As I usually like to do, let's start with some background information.

I recommend a previous post I made on the topic of updates, upgrades and hotfixes.

When it comes to hotfixes, the platform is serviced as a whole. There aren't many hotfixes for platform since it is updated monthly anyway, but when there is it's cumulative and you install the whole thing at once as an update. Even though you get the source you don't worry about it going into source control or building and compiling it, because essentially you have the binaries already in the update which is ready to deploy to other non-dev environments, and you always get the whole source anyway - as opposed to deltas of just changed objects.
For application hotfixes however, you can get individual fixes for a specific KB you want. This will change when the application sealing comes around next year, but for now you have the option. Since all VMs come with a specific version of the application, any hotfixes you install will have to be synchronized to any place where the application may be compiled - so all dev boxes, build box etc. And of course when I say "synchronized" I mean the code for these hotfixes should go into source control.

Now, this way of doing things is slightly awkward. You get a base version of the application on the VMs, and any changes are in source control. This is nice and efficient because otherwise you'd have tens of thousands of source files in source control if you were to check-in the whole application. But from a source control perspective, this is weird. When you apply a hotfix and check in the changed object, source control will consider this a "new" object ("add") since you just added it to source control. But of course you technically already had it to begin with. What this means is if you were to "rollback" (undo) the add from source control - the opposite of an add is... delete. So if all dev boxes synchronized the hotfix (as an "add") and you rollback, all dev boxes when synching will say... hmm, I added this file and now it's gone, so let me delete it. As a result, you no longer have the object at all.

For this specific reason, the "prepare" option was added to the hotfix installation. Essentially, this will look at all objects in your hotfix, and add any of those objects to source control if they haven't already been. That way, if you want to rollback the hotfix, you have at least checked in the original version of the object (from before the hotfix) and you can roll back to that. Otherwise, you're rolling back to a delete (opposing the "add"). Now, a few additional pieces of information are important here:

1. An object could already have been added to source control. Because you did manually, because you had a previous hotfix on it, etc. This means that the number of objects in a pending changeset for a prepare step could be LESS than the number of objects in a pending changeset of a hotfix. A hotfix could technically also be adding objects, and new objects of course don't exist before applying the hotfix so they also wouldn't be in the prepare step.
2. After doing the prepare option, you have to check-in the pending changes. This is simple source control logic but many people forget this. You want to check-in the original versions of the objects prior to applying the hotfix. If you don't check it in before hitting apply, you won't have that base version! Simple as that. (in PU12 a dialog box was added after doing prepare that tells the developer to make sure to check-in prior to hitting apply).

Of course this also begs the question of how to recover if you did not prepare. Or if you did prepare but forgot to check-in before hitting apply. Truly, there is no easy way to recover from this. Ultimately, you need to get the original code from another VM that wasn't affected. So, consider these points:

- Did the hotfix go into source control and was it synchronized to other VMs as well? (i.e. this means removing the hotfix would affect multiple machines)
- Was it just an issue on your dev box, for example you hit apply right after prepare but haven't checked in anything (i.e. this means it's just your VM that needs to be fixed).

If the scope is just your machine, consider just getting a new VM. If you did check-in but no other machine has synchronized yet, you can rollback from source control (synchronize on other machines will see the add+delete which cancel out - VS will assume you didn't have the file since you didn't synchronize the "add", so it won't try to delete it) and you just need to worry about your machine.
Options for recovering all come down to getting the code from an unaffected VM (either deploy one temporarily, get a VHD, or with some luck someone else may have a VM that hasn't synced yet). The option is then to either manually copy the code - or potentially add these unaffected objects to source control from there (thus, creating your own baseline copy like prepare would have done).

1 comment:

  1. Hi Joris,

    As usual, nice article!

    I've decided to write a comment here just to notice a recent problem I've been fighting with during last week and it is related to the Hotfix installation.

    Last week, installing a Hotfix, and after the prepare command, all seemed to be OK but, when trying the apply command ... an error ocurred and the installation stopped with the message "Object reference not set to an instance of an object".

    There was no log nor verbose option on the SCDBundleInstaller and I was forced to decompile it and try to take a look at the problem.
    Finally, I found the problem was on these peace of code in Microsoft.Dynamics.AX.Servicing.SCDP.Installation.ApplyDeltasInstallAction.BuildDeltaModelMap():

    private Dictionary> BuildDeltaModelMap(PackageInstallParameters installParameters, IDiskMetadataProvider metadataProvider)
    Dictionary> dictionary = new Dictionary>();
    foreach (DeltaModelManifestEntry deltaEntry in this.manifestProvider.DeltaEntries)
    ModelInfo currentModel = ((IReadOnlyModelManifestProvider) metadataProvider.get_ModelManifest()).Read(deltaEntry.ModelName);
    if (!((IEnumerable) dictionary.Keys).Any((Func) (m =>
    if (m.get_Name() == currentModel.get_Name())
    return m.get_Module() == currentModel.get_Module();
    return false;
    dictionary.Add(currentModel, new List());
    dictionary[currentModel].Add(Path.Combine(this.manifestProvider.GetDeltaFilesFolderPath(), deltaEntry.TypeLogPath));
    return dictionary;

    The problem seems to be that currentModel was null !
    The error always occurred on the 13th iteration (on the foreach) and the corresponding element inside the package's deltaActions was referencing to Retail Model.
    Following the code above, currentModel seems to be initializing from the get_ModelManifest()...

    My surprise was when I detected there was no ModelManifest.xml file inside the Retail Folder and this was crashing the installation.

    I think it would be great to be able to launch the hotfix installation with a verbose option to make easier to find any installation problem.

    Thanks Joris and sorry about my long message :P


    Manel Querol