Checkpoints (formerly known as snapshots) are something like an
“undo” button for a virtual machine. As the name suggests, you set a
marker at a particular point in time for the virtual machine. If you
decide that you don’t like what’s happened to the virtual machine at any
point after that, you can go back (
revert) to that checkpoint.
As we, and a great many others, have gone to great lengths to
emphasize, checkpoints are in no way a replacement for backup. Like an
actual “undo” button, all of the changes made after the checkpoint are
lost if you revert. There is something like a “redo” button; you are
given the opportunity to take another checkpoint when you revert to an
earlier one. However, no copies of the virtual machine are ever made.
Only changes are tracked. Do not attempt to use this feature as a backup
tool.
The purpose of this post is to explain how to delete hyper-v
checkpoints and what happens when you do so in various conditions. The
reasoning behind deleting a checkpoint is straightforward: once you’ve
decided that you are happy with the state that a virtual machine is in,
or if you have multiple checkpoints and are certain that at least some
of them are no longer useful, you can get rid of the unnecessary ones.
This post will use a virtual machine with the following checkpoint
states as an example:
Checkpoint Example
I’ve renamed them to give them some meaning. We’ll treat this as
though we are software developers testing potential patch builds to
determine which has the optimal outcome. This configuration is not
nearly as complicated as it might at first seem, but it does warranty
some explanation. To decipher the tree, start at the root. The
checkpoint named “Greenfield” represents the base virtual machine. It is
using the original VHDX which is now frozen in time. Some data might be
being read from it (particularly the operating system files) but
nothing is being written to it. Both the “A” and the “C” branches are
completely dormant; none of their files are being used at all. The “B”
branch has more activity. Any unique data in checkpoints titled “Patch
Branch B”, “Patch B1”, “Patch B2”, and “Patch B3 Variant 1” will be read
as necessary. Checkpoint “Patch B3” is completely dormant. All writes
are being directed to “Patch B3 Variant 1”.
Simple Checkpoint Deletion
For the sake of this discussion, we’ll say that each checkpoint named
“Patch Branch” is nothing but a checkpoint right off of “Greenfield”,
and that each of the other checkpoints was taken immediately
after
the application of the entity it is named after. To illustrate a simple
deletion, imagine that it’s been determine that Patch A3 had a flaw and
needed to be rebuilt. Rather than correct it in Patch A4, your
development team has decided to re-release Patch A3. In that case, it
makes sense to simply delete the checkpoint titled “Patch A3” and start
over from there. To perform the delete, right-click on the checkpoint
and click
Delete Checkpoint:
Checkpoint: Simple Delete
Because “Patch A3” is in a dormant branch, all files related to that
checkpoint will be permanently deleted and that will be the end of it.
No other checkpoints or the running state of the virtual machine will be
affected. To continue with the scenario outlined in the previous
section, you would likely
Apply the “Patch A2” checkpoint and then upgrade it with the re-released Patch A3 and then take a new checkpoint.
Branch Checkpoint Deletion
Let’s move on to something a bit more complex. Let’s say that the
development team decided that the “C” patch branch was an unmitigated
disaster and they just want to forget the whole thing to focus on “A”
and “B” branches. To effect that, right-click on the “Patch Branch C”
checkpoint and choose “Delete Checkpoint Subtree”:
Checkpoint: Subtree Delete
This action will delete all of the checkpoints and all of their
related files from the checkpoint that you chose and all of its child
checkpoints. Like the “Patch A3” object, this is in a dormant tree so
the running state of the virtual machine is not affected in any way.
Mid-Tree Deletion
Next, you get a call from the software engineering team that says
that they are perfectly happy with the testing of Patch B1 and will be
adding it to the permanent repository. Technically, you don’t have to do
anything. But, this is the actively running branch and each layer of
checkpoints impacts performance. However, Patches B2 and B3 are still
undecided, so you can’t just delete the entire subtree like you did with
the former Patch C branch. Fortunately, it’s not a problem. Just delete
“Patch B1” the same way that you deleted “Patch A3”:
Checkpoint: Mid-tree Delete
Once this completes, you’ll see that “Patch B2” and both of the
“Patch B3” items are still present and unchanged… or are they? The merge
operation combined all of its changes back into its parent of “Patch
Branch B”, as you likely expected. However, those files were the parents
of “Patch B2”. If you check, you’ll find that the parent objects of
“Patch B2” are now the files for “Patch Branch B”. The parent chain for
the items below “Patch B2” are unchanged. The action that you took has
the effect of reducing the overall length of the I/O chain for data
reads. However, any difference tracking between the former “Patch Branch
B” and “Patch B1” are now lost; “Patch B1” is permanently applied to
“Patch Branch B”.
Active Tree Deletion
After some time, the development team contacts you again, saying that
they are thrilled with the entire B patch series that you’re working
with. Patch B3 Variant 1, which you are currently working with, is
better than the original Patch B3, so they just want to get rid of that
and end testing of the Patch B branch entirely. To effect this, you
could, as before, leave everything alone. But, to improve your
performance, you could clean up all of those checkpoints. So, you choose
to right-click “Patch Branch B” and click
Delete Checkpoint Subtree:
Checkpoint: Delete Active Tree
This is the outcome:
Checkpoint: Active Tree Deleted
Was that a mistake, or was it what you really wanted to do? That
depends. Let’s go over what all has happened here. First, anything that
happened in the “Patch B3” item is completely lost. Second, all of the
changes made in “Patch Branch B”, “Patch Branch B2”, and “Patch Branch
B3 Variant 1” are all collapsed into the singular “Now” object. What did
not happen is any combining of the “A” branch with the
“B” branch, even though there are no longer any objects with the “B”
name. The “Now” object and the “Patch Branch A” object exist in the tree
at exactly the same level, which means that they are separate,
unrelated items. Since you may need to do some more testing with the “A”
branch, you might find it wise to create another checkpoint from the
level that you’re at now just to keep the “B” branch alive. If you jump
to another checkpoint without creating a new checkpoint at this level,
the entire branch formerly known as “B” will be lost.
Downstream Checkpoint Deletion
With the successful testing of the “B” branch, the development would
like you to return to testing the “A” branch. They’re not sure about A1,
so they wanted you to start there again. This is how it looks:
Checkpoint: Downstream Setup
As you test, they discover that A1 had a critical flaw that was then
propagated into A2. They don’t want either of these patches to see the
light of day. What should you do? This?:
Checkpoint: Delete Downstream Quiz
If this is what you do, you will not have met the requirements of the
development team. This action will discard “Patch A2”. However, it will
permanently roll all of the changes from “Patch A1” back into “Patch
Branch A” and set that as the running state of the virtual machine.
Since the development team wanted you to get rid of Patch A1, that means
that the “Patch Branch A” object is useless. In this scenario, you
fortunately have the “Greenfield” object to apply, but not everyone
remembers to take a checkpoint for that purpose. What you actually want
to do is first apply “Patch Branch A” (since you don’t care about
keeping anything underneath it, there’s no point in taking another
checkpoint when prompted). That will leave you with both “Patch A1” and
“Patch A2” as dormant downstream objects. You can now safely delete the
subtree from the “Patch A1” level to achieve the stated outcome:
Checkpoint: Delete Downstream Tree
Accepting the Current State — Deleting All Checkpoints
After a strange silence, the head of development calls you. Their
department has run out of money. They’ve decided to release the “Patch
B” tree. What they were working on for the “Patch A” branch is going to
be rebranded as “version 2.0” so they can make money on it, but that
will all be done at some point in the future. So, they want you to make
the “Patch B” branch permanent and get rid of everything else. As you
learned from the previous section, this means that you first need to
apply the saved “Patch B” branch so that it is active. Then, just start
at the root and
Delete Checkpoint Subtree:
Checkpoint: Delete All
A couple of things happen when you delete the root. All checkpoints
in the active tree are merged upward into the root. All checkpoint data
in all other branches are irrevocably lost. So, before you do this, take
care to start at the “Now” object and trace your way upward back to the
root to ensure that you really are in the tree that you think that
you’re in. If you want to discard all of the changes, apply the root
object first and then delete the entire subtree.
Once this operation completes, there will be no checkpoints at all.
All read and write activity will occur on the primary VHDX and VM files.