ostep-homework/dist-afs
Remzi Arpaci-Dusseau bcf0b8df1c initial commit of afs 2020-06-08 04:00:48 -05:00
..
README.md initial commit of afs 2020-06-08 04:00:48 -05:00
afs.py initial commit of afs 2020-06-08 04:00:48 -05:00

README.md

Overview

This program, afs.py, allows you to experiment with the cache consistency behavior of the Andrew File System (AFS). The program generates random client traces (of file opens, reads, writes, and closes), enabling the user to see if they can predict what values end up in various files.

Here is an example run:

prompt> ./afs.py -C 2 -n 1 -s 12

      Server                         c0                          c1
file:a contains:0
                             open:a [fd:0]
                             write:0 value? -> 1
                             close:0
                                                          open:a [fd:0]
                                                          read:0 -> value?
                                                          close:0
file:a contains:?
prompt> 

The trace is fairly simple to read. On the left is the server, and each column shows actions being taken on each of two clients (use -C <clients> to specify a different number). Each client generates one random action (-n 1), which is either the open/read/close of a file or the open/write/close of a file. The contents of a file, for simplicity, is always just a single number.

To generate different traces, use -s (for a random seed), as always. Here we set it to 12 to get this specific trace.

In the trace, the server shows the initial contents of all the files in the system:

file:a contains:0

As you can see in this trace, there is just one file (a) and it contains the value 0.

Time increases downwards, and what is next is client 0 (c0) opening the file 'a' (which returns a file descriptor, 0 in this case), writing to that descriptor, and then closing the file.

Immediately you see the first question posed to you:

                             write:0 value? -> 1

When writing to descriptor 0, you are overwriting an existing value with the new value of 1. What was that old value? (pretty easy in this case: 0).

Then client 1 begins doing some work (c1). It opens the file, reads it, and closes it. Again, we have a question to answer:

                                                          read:0 -> value?

When reading from this file, what value should client 1 see? Again, given AFS consistency, the answer is straightforward: 1 (the value placed in the file when c0 closed the file and updated the server).

The final question in the trace is the final value of the file on the server:

file:a contains:?

Again, the answer here is easy: 1 (as generated by c0).

To see if you have answered these questions correctly, run with the -c flag (or --compute), as follows:

prompt> ./afs.py -C 2 -n 1 -s 12 -c

      Server                         c0                          c1
file:a contains:0
                             open:a [fd:0]
                             write:0 0 -> 1
                             close:0
                                                          open:a [fd:0]
                                                          read:0 -> 1
                                                          close:0
file:a contains:1
prompt> 

From this trace, you can see that all the question marks have been filled in with answers.

More detail is available on what has happened too, with the '-d' ('--detail') flag. Here is an example that shows when each client issued a get or put of a file to the server:

prompt> ./afs.py -C 2 -n 1 -s 12 -c -d 1

      Server                         c0                          c1
file:a contains:0
                             open:a [fd:0]
getfile:a c:c0 [0]

                             write:0 0 -> 1

                             close:0
putfile:a c:c0 [1]

                                                          open:a [fd:0]
getfile:a c:c1 [1]

                                                          read:0 -> 1

                                                          close:0

file:a contains:1
prompt>

You can show more with higher levels of detail, including cache invalidations, the exact client cache state after each step, and extra diagnostic information. We'll show these in one more example below.

Random client actions are useful to generate new problems and try to solve them; however, in some cases it is useful to control exactly what each client does in order to see specific AFS behaviors. To do this, you can use the -A and -S flags (either together or in tandem).

The -S flag lets you control the exact schedule of client actions. Assume our example above. Let's say we wish to run client 1 in entirety first; to achieve this end, we simply run the following:

prompt> ./afs.py -C 2 -n 1 -s 12 -S 111000

      Server                         c0                          c1
file:a contains:0
                                                          open:a [fd:0]
                                                          read:0 -> value?
                                                          close:0
                             open:a [fd:0]
                             write:0 value? -> 1
                             close:0
file:a contains:?
prompt>

The -S flag here is passed "111000" which means "run client 1, then client 1, then 1 again, then 0, 0, 0, and then repeat (if need be)". The result in this case is client 1 reading file a before client 1 writes it.

The -A flag gives exact control over which actions the clients take. Here is an example:

prompt> ./afs.py -s 12 -S 011100 -A oa1:r1:c1,oa1:w1:c1

      Server                         c0                          c1
file:a contains:0
                             open:a [fd:1]
                                                          open:a [fd:1]
                                                          write:1 value? -> 1
                                                          close:1
                             read:1 -> value?
                             close:1
file:a contains:?
prompt>

In this example, we have specified the following via -A oa1:r1:c1,oa1:w1:c1. The list splits each clients actions by a comma; thus, client 0 should do whatever oa1:r1:c1 indicates, whereas client 1 should do whatever the string oa1:w1:c1 indicates. Parsing each command string is straightforward: oa1 means open file 'a' and assign it file descriptor 1; r1 or w1 means read or write file descriptor 1; c1 means close file descriptor 1.

So what value will the read on client 0 return?

We can also see the cache state, callbacks, and invalidations with a few extra flags (-d 7):

prompt> ./afs.py -s 12 -S 011100 -A oa1:r1:c1,oa1:w1:c1 -c -d 7

      Server                         c0                          c1
file:a contains:0
                             open:a [fd:1]
getfile:a c:c0 [0]
                             [a: 0 (v=1,d=0,r=1)]

                                                          open:a [fd:1]
getfile:a c:c1 [0]
                                                          [a: 0 (v=1,d=0,r=1)]

                                                          write:1 0 -> 1
                                                          [a: 1 (v=1,d=1,r=1)]

                                                          close:1
putfile:a c:c1 [1]
callback: c:c0 file:a
                             invalidate a
                             [a: 0 (v=0,d=0,r=1)]
                                                          [a: 1 (v=1,d=0,r=0)]

                             read:1 -> 0
                             [a: 0 (v=0,d=0,r=1)]

                             close:1

file:a contains:1
prompt>

From this trace, we can see what happens when client 1 closes the (modified) file. At that point, c1 puts the file to the server. The server knows that c0 has the file cached, and thus sends an invalidation to c0. However, c0 already has the file open; as a result, the cache keeps the old contents until the file is closed.

You can see this in tracking the cache contents throughout the trace (available with the correct -d flag, in particular any value which sets the 3rd least significant bit to 1, such as -d 4, -d 5, -d 6, -d 7, etc.). When client 0 opens the file, you see the following cache state after the open is finished:

                             [a: 0 (v=1,d=0,r=1)]

This means file 'a' is in the cache with value '0', and has three bits of state associated with it: v (valid), d (dirty), and r (reference count). The valid bit tracks whether the contents are valid; it is now, because the cache has not been invalidated by a callback (yet). The dirty bit changes when the file has been written to and must be flushed back to the server when closed. Finally, the reference count tracks how many times the file has been opened (but not yet closed); this is used to ensure the client gets the old value of the file until it's been closed by all readers and then re-opened.

The full list of options is available here:

Options:
  -h, --help            show this help message and exit
  -s SEED, --seed=SEED  the random seed
  -C NUMCLIENTS, --clients=NUMCLIENTS
                        number of clients
  -n NUMSTEPS, --numsteps=NUMSTEPS
                        ops each client will do
  -f NUMFILES, --numfiles=NUMFILES
                        number of files in server
  -r READRATIO, --readratio=READRATIO
                        ratio of reads/writes
  -A ACTIONS, --actions=ACTIONS
                        client actions exactly specified, e.g.,
                        oa1:r1:c1,oa1:w1:c1 specifies two clients; each opens
                        the file a, client 0 reads it whereas client 1 writes
                        it, and then each closes it
  -S SCHEDULE, --schedule=SCHEDULE
                        exact schedule to run; 01 alternates round robin
                        between clients 0 and 1. Left unspecified leads to
                        random scheduling
  -p, --printstats      print extra stats
  -c, --compute         compute answers for me
  -d DETAIL, --detail=DETAIL
                        detail level when giving answers (1:server
                        actions,2:invalidations,4:client cache,8:extra
                        labels); OR together for multiple

Read the AFS chapter, and answer the questions at the back, or just explore this simulator more on your own to increase your understanding of AFS.