Composition and silent reads
May 10, 2011 Leave a comment
Copyright 2010-2011 © Gabriel Zs. K. Horvath
So far all the read operations performed in the atomic block were being recorded, so as to be re-executed at commit time. We will see in this post that there are circumstances where one does not want the reads to be recorded. I will call these silent reads.
Composition
One of the most important and powerful concept in software engineering is the one of composition. We want to be able to compose existing data structures together to build new ones. Or we want to add new methods to existing ones without having to perform open heart surgery on that component. So let’s look at the concrete example of trying to implement the Last method on top of the set data structure:
public IEnumerable<T> Last(this IEnumerable<T> that) { var enumerator = that.GetEnumerator() if (enumerator.MoveNext()) { T t = enumerator.Current; while (enumerator.MoveNext()) { t = enumerator.Current; } yield t; } }
where we enumerate all the elements of the set and return only the last element unless the set is empty. The problem with this code is that, since we have enumerated all the elements of the set, any external change to the set during our transaction will invalidate it; while the only case where the transaction ought to be invalidated is if the last element has changed. Silent reads solve this problem: conceptually the solution consist in performing the enumeration silently and only record the result of this operation. The implementation then ensures that the same operation is performed at validation time to verify that the same value is returned.
User interface
The link software transactions establish between reads and writes is essentially one of causality. Failing the validation is just a way of preserving consistency: “you can’t write that (effect) anymore because the cause (read) has changed.” Software transactions assume that all the data read affects the writes. This is a stringent condition, which has the important advantage of erring on the side of caution, but there are cases where this turns out to be counter-productive.
Consider the case of a command line interface. Assume the user wants to change the thermostat setting of his (fully computer controlled) home. One can typically think of two scenarios, first one is along the lines of “I am feeling really cold today, I better set the thermostat to 20°C”. This case maps to a write only transaction as the user didn’t read anything from the system before making the change. The second scenario is the following: “It’s really cold in here today, I wonder what setting the thermostat is set to”. Now the user is going to read the current value of the thermostat and depending on its actual value change it. In this case we are dealing with a read-write transaction. We are assuming that the value read by the user had an influence on the updated value.
Now consider the case where the user interacts with an application with a rich GUI. Imagine that the screen now displays far more information than just the current thermostat setting. It could display the current internal and external temperatures, humidity levels, etc. This information has been read by the system and displayed to the user on the screen; some or none of this information might be used by the user to influence the write. Since we can neither track the user’s eyes or its brain activity, we are left to guess. To be on the safe side, one should assume that any displayed information might have influenced the user. The problem with this approach is that any change to any of the displayed values by a concurrent transaction will result in the user’s transaction failing. In our example it would be enough for any of the displayed ambient temperatures to change by even the tiniest amount to fail the transaction. In such cases our cautious approach has become a burden and a more flexible approach is required. One solution would consist in ignoring all the read operations by making them all silent-reads and consider making the transaction write-only. Another, probably more effective approach would consist in registering only the read of the value which is being modified, as this is the value the user is the most likely to have read. Depending on the actual application other values could be read so as to introduce a dependency on them; in our example, one could for example include the humidity level.
Read-only transaction
As read-only transactions don’t need a validation phase, there is no point in recording the reads during the execution of a transaction. So all transactions which are known to be read-only can be executed in this silent-read mode.