The initial design of AFS was based around the client server
paradigm. However, unlike (at least the original NFS), the system
addressed <#2422#> scaling<#2422#> as a base requirement. The goal as to
maximise the number of clients that could be served from one
machine. To this end, the clients cached whole files, so that a
series of passes through the file only resulted in a single access.
Even a single pass over the file (a very common pattern of access
for example on DOS and Unix systems) could take advantage of
streamed requests from the client to the server to fill the cache,
which can scale to long haul networks (with a lartge bandwidth/delay
product) better than the stop-and-wait performance of an RPC per
block of a file.
The problem with whole file caching is that it increases the
probability of concurrent accesses to a file creating an
inconsistent view at the server. In fact, concurrent accesses to
write files is rare in many environments, but in some (e.g.
workgroup environments) do have many writers of a given file.
Hence we need a mechanism for maintaining consistency, either at the
server, or to inform clients that their cache is invalid. There are
two points on a spectrum of design for how todo this: one extreme is
that we are <#2423#> optimistic<#2423#> and the server informs clients in the
hopefully `rare' event of intereing writes; the other extreme is
that clients check each time they try to carry out any action.
The first modification to the AFS (going from AFS 1 to AFS 2), was
an optimisation of the cache consistency in two ways:
firsly, the servers had an RPC <#2424#> callback<#2424#> to clients to carry
out cache invalidation. This elimates the cache validation traffic
that was present in the pessimistic AFS 1 (directories are
still written through to the server because directory integrity is
vital, and the costs are low for this); secondly, the internal
implementation of the client and server was enhanced to include <#2425#>
threads<#2425#>, which makes handling callbacks more straghtforward.
Notice that the decision to move from cache validation to cache
invalidation is based on a move in the view of the likelihood of
incoherence being pessimistic to optimistic. This is based in actual
mesasurements of typical behaviour (i.e. it is an engineering
viewpoint change, in ODP modelling terms).
The next phase of development of AFS (from AFS 2 to AFS 3) was to deal with another step in
scaling. To achieve this, a hierarchy of systems is introduced,
called administrative cells. A cell contains a set of clients, servers,
users and system adminsitrators, and is also the unit of security
management. To work between cells, as well as providing internal
security, authentication servers are used. The overall system
provides access control lists for protecting files, and groups for
aggregating users' permissions. This set of enhancements are
possible almost as a layer on top of the previous version of AFS.
A refinement of the whole file caching was introduced based (again)
on performance analysis of actual usage. This lead to a compromise
whereby clients can cache files from servers in 64Kbyte chunks
rather than the whole file. This accomodates many typical files
(e.g. documents) while also reconginizing that client machines may
not have (effectively) infinite local disk store. It can also help
with the startup performance (latency) for initial file access,
although concurrency in the client code could also help this by
allowing early completion of a call to open a file and permitting
the client to start reading the file before it has all arrived.
The last phase in the evolution of AFS has been to accomodate
`disconnected' operation. Many users are starting to use
workstations that are wireless. Laptop PCs and PDAs should be able
to use AFS, and continue to let a client work on a file even when
network access to the server is no longer available, restoring cache
consistency when the network is reconnected. This can also support
access in less relaible networks. The Coda file system is an
extension of AFS 2 (and not 3) to provide two main functions:
Server replication permits operations to continue in the even of a
link outage; disconnected operation is provided by introducing a
new notion of a directory of files that a mobile client may wish to
Replication in Coda is provided through a simple model that a client
reads data from a preferred server, but writes data back to all
servers. The fact that this is client driven again takes load (and
complexity) away from the servers. The many-update protocol uses a
two-phase scheme. The first phase attempts to write the data
optimistically to all the servers. In the second phase, which
operates asynchronously, the client carries out any consistency
check by updating version information at the servers. A neat
optimisation is that the second phase of the update protocol can be
piggybacked on requests to read (or write) new files.
Disconencted operation is based on profiling the set of files that a
user wishes to <#2426#> hoard<#2426#>, on their laptop. The client access code
switches from normal server access to accessing the local copy as
the client machine loses conenctivity. When server access is
regained, the client goes through a re-integration phase to update
the server(s). At this stage, a similar conflict resolution to that
employed for partial write fails above might be used.