Epochs (stepped upgrades)

While trying to figure out the best behaviour solution for release history across channels, we found that we need to better understand which behaviour we want when selecting the revision to return in the case of multiple revisions available, even for the same channel.

IOW, when multiple revisions are OK to be returned after applying the Epochs restriction, which one should the server choose?

There are three options:

  1. the most recently released revision with the epoch wins
  2. the most recently released revision with the epoch and the greatest other epoch wins
  3. the most recently released revision with the epoch and at least one greater epoch wins

The first option is the simplest one. Of all the revisions that are Epoch ok, let’s just get the latest one. This option is not only simple, but also in line with current “latest is what will be returned” pre-epochs behaviour, so it’s easy to think about.

However, this one is unworkable. Let’s see this example: let’s say we have the following releases (assuming same epochs in read and write, for simplicity):

r1    [0, 1]
r2    [1, 2]
r3    [2, 3]

If we boot first time a device that has epoch in 0, it will refresh to r1, then to r2 and finally to r3. At some point we found that r1 has a bug, so we fix it and release the corrected code in r4 (of course with epoch=[0, 1]). If we now boot first time another device in epoch 0, it will refresh to r4, and will never go to r2 or r3, getting stuck.

The second option is more complex but avoids the problem just described. It’s the currently defined behaviour (per talks we have had in this post in the past). The downside of this approach is that the revision selection is rigid. See this example, we have:

r1    [0, 1]
r2    [1, 2, 3]
r3    [1, 2]

When a device with epoch 0 needs to refresh it will go to r1, and then from r1 to r2 (as it has the greatest epoch!). No matter what revisions we release in the future, the only way for the device to upgrade to something different is to release a revision with a number greater than or equal to 3 in its epoch list.

The third option is slightly different, as a revision may be selected even if it doesn’t have the greatest epoch, but as it has at least one greater it allows an upgrade path to be established. See the following example:

r1    [0, 1]
r2    [1, 2, 3]
r3    [1, 2]
r4    [0, 1]

In this case the device bootstrapped with epoch 0 will end up also in r2, but through a longer sequence of refreshes. It first goes to r4 (not r1, because r4 has a higher epoch (1) than the matching one (0), and it’s released later than r1). In the next refresh it will go to r3 (which beats r4 as it has one higher epoch than the matching one (1), and beats r2 (which also have higher epochs) because it was released later). And finally it will refresh to r2, because now it’s the only one with “exceeding” epochs beyond the matching ones.

So, which option you think it’s the best? Thanks!!