Per Charlie Landau's request, I'm going to try to restate the workings of the new approach to allocation counts as a collection of assertions and constraints.
What follows is the simple version. A hairy optimization that works, but may not be advisable, is at the bottom.
Background:
In this model, prepared keys carry the in-core allocation count,
unprepared keys carry the on-disk allocation count. Prepared objects
(nodes, pages) carry both.
In general, the same constraints apply to call counts, with the additional interaction that if an allocation count is incremented on a domain the call count must also be incremented.
Key deprepare is done by a background scavenger. When an object needs to go out, it sets a bit saying so. The scavenger deprepares all of the keys that point to the object, and then schedules the object to go out. The scavenger runs semi-continuously, depreparing invalid keys to DK(0).
I. General
II. Key Validity
II.A. Prepared Key
II. B. Unprepared Key
III. Objects
III A. Unprepared Objects
III B. Prepared Objects
increment(in-core-alloc-count) != on-disk-alloc-count
2) A prepared object has been rescinded IFF the following
formula is true:
in-core-alloc-count != on-disk-alloc-count
3) When a prepared object is deprepared, it's on-disk allocation
count is incremented IFF it has been rescinded.
Note that this increment does not require further condition
checking, as it does not violate any of the necessary
constraints for validity checking.
IV. Scavenger Completion
There are various points at which you need to know if the scavenger is done with your object. The two important cases are when you need to know that all keys are deprepared and in support of the hairy optimization below. Here's how you handle it.
Actually, that would need to be expanded, since the scavenger performs
multiple functions and needs to know whether it should be depreparing
keys or merely validating them. It also needs to know what operation
(e.g. I/O) should be performed once the scavenge is complete.
In the above (simple) model, you simply deprepare an object if it's in-core count manages to hit the wall. In an optimized version, a full deprepare is not necessary.
HAIRY OPTIMIZATION:
The above constraints can be further hacked as follows to eliminate a bunch of difficulties.
when doing trivial rescind, if the in-core count ends up matching the on-disk count, just bump it twice.
ensure that the scavenger runs fast enough that invalid prepared keys are never more than 2^31 - 1 calls old. This requires that the scavenger make progress at a calculable rate, which doesn't seem unreasonable. In this even, the problem of wrapping in-core allocation counts in a way that happens to revalidate a prepared key does not arise, because the prepared key is guaranteed to be turned into a DK(0) before the in-core count can wrap that far.
Jonathan