Caching creates multiple copies of an object which introduces the problem of consistency among the copies. The consistency needs differ significantly across different application domains. For example, in a document sharing collaborative environment, changes to documents may not have to be made visible to all users immediately. On the other hand, in a multi-user interactive simulation, changes to the state of shared objects must be made visible to all users immediately and in the same order as the changes were made. Thus, we take the approach of allowing multiple consistency levels to coexist in an application. This can improve performance because of two reasons. Firstly, it allows the use of weaker consistency levels when applicable. Secondly, weaker consistency levels can be implemented more efficiently than stronger ones [23]. In addition to improved performance, a system that provides multiple consistency levels for shared objects can facilitate increased functionality.
Strong consistency cannot be provided in systems where clients are temporarily disconnected which can happen involuntarily or voluntarily in a mobile environment. This is because communication between the nodes, where objects are cached, may be required before an access can be completed when strong consistency is required. Thus, even applications requiring strong consistency can benefit from mechanisms that facilitate a graceful weakening of consistency requirements in order to be able to make progress. We allow applications to employ multiple levels of consistency which can increase functionality and improve performance. In this section, we briefly explain the mutual consistency technique that is used to implement multiple consistency levels. The idea behind the technique is to keep cached object copies on a node (machine) mutually consistent.
Informally, copies of two objects are mutually consistent if they could exist together in an application's view. Consider the following collaboration example. Scientist I writes a chapter on results (results.old). In this application, each chapter is written as an object. Scientist II reads the copy of results and writes a discussion chapter (discussion.old) on the results. Scientist I rewrites the chapter on results (results.new) and also a discussion chapter (discussion.new) on the new set of results. Now, suppose that a third scientist on a different node tries to read the two chapters. Our definition of mutual consistency specifies that the combination < results.old, discussion.new > is not mutually consistent. It is based on the observation that the other three combinations correspond to possible global system states [8] that could have occured during the execution of the system. Note that if caching is not employed, only mutually consistent copies of the two objects can be accessed by applications. Of course, the specific two copies that the scientist reads will depend upon the consistency requirements. Thus, a mutually consistent view of a set of objects should correspond to a global system state that is meaningful with respect to the desired consistency level.
An underlying theme of the mutual consistency technique is to ensure that caching overhead on a node is proportional to the amount of caching activity on the node. For example, consider the case in which multiple read-only copies of a strongly consistent object exist when a client wishes to update its object copy. In conventional protocols, this would have induced communication with the other clients that have the read-only copies in order to either invalidate or update them. However, our protocol for strong consistency which is based on the mutual consistency technique does not invalidate or update all the read-only copies. Instead, consistency is maintained by using a novel technique that invalidates some of the locally cached object copies since the idea is to keep the cached copies mutually consistent.
As we saw in the example above, two object copies are mutual
consistent if the copies and more specifically, their corresponding
values, coexisted in a consistent global system state.
The more specific question in a test for mutual consistency
is whether the ``older'' object copy is still ``valid" in the
global system state (view) corresponding to the ``newer'' object copy.
For example, in the above application, results.old is not
``valid'' in the view corresponding to discussion.new, since it
has been overwritten by results.new.
Our implementation of the mutual consistency technique uses
the notion of a lifetime for a value of an object.
The lifetime of a certain value of object is the
duration defined by two logical times: the time when this
value was created (the creation time) and the time until
which the system has been able to establish that this value of the
object has
not been overwritten or become invalid (the validation time).
Note that the creation and
validation times are assigned differently for different consistency
levels. For example, suppose that causal consistency
is desired for an object. Causal consistency only makes use of
causal orderings to determine if a cached copy of an object is current.
In this case, the creation time assigned to a copy of the object is
such that all causally preceding events in the system are reflected.
The creation time would be assigned differently if in addition to
causal consistency, coherency
is also desired where coherency requires
that all writes to any given object are totally ordered. We call this
consistency level as causal coherency. In this case,
the creation time would reflect causally preceding events as
before; additionally, the creation time also needs to be greater than
creation times assigned to all previously created copies of the
object. Different consistency levels
can be implemented using the mutual consistency technique by
prescribing rules to assign creation and validation times
to object copies.
The reader is referred to [20] for more details on the mutual consistency technique and the protocols used to ensure causal consistency, causal coherency and strong consistency (SC). SC is related to serializability and sequential consistency which are used in databases and shared memory systems respectively. The current implementation of Flex supports causal and strong consistency.