Submind YouTube summaries
Thumbnail for CS162 Lecture 22: Transactions (Con't), End-to-End Arguments, Distributed Decision Making

CS162 Lecture 22: Transactions (Con't), End-to-End Arguments, Distributed Decision Making

Watch on YouTube

Video summary

This lecture continues the exploration of file system reliability by distinguishing between availability, which refers to system responsiveness, and durability or integrity, which concern data recovery and correct operation. To ensure data survives multiple simultaneous failures, modern systems often employ erasure codes like RAID 6 rather than relying solely on traditional RAID 5. The discussion introduces Copy-on-Write mechanisms that preserve old data versions until updates are complete, providing crash resilience at the cost of additional space and time overhead. This concept evolves into formal transactions using Write-Ahead Logging, where changes are recorded in a log before being applied to storage; only after a commit record is written does the change become permanent, guaranteeing atomicity even during mid-operation crashes. Journaling file systems take this further by logging metadata first and optionally data, replaying logs upon reboot to apply pending changes, while log-structured systems optimize for sequential writes but may sacrifice read performance without heavy buffering. The focus then shifts to the specific constraints of flash memory, where floating gates trap electrons to store bits, necessitating erase-before-write operations on large blocks rather than byte-level overwrites. Because these erase cycles wear out the storage medium, Solid State Drives utilize a Flash Translation Layer to map logical sectors to physical blocks and perform automatic wear leveling. To mitigate the detrimental effects of random writes that force data copying before erasure, specialized file systems like F2FS assume sequential writes are faster and adapt by using log-structured approaches with Copy-on-Write and translation tables. These systems manage storage by splitting logs into segments based on write frequency and performing garbage collection to reclaim fragmented space when old segments develop holes, effectively balancing performance with the physical limitations of flash hardware. Beyond local storage, the lecture contrasts centralized client-server models with peer-to-peer architectures, highlighting that while P2P offers superior scalability, availability, and user control, distributed systems remain vulnerable if any unknown component fails or is compromised. A primary objective in this domain is achieving transparency, which masks complexities such as resource locations, replication details, and failure occurrences from the user, though this can be selectively broken for privacy needs. Maintaining state consistency across nodes despite failures relies on protocols that treat systems as state machines agreeing on a common state. Furthermore, to manage the complexity of diverse hardware and software without creating an unmanageable $N^2$ communication matrix, networks adopt an hourglass architecture where the IP protocol forms a narrow waist, allowing independent evolution of upper-layer applications and lower-layer physical media despite the overhead introduced by layering. The lecture concludes with the End-to-End argument, a principle stating that critical functionalities like reliability and security should be implemented at the endpoints rather than within the network core. Since ensuring perfect performance across every router hop is difficult and prone to unforeseen bugs, it is more effective for endpoints to verify data integrity using checksums or hashes and request retransmission if corruption is detected. While intermediate layers can enhance performance on lossy links, there is no functional necessity for them to implement reliability features that endpoints can handle themselves, with exceptions reserved for specific cases like firewalls or routing protocols. The session wraps up by summarizing key concepts regarding availability, durability, and reliability before previewing the next lecture, which will focus on programming distributed applications using message passing without shared memory, specifically addressing state machine synchronization and two-phase commit protocols.
Read the full video transcript
welcome back everybody uh to um cs162 we're going to continue our discussion of ways of uh getting reliability out of file systems and then we're going to dive into some interesting material on distributed decision-making um if you remember last time we were talking about one of the ways that we get performance out of a file system and that's with a buffer cache and the buffer cache of course is the um chunk of memory that's been set aside to hold various items including dis blocks and the example that I've shown here was basically that uh when we talk about a file system and we have uh directory data blocks and iodes and data blocks Etc they're actually put into the buffer cache which is typically handled lru and uh is the temporary Waypoint for data moving in and off the dis and this is of course the starting point for allowing us to read and write single bytes of data at a time but it also is an important uh performance enhancer and we and we talked among other things about um keeping dirty data in the buffer cache and not pushing it out to disc right away and that that had some pretty important uh performance benefits it also has some potential issues with reliability If You Should Crash and the dirty data is still only on in memory and not on dis so um the other thing uh that we started talking about then in that along those lines was what I like to call the ilities and so that's availability durability and reliability uh and keep in mind that availability is kind of the minimum um bar to meet and it's not a very good one oftentimes so availability is typically the fact that you can actually talk to the system and it will respond to you it doesn't say that it'll respond correctly and um the other thing that's often the case is uh we'll talk about number of nines of availability so three nines typically means that there's a 99.9% probability that the system will uh respond to you more important than availability in my opinion at least is durability and reliability durability says that the system can recover data despite the fact that things are failing and uh reliability is the ability of the system to essentially uh perform things correctly and that's really what you want is you want reliability not availability okay all right and and by the way the example I like to give about the difference between durability and availability for instance is that uh if you think about the Egyptian pyramids there was a time when people didn't know what the various hieroglyphs meant that those uh what was written on the pyramids was extremely durable but it wasn't available because people couldn't uh decipher it okay and it became available only after the Rosetta Stone was discovered so the other thing uh we talked about last time is we started talking about ways to protect bits not necessarily ways to protect the Integrity of the operating system and file system so to speak but Integrity of the bits and we talked about raid which you know from 61c and in general raid uh X you know whatever your level is is a type of eraser code which is uh a code in which you know certain discs are gone and you fill in the missing discs using the code okay that's called an eraser code and the reason you're able to do that is essentially because the diss uh have error correction codes on them that let them recognize when the discs themselves are bad and then you treat the whole disc as an eraser and you bring in the raid codes and what I did say was that today discs are so big that raid five which is what you learned about in 61c for instance is really not sufficient because uh it can only recover from one failed disc and uh discs are so big now that while you uh are busy recovering that disc by putting a new one in uh it might fail again and at that point you just lose all your data so if you ever have a big file system on a big file server make sure you pick at least raid six which is a possibility of two failed discs and um for instance even odd is a is a code that works for two diss uh there that's available on the readings in general you can do something that um called a a general read Solomon code like um this based on pols and if you remember um as I I mentioned this last time but I thought I'd put this out there uh when you were learning about polinomial back in grade school what you learned was that if you have a an Min minus one degree polinomial here uh as long as you have M Points then you can reconstruct the coefficients okay and so the the clever trick uh with readed Solomon codes is you start with something that behaves like real numbers called a gallawa field we can talk about that offline if you like and then you put your data at the coefficients and then you just generate a bunch of points and here's an example where I generate n points where n is bigger than M and as long as I get M of them back then I can recover the polinomial and then I can get back my data and so that's an eraser code because I can erase any number of these uh points here as long as I still have M left so I can erase up to n minus M of them and still get my data back and that's a pretty powerful code and you can choose how many uh you need to recover from how many failures okay and so oftentimes in Geographic replication you can arrange to be able to lose uh you know 12 out of 16 chunks of data and that's extremely efficient good so um I'm glad that cs170 also talked about this in in general um the uh other thing we talked about last time by the way was there were there any questions on on eraser codes at all so um well you know that raid five is a simple as uh xoring um even odd is is a slightly different type of xoring so that's those are all very fast operations the readed Solomon um codes come in a bunch of different forms some of which are fast and some of which aren and so um there's a bunch of different types of read Solomons which are all isomorphic to this idea but they're rearranged in a way where it's really fast to encode um in some instances and then it's it's pretty fast but um typically the decoding phase is an N squared uh complexity so decoding can be uh when you failed it can be expensive um so the other thing I talked about uh was well we we' were looking at file systems like the fast file system and NTFS which are overwritten when you write new data so when you put new data into a file you overwrite the blocks that had the old data in it an alternative uh which you might imagine is a lot more um reliable as copy on write file system so here's an example of a file system where um I'm just showing you a bi a binary tree think of these as the uh the pieces of the iodes and the old version of the file sort of the blocks are down here in blue and they're in this tree and the idea behind a copy on write system is that if I want to say write some new data at the end or overwrite something I don't actually overwrite the original data but I build a whole new version of the file that uses as much as the old one as possible so here was an example where I took this old block here I added some new data to it and I made a new block with a copy and now by time uh my new iodes in with the old ones I have uh by following the new version you can see that we've got a new version of the file with this is updated but the old version's still there and so if I have a really bad crash in the middle of writing the new version I can still recover the old version and I can pull various tricks to decide how much of the old version to keep around or how many old versions to keep around and um this is much more resilient to random failures okay and and there's uh file systems that are like that now it would be um potentially the question here is this more expensive in space or time it's certainly is uh more expensive in space if you want to think of it that way but what you're getting back is um extreme resilience to crashes and failures and the ability if you decide that this uh that you wrote something incorrectly you can go back to a previous version so this has some pretty nice benefits you get from the space overhead because you notice that we're um we're not deleting old data right away um and it's can be a little bit more expensive in time if uh you have to worry about how these things are laid out maybe it doesn't have as fast of a read performance as something like the fast file system might be so um what about more General reliability Solutions well if we wanted to go back to the fast file system let's say because we were worried about performance and we wanted to make sure that the file system and the operating system couldn't crash in a way that leaves things uh vulnerable then what might we do and one of the things we talked about was very carefully picking the order you write the blocks and then you write the iodes and then you put the iodes in a directory and so on and you do this in an order such that if it fails at any point you can kind of throw out the things that weren't quite finally committed and um go through a pass on the file system and find everything that's disconnected and you're good to go the problem is that requires very careful thought so more general idea here is to use a transaction which youve probably heard about if you've taken any of the database classes but the idea here is that when you go to update a file you're going to use transactions to provide automic updates to the file system such that there's a single commit point in which the new data is uh or the new version of the file system is ready to go and until you reach that commit Point any of the things that you do to the file system can be undone now if you think back to this copy on wrs uh example as I'm writing everything here and producing my new version the old version is fine so if anything gets uh screwed up including just throwing out the new version uh the old version's still there and if the only thing I need is to swap the old version for the new version which with a single operation that's a single point of commit for the new file system okay and so that's kind of like a transaction um the transactional ideas are a little bit more General okay and so we're going to use transactions to give us clean commits to the uh Integrity of the file system and then of course we're going to use redundancy to protect the bits so the bits can be protected with uh read Solomon codes and eraser other error correcting codes raids Etc okay now just to remind you a little bit about what we mean about transactions it's closely related to critical sections uh that we talked about earlier in the term they extend the concept of atomic updates from memory which is where they came up originally in ear part of the term to stable storage and we're going to automically update multiple persistent data structures with a single transaction and as a result we'll never get in a situation where the file system is partially updated and therefore corrupted so there's lots of ad hoc approaches to this transactional like thing I just talked to you through the copy on right the fast file system uh they originally would order sequences and updates in a way so that if you crashed you could do uh a process that scanned the whole file system called fsck to recover from that those errors um but again that's very ad hoc so this idea of a general transaction is like this you start with consistent state number one in the file system and you want to get to consistent state number two maybe consistent state number one uh is the original file system and number two is what you get when you add some new files and directories and data and the transaction is a atomic way to get from the first state to the second one and we know underlying the uh those that single Atomic view change here there's going to be a whole bunch of underlying um a whole bunch of underlying changes to individual blocks the question in the uh the chat here is what did I mean by ad hoc what I mean by ad hoc is that a person sits down and they very carefully think through well if I update this and then I update that and then I update that and then I update that and the final thing I do is this then I know that if it crashes anywhere along the way I'll be able to recover the original file system so ad hoc here means that you come up with a A solution that is uh maybe it works but you've had to go through a long process of thinking it through to make sure it works and it's possible that you've got it wrong okay so that's what I mean by ad hoc here we want something a little more systematic okay so um and we're going to use transaction for this um so Atomic here Atomic is really the process of making sure that uh either everything happens or nothing happens okay and atomic in the log will happen even if the machine gets unplugged you want to make sure that we still have that Atomic property probably if you unplug it and you've got this Atomic property what's going to happen is your changes aren't going to happen Okay so let's let's walk through this a little bit more so transactions are going to extend this idea from memory to persistent storage and here's a typical structure of course you start the transaction you do a bunch of updates if anything fails along the way you roll back if there are any conflicts you roll back um but then once you've committed the transaction then that mirror Act of the of the commit operation causes everything to be permanent now okay and so we'll talk about how to do this in a moment but um this do a bunch of updates thing could be arbitrarily complicated it could be allocating new iodes it could be grabbing some new blocks it could be linking them it could be doing all sorts of stuff and the point is that none of that is going to be permanently affecting the contents of file system until we commit and so that's what we're going to try to figure out how to do okay that's the atomic here is all of a sudden it it happens or it doesn't happen at all now um of course a classic example you know uh transfer $100 from Alice's account to Bob's account you see there's a bunch of these different pieces right Alice's account gets debited 100 uh the branch account uh that 100 goes to the other uh bank and then um Bob's account somehow gets the balance and so on and so uh there are a series of operations in different parts of various people's databases and if only some of them happen then the banking system becomes inconsistent um for instance if it crashes the whole system crashes between de debiting Alice's account and incrementing Bob's account then not only did Alice lose money well she didn't get her $100 but Bob didn't get it either and so that would be bad okay and so this idea of beginning transaction ending committing transaction is one in which none of these things happen until the commit now modern operating systems uh the question is do they expose the transactions to the user um depends a little bit on uh which file system you've got certainly there are some Notions of transactions that are available others are uh others are less available right now what we're going to talk about is mostly under the covers in a way that the user doesn't have access to so the concept of a log to make all this work is the following if you look at all of these pieces I've got here that represent parts of a global transaction I'm going to write them in a chunk of memory slisk that sort of uh think of this as a this is the log and think of this as a big chunk of dis and all of these things are going to be in there and they might be interleaved with other transactions but what we're going to do is View this log serially starting from the left and going to the right and uh we're going to start the transaction by putting a start transaction marker in the log and then we can go ahead and do all of our stuff and everybody else can do their stuff and it's only when we put a commit transaction at the end that now all of a sudden these actions automically happen okay now a couple of things that should be clear from this one is when I put start transaction that needs to get committed to the log kind of before anything happens and then when I put my various actions in here before the final commit happens it has to be the case that all of these other things are in the log so it can't be the case that I do a commit it gets on disk but all of these other things are still in memory somewhere because then the machine could crash and I see well start transaction commit transaction but I have no idea what I just committed and that would be bad okay okay so um the log is is clearly going to be something that we're going to need to be pushing out to disk and it's going to be have an ordering requirement that's very important in order to make this all work and the other thing that I'll point out here is notice that if I write these uh operations in the log dump dump a b c d dump and then I say Commit This doesn't necessarily mean that I've actually put them into the file system or actually produced the actions yet what it means is that if if I were to crash and I hadn't done them yet I'd be able to uh wake up after the crash and go through the log and figure out what the the state of the system is supposed to be so the state of the system is not only what's on disk in the file system but also what's in the log and um and ordered in a way that I can go back and reconstruct after a crash and I'm going to show you um a couple of animations here just to give you a better idea how that works all right okay so the commit is like sealing an envelope and saying the transaction now happened now um so now the question is uh and shouldn't things be logged after they happen well uh in this type of log we're we're doing um something in which it's called write ahead logging we're actually writing into the log before it's put into the file system okay and the reason for that is so it's the opposite of the way you're thinking of this I think um we want to write in the log first rather than modifying the file system so that if the commit never comes because we crash then the file system's okay if we were to start modifying the file system and then put the commits into the log now we're in a bad situation where we might have already corrupted the file system by doing a partial update okay so this is uh I'm glad you asked that question this is the opposite maybe of the way you were thinking so thanks for that clarification question um so here's a transactional file system uh example so we're going to get better reliability through the log a log changes are treated as transactions and a transaction's committed once it's written to the log data is going to be forced uh to disk to get for reliability uh there's a possibility of using nonvolatile Ram or Flash or whatever to make this faster because we can put things into non-volatile RAM maybe maybe more quickly than we can write it to the dis so perhaps the EnV Ram can serve as the head of our log um and alth the file system may not be updated right away the data is going to be in the log now the question here is does the log negate the performance benefits of a buffer cache uh and the answer is it depends um it depends on what you're logging not everybody uh not all versions of journaling file systems we'll talk about that in a moment write all the data to the log first and then back to the file system okay um so let's just uh let's go forward a few more here before um I answer that last question in the in the chat here and then maybe I'll answer it for you hold on one second okay so the difference by the way between a log structured and a journal file system is in a log structured file system uh all the data is only in the log it doesn't even go to the file system whereas in journaled file system the log is really just helping us get reliability okay and um when do we start logging well as soon as we've started up the file system we start the logging okay all right now um maybe I will uh just give you a little bit of a preview here the question that's in the chat which I hadn't answered yet is if not all actions have been completed and you crash how do you figure out which have and haven't uh been completed and let me let's just hold that question and see if this gets answered okay so we're going to focus in the next several slides on something called a journaling file system where we don't modify the data structures on the disk directly uh right away we write updates in as a transaction into the log kind of typically called a journal or an intention list and um then when we commit then we're going to have the potential to put them into the file system okay once changes are in the log they can be safely applied uh to the file system modifying IE pointers directory mappings Etc and the question that's uh in the chat here about well do we have to have all of our operations be idempotent to make this work the answer is no and we'll see how this works in just a second so well some of them need to be idempotent but um let's see if this answers your question so garbage collection is uh going to be a possibility here so once we've actually applied things out of the log successfully into the file system then we can remove things from the log okay now Linux essentially took the original fast file system called it EXT2 uh and then they added a journal to it to get ext3 so ext3 is really just like a fast file system Linux Style uh with a journal okay and there are a bunch of options that Linux gives you about whether for instance to write all the data to the to the uh log first and then to the file system uh and that double writing surprisingly enough uh doesn't always hurt you from a performance standpoint because the log remember is is sequential and so it's very fast so a lot of other examples of journaling file systems NTFS Apple HFS plus Linux xfs JFS C xt4 there's bunch of options here okay so let's create a file but this no journaling yet so think of this is like Fast file system or ext3 so we can see where we're going with this so when you create a brand new file and write some data there's a bunch of independent things that have to happen so first thing is you got to find some free data blocks so here's an example let's call this yellow thing a single free data block have to find ourselves a free inode entry so on in the iode table find a an insertion point in the directory so maybe there's some blocks on the directory we're going to change excuse me all right and then we're going to link things together so we're going to write uh the map which basically says uh Mark which blocks are in use okay we're going to um write the iode entry to the blocks we're going to write the directory entry to point to the iode all right and when we're done uh now we've got a a new um pointer in a directory this a mapping between a name and an i number that points to an iode which we've allocated which points to a dis block and now so notice all these different individual pieces uh and and the free space update uh have all happened just to create a file and write to it and if we sort of partially do this and we crash then we're going to end up with dangling blocks uh like for instance if we didn't successfully write the directory entry then we could have an note entry pointing to a data block and it's not in any directory and it's effectively lost okay so let's see how we could add a log to this or a journal so if you notice here um we're going to put this log in some non-volatile storage flash or on disk for instance is the simplest thing it's going to have a head and a tail so the head is the point at which we write the tail is the point at which we read okay and let's go through and see what happens when we write our new file so we're going to first find a free data block and notice that I found the block but what I'm going to do is I'm actually going to find my free I note entry going to find my directory insertion point but I'm not going to actually do anything instead what I'm going to do is I'm going to write a start transaction in the log I'm going to uh write the free space map I'm going to uh write the inode entry uh pointing at the uh you know which where it's supposed to go so I'm going to excuse me write an iode entry here without actually writing the disk and then I'm G to write a directory entry without actually writing it on disk okay and notice that all of these things are reversible because if I crash at any point up until now I haven't actually modified anything in the file system so the file system is going to look exactly like it did before I started this process okay now when I hit commit poof all of a sudden it's committed now think this through for a second notice there's no changes to the disk okay and yet the mere Act of writing commit to the log now makes that file committed and the reason is that the state of the file system is considered what's on disk Plus what's in the log okay and so if I crash at any point after the commit gets written in there what I'm going to do is I'm going to scan the log and at that point I can apply the updates to the file system and things are going to look okay and I can keep crashing so these are idempotent this was a question earlier because uh the um the log has basically been choosing blocks for us but we can keep overwriting the same block over and over again with the same data and it's not going to matter and so I can keep trying until I eventually get past the commit at which point the file system will actually be updated to reflect this change so the mere Act of writing the commit in the log means that that file has been written when it with its new data okay so after commit we can replay the transaction like I said supposing we don't crash we can replay the transaction by just writing stuff on the disk eventually copying everything there and once it's copied then I can start moving the tail see how I'm applying stuff and if I get past the commit at that point then I can throw out everything that's in the log okay now here's a good question in the in the um chat here so what about reads do they have to scan the log for changes that haven't been flushed yet no this is where the block cache comes into play right so the block cache has the most up-to-date state of the blocks uh as as reflected by the total state of the file system including what's on the log and what's on disk and so the block cache uh since the block cache filters the reads and writes from the user uh it basically makes everything fast regardless of whether it's actually only in the log or if it's on disk okay so the block cache is important aspect of making things fast here now uh the question here you can't flush until you commit uh that's correct so this is again right ahead uh logging here well right ahead logging says that you have to get the log values on the in the log before you hit commit once they're in the log then you can flush things out to dis so yes you have to get them to the log first and then they can be flushed onto uh onto the file system on the disk well if the cach is full and it's a really large right then uh then you have to make sure that uh you've committed first okay um so there's a lot of uh potentially complicated questions about scheduling here and when you're allowed to schedule things Etc um I don't want to go into it too much right now but what I will say is you can imagine that uh the file system knows when it's in TR when it might be in trouble by allowing too many rights uh before the log has been cleared and all it has to do is put the clients to sleep uh until things have been properly flushed and then it can wake the clients up okay and so this is you just have to keep track of what the current state is so that you always have this right ahead logging property okay now um once we've committed everything then we can just throw out the log and the tail has moved here all right um what's the size of the log that's changeable so um depends on how much data you want to have now notice by the way that what we've got in this particular log actually didn't log the data necessarily so we could write our data to uh the disk and it's only the metadata that's logged that's one of the modes um that's one of the modes that basically uh the Linux file system has in another mode is one in which the data first goes into the log and then goes back out on disk okay now uh if the system crashes after commit but before we've fully applied everything that's okay because we can just keep restarting because we don't remove things from the log until we've actually gotten past commit uh with the thing not crashing and everything pushed out to dis that's a point at which we do a single atomic move of the tail which uh throws out this particular entry so we don't really need to know exactly which changes are have been applied we just take wherever the tail was you know so the tail might be here and uh we're trying to apply and we keep crashing over and over again well we just we can restart um and it's back after we get past the commit that we can then uh throw that log entry out okay this particular version of this the changes are empit there are other ways you can do things but we're going to leave it this way for now okay now um let's look at this uh and situation here where we started that process and we crashed um and notice this is what it looks like after crash so maybe we found our blocks and we started to write our updates we didn't get a commit record in here then all we do is we just uh detect at this point that we've crashed and all we have to do is we just throw everything out that hasn't been uh committed yet and we're we're good to go all right and all of this stuff can be thrown out I didn't quite have a a good um example here but you can basically throw out things that you haven't touched and um transactions without commit records that are ignored from that point on all right um the other thing is if uh we recover and we have complete transactions we scan the log we find complete start commit uh um examples and at that point we can just redo as usual and um in the process we update our uh block cache and then once we got past that part of the boot then everything works as normal all right so I've just given you the start but I I hope it gave you the idea what's going on so why do we go to all this trouble the answer is that updates become Atomic even if we crash okay so we either get all it's either a applied entirely or not at all and so all of these physical operations and there potentially many of them are single logical unit okay we get an atomic update um now you might ask isn't this expensive well it is expensive if we are in the mode where we're writing all the data twice except the log is typically um sequential on the dis and so the cost of writing to disk in the log is actually much faster than trying to write all of the different pieces throughout the file system so that's actually faster than you might think and there are some circumstances where this right to the log with your data and then put it into the file system can actually give you some boost in performance under some circumstances okay especially when you've got a bunch of random rights then you can get them out on the dis quickly um so modern file systems give you an option to to do metadata updates only in the log and this is where you're going to record the file system data structures like Dory entries iode use and and Etc and what happens in the worst case where you crash but you haven't flushed your data is now you get a file with garbage in it but you don't lose a bunch of files okay and so that's a trade-off between uh reliability and performance it gives you sort of an option uh to do slightly less than uh full atomicity when it comes to the data itself okay now um a full buffer cache is is uh could be an example yes where your right call comes back and not everything's been written that's correct okay all right now let's talk briefly about something that I wanted to remind everybody of are there any more questions okay and so by the way ext3 is the uh is the version of the Linux EXT2 file system fast file system that's got a journal in it and all they did was they took that file system and they had a special file that serves as the journal so all right so I wanted to remind everybody because we've had some people that I think have forgotten a little bit about the collaboration Pro uh policy for cs162 so you got to be careful okay we we do not want uh people importing parts of code from other people okay so things that are okay here are for instance explaining a concept to somebody in another group could be okay but don't explain exactly how to do something if it's a concept that for instance I talk about in class that's a perfectly okay thing to talk about okay um discussing algorithms or strategies at a high level is probably okay okay discussing de debugging approaches like um sort of using you know how do you produce uh print FS that you can go through easily to find out what's going on or you know what is your overall structure for testing those kind of things are okay uh searching online for generic algorithms like hashtables that's okay all right things that are not okay are things that are likely to get caught by our uh catch by uh the code that we run to to uh catch collaboration cases all right so sharing code or test cases explicitly with group or write out uh copying or reading another group's code so you shouldn't be even looking at other people's code okay or their test cases uh copying or reading online code or test cases from prior years that's not okay okay so if if you're straying into specifics about a particular uh project or homework you're you're probably in the Red Zone okay helping somebody in another group to debug their code that's also not okay um we did have a good example in a past term uh where somebody sat down with a group that was having trouble and they were helping him debug and um but they this person sat with them for so long that as they kept kind of incrementally changing their code the code ended up with a structure that looked so much like uh this uh the helpers group's code that the two groups uh were flagged for over collaboration and that that's a problem okay so uh um be very careful not to do that okay because we want you to be all doing your own personal work on homeworks and uh exams of course and your own groups work in group work okay so we compare all the project submissions against prior prior year submissions online Solutions Etc and we will take actions uh against offenders that um have sort of uh violated this code okay and you can take a look on um on the homepage we have uh discussion of this in more detail um so and don't put a friend in a bad position by asking for help that they shouldn't give you okay we've had in past terms we've had people that have pleaded with friends of theirs uh until the person just gave them some code to get them to leave them alone and um that ended up uh ending not well for both of the people so just uh try to do your own work okay and I remind you this because we we have caught uh what appear to be a number of collaboration cases and uh we've only gone through some some of the things so try to try to not put yourself in a bad position okay all right now that we have stunned everybody into silence Let's uh let's talk about some real topics again here so I'm gonna assume that everybody will be very careful okay so let me um take this idea of logging that we just had in journaling and take it to its extreme okay so one extreme is called the log structured file system which is an actual research file system on the Sprite operating system that was uh I have a paper up in the resources page you can take a look at it and in this case it's like what I just told you with the journal but there is no file system underneath so the log is the storage okay so the log is one continuous sequence of blocks that wraps around the whole dis inodes get put into the log when they're changed uh data is put into the log Etc and everything's just in the log okay so here's an example where we create two new files deer one file one deer two file two um and write new data for the files and here's the log and notice this is a Sprite log structure file system and notice what happens is that there were some blocks and stuff in the files uh there were some parts of the file system in the log prior to this picture but we're writing file one and what we do is we write some data which goes into the log and then we uh we change the iode for the directory that also goes into the log okay and then we write some data for the second file and for the directory and all that stuff goes into log and ultim ultimately uh if the since the inodes for deer 1 and deer 2 have changed then we're going to put uh the updated iodes for say the root file system also in the log and when all is said and done all of our data is in the log it's just in the in the order in which it was written all right and um we never take it out of the log just stays in the log and then if we overwrite say uh part of file one what'll happen is we'll put the new overwritten data and then we'll put a new iode which links to it and so on and at some point the data is uh going to be kind of obsolete in parts of the log there'll be a bunch of holes and at that point we're going to do some garbage collection but up until that point the log is the file system okay and it's kind of like git yep there's a little bit of of that aspect so here's an example of the Unix file Sy fast file system where when we write data we're actually um writing the data on the Block groups where it was intended to be close to the inodes um for that directory so here's an iode for the directory we write some directory data here's an iode for the file we write some file data it's in a specific spot on the disk it's been laid out in a way to try to make it fast and if you notice the data here is laid out to be fast for reading but the rights go all over whereas the data in the log structure file system is made to be very fast for writes but reads will suffer and the whole if you read that paper which is which is a classic what you'll see as a justification for this is right bandwidth is often at premium so you're going to make it the rights go really fast and um you're going to rely on the Block cach to be big enough to give you really fast read performance okay and um so the other interesting aspect of this is as as we've been talking about transactions is that um this fact that things are structured as a log means that we can really easily undo things if we've got a failure okay and so part of what's in here are commit records and so if we crash in the middle of writing then we just go back to an earlier part of the log and our file system is good to go without any changes so the log structured file system kind of has built into it this idea of journaling because the log is the file system okay now um so the logs what's recorded on disk uh file system operations to figure out what's going on kind of logically replay the log to figure that out and put things in the block cache to make it fast okay everything gets written in the log um large and Portion uh large important portions of the log is cached in memory which is how we get things to be fast and you do everything in bulk so the log is a collection of large segments on the discs that are uh completely uh sequential relative to each other to make things f fast and if you read the paper you'll see that rather than what I first told you where there's a single log that goes through the whole disc in fact there's a whole series of these big segments and they garbage collect in segments all right um and the way you get free space back is you got to garbage collect um all of the holes that are in the log after you've overwritten data and so there's a garbage collection process too that we won't go into for now all right now the reason I brought this up is one thing I promised you a couple of weeks ago but never did what about flash file systems okay how are they different from the fast file system and I wanted to remind you what flash is like so this is a um a camos transistor which you've uh probably seen in some of your early classes and the idea here is that um when this floating gate is uh High then uh we end up with essentially turning a switch on so that the data can flow through this switch and when this uh floating gate is low then the switch is turned off so without this extra gate we um or say no floating gate just the control gate we end up with a transistor the way that flash works is uh don't want to say that yet the way that flash works is we actually trap electrons in this floating gate uh which has oxide on either side of it and the result of trapping the the electrons in there give us enough of a difference that we can detect and that's a way that we can store a one or a zero in here uh in distinguishing from the um non charge trapped State okay the thing that's funny about this is we can't write it once we've written it we cannot overwrite it until we erase it so if you remember um I talked about this a couple of weeks ago you can never overwrite Pages what you need to do is you need to erase Big Blocks of bits and then you keep them on a free list and you get these uh 4K byte pages that you use off the free list to build your file system with and then eventually um you garbage collect a big block and erase it again okay and so this is a little different from say a disc okay um and another thing that's important here is that these the way I write as I as I alluded to is I trap electrons on this floating gate now the way that that happens is I raise this word line so high that the electrons go zooming across the insulators and get on the floating gate and if I go even higher I can encourage them to go away and clear the gate off well that's a pretty uh harsh process and eventually electrons get trapped uh in the ins in the insulator and then this doesn't work as well and so the flash actually wears out and so anybody making a file system out of this has to be careful not to erase and overwrite too many times okay and uh yes we trap electrons to uh to store Reddit po post and CAD videos and um as I mentioned uh because we're trapping uh things in here this is a higher energy State it's technically it's heavier and so you can go look at where I talked about a few lectures ago uh the fact that a Kindle is technically heavier once you've put uh books on it okay now the the part that one of the parts that makes this easier is what's called The Flash translation layer which uh basically says that unlike a dis where we number all the sectors and then the system says I want you know sector 5,496 what happens in a Flash or SSD is there's actually a translation layer so when you ask for a particular number that goes through a translation layer and tells you which block on the flash is actually the current version of 5,226 and as you go through overwriting that from the uh operating system level the underlying flash translation level will keep changing which physical block there is okay okay and so that underlying flash transation layer automatically takes care of wear leveling and making sure we're not wearing out our bits but the question might be is there something we could do with the file system and make that work better okay and um there's firmware that run on ssds and so on and so the question is can we take uh advantage of this information to do something with it and the answer is yes so the flash file system um the f2fs file system which is actually used on mobile devices like pixel 3 from Google it was originally from Samsung um is actually a file system that's been adapted to use the properties of flash um it assumes that this SSD interface which looks like a dis for all practical purposes has underneath it a a flash translation layer the fact that random reads are very fast they're as fast as sequential reads and that random wrs are essentially bad for flash storage and the reason is that if I randomly then I make it a little harder for uh for the underlying flash translation layer to erase Big Blocks because to erase a big block where you have a bunch of random blocks written you actually have to copy the data out of the blocks um onto some pristine ones and then you can erase and so that actually ends up wearing the flash out a little bit more if I do random rights okay and so we're going to minimize rights or updates and try to keep rights sequential and so what they do is they actually start with a log structured file system uh with Co and a copy on write file system made out of it um keeping wrs as sequential as possible and there's a a node translation table to help us keep things sequential and you can for more details you can actually uh check out paper in the reading session section as well um called the f2fs a new file system for flash storage okay um but just to show you a little bit uh the log in the flash file system which I'm showing you here is actually split into a whole bunch of segments and those segments are ones that get written a lot versus ones that aren't written as frequently and so they actually lay out a bunch of different logs to try to manage how how busy the file system area is um there's a translation table um inside the operating system in addition to the one that's on the SSD and they try to classify blocks as being written frequently and not okay and there's a checkpoint operation and so on I'm not going to go into great detail on this but I did want to mention some of these things so if you're curious you can take a look um for instance here is uh an index structure of iodes and if you look at the log structured file system what you see is that if I update a file file data I write that in the log then I've got to write the uh direct pointer block over again into the log then I got to write the indirect pointer and then I got to write the inode and then I got to write the inode maps and so on I got to write a whole bunch of blocks just because I changed some data in the log structured file system and that's because uh I never update in place in the log structured file system I work my way through uh by writing all of the change things into the log while this means that there's a lot more changes and so one of the things that they do in this f2fs is they actually use a second translation table to to uh translate so that the inode for instance at a higher level has a name for this block and that block is in a translation table okay and so they make some interesting modifications to log structured file system all right I'm not going to go into this in any more detail but I just wanted to give you some ideas of what you might go through to try to make things faster okay and to take advantage of the fact that you can do random reads but random wrs are uh expensive and wear the file system out okay all right now time to switch gears um unless there were any additional questions on log structured file systems or transactions or what have you maybe I'll pause for a second while everybody's digesting the thoughts here so in both of f log structured file system and in the f2fs files are just in the log right there is no file system there's no other file system underneath it so log structured file systems are good for rights can anybody answer why the log structured file system might be good for rights it's a good question right the log is sequential so therefore uh it on a dis it goes on the track rather than randomly writing all over and so doesn't matter what your rights are they all go right at one after another on a sequential set of tracks on the dis and so they're very fast because you're avoiding seek time in the uh f2fs the advantage is is a little bit different but you're sequentially writing a whole bunch of blocks so that um when you go back to overwrite them again the uh the log can be erased as a group of blocks can be erased and so it's it uh matches up with with the underlying um architecture of the system so the the log structured file system does lead potentially to fragmentation in the sense that you got a lot of holes in Old parts of the log and that's where Garbage Collection comes into play and so if you take a look at the papers you'll see that what really happens is the log as as time goes on the old parts of the log have more and more holes in them because you've overwritten data that is in those places and at some point you just take the data that's remaining you copy it to a new part of the log and then you uh reclaim everything that was in that old part of the log so it's a type of garbage collection all right good now so Switching gears um if you remember I think the first day I kind of said what's what's cool about operating systems is they are part of this huge worldclass uh system everything from little tiny devices tied into local networks to cars to uh phones to refrigerators and computers and up in through big machine rooms and the cloud and so on all are part of one huge system and um the when I when I think about um when I think about what I'm interacting with on a day-to-day basis I like to think about how the the things I do down at the small scale are actually utilizing resources spread throughout the globe okay and it's amazing when you think about it um sometimes when I think about the whole thing it's it's astounding to me that it all works somehow and sometimes it doesn't entirely work but it mostly works but the interesting question that comes to mind is sort of how do you get all of these things that are spread uh geographically and in domains of fast local connection but really slow longdistance connection Etc get them to all work together and so uh for the last few lectures um we're getting down to the last like five or six lectures here I'm going to talk a bit about um distributed systems and how they can all work together to do for instance distributed decision making which is a topic we're going to start today um and so to start that topic let's bring back some what it turns out to be very old terminology but I thought I'd make sure we were all on the same page here so a centralized system is one in which there's a central component a server of some sort that is uh performing all the major functions and you have a bunch of clients that are all talking to the server and that's typically called a client server model okay and um many of the things that you deal with with your cell phone for instance where the cell phone is one of the clients and something in the cloud is a server that's actually that's actually like a modern analog of this traditional client server uh situation here the question that immediately comes to mind with a centralized server is well how do you scale this I mean what happens if you've got not three clients but a 100 thousand or a million clients clearly one server can't do it okay and so you know we know that in the cloud there are many servers but the question might be how do you structure them to do something intelligent when you've got many components okay now a completely different model is what I like to call the peer-to-peer model with in which um every component in the peer-to-peer model is a peer of the other components so if you notice in this client server model we really had uh the server was kind of King and uh the clients were subjects or something like that whereas in the case of the peer-to-peer model we we have a whole bunch of peers that are all interacting with each other and uh you know you might ask the qu you know in the client server case it's pretty obvious who's responsible for what you get in the peerto peer model it becomes unclear okay but the peer-to-peer model is uh kind of a good starting point for if we want to try to make this server idea uh spread and handle a really high load so for instance maybe we could draw a box around a bunch of these guys working in peer-to-peer mode and treat that as a server okay um so what's the motivation for Distributing in that way rather than having a single client and you know you could come up with lots of reasons right why do people do anything well here maybe it's cheaper and easier to build lots of little simple computers rather than a huge server in the middle or maybe it's e easier to add power incrementally so what I mean by that is if I've got a good peer-to-peer model and I and I need more power I just add some more computers to it right and if things work then by adding a few more servers or whatever now I've got a more powerful system than I started with okay and I can do that incrementally um maybe users have complete control over some of their components so maybe that big peer-to-peer system I've got some that I own and yeah I'm going to help everybody else a bit but I have full control over my hardware and I can bring it back when I want and of course collaboration um is an obvious goal here because maybe by putting together a peer-to-peer model it's easier to collaborate um so the promise of these distributed systems is really that it's they're much more available because there's more components that are likely to be up uh it's better durability so maybe by copying my data to lots of different machines it's more likely it'll survive a crash and maybe there's more security because each piece is smaller and maybe easier to make secure okay now you should be questioning some of these statements here for a moment um the reality uh is typically different okay so this is Lesley Lamport uh he's he's done all sorts of really uh cool system stuff and we'll talk a little bit about um a couple of them uh in the next lecture and a half but uh what he liked to talk about is the fact that the reality behind a lot of distributed systems are actually disappointing so the availability is worse rather than better because it depends on every machine being up he's got a very famous quote uh which is a distributed system is one in which the failure of a computer you didn't know existed can re render your own computer unusable all right it could have worse reliability because you lose data if any machine crashes uh it could have worse security of course because anyone in the world can break into one component and if they're all tied together they've broken into everything so distributed systems have a high promise but you got to be really careful how you use them right coordination becomes very difficult so you got to coordinate multiple copies of shared State information and what would be easy in a centralized system because everybody's going through one Central Computer becomes a lot more difficult when you've got things distributed and of course trust security privacy denial service these are all words that you've heard a lot of but um many new variants of these problems arise as soon as we start Distributing so um can you trust other Machines of a distributed application enough to uh perform a protocol correctly I think there's a corollary of lamport's quote that I like to to think of which is a distributed system is one where you can't do work because some computer you didn't even know existed is successfully coordinating an attack on your system all right that's the standard DDOS so U what are some goals of this kind of system so you'd like transparency which is the ability of the system to mask its complexity remember earlier I said well the way we go from a server system to something that can handle lots of clients 100 thousand or a million is we put a bunch of things together but we draw a box around them and we make it transparently behave the same way as a single computer would okay so we don't have to know about the complexity so what are some transparencies we might come up with well one is location transparency where you don't have to know where resources are located pretty much anybody who's dealt with the cloud has uh understood what location transparency is like perhaps migration so that resources can move around maybe for better performance or better durability or what have you without us having to know uh that they've been moving maybe replication well perhaps I pay to make sure my data doesn't go away and so underneath the cover is a syst system transparently increases the number of copies or maybe it does eraser coding uh transparently in a way that I don't need to know about but makes my data much uh more durable um maybe I don't have to know how many users are out there so uh one of the things that has worked pretty well about the cloud is everybody's kind of interacting Point too between their phone and something out there uh without having to know how many other people are acting with something out there okay and so that level of concurrency uh works pretty well if you're just working uh one to one on something now if you're actually collaborating on something then that gets a little more tricky and so concurrency uh is is problematic under some circumstances parallelism so the system May speed up large jobs by splitting them into small pieces transparently without telling you fall tolerance okay that's kind of like what I said about replication maybe the system is going to hide the fact that things are going wrong um and do so in a way that you still make forward progress okay so transparency and collaboration require some way for different processors to communicate with one another and of course that's going to lead to the need for networks and so on and we're going to talk about networks um in more detail in a lecture or two but for now um I want to talk about uh this idea of decision making being spread across a bunch of nodes because that's kind of the beginnings of how we do this particular thing so um the question about is it a goal for us to not be able to tell where resources are located uh I I would say yes and no I think it's better to think of it as I don't want to know have to know where the Lo where the um resources are unless I care right I'd like the system to transparently adapt them as long as it's within the uh boundaries of my policies and my goals for privacy and what have you I'd like to system to deal with that without me having to deal with it and if I care then another goal would be able to selectively break the transparency to meet some goal for why I wanted to care but then the rest of the transparencies are still there um so it's really the desire to not have to know um and a really important transparency by the way is what happens when a machine crashes that's storing some of your data you don't want to have to somehow go log into your um application and change an IP address uh to point to a different server just because some surger crashed you'd like that process to be transparent okay so think of these uh goals as things that I would like to be transparent unless I care okay um perhaps you think of it as opacity but I think it's really transparency it's uh masking complexity behind Okay so I don't have to know um so how do empties communicate well some sort of protocol so clearly there's going to be um communication through a uh a network of some sort of messages and a protocol is really an agreement on how to communicate including things like syntax how does a communication uh structured and uh specified and semantics about what a communication means so actions taken where uh transmitting receiving when a timer expires Etc okay um the uh so I'm noticing on the chat here so masking equals transparency so that is a funny uh a funny use of terminology perhaps but um You' like things to um be uh invisible to you happening under the covers that's where the word transparent so sort of the you see the functionality uh without having to know what's happening underneath and so that's that's often called a transparency I realize it seems it seems a little little strange but that is a use of that terminology um so for instance um Protocols are often described uh by a state machine on either side so here's an example where I've got two State machines and part of what the protocol is doing is it's tracking the states on both sides so that both sides have the same notion of the state of the world and um the protocol is responsible for making sure that that state is maintained so that if both sides suppose this is separate sides of the world and the state machines are being transparently replicated that's again the use of the word transparent then um then I can act on the current state of the system here say at Berkeley uh in Beijing and I'm and I have confidence that I'm working on the same information as the other side um and so you usually there's some stable storage that's part of this state replication um you could even think of uh a simple example might be that these are two versions of the same file system there's a transparent protocol and the states represent the state of the file system and it's keeping things in sync okay so that's another example of uh of a good protocol okay and so um you know we want among other things stability in the face of failure so even when parts of the system are failing or the storage falls apart in one place but it's there's still storage in other places we'd like the state machine replication to uh continue to work properly it may be that endpoints uh are selectively failing but if I were to vote let's say among the states of all the different uh participants so suppose I've got three participants and one of them fails a voting process could uh maybe be employed to figure out what the real state of the system actually is and we'll talk about some of this in a moment so examples of protocols in human interaction I mean I thought I'd put this down just for the heck of it you know you got you got a phone you pick up the phone call somebody you listen for the dial tone okay so maybe you don't do that on a cell phone but see you have service you uh you dial the number you hear ringing and the colag says hello you say hi it's John or U hi it's me um that's my favorite kind of goofy introduction it's like well what's that about who's who's me um but then uh you kind of say uh Hey do you think blah blah blah blah blah and they say yeah blah blah BL blah blah and you say goodbye and they say goodbye and you hang up now uh this is probably a conversation that you had um late at night sometimes including the blah blah blah blah I know I've had a few of them myself but really you're thinking about a protocol because there's a protocol which goes from ringing to answering at the other side uh to responding so the answer comes back and now you know that that connection's been uh set up uh or the caller says something the collie responds with a response and then there's some process for hanging up and so this protocol of synchronizing the states between the person that made the call and the other person is is a human interactive version of uh what we would like to do in our protocols okay and um the problem is you know there's many pieces of Hardware this has been our standard issue throughout this whole term where we talked about the fact that Hardware is vastly different uh at you know at the io level and so how do we deal with that and so if you look here uh when we're talking about communicating we have a bunch of applications at one level we have a bunch of ways that things are communicating you know maybe a coax or cable or fiber optics or whatever and the question is um the the many different applications have to communicate over a bunch of different media and um there many different styles and what do you do well you don't want to make a point-to-point uh application where Skype talks uh one way through coaxial and another through fiber optic and another through Wireless and so on because you're going to very rapidly get nsquared uh blow up in complexity right so for instance we added some new application like HTTP it shouldn't be the case that we have to write a new communication module for every type of thing that we're going to communicate okay communicate to and similarly if we come up with a new uh way to communicate like a packet radio or something we don't want to have to do an N squared uh communication between every application and every new communication media you know this this looks silly when you think about it but clearly there's a level of abstraction kind of like our device drivers uh that needs to be employed here and if you takeen uh you know if you take a networking you certainly know what that's about right so how does the internet avoid this well we put a layering in here okay we put intermediate layering and um a set of abstractions provi providing Network functionality and Technologies and so as a result new application uh that we add on here like HTTP really has to figure out how to communicate with this intermediate layer which is often called the narrow waist of the Internet Protocol um looks kind of like an hourglass um and you know if I put some new communication technology um I B basically have to figure out how to match the intermediate layers to communication technology and I've just made my problem much simpler because of abstraction okay and of course this is the typical hourglass that everybody sees when they take an an IP class networking class um where IP is the protocol of choice at the narrow layers it wasn't always that way but it's became become that way and now all of the layers above have to just send IP packets and all the layers Below have to uh communicate IP between different sites and if we do that then we basically have the internet okay and um it's astonishing how well this has worked uh to to basically connect a whole bunch of devices and computers and storage and everything simply by standardizing uh IP in the middle here okay um so what are the implications of this hourglass so there's a single internet layer module that's the IP protocol allows arbitrary networks to operate uh any technology that supports IP can exchange packets it allows applications to function on all networks so applications that can run on IP can use any network um it supports simultaneous Innovations above and below so um you know you can do all sorts of stuff to the above the application layer uh you can do all sorts of stuff below the the physical layers you can have many different physical layers but changing IP itself has turned out to be very challenging so um there's a funny story about IPv6 which has been the you know the next IP protocol for the last 20 years um only in the last I would say five years has it really taken a hold and started to become a reasonable protocol um it's been very hard to swap out ipv4 which is the traditional one with IPv6 because it had been so uh embedded in the world um so some drawbacks however of layering are the kind all of the drawbacks that you could imagine especially now that you've been through 162 so you know layer n may end up duplicating stuff that layer n minus one is doing or layers need a bunch of the same information so you end up communicating a bunch of information up and down the layers and you got a bunch of memory copies and it's expensive um layering may hurt performance well that you know the any API could potentially be made Faster by flattening the API out uh but then again you know if you do this the wrong way you end up with this N squared communic or N squared pattern again and that's not a good idea right so there's this tradeoff between performance and uh and apis and and layering and it turns out that without IP that's been an extremely powerful tradeoff okay now um the what I'd like to talk about is uh the end to end argument um there was a hugely influential paper which again is on the resources page um by sulzer reeden Clark from 1984 um so I realize that's ancient history now but it's one of these papers it still has uh some very important philosophy uh in it that I think I want to make sure everybody gets here so it's the some would call it the sacred text of the internet um there's been endless de debate sorry talking too long endless disputes about what it actually means um Everybody cites it as supporting their position um you know uh you could imagine that that's true of pretty much any good document uh that lots of people read um they'll get into philosophical arguments about it um the message however is pretty simple which is that some type YP of network functionality can only be correctly implemented from end to end uh and things like reliability security Etc are examples of such okay and because of this the end hosts can basically satisfy the requirements without the network help and therefore um and they must do it anyway and therefore you could imagine that the network didn't have to do that okay so the the way that this paper ends if you go to read this is basically that you don't have to go out of your way to implement stuff in the network because you got to do it at the endpoints anyway all right and the simplest example here um that they give which I think is very uh telling is the idea of you got two hosts and host a has a file they want to send to host B and of course you've got uh applications for the file transfer you've got the operating system you got networks Etc all of these are parts of that and you might ask yourself well how do I transmit well um you know the application reads it off the disk uh it sends it to the operating system which then you know sends it out of a socket um which then comes up the operating system the other side um goes into the application which then writes it to the disk and the question is how do you make that reliable well one option is you make everything reliable okay so you make it 100% reliable that you load the file off the dis and then 100% reliable that uh things get trans transfer from the application to the OS so that transfer might not be so bad but then you got to somehow uh make sure that when it goes across the network um every link that's in the middle so if we're transmitting from Berkeley to Beijing uh there's a whole bunch of other things there's transatlantic cables you know there's a bunch of Hops at different levels and there's a lot of uh detail in this link that we're not talking about right now and we'd have to make sure that every link was 100% reliable okay and that way we compen everything together and we get 100% transfer okay except it never works that way right it's very hard to make something 100% reliable and furthermore it's still possible that um you missed something and uh one of the things that is uh is interesting about that paper is they relate a story from uh 1984 in which uh they were transmitting um copies of the kernel source code from one host to another and it was only going across a few buildings or whatever but there were a lot of hops in between and they were carefully check summing and catching every hop along the way to try to make sure that this was never screwed up except what they didn't realize with the was that in some of the routers along the way um even though each of the links were carefully check summed and made to be reliable the routers actually had a bug in them that would uh I think it would transpose bits every million bytes that it transmitted in memory because there was a bug in the source code of the router and as a result even though they check summed everything along the way the data got slowly corrupted and the uh the colel had been transferred back and forth across these links a coule couple of times many times and as a result the data was slowly getting corrupted okay we used to call that bit rot all right and it was totally unexpected and it uh things got so corrupted they had to pull things back off of tape in order to fix it okay so this idea of making things uh reliable by fixing everything in the middle is uh not only very hard it might not be the right thing so what's the other option is you take it from uh point a and you transmit it as well as you can to point B and then you check at the end you say well did I uh did I get the file that I was expected and so I compute a a hash or a check sum at one end I send it to the other I check it out and either I've got the file or I don't and if I don't then I can retransmit okay and so what's good about this end to endend approach is it actually makes up for all sorts of problems in the middle by catching uh bad transmission okay now of course what's pointed out in the paper is uh if you've got a A 1 kilobyte File versus a versus a you know gigabyte file the problem is the more data you're transmitting so the gigabyte file is more likely to fail in the middle than the one kiloby file and so if you have a really large file and you wait until the very end before you check Summit you're going to have a lot of failures before you succeed and in fact it may take a very long time and so that's why you want to break things into chunks and uh sort of individually check and add but the point of this uh example is that if things have to be done at the end points then maybe you don't need to do them as carefully in the middle as you might otherwise okay and and then as a result any reliability you might do in the middle is really for per improving performance okay now um so the second option is basically uh saying well here's the check sum of what I got it goes back and as a result um you pull the file off the dis and this application the original one checks it and sees whether uh you're good to go okay now um solution one as I said was incomplete because if the memory is corrupted the receiver has to do the check anyway solution two is complete because you you had to do it anyway and so um is there any need to implement reliability at all at the lower layers okay and the end to- endend argument by the way if you know anything about the history of the internet is kind of what was used to justify the um the structure of the basic internet as it is right now which is a datagram service we'll talk more about that um in a lecture or two where packets of of small size are sent across and they either make it or they don't but um uh we don't worry about that because we're checking everything at the end to end okay and so this paper and the end to end uh philosophy in general was kind of the reason the internet's the way it is now um it could be more efficient though to do something okay so as I mentioned yes we could just send the the uh the data to the other side and hope it gets there and retransmitted if it doesn't but at some point that might be too expensive to keep retransmitting if I had a really bad Link in the middle and so there's a performance reason for improving things in the middle but there isn't a functionality need to improve things in the middle and so this discussion leads to a trade-off of about how much work do you want to do in the middle okay so implementing complex functionality in the network doesn't reduce the host implementation complexity because you still got to do it and it does increase the network complexity probably gives you delay and overhead on every application even if they don't need it so this is kind of arguing that maybe you don't need to do something in the middle if you have to do it at the ends okay but implementing things in the network can enhance performance in some cases like very lossy links now uh what's interesting is a consern conservative interpretation of the end to end argument just like there's always conservative and liberal interpretations of pretty much anything could say well don't bother implementing it at all at the lower level unless it can be completely implemented at that level and doesn't need to be in the end points um or unless you re actually relieve burden from the host don't bother a modern interpretation or a moderate I like to think of moderate as well is basically think twice before implementing something in the network if the host can do it correctly then um implement it in the lower layers only if it's going to be a performance enhancement uh or has a good justification and only do it if it doesn't impose burden that uh on apps that don't need it okay and this is the interpretation that I always use and that I suggest in this class and you might ask well is this still valid uh and there are some instances where this particular monitor interpretation is in fact uh not even quite enough okay which is what about denial of service so somebody is going to attack a communication stream from outside there might actually be a pretty good argument for um putting firewalls and check sums and everything on intermediate links to basically prevent the denial of service so in that instance uh even though the end to end communication still has to happen you're enhancing the overall path in the middle by putting functionality in there or privacy all right if I want to prevent privacy putting firewalls in the middle Mak sense okay or maybe there's things that have to be done in the network so certain routing protocols which pick paths from point A to point B have to be done in the network they can't really be done too well end to end all right so how do you actually program a distributed application so this is going to be our topic for next time you need to synchronize multiple threads running on different machines uh there's no shared memory there's no test and set so all of the stuff that we talked about earlier in the term really isn't quite available to you in this simple view of the world which is a bunch of messages I send from one thing and I receive on the other so there's one abstraction over the network um it's already Atomic so no receiver gets a portion of the message because typically we check some things and so if a bad message goes through we stop uh we throw it out and retransmit so the interface is sort of like a mailbox where the sender directs a message at a receiver's mailbox as a temporary holding area at the destination um and we have the idea of a send of a message to the mailbox and a receive which is blocking often to wait for a message to show up now what we're going to do next lecture is we're going to say can we take this basic idea and can we build something interesting on top of it that will allow us to build these distributed applications will allow us to do um to synchronize State machines amongst uh multiple machines and ultimately lets us do pretty interesting uh distributed peer-to-peer style applications so that'll be for next time so in conclusion um I brought back this idea of the ilities okay availability is how often is the resource available durability how often is it preserved against faults reliability how often is the resource performing correctly we talked about preserving the bits so I like to think of eraser codes AR raid as preserving the bits copy on write is about preserving the Integrity not the bits so with by copy on write I make a bunch of changes that are new by not overwriting anything but rather sort of using pointers to the old data that's copy on write and that allows us to uh basically preserve the Integrity of the old data even while I'm changing it uh we talked talked about how logs can improve reliability um we talked about Journal file systems such as ext3 and NTFS as similar and in general we talked about transactions over a log as a general solution um and hopefully uh the examples that I gave there worked out well we talked started talking about protocols between parties uh that will help us build distributed applications we spent some time with the end to end argument which will hopefully uh inform us as we go forward and next time we'll start talking about distributed decision making such as two-phase commit um didn't quite get there this time but we'll definitely do that next time so I'm going to say goodbye to everybody I'm sorry for going over I guess I've been doing that a lot this term my apologies but I hope you have a good evening and we will see you on Wednesday